X‑CUBE‑CLASSB 4.0 自检库提供了基础安全检测能力,原生自带 CPU、FLASH、RAM 自检,参考工程附带时钟自检示例代码。但面向家电 IEC60730‑1 Class‑B 认证完整要求,官方库并未直接提供堆栈溢出自检、PC 程序指针自检、GPIO IO 自检、ADC 自检这几项运行时测试。 很多开发者做到认证集成阶段才发现缺失这部分测试逻辑,需要自行补充实现。LAT1688 给出经过 NUCLEO‑G031K8 验证的参考实现方案。本文还原四项扩展测试的原理、工程修改要点、实现步骤与落地注意事项。重要提示:文中代码仅为参考示例,产品最终版本需要结合项目业务,并且和认证评估机构确认测试方案合规性。
资料获取:实战经验 | LAT1688 ClassB 4.0扩展测试项参考示例
1. ClassB4.0 库能力边界说明
1.1 STL 原生内置测试项
1.2 需要用户自行扩展实现的测试项(认证经常要求)
- 堆栈溢出自检
- PC 程序指针自检
- GPIO IO 端口自检
- ADC 模数转换器自检
所有扩展示例代码基于 MDK‑Keil 环境,在 NUCLEO‑G031K8 开发板验证通过,附件工程包:stm32g031‑classb‑extension.zip。
2. 堆栈溢出自检
2.1 测试目的
检测运行过程堆栈上溢,堆栈溢出会破坏局部变量、返回地址,造成程序跑飞、不可控行为。检测到溢出故障,软件需要切入安全故障处理状态。
2.2 实现原理:栈底魔术字标记法
分为两大步骤:初始化写入魔术字;运行周期校验魔术字。
- 初始化阶段:修改 Keil 启动汇编
startup_stm32g031xx.s,导出Stack_Mem栈底符号;C 语言 extern 引用该地址,向栈底连续写入多组约定魔术字模式。
魔术字示例:
STACK_BOTTOM_PATTERNA ~ PATTERND四组 32bit 常量。
- 运行周期性检测:在周期自检任务 / 定时器中断中读取栈底魔术字,和预设值比对。如果任意魔术字被改写,判定堆栈发生溢出或者已经非常接近溢出,上报
STL_FAILED故障状态,进入安全处理流程。
2.3 工程修改关键点
- 启动汇编文件
AREA STACK段,对Stack_Mem做EXPORT Stack_Mem导出; - C 代码使用
extern uint32_t Stack_Mem;获取栈底地址; - 初始化函数
STL_StackOver_InitTM()完成魔术字填充; - 周期调用
STL_StackOver_RunTM()执行比对校验。
2.4 局限说明
该方案只能检测栈底是否被改写;如果堆栈增长没有到达栈底标记位置,不会触发检测。需要配合链接脚本配置足够安全裕量的栈大小。
3. PC 程序指针自检
3.1 测试目的
检测程序计数器 PC 硬件、控制流异常跳转,避免因为内核故障导致程序跳转到非法地址执行。
3.2 两种实现思路
- 参考 ClassB2.2.x 旧版本源码实现控制流检测;
- 文档重点介绍函数指针调用测试方法。
3.3 函数指针方案原理
定义若干简单返回固定值的测试函数;定义函数指针变量。运行时周期改变函数指针指向,依次调用不同测试函数,校验返回值是否与预期返回值完全匹配。
本质:人为受控修改跳转目标地址,模拟 PC 指针跳转,校验跳转、调用返回通路是否正常。如果返回结果不符合预期,判定 PC / 控制流存在故障,上报失败。
3.4 使用约束
- 该测试属于运行时周期自检,需要放入 Class‑B 周期调度;
- 示例仅提供逻辑框架,开发者需要根据 MCU 内核、安全方案完善,同时与认证机构确认该实现是否满足标准要求。
4. GPIO IO 端口自检
4.1 测试原理:回读闭环测试
选取可用 GPIO 引脚,软件配置为输出模式;周期性输出高电平、低电平,立刻读取该引脚输入电平,对比读回电平与输出电平是否一致。输出与回读不一致,判定 IO 硬件故障。
硬件前提:用于自检的 GPIO 引脚外部不能接强驱动信号,否则外部电平会覆盖输出电平,造成自检误判。优先选用板子预留测试引脚。
4.2 软件执行流程
- GPIO 初始化配置为输出模式;
- HAL_GPIO_WritePin 输出高电平;HAL_GPIO_ReadPin 回读,校验是否为高;
- HAL_GPIO_WritePin 输出低电平;HAL_GPIO_ReadPin 回读,校验是否为低;
- 两次电平都匹配,返回
STL_PASSED;任意一次不匹配返回STL_FAILED。
4.3 落地注意事项
- 产品实际 IO 如果外接外部电路,不可以直接复用业务功能引脚做回读自检,需要单独预留测试引脚;
- 该测试是周期运行时测试,放在安全周期检测调度中执行。
5. ADC 模数转换器自检
5.1 测试原理:利用片内 VREFINT 内部参考通道自检
STM32G0 内置 VREFINT 内部参考电压,典型电压区间1.18V ~1.23V;当VDDA=VREF+=3.3V供电条件,ADC 采样 VREFINT 通道转换结果理论落在0x5B8 ~ 0x5F6区间。周期性读取 VREFINT 通道 ADC 采样值,如果转换结果落在有效区间外,判定 ADC 模块故障。
5.2 备选方案
如果硬件条件允许,也可以外部接入稳定参考电压注入 ADC 通道,校验采样输出是否符合预期。
5.3 关键约束
- VDDA 供电电压必须稳定在 3.3V;VDDA 波动会直接改变 VREFINT 采样输出;
- 注意 ADC 校准流程,采样前执行 ADC 硬件自校准;
- 不要直接硬写固定数值,要根据芯片数据手册的 VREFINT 电气参数计算有效采样窗口。
6. 工程落地实施与故障排查清单
6.1 整体集成顺序
- 完成原生 ClassB‑4.0 库基础集成(CPU/Flash/RAM/ 时钟);
- 根据硬件情况,选择需要启用的扩展项:堆栈、PC 指针、IO、ADC;
- 修改 Keil 启动汇编,导出 Stack_Mem 符号,实现堆栈魔术字初始化;
- 将四项扩展自检函数加入 Class‑B 周期任务调度;
- 故障状态统一接入 STL 故障处理逻辑;
- 与认证机构评审所有扩展测试逻辑,确认满足 IEC60730 Class‑B 要求。
重要:文档示例代码仅作为开发参考,不等于可以直接通过认证,认证机构需要对测试逻辑做评估。
6.2 高频踩坑排查清单
- 堆栈自检永远报失败
- 核查启动汇编是否
EXPORT Stack_Mem;C 语言 extern 地址是否正确;魔术字初始化是否在堆栈被使用之前执行; - 实际业务堆栈峰值已经触及栈底,需要增大链接脚本 Stack_Size。
- 核查启动汇编是否
- IO 自检结果不稳定,时而失败
- 确认用于自检的 GPIO 外部没有外接强上拉、强下拉驱动;尽量选用闲置测试引脚,避开业务驱动引脚。
- ADC VREFINT 自检频繁越界
- 检查 VDDA 电源稳定性,确认 ADC 已经执行硬件校准;核对数据手册 VREFINT 电压范围,重新计算 ADC 采样上下限。
- PC 指针自检返回值错乱
- 确认函数指针类型定义、测试函数返回类型保持一致;编译器不要做过度优化,避免测试函数被编译器优化裁剪。
7. 小结
- STM32 ClassB4.0(X‑CUBE‑CLASSB)官方 STL 库仅内置 CPU、Flash、RAM 基础自检,时钟提供示例代码;堆栈溢出、PC 指针、GPIO、ADC 四项运行时自检需要开发者自行扩展实现。
- 堆栈自检采用栈底魔术字标记法,需要修改 Keil 启动汇编导出栈底符号,启动阶段写入魔术字,周期任务校验;需要预留足够栈安全裕量。
- PC 指针可采用函数指针受控跳转测试;GPIO 采用输出‑回读闭环自检;ADC 利用内部 VREFINT 参考通道完成自检。
- 全部参考代码仅为工程实现参考,不能直接等同于认证合规代码,产品项目必须和功能安全认证机构确认测试逻辑是否满足 IEC60730 标准。
- 附件工程
stm32g031‑classb‑extension.zip,基于 NUCLEO‑G031K8、MDK‑Keil5.4.1 验证,可作为二次开发起点。
8. FAQ
Q:ClassB4.0 为什么官方库不直接包含堆栈、IO、ADC 自检?
A:堆栈大小、IO 引脚分配、ADC 外部硬件电路每个产品项目差异大,很难做成通用库组件,ST 给出参考实现,由客户结合硬件业务做适配。
Q:堆栈魔术字方法能否 100% 捕获全部堆栈溢出?
A:不能。只有堆栈增长触及栈底标记区域才会检测到;必须链接脚本配置栈大小留有足够安全余量,降低漏检概率。
Q:我使用 IAR 编译器,堆栈自检的启动文件修改方式一样吗?
A:不一样。文档示例针对 MDK‑Keil,IAR 需要修改 icf 链接脚本获取栈底符号,魔术字初始化逻辑不变,但是符号获取方式需要适配 IAR 工具链。
免责声明:本文全部基于 ST 官方 LAT1688 文档。示例代码仅供开发参考,功能安全认证项目需要结合标准、硬件、认证机构评估,实际产品以 ST 官方手册、安全手册为准。
215