1. 烧录基础概念:什么是固件烧录、串口烧录的原理、为什么需要镜像校验

各位同学好,我是老周。做嵌入式这行十几年了,今天咱们聊聊烧录这件事。很多人觉得烧录就是把文件丢进芯片,没什么技术含量。其实不然,这里面的门道,尤其是镜像校验,搞不好会让你在产线上一夜回到解放前。

咱们先打好地基,把三个核心概念掰扯清楚。

1.1 什么是固件烧录

固件烧录,说白了就是把编译好的二进制代码,写入芯片的非易失性存储器里。比如 Flash、EEPROM 这些。芯片上电后,从那里把代码读出来运行。

你想想看,芯片出厂时就是一块白板,啥也没有。你得把“灵魂”灌进去,它才能干活。这个灌的过程,就是烧录。

烧录的本质:将二进制镜像文件,通过某种物理接口(如串口、JTAG、USB),传输到目标芯片的存储介质中,并确保写入后的数据与原始文件完全一致。

我个人习惯把烧录分为三类:

  • 量产烧录:产线上成百上千片一起烧,讲究速度和稳定性。我见过一个项目,因为烧录时序没调好,良率直接掉了 5 个点,老板脸都绿了。
  • 开发调试烧录:工程师自己用,频率高,但每次只烧一两片。这时候更看重灵活性和调试能力。
  • 远程/OTA 烧录:设备已经在客户手里了,通过网络或无线升级。这种场景下,镜像校验就是命根子,烧坏了可没人帮你拆机。

1.2 串口烧录的原理

串口烧录,是嵌入式开发里最古老、也最通用的方式。为什么?因为几乎所有 MCU 都带 UART,成本低,接线简单,就三根线:TX、RX、GND。

它的原理其实不复杂,我画个图你就明白了。

串口烧录流程图 串口烧录原理流程图 PC 上位机 发送镜像文件 串口协议层 分包 + 校验 + 流控 Bootloader 接收 + 解析 + 写入 Flash 存储器 写入镜像数据 关键点:Bootloader 是串口烧录的“守门员” 它负责解析串口数据包,校验完整性,然后调用 Flash 驱动完成写入

流程其实就三步:

  1. PC 上位机把固件镜像读进来,按协议分包,加上帧头、帧尾、校验码,通过串口发出去。
  2. 芯片里的 Bootloader收到数据包,先校验对不对,没问题就解析出有效数据。
  3. Bootloader 调用 Flash 驱动,把数据写入指定地址。写完再读回来验证一遍。

我的经验:串口烧录最怕什么?波特率不匹配。我曾经在调试一块 STM32F4 时,上位机设了 115200,Bootloader 里配的却是 9600,结果全是乱码。查了整整一下午,最后发现是初始化顺序问题。所以,建议你在 Bootloader 里加一个自动波特率检测功能,能省很多事。

1.3 为什么需要镜像校验

这个问题,我直接说结论:没有校验的烧录,就是在赌运气。

你可能会想,串口传输不是有奇偶校验吗?Flash 写入不是有 ECC 吗?为什么还要单独做镜像校验?

嗯,这里要注意。串口的奇偶校验只能保证单个字节在传输过程中没出错,但它管不了以下这些情况:

风险场景 串口校验能否发现 镜像校验能否发现
串口线受到干扰,整包数据错位 部分能 能
Bootloader 程序本身有 Bug 不能 能
Flash 写入时电压不稳,写入了错误数据 不能 能
镜像文件本身被损坏或版本不对 不能 能
用户选错了芯片型号,Flash 地址映射错误 不能 能

看到了吧?串口校验只能覆盖传输层的一小部分问题。而镜像校验,是从文件源头到写入终点,全链路做一次完整性确认。

血的教训:我曾经负责一个智能家居网关项目,量产时为了赶工期,把镜像校验给跳过了。结果有一批芯片的 Flash 在写入时刚好遇到电源纹波,导致 200 多台设备上电后直接变砖。售后成本花了十几万,老板开会时直接拍桌子。从那以后,我定了个死规矩:任何烧录流程,必须过镜像校验,否则不准进产线。

1.4 镜像校验的核心逻辑

镜像校验到底在做什么?说白了就是两件事:

  • 烧录前校验:检查镜像文件本身是否完整、是否与目标芯片匹配。
  • 烧录后校验:把写入 Flash 的数据读出来,跟原始镜像做比对。

常用的校验方式有这么几种:

校验方式 原理 安全性 速度
CRC32 循环冗余校验,计算整个镜像的 32 位校验值 中等 快
MD5/SHA1 哈希算法,生成固定长度的摘要 较高 较慢
逐字节比对 将 Flash 内容逐字节与原始镜像比较 最高 最慢
双备份校验 写入两份镜像,启动时比对 极高 占用双倍空间

我个人最常用的是 CRC32。为什么?因为它速度快,硬件上很多 MCU 都集成了 CRC 计算单元,几乎不占 CPU 时间。而且对于 99% 的嵌入式场景,CRC32 的碰撞概率已经足够低了。

一个简单的 CRC32 校验代码片段(C 语言):

// 计算镜像的 CRC32 值
uint32_t calculate_image_crc(uint8_t *data, uint32_t len) {
    uint32_t crc = 0xFFFFFFFF;
    for (uint32_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 1) {
                crc = (crc >> 1) ^ 0xEDB88320;
            } else {
                crc >>= 1;
            }
        }
    }
    return ~crc;
}

// 烧录完成后,读回 Flash 内容并校验
bool verify_flash_crc(uint32_t flash_addr, uint32_t *expected_crc, uint32_t len) {
    uint8_t buffer[256];
    uint32_t computed_crc = 0xFFFFFFFF;
    
    for (uint32_t offset = 0; offset < len; offset += sizeof(buffer)) {
        uint32_t chunk_len = (len - offset) > sizeof(buffer) ? sizeof(buffer) : (len - offset);
        read_flash(flash_addr + offset, buffer, chunk_len);
        for (uint32_t i = 0; i < chunk_len; i++) {
            computed_crc ^= buffer[i];
            for (int j = 0; j < 8; j++) {
                if (computed_crc & 1) {
                    computed_crc = (computed_crc >> 1) ^ 0xEDB88320;
                } else {
                    computed_crc >>= 1;
                }
            }
        }
    }
    computed_crc = ~computed_crc;
    
    return (computed_crc == *expected_crc);
}

1.5 小结一下

这一章咱们把烧录的基础概念捋了一遍。固件烧录就是把二进制代码写进芯片,串口烧录是最常见的实现方式,而镜像校验是保证烧录不出错的最后一道防线。

你记住一句话:烧录可以慢,但不能错。 尤其是在产线上,一片芯片烧坏了,可能意味着整个批次都要返工。镜像校验就是你的保险丝,千万别省。