尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

烧录地址0x08000000/0x6000/0的区别:从内存映射到ESP32分区表

烧录地址0x08000000/0x6000/0的区别:从内存映射到ESP32分区表 干嵌入式的人十个有八个被“烧录地址”锤过。尤其是同时玩过 STM32、ESP32、传统 51 或者各种国产 M0 核芯片之后你会在下载工具里遇到三种非常迷惑的地址有人写0有人写0x08000000还有人写0x6000。最烦的是这三个地址在各自的工程里居然都能正常工作。我当年也在这个问题上卡了很久。今天咱们一次性把这层窗户纸捅破烧录地址不是随便填的数字它背后对应的是芯片的内存映射、引导启动方式、以及你手里下载工具对这个字段的定义。搞清楚这三件事你再看那些0、0x08000000、0x6000就能一眼判断出它到底在说什么。这篇文章适合刚接触单片机烧录的新手也适合正在同时维护多种芯片工程的开发者。我会从原理讲到实操再把 ESP32 怎么看烧录地址这件事单独拆开最后分享一些我踩过的坑和排查经验。1. 先搞清楚烧录地址到底是个什么东西1.1 芯片的“门牌号”内存映射决定了地址的骨架芯片内部不是一整块可以随便乱写的内存。每一颗芯片在流片出厂时就已经把地址空间规划死了哪一段是 Flash、哪一段是 SRAM、哪一段是外设寄存器都在芯片手册的“Memory Map”章节里写得清清楚楚。拿最常见的 STM32F103 举例芯片上电后0x08000000是内置 Flash 的起始地址代码要持久化存储就只能写到这里0x20000000是 SRAM 的起始地址运行时的变量、栈都在这0x40000000往下是各种外设寄存器GPIO、串口、定时器都在这个区域。调试器下载固件时本质上就是“按门牌号送快递”你把地址填成0x08000000它就把数据写进0x08000000对应的 Flash 空间。如果填一个芯片根本不认识的地址工具要么直接报错要么把数据写到一个不存在的地方白干。所以烧录地址的第一个底层逻辑是它必须落在目标芯片 Flash 的实际映射区间里。芯片的地址规划不同烧录地址就不同这是“为什么有时是 0有时是 0x08000000有时是 0x6000”的根源所在。1.2 同叫“烧录地址”工具里的实际含义却分三种事情麻烦在不同下载工具和不同文档里说的“烧录地址”根本不是一个维度上的东西。我总结下来至少有三种常见含义。第一种绝对物理地址。这是 STM32 这类 Cortex-M 芯片的典型情况。芯片手册写了 Flash 从0x08000000开始下载工具就把这个地址填进去。你写0x08000000就是告诉调试器把固件放在芯片物理 Flash 的这个位置。第二种Flash 分区偏移。ESP32 这类带 Bootloader 和分区表的 SoC 更常用这种方式。芯片的 Flash 往往从0x0000开始但前面有好几段被 Bootloader、分区表、NVS 配置占用了真正放 App 的地址是一个相对 Flash 起点的偏移比如默认的0x10000。有些定制工程里App 偏移可能是0x6000。这个偏移要和项目里的分区表完全对应不能拍脑袋填。第三种文件内偏移或者干脆被工具忽略。很多工具在烧录 HEX 文件时是不会看你填的起始地址的。因为 HEX 文件每一行都自带绝对地址工具直接读文件里的地址就能定位。这时候你填0或者填别的什么数字结果都一样给很多人造成了“填 0 也能跑”的错觉。除了上面三种还有一个经常被混为一谈的概念链接地址和运行地址。链接地址Link Address是编译链接阶段代码认为自己会被加载到的地址它由链接脚本.ld文件或 IDE 的 Target 设置决定。烧录地址是下载工具把固件数据写入芯片物理 Flash 的地址。对 STM32 这类内部 Flash 芯片来说两者通常一样但对 ESP32 这种从外部 SPI Flash 启动的芯片来说App 在 Flash 里的偏移是0x10000但它运行时的主要代码段入口可能在0x400806F0。如果你把这两个地址当成一回事后面排查问题会绕很远的路。概念含义典型值由谁决定绝对物理地址芯片内部 Flash 映射的固定地址0x08000000芯片厂商/数据手册Flash 分区偏移相对 Flash 起点的偏移用于定位某个分区0x10000、0x6000工程分区表/Bootloader文件内偏移裸 bin 文件从头开始的位置或 HEX 自带地址时的忽略项0x0下载工具定义链接地址/运行地址代码编译时认定的运行位置0x400806F0链接脚本/编译器2. 三个典型地址三个完全不同的故事2.1 0x08000000ARM Cortex-M 内部 Flash 的“出厂门牌”0x08000000是 STM32 以及大量国产 ARM Cortex-M 芯片的内置 Flash 起始地址。为什么偏偏是这个数这是芯片设计时地址映射规划的结果厂商把 Flash 放到了这一段把 SRAM 放到了0x20000000附近外设放到高段地址。没太多为什么它就是硬件规定。不过这里有一个很多人容易忽略的细节Cortex-M 内核上电复位后CPU 是从0x00000000读栈顶地址、从0x00000004读复位向量的并不是直接从0x08000000跑。芯片能够正常从0x08000000启动是因为内部做了“内存别名映射”把0x00000000这一段在启动时映射到了0x08000000所代表的 Flash。所以你会发现一个现象对 STM32 来说有些人下载地址填0x08000000也有人填0x00000000程序都能跑。填0x08000000是直接写 Flash 物理地址填0x00000000是利用了内核的别名映射区。但稳妥做法永远是填数据手册里明确写的 Flash 起始地址0x08000000这也是 ST-Link、J-Link 等工具默认的下载地址。实际项目的经验是主程序烧在0x08000000如果做了 BootloaderApp 一般要往后偏移比如0x08010000。这时候不能只改下载地址还得改工程的链接脚本和向量表偏移量否则 App 烧进去也能跳转但中断一进来就飞。2.2 0x6000ESP32 生态里的“分区偏移地雷”接着看0x6000。这个地址出现的地方通常是 ESP32、ESP8266 或者类似的 Wi-Fi SoC 项目里。它和0x08000000完全不是一个概念0x6000不是芯片 Flash 的物理起始地址而是某个工程里 App 分区的偏移量。ESP32 的启动流程大概是这样的芯片内部 ROM Bootloader 先运行然后去 Flash 的0x1000位置加载二级 Bootloader二级 Bootloader 再根据 Flash0x8000位置的分区表决定从哪个偏移加载真正的 App 代码。默认的官方分区表把 App 放在0x10000这也是绝大多数 ESP32 工程烧录日志里出现的地址。那0x6000是哪来的它经常出现在一些模组厂商、定制板卡或者非标准分区表的工程里。为了让 Bootloader、分区表、配置数据、App 排布更紧凑有人会把 App 的偏移放到0x6000。这本身没有对错只要工程里每一个人都遵循同一个分区表就行。但问题在于很多教程只扔出来一句“烧录地址填 0x6000”却不解释这个数字来自哪里。你一看别人的成功经验照着填了结果自己板子上的 Bootloader 去的还是默认分区表于是怎么烧都起不来。所以遇到0x6000这种地址第一反应绝对不是背下来而是去翻这个工程的partitions.csv、sdkconfig、编译日志或者启动脚本确认 App 分区偏移到底是多少。它可以是0x6000也可以是0x10000还可以是别的值关键是要和分区表一致。2.3 0看着像“从零开始”其实是“多重影分身”最后一个0更迷惑因为它的含义取决于你正在用的工具和芯片。有时候填入 0 是合法的。比如你烧的是 HEX 文件工具会直接读取文件内地址这时候填 0 本质上是被忽略的又比如某些 STM32 工程里有人填 0是因为利用了0x00000000到0x08000000的启动映射工具通过别名区完成了写入。但有时候填 0 就是大坑。如果你给 ESP32 烧裸 bin 文件把 App 的烧录偏移填成0x0000会把 App 覆盖到 Bootloader 区域轻则启动失败重则整板变砖。还有的时候工具里的0表示“从文件头开始”它只是一个相对位置不是芯片的物理地址。在遇到“填 0 也能用”的情况时我给的建议是先看工具字段的名称是“Target Address”目标地址还是“File Offset”文件偏移。如果是 Target Address0 往往代表芯片的起始映射区需要确认芯片有没有别名映射如果是 File Offset那 0 就只是文件开头跟芯片地址无关。搞清楚这一点你才不会被“0”骗到。3. 实操怎么看 ESP32 的烧录地址不再靠猜3.1 最直接看烧录日志中 esptool 打印出的地址ESP32 官方烧录工具是 esptool无论你用 ESP-IDF、Arduino 还是 PlatformIO底层最终都会调用它。在日志里找到write_flash这一行后面跟着的就是烧录地址和对应文件。比如 ESP-IDF 编译烧录时常见日志长这样esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write_flash \ --flash_mode dio --flash_size detect --flash_freq 80m \ 0x1000 build/bootloader/bootloader.bin \ 0x8000 build/partition_table/partition-table.bin \ 0x10000 build/hello_world.bin这里面的0x10000就是 App 固件实际写入 Flash 的位置。你可以看到0x1000放的是 Bootloader0x8000放的是分区表0x10000放的是用户 App。这个地址不靠猜编译烧录时已经打印出来了。如果你用的是 Arduino IDE 或 PlatformIO上传日志里的 esptool 命令同样会显示地址。大多数情况下 Arduino-ESP32 的默认 App 偏移也是0x10000个别开发板配置可能不同但日志里一定会写。3.2 标准答案直接看分区表ESP32 的固件布局不是散装的所有分区都记录在分区表里。想确认某个固件应该烧到哪个地址最标准的做法就是打开项目里的partitions.csv文件里面每一行都写着分区类型、子类型、偏移和大小。# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x4000, otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x100000,看到factory这一行了没有它的 Offset 是0x10000四个字节十六进制就是 65536。这种布局下你的 App 就必须烧到0x10000。如果工程用的非标准分区表把factory写成了0x6000那么烧录地址就跟着变成0x6000。ESP-IDF 里也可以用idf.py menuconfig查看分区表配置Serial flasher config→Flash size和Partition Table。如果你选的是 “Single factory app”偏移通常就是0x10000如果你选 “Custom partition table CSV”那么以你自己那份 CSV 里的 Offset 为准。3.3 进阶验证bin 文件里的段地址不等于 Flash 偏移有人会问我拿到一个别人编译好的 bin 文件能不能直接看出它该烧到哪个地址可以用 esptool 的image_info命令读一下esptool.py image_info firmware.bin输出里能看到入口地址和各个段的加载地址比如Entry point: 0x400806f0这种。但注意这些地址是 CPU 运行时的加载地址不是 Flash 里的偏移。ESP32 的代码段会被映射到0x40000000附近的指令总线地址数据段可能落在0x3f400020附近这跟烧录时填的0x10000完全不是一回事。因此想验证 bin 文件烧录地址最靠谱的还是看它对应的分区表、编译脚本或者烧录命令。bin 文件本身不带“我该烧到 0x6000 还是 0x10000”这种信息它是裸数据必须由外部告诉工具放在哪里。这也解释了为什么很多人喜欢把工程脚本里write_flash后面的地址保留下来因为丢了它等于丢了一半的“地图”。4. 地址填错后的故障现场与排查思路4.1 故障现象与排查速查表烧录地址填错不一定立刻报错。很多时候下载工具显示“擦除成功”“写入成功”看起来一片祥和但一上电设备要么没反应要么陷入不断重启的循环。我把实际遇过的现象整理成了表格方便你对照排查。现象可能原因排查方向烧录成功上电完全没输出App 覆盖了 Bootloader 区域或向量表取错位置检查烧录日志里的地址重新按正确偏移烧写 Bootloader 和 App反复重启、串口打印乱码或卡在引导阶段分区表与 App 的烧录偏移不匹配确认使用的分区表重烧partition-table.bin再烧 App下载工具直接报地址越界或无法写入填写的地址不在芯片 Flash 映射区间内查数据手册 Memory Map确认 Flash 起始地址和大小程序能跑但一进中断就死机/跑飞链接地址和实际烧录地址不一致向量表偏移没改检查链接脚本的 Flash 起始地址以及启动文件里的 VECT_TAB_OFFSET填 0 能跑换台设备又不行0 可能依赖芯片启动映射或 HEX 文件自带地址不是通用规则改成数据手册明确标注的 Flash 实际地址4.2 两个真实翻车现场第一个案例是我自己把 ESP32 的 App 烧到了0x0000。当时是手写了一条 esptool 命令想快点烧一个测试固件结果地址直接用了 0。烧录过程没报错但板子重启后彻底没反应串口也没有任何输出。排查下来0x0000是 Bootloader 所在区域App 写进去把第二级 Bootloader 覆盖了。后来重新按0x1000、0x8000、0x10000依次烧好 Bootloader、分区表和 App板子才恢复正常。那个板子幸好只是测试板如果是量产设备这一步操作很可能就直接导致返工。第二个案例是关于 STM32 的。有个工程为了做 Bootloader 跳转把 App 的链接地址改到了0x08010000但下载工具里还是默认的0x08000000。结果烧录后直接覆盖了 Bootloader两个程序全没了。这类问题排查起来很迷惑因为烧录日志是成功的编译也正常只有仔细比对链接脚本和下载地址才会发现一个程序“认为自己住在 0x08010000”但实际被放到了“0x08000000”住在错误的位置跑起来自然面目全非。遇到烧录地址导致的异常我的排查顺序一般是这样的先看工具日志确认实际写入的地址是多少不是看界面里填了什么而是看日志最终执行的命令再查芯片数据手册的内存映射确认这个地址在不在 Flash 区间然后查分区表或链接脚本确认代码期望的烧录位置和实际烧录位置是否一致最后排查向量表偏移和启动配置。5. 实战总结我这些年养成的三个地址习惯5.1 拿到新平台先找三个信息源现在每接触一款新芯片我不会急着点 Download而是先做三件事翻数据手册的内存映射章节看 Flash 起始地址查官方烧录工具或 Flash Tool 的预设地址看官方默认把固件放哪最后看 IDE 的烧录日志确认实际地址。这三个信息源互相印证基本就不会犯“拿 STM32 的地址套 ESP32”这种低级错误。5.2 下载地址和链接地址永远分开记我见过太多人把这两者混为一谈。链接地址是编译器在链接阶段给代码定义的“逻辑位置”它决定代码跳转、变量寻址时按什么地址来算。烧录地址是下载工具把二进制写入芯片时的“物理位置”。对内部 Flash 芯片来说两者常常重合所以大家没觉得有问题但到了 ESP32、外部 Flash、Bootloader 跳转这种场景两者一旦不匹配程序就会跑飞。你可以在一个工程里同时打开.ld文件和烧录日志对比一下这两个地址就知道它们是不是同一个数了。5.3 改地址永远不是改一个地方的事改烧录地址从来不是下载工具里填个新数字就完事的。你要同时检查 Bootloader 的跳转逻辑、分区表的偏移定义、链接脚本的加载地址、启动代码里的向量表偏移。只改其中一环另外几环没跟上运行结果就是各种奇怪问题。用 ESP32 时分区表是真正的中心用 STM32 类芯片时链接脚本和 Bootloader 跳转地址是核心。先把这些关系理顺再动地址才能稳。最后再分享一个小技巧无论用什么工具真正决定烧录地址的永远是“工具日志里最终输出的那行命令”而不是你在界面配置框里填了什么。养成看完整日志的习惯很多地址问题不用查手册就已经有答案了。
返回列表