第九章:OTA 与 Flash 驱动 —— 如何用TDD验证固件升级逻辑的鲁棒性

这一章我们将切入一个非常关键且具有挑战性的场景:OTA(Over-the-Air)固件更新

OTA 逻辑最怕的是什么?是中途断电、Flash 写入失败、校验和不匹配。如果你在真机上测试这些异常,可能需要反复烧录、断开电源,甚至不小心把片子变"砖"。在 TDD 的世界里,我们可以优雅地模拟这些灾难。

9.1 OTA 测试的痛点

  1. 硬件寿命:频繁擦写内部 Flash 也是有寿命限制的。

  2. 场景模拟:你很难手动模拟"在写入第 1024 个字节时突然断电"。

  3. 复杂校验:CRC32、SHA256 的验证在 PC 上跑比在 MCU 上快得多。

9.2 接口抽象:Storage_Interface.h

为了让 OTA 逻辑可测,我们必须隔离 STM32 的 HAL_FLASH_Program

// Storage_Interface.h

#ifndef STORAGE_INTERFACE_H

#define STORAGE_INTERFACE_H

#include <stdint.h>

#include <stdbool.h>

typedef enum { FLASH_OK, FLASH_ERROR, FLASH_BUSY } FlashStatus_t;

FlashStatus_t HW_Flash_ErasePage(uint32_t page_addr);

FlashStatus_t HW_Flash_Write(uint32_t addr, uint8_t* data, uint32_t len);

void HW_System_Reset(void);

#endif

9.3 实战:测试"断点续传"逻辑

我们要实现一个 OTA 接收器:它按包接收固件,记录已写入的偏移量,如果写入失败要能重试。

编写测试 (test_Ota_Service.c): 我们要模拟一个非常极端的情况:第一次写入成功,第二次写入失败。

#include "unity.h"

#include "mock_Storage_Interface.h"

#include "Ota_Service.h"

void test_Ota_Should_HandleFlashWriteFailure(void) {

uint8_t dummy_data128 = {0xA5};

// 1. 模拟第一包数据写入:返回成功

HW_Flash_Write_ExpectAndReturn(0x08010000, dummy_data, 128, FLASH_OK);

bool result = Ota_ProcessPacket(0, dummy_data, 128);

TEST_ASSERT_TRUE(result);

// 2. 模拟第二包数据写入:硬件突然报错(比如电压不稳或页损坏)

HW_Flash_Write_ExpectAndReturn(0x08010080, dummy_data, 128, FLASH_ERROR);

result = Ota_ProcessPacket(128, dummy_data, 128);

// 验证:逻辑层应该识别出失败,并返回 false,以便触发重传机制

TEST_ASSERT_FALSE(result);

}

9.4 进阶技巧:模拟"脏数据"与校验失败

在 OTA 中,校验和(Checksum)是最后一道防线。

void test_Ota_Should_RejectFirmware_WhenChecksumMismatch(void) {

// 模拟 Flash 读取出的数据

// 我们不需要真的读 Flash,直接 Mock 掉读取函数返回"坏数据"

HW_Flash_Read_StubWithCallback(my_Fake_Flash_Read_Bad_Data);

// 执行校验逻辑

bool is_valid = Ota_VerifyChecksum();

// 验证:校验不通过,且绝不调用系统重启去运行坏固件

TEST_ASSERT_FALSE(is_valid);

// 确保没有误触发系统重启(这也是一种验证)

HW_System_Reset_Expect(); // 如果你预期它报错后重启到 Bootloader

}

9.5 本章核心:防御式编程:

  1. 状态机保护:OTA 过程中如果收到无关的串口命令,状态机是否会崩溃?

  2. 原子操作模拟:模拟擦除了一半突然断电,Bootloader 能否识别出固件不完整?

  3. 边界检查:Mock 掉 Flash 大小接口,验证如果固件包超过 Flash 容量,代码是否会越界写入。

本章小结

这一章证明了 TDD 不仅能测"好用"的情况,更能测"坏掉"的情况。对于 OTA 这种高风险模块,这种"离线异常注入"是保证产品不批量变砖的唯一手段。

相关推荐
Groundwork Explorer19 分钟前
W5500 网卡编程初始化与使用指南
python·单片机
单片机仿真设计1 小时前
【proteus仿真】基于 STM32 的智能垃圾桶设计(仿真图+程序)
c语言·stm32·stm32单片机·proteus·毕设·仿真
智闲电子设计2 小时前
STM32 SD 卡 + FatFS 实战:掉电丢数据?f_sync 和簇对齐写救你
stm32·单片机·嵌入式硬件
ヾChen4 小时前
ESP32 OTA 踩坑实录
stm32·单片机·嵌入式硬件·物联网·学习
DevHub4 小时前
probe-rs 教程:一行命令替代 OpenOCD 烧写调试 Cortex-M
arm开发·嵌入式硬件·mcu·rust
小僧景贤4 小时前
嵌入式ADC高阶深度解析:基于STM32底层原理、误差溯源、硬件调校与工业级落地
单片机·嵌入式硬件·stm32 adc·工业级高精度采集·adc 误差校准·量产固件开发
黑猫学长呀4 小时前
【驱动开发常见问题】编译出现rm: cannot remove `asm/arch‘: Is a directory
驱动开发·单片机·嵌入式硬件·bug·编译报错·kernrl
雨田言炎5 小时前
STM32专题之内部FLASH详解
笔记·stm32·单片机
DsirNg5 小时前
把 AI 放进一次合并请求:一个批量归档功能的交付闭环
软件工程·测试·代码审查·开发效率·pull request·ai辅助编程·需求拆解
Be for thing7 小时前
【嵌入式成长10】STC89C51蜂鸣器|有源/无源蜂鸣器区分、三极管驱动、定时器播放音乐实战
单片机·嵌入式硬件·学习