FreeRTOS下SPI读写FLASH的坑:任务切换导致数据全FF的解决方案

FreeRTOS下SPI读写FLASH的坑:任务切换导致数据全FF的解决方案

最近在做一个基于STM32和W25Q128的项目,用FreeRTOS管理几个并发的任务。其中一个数据采集任务需要频繁地将传感器数据写入外部FLASH,另一个任务则负责在需要时读取这些数据。调试初期,一切看起来都很顺利,直到我开始进行长时间的压力测试。系统偶尔会莫名其妙地重启,看门狗超时了。更诡异的是,重启后读取FLASH里的数据,有时会得到一整片0xFF,仿佛数据从未被写入过。这可不是简单的硬件故障,因为单步调试时写入读取都完全正常。问题只在高负载、多任务并发运行时才显现。如果你也在FreeRTOS环境下被SPI设备(尤其是FLASH)的“灵异”读写错误困扰,特别是那种“全FF”或随机错误,那么你很可能踩进了任务调度与SPI时序冲突这个经典的坑里。这篇文章,我就结合自己的踩坑经历,聊聊问题的根源,并对比几种保护方案的优劣与实战细节。

1. 问题现象与根源剖析:当FreeRTOS遇上SPI时序

最初发现问题时,现象非常具有迷惑性。我的数据记录任务大致逻辑如下:

void data_logger_task(void *pvParameters) {
    while(1) {
        // 采集数据
        acquire_sensor_data(&buffer);
        // 写入FLASH
        W25QXX_Write(buffer, current_addr, DATA_SIZE);
        current_addr += DATA_SIZE;
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

同时,一个通信任务会在收到指令后,读取特定地址的数据并上传:

void comm_task(void *pvParameters) {
    while(1) {
        if (rx_command == READ_DATA) {
            W25QXX_Read(read_back_buffer, requested_addr, DATA_SIZE);
            // 发送数据...
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

在轻负载下,系统运行数小时无异常。但当我增加任务数量,或者让数据记录任务以更高频率(比如去掉vTaskDelay)连续写入时,系统就开始不稳定。最直接的崩溃表现是看门狗复位。通过调试器追踪,发现程序卡在了某个任务的vTaskDelay内部,或者更准确地说,卡在了任务切换相关的列表操作中。但这只是表象,根源在于SPI的读写被打断了。

SPI(Serial Peripheral Interface)通信是一种全双工、同步、串行的通信方式。一次完整的字节传输,要求时钟(SCLK)、片选(CS)、数据输入(MISO)和数据输出(MOSI)信号之间保持严格的时序关系。以常见的STM32标准SPI库函数操作为例:

uint8_t SPI_ReadWriteByte(SPI_TypeDef* SPIx, uint8_t TxData) {
    // 等待发送缓冲区为空
    while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
    // 写入数据,启动传输
    SPI_I2S_SendData(SPIx, TxData);
    // 等待接收缓冲区非空
    while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
    // 读取接收到的数据
    return (uint8_t)SPI_I2S_ReceiveData(SPIx);
}

这段代码在裸机程序中毫无问题。但在FreeRTOS中,while循环等待标志位的位置,正是任务切换可能发生的“窗口”。FreeRTOS的调度器可以在任何地方发生任务切换,只要它没有被明确禁止。当任务A正在执行SPI_ReadWriteByte函数,刚刚发送完数据(SPI_I2S_SendData),在等待接收完成(第二个while循环)时,如果系统定时器滴答(Tick)中断到来,调度器就可能认为任务A的时间片用完,或有一个更高优先级的任务就绪,从而强行保存任务A的上下文(寄存器状态),切换到另一个任务B

注意:对于W25Q128这类SPI FLASH,一次写入或读取操作往往由多个字节命令组成。例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值