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

STM32 UART转SPI变长传输时SPI TXE标志异常问题求解

问题背景

在STM32 IoT开发套件上开展ES-WIFI模块通信开发:该WIFI模块默认通过SPI2接口连接,若需切换为UART通信模式,必须先通过SPI接口为模块烧录对应固件。开发目标为实现AT指令双向透传:从连接PC的USART1接收串口调试助手下发的AT指令,转发至SPI2连接的WIFI模块;同时从SPI2读取WIFI模块返回的响应数据,回传至USART1送到PC端串口调试助手,核心要求为支持变长数据传输。
程序运行时,调试器卡在SPI状态寄存器TXE非空的轮询判断处,此时SPI FIFO发送等级为满(FTLVM: 11),需定位代码错误。

相关代码

USART1中断服务函数

void USART1_IRQHandler(void)
{
  while (!((USART1->ISR) & USART_ISR_TXE));

  char temp1 = USART1->RDR;

  SPI2->DR = temp1;

  while (!(USART1->ISR & USART_ISR_TC)); // 检查UART状态寄存器的传输完成位

  while (!((SPI2->SR)& SPI_SR_TXE)); //检查SPI状态寄存器的发送缓冲区空标志
  while (((SPI2->SR)& SPI_SR_BSY)); //检查SPI状态寄存器的忙标志

  uint8_t temp2 = SPI2->DR; // 读数据寄存器和状态寄存器清标志
          temp2 = SPI2->SR;
}

SPI2初始化函数

void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

USART1初始化函数

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_8;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}
错误定位与修正

核心故障原因

SPI是全双工同步通信协议,只有主机输出SCK时钟时,数据才会从移位寄存器实际发出,同时从机返回的数据才会被采样存入移位寄存器。当前代码向SPI2->DR写入1字节数据后,没有形成有效通信时序驱动数据发送,FIFO内数据持续堆积到满,最终卡死在TXE轮询逻辑。
具体代码问题点:

  • 中断触发判断逻辑完全错误:进入USART1中断后第一行轮询USART_ISR_TXE标志,该标志属于UART发送方向状态位,和接收中断触发源无关。接收场景下进入中断后应判断RXNE(接收缓冲区非空)标志,确认收到数据后再读RDR寄存器。
  • SPI片选操作缺失:SPI初始化配置为软件控制NSS,但全流程没有NSS引脚拉低/拉高的操作。ES-WIFI模块作为SPI从机,未被NSS信号选中时不会响应任何SPI通信,SCK无法产生有效通信时序。
  • SPI收发流程逻辑错误:向SPI_DR写入1字节后,未等待收发完成就轮询TXE标志,且读DR寄存器时机错误。SPI全双工通信下每发送1字节,必须等待BSY标志清零后再读DR获取从机返回字节,否则RXNE标志持续置位,后续写入DR会触发溢出错误,直接堵死FIFO。
  • 中断内存在无意义阻塞逻辑:在UART接收中断中轮询USART_ISR_TC(UART发送完成)标志,和当前接收操作完全无关,既拉长中断阻塞时间,也会打乱外设状态判断逻辑。
  • SPI波特率配置过高:SPI预分频配置为2,若APB1总线时钟为80MHz则SPI时钟达40MHz,多数ES-WIFI模块的SPI接口最高支持时钟不超过20MHz,时钟过高会直接导致通信失效。

修正方案

  • 重新配置USART1中断触发源为RXNE(接收到数据触发),进入中断后直接读取RDR获取收到的字节,删除中断内和当前操作无关的轮询逻辑。
  • 补全SPI NSS软件控制逻辑:每次SPI传输启动前拉低对应NSS引脚,整段传输完成后拉高NSS。
  • 修正SPI单字节收发流程:写DR寄存器 → 等待BSY标志清零 → 读DR寄存器获取从机返回字节,不要在写完DR后立刻轮询TXE,TXE置位仅代表发送缓冲区/FIFO空,不代表字节已经完成发送。
  • 透传逻辑不要全部放在USART接收中断中实现:用环形缓冲区做数据缓存,USART中断收到的字节存入待转发缓存,主循环中分别处理USART到SPI、SPI到USART的双向转发,避免中断阻塞导致丢数据或外设状态异常。
  • 先将SPI波特率降到10MHz以内(预分频配置为8或16),确认通信稳定后再逐步提升速率。

内容的提问来源于stack exchange,提问作者Karthikeyan Krishnanandan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:27:42