2. 理解蓝牙芯片内存布局与启动流程

任务目标

以 Nordic nRF52832 为例,理解蓝牙芯片的 Flash 与 RAM 内存布局,以及从复位到进入 main() 的启动流程。通过阅读链接脚本与启动文件,明确代码段、数据段、堆栈的存放位置,并掌握如何通过编译生成的 .map 文件验证实际内存占用。

内存布局核心概念

nRF52832 内置 512KB Flash(地址 0x00000000–0x0007FFFF)和 64KB RAM(地址 0x20000000–0x2000FFFF)。Flash 中依次存放:中断向量表、代码段(.text)、只读数据(.rodata)、初始化数据镜像(加载域)。RAM 中存放:RW 数据(.data,从 Flash 拷贝而来)、零初始化数据(.bss)、堆(heap)与栈(stack)。

关键文件与依赖

本案例基于 Nordic SDK 17.1.0 + SoftDevice s132。需要以下文件:

  • 链接脚本:nrf52832_xxaa.ld(位于 examples/ble_peripheral/ble_app_blinky/pca10040/s132/armgcc/)
  • 启动文件:gcc_startup_nrf52.S(位于 components/toolchain/gcc/)
  • 链接映射文件:编译后生成的 ble_app_blinky_pca10040_s132.elf.map

链接脚本关键段解析

以下为链接脚本中决定内存布局的核心片段(已省略非关键部分):

```ld /* nrf52832_xxaa.ld 关键片段 */ MEMORY { FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x80000 /* 512KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000 /* 64KB */ } SECTIONS { .text : { KEEP(*(.isr_vector)) /* 中断向量表,起始地址 0x0 */ *(.text*) /* 代码段 */ *(.rodata*) /* 只读常量 */ _etext = .; /* 代码段结束地址(加载域) */ } > FLASH .data : { _data_start = .; *(.data*) /* 已初始化全局变量 */ _data_end = .; } > RAM AT > FLASH /* 加载域在 Flash,运行域在 RAM */ .bss : { _bss_start = .; *(.bss*) /* 零初始化变量 */ *(COMMON) _bss_end = .; } > RAM .stack (NOLOAD) : { . = ALIGN(8); . = . + 0x1000; /* 4KB 栈空间 */ _stack_top = .; } > RAM } ```

启动流程(gcc_startup_nrf52.S)

复位后硬件从 Flash 地址 0x00000000 读取初始栈指针(SP),从 0x00000004 读取复位向量并跳转执行。启动文件完成以下步骤:

  1. 将 .data 段从 Flash 加载域拷贝到 RAM 运行域(由 _data_start、_data_end、_etext 三个符号控制);
  2. 将 .bss 段清零(从 _bss_start 到 _bss_end);
  3. 调用 SystemInit() 配置时钟;
  4. 跳转至 main()。

验证方法

编译工程后,在生成的 .map 文件中搜索以下符号,确认内存布局符合预期:

  • _etext:应接近 0x0 起始的某个地址,表示代码段结束位置;
  • _data_start:应等于 0x20000000(RAM 起始);
  • _stack_top:应等于 0x20010000(RAM 末尾,64KB 对齐);
  • FLASH USED 与 RAM USED 汇总行:确认总占用未超出芯片容量。

典型预期输出(示例,非实测):_etext = 0x0002a5c0,_data_start = 0x20000000,_stack_top = 0x20010000。实际数值取决于编译选项与代码量,但地址范围必须落在对应存储区域内。

常见问题

  • 栈溢出:若 .stack 与 .bss 或堆重叠,链接会报错;可通过增大 RAM 中栈大小(修改 0x1000)解决,但需确认总 RAM 占用不超 64KB。
  • SoftDevice 占用:若使用 SoftDevice,Flash 起始地址需偏移(如 0x26000),链接脚本中 ORIGIN 需相应调整,否则中断向量冲突。