1. 存储器概述:单片机存储体系结构

做嵌入式这些年,我调试过不少“跑飞”的板子。十有八九,问题都出在存储器上——要么是Flash写穿了,要么是SRAM溢出,要么是EEPROM数据丢了。说白了,不理解存储器,你就没法真正掌控单片机。

这一章,咱们先把存储器的底牌翻出来看看。

1.1 哈佛结构与冯·诺依曼结构

这两种结构,是单片机存储体系的“基因”。

冯·诺依曼结构:程序指令和数据共用一套总线、一个存储空间。CPU取指令和读写数据不能同时进行,得排队。PC、x86这类处理器大多走这条路。

哈佛结构:程序存储器和数据存储器物理上分开,各有各的总线。CPU可以一边取指令,一边读写数据——并行操作。绝大多数单片机(比如AVR、PIC、8051、ARM Cortex-M)都采用哈佛结构或改进型哈佛结构。

对比项 冯·诺依曼 哈佛
存储空间 统一编址 程序/数据独立编址
总线 单套总线 两套独立总线
取指与数据访问 不能同时 可同时进行
典型代表 x86、ARM Cortex-A 8051、AVR、ARM Cortex-M
执行效率 较低(总线瓶颈) 较高(指令流水线友好)
我的一点经验: 用STM32做项目时,我习惯把关键中断服务函数放在SRAM里执行。因为Flash读取有等待周期,而SRAM零等待——这就是哈佛结构带来的优化空间。

1.2 存储器层次:Flash、SRAM、EEPROM

单片机内部一般有三层存储器,各有各的脾气。

Flash(程序存储器)

  • 非易失性,掉电不丢
  • 读快写慢(擦除按扇区,写入按页)
  • 寿命约1万~10万次擦写
  • 存放固件、常量、启动代码

SRAM(数据存储器)

  • 易失性,掉电即丢
  • 读写速度极快,零等待
  • 容量小(几KB到几百KB)
  • 存放堆栈、全局变量、动态数据

EEPROM(非易失数据存储器)

  • 非易失性,可字节级擦写
  • 速度比Flash慢,但比Flash灵活
  • 寿命约10万~100万次
  • 存放配置参数、校准值、掉电保存数据
我曾经踩过的坑: 有个产品量产5000台后,陆续出现参数丢失。查了三天,发现是EEPROM擦写次数超了——代码里每次上电都写一次配置,一天开关机20次,一年就7000多次。后来改成“值变化才写”,问题解决。记住:EEPROM不是无限寿命的。

1.3 存储器映射图

存储器映射,说白了就是给每个存储单元分配一个地址。CPU通过地址总线找到对应的存储单元,然后读写数据。

以STM32F103为例,它的存储器映射是这样的:

地址范围 存储器类型 用途
0x0000 0000 - 0x0000 FFFF 别名区/系统存储器 启动配置、Bootloader
0x0800 0000 - 0x0801 FFFF Flash(128KB) 程序代码、常量
0x2000 0000 - 0x2000 4FFF SRAM(20KB) 变量、堆栈
0x4000 0000 - 0x4002 3FFF 外设寄存器 GPIO、USART、TIM等
0xE000 0000 - 0xE00F FFFF Cortex-M3内部外设 NVIC、SysTick、MPU

你想想看,为什么Flash地址从0x0800 0000开始?因为STM32把Flash映射到了这个区域,CPU复位后从0x0800 0000取第一条指令。SRAM从0x2000 0000开始,外设寄存器从0x4000 0000开始——每个区域都有明确的边界。

调试时最实用的技能: 在IDE的Memory窗口里直接输入地址,就能看到该地址存储的原始数据。我曾经靠这个定位过一个堆栈溢出——在0x2000 4F00附近看到本该是0x00的地方出现了0xA5,立马知道堆栈踩到了变量区。

下面这张图,把单片机存储体系的三个核心维度串在了一起:

单片机存储体系结构总览 存储体系结构 冯·诺依曼结构 哈佛结构 统一编址 程序/数据独立编址 Flash SRAM EEPROM 地址映射:0x0800 0000 地址映射:0x2000 0000 地址映射:0x4000 0000 体系结构 → 存储器层次 → 地址映射,三者共同决定了单片机的存储行为
我的习惯: 拿到一款新单片机,第一件事就是打开参考手册的“Memory Map”章节,把Flash、SRAM、外设寄存器的起始地址和大小记下来。调试时遇到“HardFault”,十有八九是访问了非法地址——看一眼映射图就能定位。