STM32F4 FSMC NOR Flash扩展程序存储
STM32F4 FSMC NOR Flash扩展程序存储
在高性能嵌入式系统中,你有没有遇到过这样的窘境:功能越做越多,代码越写越长,结果编译完发现—— Flash爆了!💥 尤其是当你用着STM32F4这种“性能猛兽”,本以为1MB片上Flash够用了,结果UI渲染、通信协议栈、RTOS一加,直接超标。这时候,是换芯片?还是……把程序“搬出去”住?
这就是我们今天要聊的硬核操作: 用FSMC外接NOR Flash,让STM32的程序跑到外部存储器上运行!🚀
别急,这可不是什么黑科技,而是工业级系统里早已成熟的方案。关键就在于—— XIP(eXecute In Place)就地执行 。也就是说,CPU可以直接从NOR Flash取指运行,不用先把代码拷贝到RAM里,既省内存又快!
🧩 为什么选FSMC + NOR Flash?
STM32F4系列有个宝藏外设叫 FSMC(Flexible Static Memory Controller) ,它就像一个“万能翻译官”,能把外部并行存储器(比如SRAM、NOR、NAND)无缝接入CPU的地址空间。
而NOR Flash呢?它和我们常见的SPI Flash不一样,虽然贵一点、体积大一点,但它支持 字节寻址 + 随机读取 + XIP ,特别适合存放需要频繁跳转执行的代码段。相比之下,NAND或SPI Flash通常得先加载到RAM才能运行,多了个搬运过程。
所以,当你的项目面临以下问题时👇
➡️ 固件太大,内部Flash塞不下
➡️ RAM紧张,不想再额外占用空间加载代码
➡️ 要求启动快、运行稳,不能有延迟卡顿
那答案就很明确了: 上FSMC + NOR Flash!
📡 FSMC是怎么工作的?
简单来说,FSMC把外部设备映射成一段连续的内存区域。STM32F4的地址空间中, Bank 1 (0x6000_0000 ~ 0x6FFF_FFFF)就是专为NOR和PSRAM准备的舞台。
这个Bank还细分为4个小分区(Nor/SRAM1~4),每个对应一个片选信号NE1~NE4。我们一般用 NE1 来接NOR Flash,起始地址就是 0x60000000 。
一旦配置好,你就可以像访问内部Flash一样去读写这个地址——背后全是FSMC帮你搞定地址锁存、数据总线控制、读写时序这些琐事。
📌 关键引脚一览(以IS62WV51216为例) :
| STM32F4 | 功能 | NOR Flash |
|---|---|---|
| PD0~PD15 | D0~D15 | 数据总线 |
| PE0~PE15 | A0~A15 | 地址总线低16位 |
| PF0~PF4 | A16~A20 | 地址高位 |
| PF12 | NE1 | CE# 片选 |
| PD4 | NOE | OE# 读使能 |
| PD5 | NWE | WE# 写使能 |
| PG9(可选) | NWAIT | WAIT 等待反馈 |
⚠️ 注意:高位地址(A21以上)由FSMC内部生成,实际引脚数量取决于MCU封装。
⚙️ 配置FSMC其实没那么复杂
很多人一听“寄存器操作”就头大,但其实只要理解几个核心参数,再配合HAL库,几分钟就能搞定。
下面是使用HAL库初始化FSMC连接NOR Flash的关键代码👇
static void FSMC_NOR_Init(void)
{
FSMC_NORSRAM_TypeDef *device = FSMC_Bank1;
FSMC_NORSRAM_EXTENDED_TypeDef *ext_device = FSMC_Bank1E;
// 开启FSMC及GPIO时钟
__HAL_RCC_FSMC_CLK_ENABLE();
// 配置BTCR[1]:启用Bank1 Region1,16位数据宽度,NOR模式
device->BTCR[1] =
FSMC_BCR1_MWID_0 | // 16-bit data bus
FSMC_BCR1_MTYP_0 | // NOR Flash type
FSMC_BCR1_MBKEN; // Enable memory bank
// 设置读写时序(BTR)
device->BTR[1] =
(15UL << FSMC_BTR1_ADDSET_Pos) | // 地址建立时间:15 HCLK cycles
(15UL << FSMC_BTR1_DATAST_Pos) | // 数据保持时间:15 HCLK
(3UL << FSMC_BTR1_BUSTURN_Pos); // 总线恢复时间
// 如果使用NWAIT等待信号,开启扩展模式
device->BTCR[1] |= FSMC_BCR1_EXTMOD;
device->BWTR[1] =
(15UL << FSMC_BWTR1_ADDSET_Pos) |
(15UL << FSMC_BWTR1_DATAST_Pos);
}
💡 小贴士:假设HCLK=168MHz(周期约5.95ns),ADDSET=15 ≈ 89ns,足以满足大多数NOR Flash的建立时间要求(如IS62WV51216典型值70ns)。如果Flash较慢,还可以启用NWAIT引脚,让Flash主动告诉MCU“等等我”。
🔗 链接脚本才是灵魂所在!
光有硬件配置还不够, 编译器得知道把代码放在哪 。这就轮到 .ld 链接脚本来登场了。
默认情况下,所有代码都放内部Flash(0x08000000)。我们要做的,是把 .text 段挪到 0x60000000 开始的外部区域。
/* stm32f4_nor_flash.ld */
MEMORY
{
FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K /* 中断向量保留 */
FLASH_CODE (rx) : ORIGIN = 0x60000000, LENGTH = 8M-16K /* 外部主程序区 */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.isr_vector :
{
KEEP(*(.isr_vector))
} > FLASH_ISR
.text :
{
*(.text*)
*(.rodata*)
} > FLASH_CODE AT> FLASH_ISR
.ARM.exidx :
{
*(.ARM.exidx*)
} > FLASH_CODE
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
} > FLASH_CODE
.data : { ... } > RAM AT> FLASH_CODE
.bss : { ... } > RAM
}
🎯 关键点解析:
- 中断向量表必须留在内部Flash !因为复位后CPU只会从0x08000000开始取SP和PC。
- .text 段被重定向到外部Flash,但加载域仍可在内部(AT> FLASH_ISR),首次运行前需通过Bootloader烧录过去。
- .data 段依然加载到RAM,只是初始值从外部Flash拷贝。
🚀 启动流程怎么安排?
MCU上电后,默认从内部Flash启动。因此我们需要一个 两阶段启动机制 :
-
第一阶段(Bootloader)
运行在内部Flash的小程序,负责:
- 初始化系统时钟
- 配置FSMC控制器
- 检查外部NOR Flash中的应用程序是否有效
- 跳转至外部Flash入口地址 -
第二阶段(主应用)
存放在NOR Flash中的真正业务逻辑,地址从0x60001000开始。
跳转代码示例:
// 假设主程序Reset_Handler地址存在0x60001004
uint32_t *app_entry = (uint32_t*)0x60001004;
uint32_t app_stack = *app_entry; // 获取MSP初始值
__set_MSP(app_stack); // 切换主堆栈
((void(*)(void))(app_entry[1]))(); // 跳转到Reset Handler
✅ 成功跳转后,后续所有函数调用都会直接从NOR Flash执行,真正做到“XIP”。
🏭 实际应用场景有哪些?
1. 工业HMI人机界面
- UI资源丰富(图标、字体、动画)
- 使用LVGL等图形库,代码轻松突破1MB
- 外部NOR存放GUI逻辑 + 图片索引,内部Flash只留核心驱动
2. 车载T-Box模块
- 需运行FreeRTOS + TCP/IP + TLS + FOTA升级协议
- 双固件镜像设计:一个在内部,一个在外扩Flash
- 升级失败自动回滚,永不“变砖”🚫🧱
3. 医疗设备备份系统
- 主程序跑在内部Flash
- 备份固件预存于NOR Flash
- 异常重启时切换至备用系统,保障设备可用性
✅ 最佳实践 & ❌ 常见坑点
| 推荐做法 ✅ | 避免事项 ❌ |
|---|---|
| 选用工业级宽温NOR Flash(-40°C~+85°C) | 不要把中断向量表完全移出内部Flash |
| 每颗IC旁加0.1μF陶瓷滤波电容 | 忽略电源去耦,导致信号抖动 |
| 地址/数据线尽量等长布线 | 高频干扰环境下未做屏蔽处理 |
| 启用NWAIT引脚提升兼容性 | 在FSMC未初始化前访问0x60000000区域 |
| 使用带ECC校验的Flash增强可靠性 | 写操作中途断电,可能损坏扇区 |
🔧 调试建议 :
- 使用J-Link等高端调试器,支持对外部Flash进行在线下载和单步调试
- 在IDE(如STM32CubeIDE)中正确设置Flash算法文件,避免烧录失败
💭 写在最后:这条路还走得通吗?
你可能会问:“现在QSPI Flash这么流行,为啥还要搞复杂的FSMC+NOR?”
问得好!👏
确实,像 QSPI + Octal-SPI NOR 凭借引脚少、成本低、密度高,在很多消费类产品中已成主流。尤其是STM32H7系列支持HyperBus,速度甚至超过传统FSMC。
但别忘了, FSMC的优势在于确定性与时序可控性 。它是纯并行接口,没有协议开销,随机访问延迟极低,特别适合对实时性要求高的工业场景。
换句话说:
- 想省钱、引脚紧张 → 选QSPI
- 要极致稳定、高速响应 → FSMC+NOR仍是王者 👑
而且,掌握这套技术,不仅能应对复杂项目需求,更能深入理解 存储器映射、总线架构、启动流程 这些底层机制,是嵌入式工程师进阶的必经之路。
所以,下次当你的STM32又“Flash不够用了”,别急着换芯片,试试给它加块NOR Flash吧!💡
毕竟,真正的高手,都是会“扩容”的 😉。
更多推荐



所有评论(0)