4、选型第二步:存储资源——Flash与SRAM的容量、速度、功耗权衡,以及XIP(片内执行)机制。

好,我们接着往下聊。上一节我们把内核和架构理清了,这一节咱们来聊聊存储资源。说白了,就是Flash和SRAM。这两个东西,是MCU的“记忆体”,选不好,后面开发会非常痛苦。

我个人习惯,拿到一款新MCU,第一件事不是看主频,而是先看它的Flash和SRAM配比。为什么?因为主频不够可以降频用,但存储不够,你连代码都塞不进去,或者跑起来动不动就“Out of Memory”,那才叫真抓瞎。

4.1 Flash:你的代码仓库

Flash,就是用来存你的程序代码和常量数据的。掉电不丢失,这是它的核心价值。

容量怎么选?

这个其实没有标准答案,但我给你一个经验法则:

  • 简单传感器节点(比如一个温湿度采集器):16KB ~ 64KB 足够了。
  • 带蓝牙或Wi-Fi的复杂设备(比如智能家居网关):256KB ~ 1MB 是主流。
  • 跑RTOS + 图形界面(比如智能手表、HMI面板):2MB起步,上不封顶。

我在项目中遇到过一件事。有一次做一款智能门锁,选了颗64KB Flash的芯片。结果功能越加越多,最后代码编译出来63.8KB,差一点就爆了。那段时间我天天盯着编译日志,生怕哪个小改动让代码超限。后来我学乖了,选型时至少留30%的余量。你想想看,产品迭代总要加新功能吧?

速度与功耗的博弈

Flash的读取速度,直接影响CPU的“取指”效率。传统MCU的Flash读取需要等待周期(Wait State)。比如CPU跑100MHz,Flash读取需要2个时钟周期,那CPU就得等。这就是所谓的“Flash瓶颈”。

功耗方面,Flash读取是耗电大户。尤其是高频读取时,电流可能飙到几十毫安。低功耗场景下,你得考虑能不能把代码搬到SRAM里跑,或者用更慢的时钟去读Flash。

核心权衡点:

  • 容量越大,芯片成本越高,封装也可能更大。
  • 速度越快(零等待),功耗越高,芯片设计越复杂。
  • 低功耗模式下,Flash通常需要进入“深度睡眠”,唤醒后重新读取有延迟。

4.2 SRAM:你的工作台

SRAM是MCU的“临时工作区”。变量、堆栈、堆,全在这里。掉电就丢,但速度快,没有等待周期。

容量怎么定?

这个比Flash更难估算。我一般这么算:

  • 裸机程序:全局变量 + 堆栈,通常几KB到几十KB。
  • 跑RTOS:每个任务需要独立的栈空间。比如一个任务栈给1KB,10个任务就是10KB,再加上内核、队列、信号量等,至少翻一倍。
  • 带网络协议栈(比如LWIP、BLE Stack):协议栈本身就要占用几十KB的SRAM做缓冲。我记得有一次调试LWIP,光一个TCP连接的内存池就吃了8KB。

我的个人习惯:

SRAM容量至少是Flash的1/4。比如你选了512KB Flash,那SRAM最好有128KB以上。如果跑复杂的应用,1/3甚至1/2都不嫌多。

速度与功耗

SRAM速度通常比Flash快,但功耗也高。尤其是“待机”时,SRAM需要保持供电才能不丢数据。低功耗MCU通常有“保留SRAM”功能——在深度睡眠时,只给一小块SRAM供电,保存关键上下文。

我曾经踩过一个坑。选了一颗号称“超低功耗”的MCU,结果发现它在深度睡眠时,SRAM内容全丢了。每次唤醒都要重新初始化所有变量,导致系统启动慢得像蜗牛。后来我换了一颗带“备份SRAM”的芯片,才解决问题。

4.3 XIP(片内执行):省掉外部Flash的妙招

XIP,全称是Execute In Place。说白了,就是CPU直接在Flash里执行代码,不用先把代码搬到SRAM里。

为什么需要XIP?

传统做法是:上电后,把Flash里的代码复制到SRAM,然后从SRAM执行。这叫“加载执行”。但SRAM贵啊,容量也小。如果代码有1MB,你总不能配1MB的SRAM吧?成本受不了。

XIP就解决了这个问题。CPU直接通过总线读取Flash里的指令,边读边执行。SRAM只用来放变量和堆栈,省了一大笔。

XIP的代价

嗯,这里要注意。XIP虽然省SRAM,但Flash读取速度慢。如果CPU跑得很快,Flash跟不上,就得插入等待周期。性能会打折扣。

怎么解决?

  • Flash缓存:很多MCU在Flash和CPU之间加了一个小容量的Cache(比如4KB、8KB)。把最近用过的指令缓存起来,下次读取就快了。我习惯把热点代码(比如中断服务函数、循环体)放在Cache能覆盖的地址范围。
  • 预取机制:CPU预测你接下来要读哪条指令,提前从Flash里取出来。这个很依赖编译器的优化。

避坑指南:

我曾经在一个项目中,把延时函数放在了Flash里,通过XIP执行。结果发现延时不准,因为Flash读取的等待周期让延时时间变长了。后来我把延时函数强制放到SRAM里,问题才解决。所以,对时序敏感的函数,建议还是放在SRAM里跑。

4.4 实战权衡:一张表说清楚

为了让你更直观地理解,我整理了一张对比表。你选型时可以对着看。

特性 Flash SRAM
存储内容 程序代码、常量 变量、堆栈、堆
掉电保持
读取速度 慢(有等待周期) 快(零等待)
写入速度 极慢(按页擦写) 快(按字节写)
功耗(读取) 中等 较高
功耗(待机) 极低(可断电) 需要保持供电
单位成本
典型容量 16KB ~ 2MB+ 4KB ~ 512KB
XIP支持 是(核心功能) 不适用

4.5 选型决策清单

最后,我给你一个实用的决策清单。下次选型时,按这个顺序问自己:

  1. 我的代码有多大? 编译后看.map文件,估算Flash需求,然后乘以1.5(余量)。
  2. 我的运行时数据有多少? 全局变量 + 最大堆栈深度 + 堆大小,再乘以1.5。
  3. 我需要XIP吗? 如果代码超过SRAM容量,必须用XIP。否则可以考虑全SRAM执行。
  4. 我的时序要求严苛吗? 如果有时序敏感函数,考虑把它们放到SRAM里,或者选带Flash Cache的MCU。
  5. 低功耗要求有多高? 如果待机电流要求uA级,选带“保留SRAM”和“Flash深度睡眠”的芯片。

好了,这一节就到这里。存储资源这块,说白了就是“够用就好,留有余地”。下一节我们聊聊外设接口——GPIO、UART、I2C、SPI这些,怎么选才不浪费?