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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 15:52:42