STM32 UART Rx中断SPI传输异常求助:主循环有无延迟导致差异
问题分析与解决方案
一、主循环移除HAL_Delay后SPI传输失败的原因
结合代码中W5500网络芯片的SPI接口操作(getSn_SR/recv/send均为W5500相关函数),核心问题是SPI总线资源竞争,具体逻辑如下:
- 当主循环存在
HAL_Delay(100)时,CPU在延迟期间会暂停W5500的轮询操作,SPI总线处于空闲状态。此时UART中断触发后,回调里的send函数可以独占SPI总线,完整完成W5500的数据发送流程。 - 移除
HAL_Delay后,主循环会高频轮询W5500的socket状态,持续占用SPI总线执行getSn_RX_RSR、recv等操作。当UART中断触发时,回调里的send函数会强行抢占SPI总线,打断主循环中正在进行的W5500 SPI传输——而W5500的SPI操作需要完整的时序周期,被打断后会导致通信异常,最终传输失败。
另外一种可能是:部分W5500驱动的send函数依赖主循环的状态同步(比如socket发送缓冲区的状态更新),主循环高频轮询时会频繁修改相关状态,导致中断回调里的send无法获取有效状态而失败。
二、让中断回调优先返回主循环的实现方案
要实现中断回调不执行当前操作、优先返回主循环处理任务,核心思路是将中断回调的操作异步化,只在中断里做最轻量化的标记,把实际业务逻辑放到主循环执行:
修改后的代码示例
// 全局/静态变量,用于标记有数据需要发送 uint8_t uart_rx_ready = 0; uint16_t uart_rx_size = 0; uint8_t uart_rx_buffer[RxBufferSize]; // 假设RxBufferSize为已定义常量 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){ // 重新开启UART接收中断 HAL_UARTEx_ReceiveToIdle_IT(&huart3, uart_rx_buffer, RxBufferSize); // 仅做数据缓存与标记,不执行send操作 uart_rx_size = Size; uart_rx_ready = 1; // 直接返回,不做其他耗时操作 return; } while (1) { // 优先处理UART接收后的发送任务 if(uart_rx_ready){ send(1, uart_rx_buffer, uart_rx_size); uart_rx_ready = 0; // 清除标记 } // 原W5500处理逻辑 if(getSn_SR(1)==SOCK_ESTABLISHED){ if((size = getSn_RX_RSR(1)) > 0){ recv(1, TxBuffer, size); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1,GPIO_PIN_SET); HAL_UART_Transmit_DMA(&huart3, TxBuffer,size); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1,GPIO_PIN_RESET); size=0; } } }
关键说明
- 中断回调仅完成重新开启接收中断、缓存数据、设置标记位三个轻量化操作,耗时极短,能快速返回主循环。
- 主循环每次迭代优先检查标记位,有数据需要发送时再执行
send操作,既避免了中断与主循环的SPI总线竞争,也保证了主循环任务的优先执行。 - 裸机环境下用全局变量标记即可满足需求;如果是多核MCU,可给标记位加原子操作保证线程安全。
内容的提问来源于stack exchange,提问作者Samet Özdemir
相关产品推荐
相关产品推荐

