3. SRAM 与 DRAM:静态 RAM 与动态 RAM 的原理差异
说到单片机里的存储器,SRAM 和 DRAM 这对兄弟,很多人容易搞混。我刚开始做嵌入式那会儿,也以为它们差不多,直到有一次项目里内存频繁出错,才逼着我彻底搞清楚了它们的区别。
说白了,SRAM 是静态的,DRAM 是动态的。这个“静态”和“动态”,指的是数据保存的方式,不是指能不能动。
3.1 原理差异:一个靠锁存,一个靠电容
SRAM(静态随机存取存储器):每个存储单元由 6 个晶体管组成一个触发器(锁存器)。只要不断电,数据就能一直保持,不需要刷新。读写速度极快,几乎可以跟上 CPU 的节奏。
DRAM(动态随机存取存储器):每个存储单元只有 1 个晶体管 + 1 个电容。电容会漏电,所以需要定期刷新(通常每 64ms 刷新一次)。读写速度比 SRAM 慢,但集成度高、成本低。
| 对比项 | SRAM | DRAM |
|---|---|---|
| 存储单元结构 | 6 个晶体管(锁存器) | 1 个晶体管 + 1 个电容 |
| 是否需要刷新 | 不需要 | 需要(约 64ms 刷新一次) |
| 读写速度 | 快(ns 级) | 较慢(需要刷新周期) |
| 集成度 | 低(占面积大) | 高(同样面积容量更大) |
| 功耗 | 静态功耗较高 | 有刷新功耗,但待机功耗低 |
| 成本 | 高 | 低 |
| 典型应用 | Cache、TCM、寄存器 | 主内存(PC、手机) |
关键结论:单片机内部几乎只用 SRAM,不用 DRAM。因为单片机不需要大容量内存,但对实时性和可靠性要求极高。DRAM 的刷新延迟和刷新失败风险,在工业控制场景里是致命的。
3.2 单片机中 SRAM 的使用场景
单片机的 SRAM 空间,通常被划分为三个主要区域:堆(Heap)、栈(Stack) 和 全局变量区(.bss/.data)。我见过不少新手,因为搞不清这三个区域的区别,导致程序跑飞或者内存泄漏。
3.2.1 栈(Stack)
栈是函数调用时自动分配和释放的内存区域。它由编译器自动管理,遵循“后进先出”原则。
- 存放内容:局部变量、函数参数、返回地址、寄存器现场。
- 分配方式:函数调用时压栈,函数返回时弹栈。
- 大小限制:通常由链接脚本指定,比如 1KB、4KB。
我曾经踩过的坑:在一个 STM32 项目里,我写了一个递归函数,递归深度稍微大了一点,栈就溢出了。程序表现得很诡异——有时候正常,有时候死机。后来用调试器一看,栈指针已经跑到全局变量区了,把关键数据全覆盖了。从那以后,我养成了一个习惯:嵌入式开发中尽量不用递归,或者严格控制递归深度。
3.2.2 堆(Heap)
堆是用于动态内存分配的区域,由程序员通过 malloc() / free() 或 new / delete 管理。
- 存放内容:动态分配的变量、数据结构、缓冲区。
- 分配方式:运行时手动分配和释放。
- 大小限制:由链接脚本指定,通常比栈大。
我的建议:在单片机开发中,能不使用堆,就尽量不用。堆管理有碎片问题,而且 malloc() 的执行时间不确定。我一般只在初始化时一次性分配好内存,之后不再释放。如果必须动态分配,我会用静态内存池替代标准堆。
3.2.3 全局变量区(.bss / .data)
全局变量和静态变量存放在这里。它们在程序启动时分配,程序结束时才释放。
- .data 段:存放已初始化的全局变量和静态变量。初始值从 Flash 复制到 SRAM。
- .bss 段:存放未初始化或初始化为 0 的全局变量和静态变量。启动时由启动代码清零。
// 示例:不同存储区域的变量
int global_init = 100; // .data 段(已初始化)
static int static_zero; // .bss 段(未初始化,默认为 0)
const int constant_val = 50; // 存放在 Flash(只读)
void func(void) {
int local_var = 10; // 栈(Stack)
static int local_static; // .bss 段(静态局部变量)
int *p = malloc(20); // 堆(Heap)
free(p);
}
3.3 Cache 与 TCM:速度的终极武器
说到 SRAM,就不得不提两个特殊的存在:Cache 和 TCM(Tightly Coupled Memory,紧耦合内存)。它们都是 SRAM,但用法完全不同。
3.3.1 Cache(缓存)
Cache 是 CPU 和主存(Flash 或外部 RAM)之间的高速缓冲存储器。它自动缓存最近使用的指令和数据,减少 CPU 等待慢速存储器的时间。
- 指令缓存(I-Cache):缓存指令,减少取指等待。
- 数据缓存(D-Cache):缓存数据,减少数据读写等待。
Cache 对程序员是透明的,但有时候也会带来麻烦。我记得有一次调试一个 DMA 传输的问题,数据总是对不上。查了半天才发现,CPU 从 Cache 里读数据,而 DMA 直接访问了 SRAM,两边数据不一致。这就是经典的 Cache 一致性问题。
避坑指南:使用 DMA 时,如果涉及的数据也在 Cache 中,一定要做 Cache 清理(Clean)或无效化(Invalidate)操作。否则,你读到的可能是“过期”的数据。
3.3.2 TCM(紧耦合内存)
TCM 是直接挂在 CPU 内核总线上的 SRAM,没有 Cache 那样的复杂控制逻辑。它的特点是:访问延迟固定、确定性高。
- ITCM(指令 TCM):存放关键代码,比如中断服务程序、实时性要求高的算法。
- DTCM(数据 TCM):存放关键数据,比如中断栈、实时变量。
TCM 和 Cache 最大的区别是:TCM 由程序员显式管理,而 Cache 是硬件自动管理的。你想想看,如果你的中断服务程序被 Cache 换出去了,那响应时间就没法保证了。所以,实时系统里,TCM 比 Cache 更可靠。
我的经验:在做电机控制项目时,我把 FOC 算法的核心计算代码放在 ITCM 里,把电流采样数据放在 DTCM 里。这样保证了每次中断都能在固定时间内完成,不会因为 Cache 命中率波动而影响控制周期。
3.4 知识结构图:SRAM 在单片机中的角色
下面这张图,帮你理清 SRAM、Cache、TCM 以及堆栈全局变量之间的关系。
3.5 总结要点
- SRAM 快但贵,适合做 Cache 和 TCM;DRAM 慢但便宜,适合做大容量主存。
- 单片机里,栈、堆、全局变量 都放在 SRAM 中,但管理方式不同。
- Cache 是透明的,但要注意 DMA 时的数据一致性问题。
- TCM 是确定性的,适合实时性要求高的场景。
最后说一句:选型时,别只看 SRAM 的总容量。要搞清楚你的应用里,栈需要多大、堆是否需要、Cache 是否开启、TCM 怎么分配。这些细节,往往决定了项目的成败。