1. 烧录基础概念:什么是固件烧录、串口烧录的原理、为什么需要镜像校验
各位同学好,我是老周。做嵌入式这行十几年了,今天咱们聊聊烧录这件事。很多人觉得烧录就是把文件丢进芯片,没什么技术含量。其实不然,这里面的门道,尤其是镜像校验,搞不好会让你在产线上一夜回到解放前。
咱们先打好地基,把三个核心概念掰扯清楚。
1.1 什么是固件烧录
固件烧录,说白了就是把编译好的二进制代码,写入芯片的非易失性存储器里。比如 Flash、EEPROM 这些。芯片上电后,从那里把代码读出来运行。
你想想看,芯片出厂时就是一块白板,啥也没有。你得把“灵魂”灌进去,它才能干活。这个灌的过程,就是烧录。
烧录的本质:将二进制镜像文件,通过某种物理接口(如串口、JTAG、USB),传输到目标芯片的存储介质中,并确保写入后的数据与原始文件完全一致。
我个人习惯把烧录分为三类:
- 量产烧录:产线上成百上千片一起烧,讲究速度和稳定性。我见过一个项目,因为烧录时序没调好,良率直接掉了 5 个点,老板脸都绿了。
- 开发调试烧录:工程师自己用,频率高,但每次只烧一两片。这时候更看重灵活性和调试能力。
- 远程/OTA 烧录:设备已经在客户手里了,通过网络或无线升级。这种场景下,镜像校验就是命根子,烧坏了可没人帮你拆机。
1.2 串口烧录的原理
串口烧录,是嵌入式开发里最古老、也最通用的方式。为什么?因为几乎所有 MCU 都带 UART,成本低,接线简单,就三根线:TX、RX、GND。
它的原理其实不复杂,我画个图你就明白了。
流程其实就三步:
- PC 上位机把固件镜像读进来,按协议分包,加上帧头、帧尾、校验码,通过串口发出去。
- 芯片里的 Bootloader收到数据包,先校验对不对,没问题就解析出有效数据。
- 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 小结一下
这一章咱们把烧录的基础概念捋了一遍。固件烧录就是把二进制代码写进芯片,串口烧录是最常见的实现方式,而镜像校验是保证烧录不出错的最后一道防线。
你记住一句话:烧录可以慢,但不能错。 尤其是在产线上,一片芯片烧坏了,可能意味着整个批次都要返工。镜像校验就是你的保险丝,千万别省。