STM32对函数进行CRC校验

STM32F4xx 内置了硬件 CRC 计算单元(CRC-32 以太网多项式 0x4C11DB7),可在运行期快速计算软件签名,与链接时生成并存放在指定存储单元的参考签名比较,用于验证 Flash 完整性。其计算方法为:复位 CRC 模块(置 CRC_CR=0x01,余数初始化为 0xFFFFFFFF),把数据按 32 位写入 CRC_DR,最后读回结果。

方案一:上电时对整个 Flash 区域做 CRC32 校验(最常用)

第 1 步:在程序 Flash 中预留 CRC 参考值存储区

把应用区起始地址、大小、CRC 参考值地址定义好。CRC 参考值通常由编译后处理工具在烧录时写入。

复制代码
#define FLASH_APP_START_ADDR  0x08002000U   /* 应用程序起始地址 */
#define FLASH_APP_SIZE        0x0003C000U   /* 应用程序大小 */
#define FLASH_CRC_REF_ADDR    0x0803E000U   /* CRC 参考值存储地址 */

第 2 步------用软件/硬件 CRC 计算 Flash 内容

复制代码
// 硬件 CRC 方式(利用 STM32F4 内置 CRC 外设)
uint32_t calculate_flash_crc(uint32_t *addr, uint32_t len_words)
{
    uint32_t crc;
    // 复位 CRC 单元
    CRC->CR = CRC_CR_RESET;
    for (uint32_t i = 0; i < len_words; i++) {
        CRC->DR = addr[i];
    }
    crc = CRC->DR;
    return crc;
}

或软件 CRC32(不依赖外设,便于 bootloader 使用):

复制代码
uint32_t calculate_crc32(uint32_t *data, uint32_t length) {
    uint32_t crc = 0xFFFFFFFF;
    for (uint32_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (int j = 0; j < 32; j++) {
            crc = (crc & 0x80000000) ? (crc << 1) ^ 0x04C11DB7 : crc << 1;
        }
    }
    return crc;
}

第 3 步------上电校验并在跳转主应用前执行

复制代码
void verify_flash_integrity(void) {
    uint32_t stored_crc = *(volatile uint32_t *)FLASH_CRC_REF_ADDR;
    uint32_t calc_crc = calculate_crc32((uint32_t *)FLASH_APP_START_ADDR,
                                        FLASH_APP_SIZE / 4);
    if (calc_crc != stored_crc) {
        // CRC 不匹配!Flash 可能被位翻转/破坏,进入安全状态
        error_handler();
    }
    // 校验通过,正常跳转主应用
    jump_to_app();
}

校验函数必须在跳转到主应用之前调用,一旦校验失败就停止启动并进入安全状态。去除未使用的函数、保证整个 Flash 区域计算结果与参考值一致是方案要点。

方案二:只校验关键函数(针对性保护)

单粒子翻转通常是小概率、随机的,对全部 Flash 做完整校验耗时较长。可以只对关键函数(如安全关断、中断处理)所在段做 CRC 保护 ,配合上文的 __attribute__((section)) 分段机制:

  1. 把关键函数放入单独段(如 .func_crc);

  2. 用链接脚本的 __START__/__END__ 符号标记该段起止地址;

  3. 用同段内 CRC 与该段的参考值比对。

    关键函数定位到固定段 →
    extern uint32_t __func_crc_start, __func_crc_end;
    uint32_t len_words = (uint32_t)&__func_crc_end - (uint32_t)&__func_crc_start;
    crc = calculate_crc32((uint32_t*)&__func_crc_start, len_words);

方案三:运行时周期自检(持续防护)

单粒子翻转可能在上电后的运行中发生,因此周期性(由定时器触发)对运行代码区重新计算 CRC 比对,是更完整的防护。注意:

  • 自检期间避免同时执行被校验区代码,或选用短小的临界区段;
  • 校验本身使用的函数应放在 CRC 保护区之外,避免"以被篡改的代码验证被篡改的代码";
  • 翻转本质是位翻转检测,检测到后应转入安全状态(停机、进入安全模式、复位),因为多数场合无法在运行中纠正 Flash 内容(需要重新烧写/从备份区恢复)。

关键注意事项汇总

  1. 参考值需与内容严格一致:CRC 参考值必须是在链接后基于最终烧录内容的计算结果,否则无法匹配------一般由编译后脚本(如 srec_cat、Python 脚本)在生成 bin 后计算并追加到预留地址。
  2. 硬件 CRC 速度优势:STM32F4 硬件 CRC 每 4 个 AHB 周期完成一次 32 位输入计算,远快于软件实现,适合运行期自检。
  3. KEEP/满引用函数 :固定分段函数若未被调用需 __attribute__((used)) 或 KEEP 保留,否则链接器会剔除,导致地址/CRC 不匹配。
  4. 覆盖范围 :CRC 校验应覆盖 .text + .rodata + .data 初始值 等程序区,以及关键配置数据,避免遗漏。
  5. 无法自修复 :SEU 翻转基本是破坏性的(可擦除 Flash 但难以原位纠错),所以工程上通常是检测并进入安全态 ,配合备份机制(双分区 A/B、从备份区恢复)提升可靠性。
相关推荐
HQB202610 小时前
EG1198|屹晶微电子 120V 内置 MOS 异步降压 DC‑DC(高性价比)
嵌入式硬件·硬件工程
果壳science12 小时前
张祥前实验如何获取正电荷
笔记·科技·嵌入式硬件·硬件工程·制造
南山电子nscn12 小时前
中科芯CETC汽车电池管理系统BMS进口芯片国产型号替代指南
单片机·汽车·bms
沉淀的.晴天13 小时前
can总线
stm32
桀人13 小时前
认识AT24C02
stm32·单片机·嵌入式硬件
国科安芯14 小时前
星间激光通信终端CANFD控制网络的时延确定性与辐射可靠性
网络·嵌入式硬件·信息与通信·canfd·低轨卫星·商业航天·抗辐射
一直C14 小时前
嵌入式 Linux 驱动开发学习笔记:内核定时器、platform 总线与 I²C 驱动
linux·驱动开发·嵌入式硬件·学习·imx6ull
传感器与混合集成电路14 小时前
150MHz主频、8路DAC与6路PWM:JLH221360伺服控制SIP解析
嵌入式硬件
comyccccc15 小时前
STM32PID控制板设计全解析学习笔记
单片机·嵌入式硬件
碧海银沙音频科技研究院15 小时前
实现炬芯ATS308X的usb cdc 串口IAP 固件升级功能实现
人工智能·嵌入式硬件·深度学习·算法