嵌入式从零开始(第七篇):存储与地址 —— 大小端、内存映射、4GB 空间之谜

张开发
2026/6/10 9:29:13 15 分钟阅读
嵌入式从零开始(第七篇):存储与地址 —— 大小端、内存映射、4GB 空间之谜
前言两个“内存”概念的冲突初学 STM32 时我们一定会遇到两个矛盾的说法数据手册说Flash 64KBRAM 20KB。教程里讲32 位单片机可以寻址 4GB 空间。到底哪个是对的 ——两个都对但说的是不同维度。64KB Flash / 20KB RAM是芯片内部实际存在的物理存储器大小。4GB 寻址空间是 CPU 能访问的地址范围地址总线的能力并不代表真有 4GB 的存储器。就像你家有一个 100 平米的房子实际存储但你手里的钥匙可以打开整栋楼 1000 户的门寻址能力——只不过其中 990 户是空的保留地址你不能随便闯进去。一、存储器映射一张 4GB 的“地址地图”存储器映射Memory Map是芯片设计者预先定义的一张表规定了 4GB 地址空间里每一块区域用来干什么。ARM Cortex-M 内核规定了一个统一的 4GB 地址布局而芯片厂商如 ST在这个框架内填充自己的具体内容。以 STM32F103 为例地址从低到高起始地址结束地址大小用途说明0x000000000x1FFFFFFF512MB代码区Code通常映射到 Flash存放程序0x200000000x3FFFFFFF512MBSRAM 区实际只有 20KB 在这里0x400000000x5FFFFFFF512MB外设寄存器区GPIO、USART、定时器等寄存器0x600000000xDFFFFFFF2GB外部存储器区FSMC 连接外部 SRAM/NOR0xE00000000xFFFFFFFF512MB内核私有区NVIC、SysTick、调试等注意大部分区域标注的是“保留”Reserved访问它们会导致 HardFault 或读出随机数据。关键点统一编址—— 无论是 Flash、RAM 还是外设寄存器都被分配了独一无二的地址。CPU 不需要特殊的 I/O 指令直接用ldr/str就能操作外设。这正是 C 语言指针可以操控寄存器的根本原因。二、实际物理存储器在哪里虽然地图有 4GB 大但芯片里真正焊接的“仓库”只有这几处Flash从0x08000000开始通过映射也可从0x00000000访问。大小通常 16KB~2MB。SRAM从0x20000000开始。大小通常 6KB~512KB。外设寄存器从0x40000000开始每个寄存器占 1~4 字节。访问其他地址比如0x30000000会导致总线错误HardFault。所以4GB 是理论寻址范围不是物理存储容量。三、什么是大小端模式大小端Endianness是指多字节数据在内存中的存储顺序。假设有一个 32 位整数0x12345678它在内存中的存放方式有两种大端模式Big-Endian高字节存低地址低字节存高地址。地址 0x1000:0x120x1001:0x340x1002:0x560x1003:0x78。符合我们的阅读顺序如12341-千位2-百位3-十位4-个位从左到右读但是左边代表的是最大的数。小端模式Little-Endian低字节存低地址高字节存高地址。地址 0x1000:0x780x1001:0x560x1002:0x340x1003:0x12。这是 x86、ARM默认采用的方式。为什么会有大小端主要是历史原因和 CPU 设计哲学差异。网络协议规定使用大端称为网络字节序而 PC 和大多数嵌入式处理器使用小端。因此跨平台通信时必须转换字节序。四、如何判断你的系统是大端还是小端下面这段经典代码利用了联合体union共享内存的特性#includestdio.hintmain(){union{uint32_tword;uint8_tbytes[4];}test;test.word0x12345678;if(test.bytes[0]0x78)printf(Little-endian\n);elseprintf(Big-endian\n);return0;}如果bytes[0]是0x78说明低地址存低位 → 小端。如果bytes[0]是0x12说明低地址存高位 → 大端。STM32ARM Cortex-M默认是小端模式但有些 ARM 核心可以配置为大端需要硬件支持。五、大小端对嵌入式开发的实际影响1. 数据解析串口、I2C、SPI 接收假设你通过串口收到两个字节0x12和0x34协议规定这是大端的一个 16 位整数。正确拼装value (buf[0] 8) | buf[1];如果协议是小端value (buf[1] 8) | buf[0];搞反了就会得到错误数值。2. 寄存器访问当你用 32 位指针访问一个 8 位寄存器数组时大小端影响哪个字节对应哪个地址。不过外设寄存器通常按小端设计直接赋值即可。3. 网络通信TCP/IP 协议规定使用大端。所以发送多字节整数前要用htonl/htons转换接收后用ntohl/ntohs还原。4. 文件存储如果直接保存内存中的结构体到 SD 卡换个不同端序的设备读取就会乱码。解决办法统一使用小端或大端存储或使用文本格式如 JSON。六、一个容易混淆的概念地址 vs 数据很多新手会问“地址 0x20000000 里存的是大端还是小端”大小端是针对多字节数据的存储顺序地址本身没有端序。地址0x20000000是一个数值它在指令中是以字节序列存在的但 CPU 会自动处理。七、STM32 的实际内存布局以 F103C8T6 为例Flash0x08000000~0x0800FFFF64KBSRAM0x20000000~0x20004FFF20KB外设GPIOA0x40010800~0x40010BFFUSART10x40013800~0x40013BFFTIM20x40000000~0x400003FF我们可以在参考手册的“Memory Map”章节找到完整列表。八、常见误区与注意事项“STM32 有 4GB 内存”❌没有只有几十 KB 到几 MB 的物理 RAM但可以外扩。“指针可以随便指向任何地址”⚠️指向保留地址会触发 HardFault。除非你确定该地址有有效设备。“大小端只在跨平台时重要”⚠️即使本地开发如果你通过 DMA 将数据从外设搬到 RAM也要了解外设的端序大多数与 CPU 一致。“联合体判断大小端不通用”⚠️在某些 DSP 或特殊架构上可能因对齐问题失效但嵌入式 Cortex-M 上没问题。九、总结存储器映射4GB 地址空间的分配表包括代码、SRAM、外设、保留区。实际物理存储Flash 和 SRAM 只占地图的一小部分其余地址访问会出错。大小端多字节数据的存储顺序大端高字节在低地址小端低字节在低地址。STM32 默认为小端但网络协议使用大端通信时需要转换。影响场景数据解析、寄存器访问、网络通信、文件存储。理解了存储映射我们就知道*(uint32_t *)0x40010800 0x55;为什么能点亮 LED因为那是 GPIO 寄存器的地址。掌握了大小端你就能轻松应对各种协议解析不再被乱码困扰。系列导航第一篇嵌入式到底是什么含 ARM/C51/STM32 关系第二篇串口江湖 —— UART、RS-232、RS-485番外篇波特率解析第三篇两线走天下 —— I2C 总线精讲第四篇极速先锋 —— SPI 总线精讲第五篇嵌入式大脑 —— 中断与事件驱动第六篇时间管理大师 —— 定时器与系统滴答第七篇存储与地址 —— 大小端、内存映射、4GB 空间之谜本文第八篇从裸机到 RTOS —— 任务、调度、FreeRTOS预告

更多文章