在《系统总览(01):编译配置如何决定固件功能》 中,用 ANC 介绍了配置对源码的影响。实际构建项目时,还要先弄清楚该执行什么命令、得到什么文件,以及是否需要后续处理。
这里通过我在实际项目开发中所使用的BES 2700这款芯片的SDK 所用的构建命令进行介绍。包含构建目标、输出路径和 CRC 处理方式。
一、执行编译命令
编译bes工程,可以在工程根目录打开终端,输入:
make T=best1306p -j
Make 会读取工程里的 Makefile,并按其中的规则完成构建。这条命令中,T=best1306p 是用于选择这份工程的构建配置,-j 用于并行构建。
平时修改的 .c、.cpp 源码。经过编译、链接,源码会变成芯片能够执行的程序,并与相关数据一起组成固件。本例生成的固件文件路径是 out/best1306p/best1306p.bin。
二、为固件补充校验信息
2.1 CRC 后处理
编译生成固件后,有些项目还需要为后续升级补充校验信息。例如,接收方要检查收到的数据是否与准备好的固件一致,就需要一个用于比较的校验值。这里以 CRC32 为例。
CRC 的中文名称是“循环冗余校验”。电脑先算好一个值,接收方拿到数据后,再按相同范围、相同算法算一次,与保存的值比较:
%%{init: {"flowchart": {"htmlLabels": true, "wrappingWidth": 1000, "rankSpacing": 30}, "themeVariables": {"lineColor": "#71829a"}}}%%
flowchart TB
subgraph R0["① 电脑上准备固件时"]
direction LR
R0N0["生成固件"]
R0N1["计算 CRC"]
R0N2["将校验值填入固件"]
R0N0 --> R0N1 --> R0N2
end
class R0N0,R0N1,R0N2 blue;
style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
subgraph R1["② 后续设备接收后"]
direction LR
R1N0["读取收到的固件数据"]
R1N1["耳机重新计算 CRC"]
R1N2["与固件中保存的 CRC 比较"]
R1N0 --> R1N1 --> R1N2
end
class R1N0,R1N1,R1N2 green;
style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
R0 -..->|"后续设备收到文件"| R1
classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;对比结果不同,说明数据在传输中发生了变化;结果相同,表示通过校验,数据传输正常。
这份工程在 build_info.c 中预留了 CRC 字段:
图 1:build_info.c 预留的 CRC 校验字段
这里代码中给了一个默认参数。等固件生成后,根据实际数据计算 CRC,将结果填入这个字段。
在这份示例工程中,这个值用于 OTA 升级。在给设备升级固件中,耳机接收完成会调用下面的 CRC 检查。
图 2:ota_control.cpp 中接收完成后的 CRC 检查调用
2.2 Python 脚本的调用方式
计算 CRC、将结果写入文件,可以交给一个Python 脚本完成。工程中采用了generate_crc32_of_image.py,调用命令为:
python3 generate_crc32_of_image.py out/best1306p/best1306p.bin
命令里有三个部分:
python3:启动 Python 3 解释器。generate_crc32_of_image.py:指定要执行的 CRC 处理脚本。out/best1306p/best1306p.bin:指定本次要处理的输入固件。
这条命令的最后一个参数指定输入固件。脚本要读取它的数据,计算 CRC;在处理其他固件,也可以复用这个脚本,只需要指定输入固件路径就行。
脚本处理前后的文件关系如下:
%%{init: {"flowchart": {"htmlLabels": true, "wrappingWidth": 1000, "rankSpacing": 30}, "themeVariables": {"lineColor": "#71829a"}}}%%
flowchart LR
N0["best1306p.bin"]
N1["Python 脚本<br/>计算并填入 CRC"]
N2["best1306p_converted.bin"]
N0 --> N1
N1 --> N2
class N0,N1,N2 blue;
classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;当前脚本对 CRC 数值字段之前的数据计算 CRC32,生成带 _converted.bin 后缀的新文件,这样就得到了 2 个固件,一个是不带 CRC 校验的固件,另一个是带 CRC 校验的固件。
三、把重复命令写成脚本
为了方便编译,可以把编译和构建命令 写进一个文本文件,交给 Shell 脚本处理。
例如,新建一个 build.sh脚本文件,将下面的编译命令写进去:
make T=best1306p -j && python3 generate_crc32_of_image.py out/best1306p/best1306p.bin
中间的 && 表示:只有前面的构建命令成功,才执行后面的 CRC 处理命令。
然后下次编译项目时,只需要执行
bash build.sh
Bash 就会读取文件中的命令,依次完成构建和 CRC 后处理。 下一篇:编译配置(02):构建目标与配置覆盖