STM32F4通过SPI+DMA与MCP2517FD通信的驱动适配问题
问题
我正采用STM32F4(Cortex-M)的SPI HAL库对接CAN-FD控制器MCP2517FD,使用的驱动为SDibla的Cortex-M4-MCP2517FD_Driver。在阻塞模式下,通过以下函数可正常实现CAN-FD传输:
int8_t DRV_SPI_TransferData(uint8_t *SpiTxData, uint8_t *SpiRxData, uint16_t spiTransferSize) { HAL_StatusTypeDef status; GPIO_PinWrite(CS_GPIO, CS_PIN, 0U); status = HAL_SPI_TransmitReceive(&hspi3, SpiTxData, SpiRxData, spiTransferSize, 10); //blocking data-transfer GPIO_PinWrite(CS_GPIO, CS_PIN, 1U); return (int8_t) status; }
现在我想将DRV_SPI_TransferData函数中的阻塞传输接口HAL_SPI_TransmitReceive(&hspi3, SpiTxData, SpiRxData, spiTransferSize, 10);替换为非阻塞DMA传输接口HAL_SPI_TransmitReceive_DMA(&hspi3, SpiTxData, SpiRxData, spiTransferSize);,请问是否需要对控制器驱动代码进行修改或有其他注意事项?
回答
要改的地方不少,核心是适配DMA的异步特性,具体注意事项如下:
CS引脚控制逻辑必须调整
阻塞模式下CS拉低、传输、拉高是同步执行,但DMA调用后立刻返回,传输在后台进行。如果直接在HAL_SPI_TransmitReceive_DMA后拉高CS,会直接打断传输导致数据错误。正确做法是把CS拉高操作放到HAL_SPI_TxRxCpltCallback(SPI DMA收发完成回调)里。同时原函数的返回值逻辑要改——DMA启动后无法立刻得到传输结果,得换成异步通知方式,比如设置全局标志位、触发上层回调。驱动同步逻辑要重构
原驱动是基于阻塞SPI写的,上层逻辑默认SPI调用完成后数据就就绪。换成DMA后,必须加同步保护:- 加全局状态标志(比如
uint8_t spi_transfer_busy = 0),发起新传输前先检查标志,防止并发操作冲突。 - 原有的寄存器读写、CAN帧收发函数不能再同步返回结果,要改成等待标志位或者回调触发的异步模式。
- 加全局状态标志(比如
DMA配置与内存合法性检查
- 确保SPI3的DMA收发通道已经正确配置(CubeMX里开启对应DMA,设置好优先级),避免和其他外设DMA资源冲突。
SpiTxData和SpiRxData不能用栈上临时变量——函数返回后栈内存会被释放,DMA访问会出问题,必须用全局变量、静态变量,或者保证传输完成前内存不被回收。
错误处理要补全
DMA模式下的传输错误(比如总线错误、超时)要在HAL_SPI_ErrorCallback里处理:拉高CS、重置忙碌标志、通知上层驱动出错,避免后续操作异常。函数返回值的意义要修改
HAL_SPI_TransmitReceive_DMA返回的status仅表示DMA是否成功启动,不是传输完成状态。原函数返回(int8_t)status的逻辑完全不适用,要么改成返回启动结果,要么改成void类型,用回调告知最终传输状态。
内容的提问来源于stack exchange,提问作者ImBr

