返回00-项目导航;先读01-工程版本与真实现状。
本篇解释真实工程怎样变成固件,以及代码、字库和数据分别占用什么空间。命令仅供后续练习,本次没有执行构建或烧录。
一、从三个源文件到固件
驱动验证工程的 main/CMakeLists.txt 登记了:
idf_component_register(
SRCS "main.c" "epd_ssd1683.c" "epd_gui.c"
INCLUDE_DIRS "."
REQUIRES driver
)
SRCS决定该组件的哪些源文件参与编译,INCLUDE_DIRS指定头文件搜索位置,REQUIRES声明组件依赖。头文件存在不代表对应实现已经参与链接;源文件放进目录也不代表构建系统一定会使用。
构建可以按下列流程理解:
- 源代码和工程配置
- 构建配置生成
- 逐个编译为目标文件
- 链接为可执行文件
- 生成烧录镜像
- 结合分区和偏移烧录
查看原始 Mermaid 源码
flowchart LR
A[源代码和工程配置] --> B[构建配置生成]
B --> C[逐个编译为目标文件]
C --> D[链接为可执行文件]
D --> E[生成烧录镜像]
E --> F[结合分区和偏移烧录]
idf.py是操作入口,CMake组织依赖,Ninja执行构建步骤,编译器处理C/C++,链接器合并实现并分配地址。.elf保留调试符号,.bin用于烧录;二者不能互相替代。
工程能编译说明语法、类型和链接等检查通过,不能证明排线方向、忙信号、升压或页面逻辑正确。
二、配置的来源有先后
本项目同时有 Kconfig.projbuild、sdkconfig.defaults 和 sdkconfig:
Kconfig.projbuild定义可配置项及默认值,例如屏幕数据引脚。sdkconfig.defaults提供建立配置时使用的默认项,不能把它当作覆盖全部现有配置的指令。sdkconfig保存当前实际工程配置,构建还会生成供源码使用的配置头文件。
驱动验证工程存在一个具体例子:配置菜单提供屏幕宽高,但 epd_ssd1683.h中仍直接定义 EPD_WIDTH=400、EPD_HEIGHT=300。所以仅在菜单里改分辨率,不代表画布和驱动就全部适配了新屏。
另一例是 N16R8的注释与8MB Flash配置不一致。应按实物、配置、启动记录三方核实,不能选看起来最像正确答案的一项。
三、Flash、内部运行内存与外部运行内存
| 类型 | 主要用途 | 断电后 | 本项目中的例子 |
|---|---|---|---|
| Flash | 固件、只读字库、配置与文件分区 | 一般保留 | 程序镜像、非易失配置、图片文件 |
| 内部运行内存 | 任务栈、变量、通信和驱动缓冲 | 通常丢失 | 静态画布、实时任务数据 |
| 外部运行内存 | 扩展可用堆空间 | 通常丢失 | 验证工程优先尝试分配的画布、加载的字库 |
Flash容量不是可直接用于malloc()的内存容量。 一个16MB Flash的系统仍可能因为内部堆不足而请求失败。
单色400×300画布每像素占1比特:
400 × 300 ÷ 8 = 15000字节
驱动验证工程的 epd_gui.c:23 起先尝试外部运行内存,失败后再尝试普通分配。只有拿到非空指针才可写画布。
信息终端工程的 main.c:100 与 main.c:212 则分别定义当前画布和上次画布,共30000字节静态数组。它们不是同一个15000字节区域;最终放在哪个内存段,要看链接映射文件,而不是因为“启用了外部运行内存”就默认它们在那里。
四、分区是Flash的地址规划
信息终端工程启用了自定义 partitions.csv:
| 名称 | 起始偏移 | 大小 | 用途 |
|---|---|---|---|
nvs |
0x9000 |
0x5000 |
小型键值配置 |
phy_init |
0xe000 |
0x1000 |
射频初始化数据区域 |
factory |
0x10000 |
0x300000 |
应用镜像分区,3MiB |
storage |
0x310000 |
0xCF0000 |
文件系统分区 |
最后一个分区结束地址为 0x1000000,对应16MiB地址空间。这里MiB按1024×1024字节计算。
大部分空间留给文件系统,不代表应用可以长到16MB;应用仍受3MiB分区限制。当前表也没有两个升级应用槽,不能因为留有空间就说已具备远程升级机制。
驱动验证工程使用单应用分区配置,不能把信息终端的分区表直接套过去。
五、字库为什么特别占空间
信息终端有中文和大字号字库,main/CMakeLists.txt明确启用 FONT48_INCLUDE,其他尺寸根据编译开关选择。源代码文件占了数兆字节,不等于链接后二进制也占同样大小,因为源文件包含文本形式的十六进制数字、逗号和注释。
反过来,字库被引用并参与链接后,就会占固件分区或只读数据空间。判断应看.map、idf.py size和实际镜像大小。
对于从存储卡加载的字库,还要算加载后的运行内存、读取失败回退和字形索引;它节省固件空间时,可能增加启动时间和存储卡依赖。
六、建立可信的构建记录
当前两个工程的旧构建缓存都带有旧目录。验证工程的批处理脚本还切到了另一个项目,所以不要先双击脚本再根据最后一行猜编译了什么。
后续在正确开发环境中,可以按以下顺序练习;这些命令未在本次运行:
# 先在终端进入所选工程目录
Get-Location
idf.py --version
idf.py -B build_current_check build
idf.py -B build_current_check size
先确认终端已加载正确ESP-IDF环境;idf.py --version应实际成功。使用新的构建目录可以避开原有缓存,但仍会写入工程文件夹,只在准备开发时执行。
记录至少包含:工程绝对路径、源码版本或校验值、ESP-IDF版本、目标芯片、配置副本、完整退出结果、镜像大小及警告。遇到分区超限、字库膨胀、缺头文件或找不到实现时,分别沿分区、编译列表与链接关系检查。
七、练习与验收
- 不运行程序,指出三个主要源文件在哪里登记,解释删去绘图源文件可能出现什么链接错误。
- 算出一帧、两帧单色画布占用;再算相同分辨率每像素16比特时的占用,解释当前项目为何不使用这种格式。
- 核算分区末地址,区分“整个Flash还很大”和“应用分区已满”。
- 后续取得一次新的成功构建日志,与旧缓存对照,明确哪一份证明当前源码可构建。
官方参考:乐鑫构建系统、乐鑫堆内存分配。具体源码位置按本次核对版本记录。
来源与关联索引
- 来源
- 嵌入式比赛
- 原笔记
- 项目/墨嵌墨水屏/知识讲解/09-构建配置与内存分区.md
- 资料性质
- 实践指南。原文的版本、来源与验证边界继续保留。