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 以及堆栈全局变量之间的关系。

单片机 SRAM 存储体系结构图 单片机 SRAM 存储体系结构 CPU 核心 ITCM(指令 TCM) 关键代码 / 中断服务 DTCM(数据 TCM) 关键数据 / 中断栈 Cache(I-Cache / D-Cache) 自动缓存,透明管理 主 SRAM(片上 RAM) 栈(Stack) 堆(Heap) 全局变量 Flash(代码 + 常量) 注意 DMA 访问 SRAM 时需处理 Cache 一致性

3.5 总结要点

  • SRAM 快但贵,适合做 Cache 和 TCM;DRAM 慢但便宜,适合做大容量主存。
  • 单片机里,栈、堆、全局变量 都放在 SRAM 中,但管理方式不同。
  • Cache 是透明的,但要注意 DMA 时的数据一致性问题。
  • TCM 是确定性的,适合实时性要求高的场景。

最后说一句:选型时,别只看 SRAM 的总容量。要搞清楚你的应用里,栈需要多大、堆是否需要、Cache 是否开启、TCM 怎么分配。这些细节,往往决定了项目的成败。