You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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传输完成回调函数触发后续逻辑,可准确获取传输状态:

  1. 重写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);
  }
}
  1. 主函数中仅启动第一次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 04:32:48