embedded software testing

    嵌入式软件测试:单元测试、硬件在环与真正能抓 bug 的 CI

    嵌入式软件测试实战指南——主机端单元测试、在板测试、硬件在环测试台、产线测试治具与 CI 流水线。经验来自我们量产过的体温枪、PLC、气体探测器和医疗监护仪。

    ·XTELL 工程团队

    为什么嵌入式测试是另一项运动

    桌面开发者把测试当成理所当然:写个测试,几毫秒跑完,看它变绿。嵌入式开发者活在更艰难的世界里:代码跑在只有 64KB RAM 的单片机上,通过 I2C 跟传感器说话,还要驱动一个堵转电流能把板子拉复位的电机。你没法直接在笔记本上跑固件——一半的固件就是硬件本身。在服务器上只是小麻烦的 bug,在这里可能是变砖的设备、过不了的认证,或者半夜停线的工厂。

    在多年量产固件的经验里——红外体温枪、基于 STM32 的 PLC 控制器、ESP8266 气体探测器、多参数医疗监护仪——我们沉淀出一套分层测试策略。没有任何一种技术能覆盖全部,每一层抓的都是别的层抓不到的 bug。这篇指南走完这五个层级,每个都配我们真实做过的硬件案例。

    第一层:主机端单元测试——测大脑,不测身体

    最便宜的 bug,是不用碰硬件就能抓住的。大量固件逻辑其实跟外设无关:温度补偿算法、CRC 计算、协议解析、状态机、校准曲线,这些全都可以在开发机上跑。

    我们做过的一个完整量产级体温枪方案(LandwinGUN)就配了一份专门的体温枪算法文档——把红外传感器的原始读数换算成体温显示的数学,补偿环境温度和发射率。这个算法是纯计算:不碰 GPIO,不碰定时器,不碰硬件。我们把它抽成硬件无关的模块,在主机上用几百组输入向量测试,包括零下环境温度、传感器饱和之类的边缘情况。Unity、Ceedling 这类 C 语言框架就是为这个生的;真正的功夫在架构上——把算法和给它喂数据的驱动分开。

    经验法则:不碰寄存器的函数,就应该能在主机上做单元测试。照这个规矩做的团队通常会发现 40% 到 60% 的固件逻辑都符合条件——也就是 40% 到 60% 的 bug 几秒钟就能抓住,而不是在工作台上熬出来。

    第二层:在板测试——硬件说的是真话

    主机测试证明逻辑正确,在板测试证明逻辑在这颗芯片上正确。Cortex-M 的编译器和 x86 编译器在细节上不一样——整数提升、结构体对齐、没有 FPU 时的浮点行为。笔记本上通过的测试,在芯片上可能失败,只有跑在芯片上才知道。

    我们基于 STM32F407 的 PLC 工业控制项目(STM32 PLC)跑 FreeRTOS,带 FatFS 文件系统、SDIO 存储、SRAM 扩展和多路 UART。对这种复杂度的系统,我们做了一个小型的在板测试框架:一个专用的 FreeRTOS 任务,在测试构建里开机就跑,逐个演练驱动——写文件、读回来、校验 CRC——结果通过 UART 上报。这个框架永久留在代码库里,用编译开关控制。新人改了 SDIO 驱动,几秒钟内就知道自己有没有把存储搞坏。

    第三层:硬件在环——测整只动物

    单元测试查的是器官,硬件在环(HIL)查的是整只动物。HIL 里,真实固件跑在真实板子上,但物理世界是模拟的:测试台按脚本喂传感器输入,自动校验执行器输出。

    基于 ESP8266 的智能气体检测卡(PC_CARD)最能说明为什么需要它。这块板子监测三路气体传感器,驱动带开关/堵转保护的阀门电机,还要走 WiFi 上报。手工测阀门逻辑意味着拿着气源对着传感器熏、盯着电机看——慢、不可重复,还难闻。我们的 HIL 台把传感器换成 DAC 输出的电压,按脚本化的气体曲线走,再用台架自己的 ADC 监测电机驱动输出。完整的"开阀—报警—关阀—堵转"循环不到一分钟,无人值守,还抓到一个手工测试几周都没发现的堵转检测时序 bug。

    HIL 台要花实打实的工程时间——一个产品预算一到两周。它在第一次拦下本该流出的回归 bug 时就回本了。诀窍是从原理图阶段就为可测试性设计:台架要驱动/观测的每个信号都留出测试点,进测试模式用一个简单、有文档的命令。

    第四层:产线测试——工厂也是测试环境

    测试不会在开发结束时结束。每个出厂的单元都要过产线测试:PCB 焊对了吗,元器件都焊上了吗,固件烧了吗、校准了吗?

    体温枪项目让我们学了两次这个道理。双版本硬件设计(ThermoGun)定义了四个工作模式——默认、记忆、设置、校准,这些模式顺手成了产线测试钩子:本来给终端用户用的校准模式,让工厂能拿黑体炉校验传感器精度。而量产方案包(LandwinGUN)带了完整的 Gerber、丝印、贴片坐标和钢网文件——测试治具工程师做针床、接触板上每个网络,靠的就是这些制造数据。

    典型的产线测试分三段:ICT 检查每个网络都连通,功能测试演练传感器和执行器(复用 HIL 脚本),校准步骤把每台的修正值写进 Flash。少任何一段,工厂就会把本该拦下的机器发出去,然后你的售后团队用十倍的成本去 debug。

    第五层:嵌入式 CI——每次提交都构建和测试

    持续集成在嵌入式领域同样适用,只是要做适配。我们固件项目的 CI 流水线每次提交做四件事:

    • 给每个目标交叉编译。构建矩阵覆盖每颗 MCU 和每种工具链。用 GCC 能过、IAR 过不了的提交,依然是坏的提交。
    • 跑主机单元测试。第一层的算法和逻辑测试在 CI runner 上几秒钟跑完。
    • 静态分析。MISRA 风格检查和分析器在人工 review 之前抓住未初始化变量和可疑的指针运算。
    • 尺寸跟踪。每个提交记录 Flash 和 RAM 用量。嵌入式项目是被一千个字节拖死的;看着固件每天涨 200 字节的曲线,容量讨论会被迫提前。

    CI 目前还做不到的是每次提交都跑在板和 HIL 测试——那需要物理板子。务实的做法是给 CI runner 接一两块板子做 nightly:每天深夜把最新固件烧进去,完整跑一遍在板和 HIL 套件。开发者几分钟内拿到主机测试反馈,第二天早上拿到硬件反馈。

    出货前我们检查什么

    对安全相关的产品,测试本身就是合规故事。我们做过的多参数医疗监护仪(Medical Monitor)集成心电、无创血压、血氧、体温、呼吸率监测,医疗级精度 ±1%,符合 YY 97064-2015 标准。对这种设备,测试计划是产品的一部分:每条需求对应到测试,每条测试结果都要记录,认证机构审的就是这份记录。即使是非医疗产品,借用这套纪律——书面测试计划、需求到测试的可追溯、结果留档——就是"我们觉得没问题"和"我们能证明没问题"的区别。

    我们的出货前检查单,是几十个产品磨出来的:

    • 主机单元测试全绿,硬件无关模块测过覆盖率
    • 在板驱动测试在每个支持的板子版本上全绿
    • HIL 套件全绿,包括掉电和欠压复位用例
    • 产线测试治具在首件上验证过,校准流程有文档
    • Flash/RAM 预算按链接脚本核对过,不只是估算
    • OTA 升级测过——包括中途断电的恢复路径(气体检测卡的 OTA 升级就是拔电测出来的)
    • 老化跑:设备连续跑 HIL 套件 72 小时

    结语

    嵌入式软件测试不是一种技术,而是五个层级,每层抓的都是别的层抓不到的:主机单元测试管逻辑,在板测试管硅片,HIL 管整机集成,产线测试管制造,CI 把所有这些钉在每次提交上。五个层级都投了的团队,出的固件是让人放心的——在工作台上,在工厂里,在客户手里,都是。

    如果你正在做带固件的产品——传感器设备、工业控制器、消费电子——我们的固件开发服务从项目第一天就把这套测试纪律建进去,而不是事后补。告诉我们你的设备要做什么,我们会告诉你我们怎么证明它做到了。想看架构那一面,可以读我们的 RTOS 嵌入式软件架构指南;想快点拿到能测的硬件,可以读快速硬件原型指南。

    需要专业服务?

    联系我们获取定制化解决方案和报价