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 选型决策清单
最后,我给你一个实用的决策清单。下次选型时,按这个顺序问自己:
- 我的代码有多大? 编译后看.map文件,估算Flash需求,然后乘以1.5(余量)。
- 我的运行时数据有多少? 全局变量 + 最大堆栈深度 + 堆大小,再乘以1.5。
- 我需要XIP吗? 如果代码超过SRAM容量,必须用XIP。否则可以考虑全SRAM执行。
- 我的时序要求严苛吗? 如果有时序敏感函数,考虑把它们放到SRAM里,或者选带Flash Cache的MCU。
- 低功耗要求有多高? 如果待机电流要求uA级,选带“保留SRAM”和“Flash深度睡眠”的芯片。
好了,这一节就到这里。存储资源这块,说白了就是“够用就好,留有余地”。下一节我们聊聊外设接口——GPIO、UART、I2C、SPI这些,怎么选才不浪费?