STM32F103C8T6 SPI DMA调用返回HAL_BUSY,中断模式正常求助
STM32F103C8T6 SPI DMA循环模式触发HAL_BUSY问题解决
问题场景
通过CubeMX配置STM32F103C8T6,实现SPI1(主机)与SPI2(从机)的回环通信,SPI2配置为DMA循环模式。运行时发现第二次DMA调用返回HAL_BUSY,但改用中断模式后程序正常运行。
DMA模式故障代码:
/* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); hal_status = HAL_SPI_Transmit_DMA(&hspi2, u8_SPI2_TxBuff, sizeofBuff); // 执行正常 HAL_Delay(1000); HAL_SPI_Receive(&hspi1,u8_SPI1_RxBuff,sizeofBuff,100); HAL_Delay(1000); hal_status = HAL_SPI_Receive_DMA(&hspi2, u8_SPI2_RxBuff, sizeofBuff); // 返回HAL_BUSY HAL_Delay(1000); HAL_SPI_Transmit(&hspi1,u8_SPI1_TxBuff,sizeofBuff,100); HAL_Delay(1000); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); /* USER CODE END 2 */
中断模式正常代码:
/* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); hal_status = HAL_SPI_Transmit_IT(&hspi2, u8_SPI2_TxBuff, sizeofBuff); HAL_Delay(1000); HAL_SPI_Receive(&hspi1,u8_SPI1_RxBuff,sizeofBuff,100); HAL_Delay(1000); hal_status = HAL_SPI_Receive_IT(&hspi2, u8_SPI2_RxBuff, sizeofBuff); HAL_Delay(1000); HAL_SPI_Transmit(&hspi1,u8_SPI1_TxBuff,sizeofBuff,100); HAL_Delay(1000); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); /* USER CODE END 2 */
问题原因
核心问题在于DMA循环模式的特性:
- 调用
HAL_SPI_Transmit_DMA开启循环模式后,DMA会持续循环执行传输,不会自动停止,SPI外设和对应DMA通道会一直处于忙碌状态。 - 你用
HAL_Delay等待1秒后直接发起新的DMA请求,此时SPI2的DMA通道仍在循环传输,外设未回到空闲状态,HAL库检测到忙状态后返回HAL_BUSY。 - 中断模式下,单次传输完成后会自动关闭相关中断,外设回到空闲状态,因此可以正常发起下一次中断传输。
解决步骤
方案1:修改DMA模式为Normal(推荐,适合单次传输场景)
在CubeMX中,将SPI2对应的DMA传输模式从Circular(循环)改为Normal(正常)。单次DMA传输完成后,DMA通道会自动停止,外设回到空闲状态,后续可正常发起新的DMA请求。
方案2:手动停止DMA循环传输
若必须保留循环模式,需在第一次DMA传输完成后手动停止DMA通道,再发起新操作:
/* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); hal_status = HAL_SPI_Transmit_DMA(&hspi2, u8_SPI2_TxBuff, sizeofBuff); // 执行正常 HAL_Delay(1000); // 手动停止SPI2的DMA通道 HAL_SPI_DMAStop(&hspi2); HAL_SPI_Receive(&hspi1,u8_SPI1_RxBuff,sizeofBuff,100); HAL_Delay(1000); // 此时可正常发起接收DMA hal_status = HAL_SPI_Receive_DMA(&hspi2, u8_SPI2_RxBuff, sizeofBuff); HAL_Delay(1000); HAL_SPI_Transmit(&hspi1,u8_SPI1_TxBuff,sizeofBuff,100); // 接收完成后停止DMA(循环模式下需手动操作) HAL_SPI_DMAStop(&hspi2); HAL_Delay(1000); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); /* USER CODE END 2 */
方案3:利用HAL回调函数替代延时(更可靠)
避免用HAL_Delay判断传输完成,改用HAL的DMA传输完成回调函数触发后续逻辑,可准确获取传输状态:
- 重写SPI2的回调函数:
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if(hspi->Instance == SPI2) { // 传输完成后停止DMA循环 HAL_SPI_DMAStop(&hspi2); // 执行后续操作 HAL_SPI_Receive(&hspi1,u8_SPI1_RxBuff,sizeofBuff,100); HAL_SPI_Receive_DMA(&hspi2, u8_SPI2_RxBuff, sizeofBuff); } } void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if(hspi->Instance == SPI2) { HAL_SPI_DMAStop(&hspi2); HAL_SPI_Transmit(&hspi1,u8_SPI1_TxBuff,sizeofBuff,100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } }
- 主函数中仅启动第一次DMA传输:
/* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); hal_status = HAL_SPI_Transmit_DMA(&hspi2, u8_SPI2_TxBuff, sizeofBuff); /* USER CODE END 2 */
内容的提问来源于stack exchange,提问作者Văn Tuấn Nguyễn
相关产品推荐
相关产品推荐

