STM32H7配置SPI:一步一步指南

STM32 H7 使用SPI&DMA小结

前段时间项目中要用到单片机(stm32h750)和dsp之间大量数据交互(利用spi,h750做主,dsp做从)。要保证实时性,为了不阻塞到其他线程,所以还是考虑使用DMA方式传输来解放CPU。期间也是遇到了一点小坑!!(个人见解,仅供参考)

SPI

SPI:3线同步串行总线(sck,mosi,miso)+ 片选信号(cs)cs一般由软件控制,开始传输时cs拉低,结束时拉高。hal库初始化spi如下图:

这里配置需要注意以下几点:

  1. spi的主从设置,从设备是需要主设备给出时钟信号后才可以进行数据的接发。
  2. 一次传输数据的位数、spi工作方式(图中是方式三)、高低位先行,都需要和通信方协调一致。
  3. 传输速率的设置,这个是需要看当前D2域中对应的AHBx总线的频率再进行分频计算
  4. 调用HAL_SPI_Init()进行配置中会默认调用HAL_SPI_MspInit(hspi)相当于是初始化对应外设的GPIO一般在stm32h7xx_hal_msp.c中自行编写 如下图:
    至此spi相应配置基本完成。

DMA

在这里简单介绍下DMA,就拿SPI中断传输和SPI DMA中断方式传输来说。
SPI中断:因为是硬件SPI所以对于CPU来说只需要把要传输的数据放到SPI总线的传输FIFO里即可,传输的过程就不需要CPU去操心。

SPI DMA:由DMA把指定地址的数据拷贝到SPI总线传输的FIFO(不需要cpu干预,期间cpu可以处理其他线程)。传输完成后cpu需要清除对应中断标志位再进入传输完成的callback函数继续后续处理即可。

可以看的出来如果程序里需要实时处理的线程不多且传输数据量不大,那么SPI中断方式完全是可以胜任的。但是如果SPI交互的数据量比较大且程序线程比较复杂时,使用DMA方式传输更便于CPU对线程的切换。

这里踩了第一个坑:
原先我也是用的SPI中断方式进行和DSP数据通信,便于测试写了两个接口函数test_read()和test_write()分别调用HAL库中的HAL_SPI_Receive_IT()和HAL_SPI_Transmit_IT(),(因为使用的这片DSP不支持全双工通信)。
test_read():发送数据量大概五百字节左右,接收数据量六千字节左右。没有丢包是可以正常使用。
test_write():发送数据量大概四千个字节左右,接收数据量五百字节左右。失败,逻辑分析仪抓包发现 发送丢包严重,DSP核验不通过,接收数据为空。
中间苦恼了一段时间。后来把需要发送的四千多个字节分段发送,发现丢包情况明显有变化。最后确定HAL库的HAL_SPI_Transmit_IT()中断发送函数一次最大传输字节数在1k以内比较稳定(最大1k),而HAL_SPI_Receive_IT()接收函数则没有这个限制。

确保通信稳定后便一步到位,修改成DMA方式通信,使用HAL_SPI_Transmit_DMA(),HAL_SPI_Receive_DMA。
HAL_SPI_Transmit_DMA()函数一次发送的数据量也没有1k字节的数据限制
DMA配置如下图:


这里配置需要注意以下几点:

  1. DMA的时钟使能一定要在配置前开。
  2. Cortex M7系列单片机外设使用DMA是不需要向M4那样看表去一一对应的,可以自由分配通道不冲突即可。
  3. 传输的数据宽度要和SPI的数据位数相对应(word在这里是32位,刚好和前面spi配置的32位对应。如果spi是8位传输,这里就改为BYTE)
  4. 记得要把配置好的DMA接收和发送结构体关联到对应的SPI收发。__HAL_LINKDMA()
  5. 使用DMA中断方式,最后需要分别打开发送和接收消息通道的中断以及SPI的中断,编写中断服务函数。

这里踩了第二个坑:
首先使用SPI_DMA中断方式,HAL库中发送,接收的函数是
HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
这里外设的地址不需要我们去考虑,只需要给到存数据的内存地址即可。
注意的是M7系列把内存分为了以下几块:DTCM、ITCM、AXI SRAM、SRAM1、SRAM2、SRAM3、SRAM4。分别对应内存地址可自行查看手册。
其中DTCM和ITCM不支持DMA1、DMA2,后面的几块内存都是支持DMA1和DMA2的。这里推荐使用SRAM4,避免不需要的麻烦要关闭使用DMA内存块的MPU和cache

到这里配置SPI DMA中断收发已经完成。这里需要再说一下SPI DMA中断收发的过程(也是困扰我一段时间的)
单次发:先调用HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size),然后会产生DMA发送中断void DMA1_Stream1_IRQHandler(void),再然后会产生SP中断void SPI2_IRQHandler()发送完后会进入发送完成回调函数
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)(可以把dma发送再写进这里这样也可以实现循环发送)进入该回调函数就可以认为本次发送已经完成,可以做相应的处理了。
单次收:HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size) ->
void DMA1_Stream0_IRQHandler(void)->void SPI2_IRQHandler()->void HAL_SPI_RxCpltCallback()
欢迎讨论!

物联沃分享整理
物联沃-IOTWORD物联网 » STM32H7配置SPI:一步一步指南

发表评论