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
相关产品推荐
相关产品推荐

