2. 固件基础概念:固件与软件的区别、固件文件格式、固件版本管理
大家好,我是你们这节课的讲师。咱们直接切入正题。做烧录工具开发,如果连固件本身都搞不清楚,那后面调试起来会非常痛苦。我个人习惯把固件理解成“半硬半软”的东西——它跑在芯片里,但又不像PC软件那样可以随便装。
2.1 固件与软件:到底差在哪?
很多人觉得固件就是软件,其实不完全对。我刚开始做嵌入式那会儿,也犯过这个迷糊。有一次把PC上的一个动态库直接扔到MCU里跑,结果可想而知——根本跑不起来。
咱们用一张表来对比,这样更清楚:
| 对比维度 | 固件 (Firmware) | 软件 (Software) |
|---|---|---|
| 存储介质 | ROM、Flash、EEPROM | 硬盘、SSD、光盘 |
| 更新频率 | 低,通常出厂后很少改 | 高,随时可以打补丁 |
| 运行环境 | 裸机或RTOS,直接操作寄存器 | 操作系统之上,依赖API |
| 调试手段 | JTAG/SWD、串口打印、逻辑分析仪 | IDE断点、日志、性能分析器 |
| 出错后果 | 设备变砖,通常需要重新烧录 | 程序崩溃,重启即可恢复 |
核心区别一句话:固件是硬件和软件之间的“翻译官”,它直接控制硬件行为。软件出了问题可以重装,固件出了问题——嗯,你可能得拿编程器来救了。
为什么会这样?因为固件在编译时就已经确定了硬件资源的分配。你想想看,一个8KB的Flash空间,代码放进去就不能再改了。而PC软件有虚拟内存,有动态链接库,灵活得多。
2.2 固件文件格式:Hex、Bin、Elf
做烧录工具,天天跟这三种格式打交道。我见过不少新手把Hex当Bin直接烧,结果芯片不工作,还以为是硬件坏了。咱们一个一个说清楚。
2.2.1 Hex文件(Intel HEX格式)
Hex文件是文本格式,用ASCII字符表示二进制数据。每一行以冒号开头,包含长度、地址、记录类型、数据和校验和。
举个例子:
:020000040800F2
:10000000C0000000C1000000C2000000C3000000C4
:04000000C4000000C8
:00000001FF
这里要注意:Hex文件里包含了地址信息。烧录工具会解析这些地址,把数据放到正确的位置。我个人习惯用Hex做量产烧录,因为它自带校验,不容易出错。
小技巧:如果你用Hex文件烧录后芯片不工作,先检查一下起始地址对不对。我曾经遇到过客户把0x08000000的Hex烧到0x00000000,结果芯片直接“装死”。
2.2.2 Bin文件(二进制文件)
Bin文件就是纯粹的二进制数据,没有地址信息,没有校验。它是什么?就是芯片Flash里最终存储的内容,一个字节一个字节地排列。
比如上面那个Hex文件,转成Bin就是:
C0 00 00 00 C1 00 00 00 C2 00 00 00 C3 00 00 00 C4 00 00 00
Bin文件的好处是体积小,传输快。但坏处也很明显——你必须知道它应该烧到哪个地址。我建议在烧录工具里固定好起始地址,或者从配置文件中读取。
警告:Bin文件没有校验和,烧录过程中如果出现位错误,你根本不知道。所以用Bin文件时,一定要在烧录完成后做回读校验。
2.2.3 Elf文件(可执行与链接格式)
Elf文件是编译器的“原生”输出格式。它包含的信息最全:代码段、数据段、调试信息、符号表、重定位信息等等。
Elf文件的结构大致如下:
| 段名称 | 内容 | 烧录时是否需要 |
|---|---|---|
| .text | 代码指令 | 是 |
| .data | 已初始化的全局变量 | 是 |
| .bss | 未初始化的全局变量 | 否(运行时清零) |
| .debug | 调试符号信息 | 否 |
| .symtab | 符号表 | 否 |
Elf文件在开发阶段非常有用,因为你可以用GDB加载它进行源码级调试。但量产时,我一般不用Elf——它太大了,而且包含很多无关信息。通常的做法是从Elf里提取出Hex或Bin。
2.3 固件版本管理
这一点我吃过不少亏。早期做项目时,版本号随便写,结果客户反馈问题,我根本不知道他手里烧的是哪个版本。后来我养成了几个习惯:
- 版本号嵌入固件:在代码里定义一个全局字符串,比如
const char* firmware_version = "v2.1.3",编译时自动更新。 - 烧录时记录版本:烧录工具在烧录完成后,把版本号和芯片ID写入日志文件。
- 支持版本回退:如果新固件有问题,能快速烧回旧版本。我建议保留最近3个版本的固件文件。
版本号的格式,我推荐语义化版本:
| 版本号 | 含义 | 举例 |
|---|---|---|
| 主版本号 | 不兼容的API修改 | v2.0.0 → v3.0.0 |
| 次版本号 | 向下兼容的功能新增 | v2.1.0 → v2.2.0 |
| 修订号 | 向下兼容的问题修正 | v2.1.0 → v2.1.1 |
避坑指南:我曾经遇到过客户拿着一个v1.0.0的固件说有问题,结果我查了半天,发现他烧的是v0.9.0——版本号写错了。从那以后,我强制要求所有固件在启动时通过串口打印版本号,并且烧录工具必须校验版本号是否匹配。
2.4 三种格式的转换关系
为了让你更直观地理解这三种格式的关系,我画了一张图:
从这张图你可以看到:源代码经过编译器生成ELF文件,然后通过objcopy等工具转换成Hex、Bin或SREC格式。ELF是“母版”,包含了所有信息;Hex和Bin是“烧录版”,只包含必要的数据。
我个人在开发阶段用ELF调试,在测试阶段用Hex验证,在量产阶段用Bin(配合地址配置文件)。这样既保证了灵活性,又提高了效率。
经验之谈:如果你在烧录时遇到“校验失败”的错误,先别急着怀疑硬件。用Hex文件烧录一次,再用Bin文件烧录一次,对比一下两次的结果。我遇到过好几次,都是因为Bin文件的起始地址设置错了。
好了,这一节的内容就到这里。固件基础概念是烧录工具开发的基石,搞清楚了这些,后面讲烧录协议和调试技巧时,你会觉得轻松很多。