资讯详情

FSMC/FMC/EXMC与NOR Flash、PSRAM接口配置:异步到同步突发全解析

📅 2026/10/5 5:55:09 | 华诺云谱 👁 阅读
FSMC/FMC/EXMC与NOR Flash、PSRAM接口配置:异步到同步突发全解析
搞嵌入式的大概都遇到过这种场景同一份外扩代码从STM32F103搬到F407上调不通或者从ST换到GD32发现寄存器名字又变了再或者明明接的是PSRAM却死活读不到数据最后发现是器件还在异步模式、而你一上来就按同步突发去读。这三个总线的名字——FSMC、FMC、EXMC——折腾过不少人。它们的本质其实是同一类东西MCU对外部存储器的访问控制器。区别在于产品线不同、叫法不同、支持的存储器种类略有差异。这篇文章想把这些事一次性讲清楚。围绕NOR Flash和PSRAM这两种最常见的静态存储器拆开异步和同步、复用和非复用、突发和不突发这几组接口模式把原理讲明白把配置步骤和时序计算写具体顺便把我在实际项目中踩过的坑和排查思路整理出来。不管是正在学总线的新手还是准备换平台的老手都能从中找到可以直接用的东西。1. 三个容易搞混的名字FSMC、FMC与EXMC到底是什么1.1 一个外设的三种马甲先把这个最基础的问题解决FSMC、FMC、EXMC到底是三个不同的东西还是同一个东西的三个名字答案是同源但确实不完全一样。FSMC的全称是Flexible Static Memory Controller灵活静态存储控制器最早大规模出现在STM32F1系列上。当时ST为了照顾大部分嵌入式产品外扩SRAM、NOR Flash、NAND Flash的需求把原来分散的逻辑接口统一成了一个可配置的控制器你要接8位还是16位、要用多少地址线、时序要快还是慢都在寄存器里配。F1系列因此成了那个年代外扩存储器的首选平台。到了STM32F4以后这个控制器改名叫FMC即Flexible Memory Controller。名字里少了Static这个词关键在于它的管控范围变了在原来NOR/PSRAM/SRAM、NAND这些静态存储器的基础上FMC增加了SDRAM控制能力。SDRAM属于动态存储器需要定时刷新所以控制器里多了刷新逻辑和相关信号。这也是F系列产品线上一次非常重要的升级。而EXMC这个名字你如果用过GD32一定不陌生。GD32把兼容的外部存储控制器称为External Memory Controller。GD32F303的技术参考手册里写的还是EXMC但它的寄存器布局、功能逻辑和ST的FSMC大体是对应的。国产芯片厂商为了兼容生态不会轻易改寄存器映射。也就是说如果你在ST的FSMC上写过驱动拿到GD32的EXMC上大概率能在改少量宏定义之后直接用。这三者最核心的底层思路完全一致把它们理解成一个可编程的总线时序发生器。你告诉它你的器件需要多长的地址建立时间、多长的数据保持时间、是同步还是异步、地址和数据是不是复用它就按照这些参数生成对应的片选、读/写、时钟和地址锁存信号。这就是为什么学透了其中一个其他两个都能很快上手。1.2 从FSMC到FMC多出来的SDRAM支持如果你手里有一块F103和一个F407对比两者外扩接口的差异会发现F1的FSMC虽然也常用但NAND和PC卡支持比较有限。而F4的FMC多出了对SDRAM的支持拍脑袋想也知道F4这颗料主频高了、外扩内存条的应用场景更多了跑GUI、跑算法、做音频缓存经常要挂SDRAM。注意这里有一个容易忽略的点SDRAM的接口和NOR/PSRAM并不相同。SDRAM需要行选通、列选通、时钟、刷新这些是动态存储器特有的东西。FMC把NOR/PSRAM/SRAM这一组和SDRAM这一组分成了两个Bank区域用的是两套独立的信号组。配置FMC的时候你要清楚自己用的到底是NOR/PSRAM这一类还是SDRAM这一类不要混着配置。我在项目里见过有人把SDRAM的初始化代码直接套在PSRAM上结果显而易见PSRAM根本不响应行选通和列选通因为这个器件压根没有这些功能。FSMC时代没有SDRAM支持这是硬件上的缺憾。但从NOR Flash和PSRAM的使用角度讲FSMC和FMC的Bank1区域、信号命名、时序参数模型是一脉相承的。你原来在F1上怎么算异步时序在F4上还怎么算。FMC新增SDRAM之前老代码几乎不需要动。1.3 地址映射与片选分配先搞清楚你用的Bank在哪不管是FSMC还是FMC它的Bank1区域都是给NOR Flash、PSRAM这类静态存储器用的按片选信号分成四块。以FMC为例FMC_NE1对应0x60000000起始的64MB空间FMC_NE2对应0x64000000FMC_NE3对应0x68000000FMC_NE4对应0x6C000000。你用哪个片选信号CPU就往哪个地址段发访问请求。这个地址映射很多人一开始不当回事后来调bug才发现问题。举个例子你把NOR Flash的片选接在了NE1上初始化也配的是Bank1的NE1那你读0x60000000地址就能直接访问到Flash的第一个单元。如果代码里不小心把地址写成了0x64000000硬件上NE2没有接任何器件或者接了别的器件你读到的就是总线上的高阻态也就是0xFFFF而不是你的数据。这种问题的排查方向一开始就偏了很浪费时间。另一个要紧的事是地址线的连接方式。FSMC/FMC的外部地址线FMC_A[25:0]在非复用模式下就是连接到NOR Flash或者SRAM的地址引脚一一对应。但如果你用的是8位器件地址线就有一个对齐问题16位模式访问半字时地址线右移一位8位模式直接对齐。这个细节后面讲具体接法时会再细说。2. 先把两对关键概念焊死同步与异步、复用与非复用接口模式这个词听起来学术其实就回答两个问题第一数据在总线上流动的时候以什么为时间基准时钟还是控制信号第二地址线和数据线是分开走还是共用2.1 异步模式没有时钟的请求-应答式访问异步意味着存储器接口上没有专门的时钟脚。控制器想读一个数据就按顺序做一系列动作把地址放到总线上拉低片选拉低读使能等一小段时间从数据总线上把数据拿走然后释放读使能。整个过程就像你去柜台办事递材料地址、按铃读使能、对方把东西递出来数据铃一响你伸手接。异步模式的时序完全靠控制信号沿来同步。芯片手册上会给出各种时间参数单位是ns例如地址建立时间、地址到数据的延迟、读使能低电平持续时间等等。MCU控制器要做的事情很简单但也最容易出问题把这些以ns为单位的时间要求换算成内部时钟HCLK的周期数填进寄存器。换算的精度取决于HCLK频率频率越高一个周期越短你能卡的边界越细。异步模式的最大优点就是简单可靠几乎所有NOR Flash、SRAM、PSRAM都支持这种模式。上电后器件通常默认就是异步模式你用异步模式去读ID、读状态基本不会踩坑。缺点是效率一般每读一个数据都要走一遍完整的握手流程没有流水线。2.2 同步模式时钟节拍下的流水线访问同步模式则多了一个时钟信号在STM32的FMC/FSMC上叫FMC_CLK。存储器和控制器按照这个时钟的上升沿/下降沿来传输数据片选和读使能只是为了界定一次访问的开始和结束至于数据在每个时钟周期怎么流动全靠CLK打拍子。同步模式真正的优势是支持突发传输。你只要给一次起始地址然后后面几个、十几个甚至更长的数据按照每个时钟周期一个或者根据位宽一个周期多个字节的节奏连续往外蹦。CPU不需要对每个数据都重新发起一次完整总线事务吞吐量显著提升。做产品的人一定会关心效率因为效率意味着MCU能以更低的频率完成同样的工作或者同样的频率下给上层算法留更多时间。这也是PSRAM和同步NOR Flash的价值所在——用普通IO口或者异步总线慢慢吭哧浪费时间不说做GUI刷屏时卡顿感非常明显。2.3 复用与非复用引脚数焦虑的解法非复用模式就是地址线和数据线物理上分开。A0~A25接地址D0~D15接数据。这个模式最直观时序上也好理解不牵扯现在总线上是地址还是数据这种问题。缺点显而易见引脚占用多。16位地址加16位数据再加上片选、读写等控制信号几十个IO就没了。对于100脚甚至64脚的芯片来说是很奢侈的事。复用模式则把地址和数据放在同一组线上。典型做法是AD[15:0]既承载地址也承载数据另加一个地址锁存使能信号ALE有的叫NL。总线的第一个阶段控制器在AD线上输出地址ALE给出锁存脉冲外部再接一个74LVC373之类的锁存器把地址固定住第二个阶段AD线转为数据线进行真正的读写。这样引脚数直接省下一半代价是多了外部锁存逻辑和额外的时序开销。大家别觉得复用模式很稀有实际上它几乎是NAND Flash、PSRAM和SoC外接存储器的标配。低引脚数的应用里想同时挂大容量Flash和RAM不用复用模式根本接不下。2.4 两两组合谁和谁常见把这两对概念交叉能得到四种组合异步非复用、异步复用、同步非复用、同步复用。异步非复用是最宽的经典NOR Flash、SRAM、PSRAM在默认配置下都是这个模式。同步复用则是PSRAM和部分同步NOR Flash的高性能工作模式也是标题里那个同步-复用-突发指向的场景。异步复用虽然也有但相对小众一般是为了省引脚又没条件上同步时序的老设计方案。把这几点想清楚你再去翻芯片手册里的时序图就会发现自己能看得懂图上那些线和箭头的含义了。3. 异步不突发模式NOR Flash的常规接法与时序计算3.1 典型异步NOR Flash的引脚与接口风格先说NOR Flash。市面上常见的NOR Flash比如SPANSION现在是Cypress/Infineon的S29GL系列、Intel的StrataFlash系列、国内一些厂商的兼容型号并行接口的引脚骨架大概是这样地址线A[25:0]、数据线DQ[15:0]16位器件或者DQ[7:0]8位器件、片选CE#、读使能OE#、写使能WE#、字节/字模式选择BYTE#还有字节使能UB#/LB#。BYTE#引脚容易被忽略。如果BYTE#拉高器件工作在16位字模式地址线A0实际上对应的是内部存储单元的偶数地址如果BYTE#拉低器件工作在8位字节模式DQ[15:8]变成高阻数据只从DQ[7:0]进出。这就引出地址对齐的问题。MCU外设寄存器里的存储器类型选择、数据宽度配置要和你的硬件连接一一对应。你在FMC初始化时配置了16位数据宽度但硬件上BYTE#接了低电平数据只通过低8位走那你读到的数据会高位不对。反过来配置8位宽度但硬件是16位连接地址线又会错位。这类问题在项目联调时非常隐蔽因为它不是时序问题而是机器字长不对齐问题。3.2 非复用与复用两种接法控制器配置有什么不同先讲非复用接法。地址线直接连NOR Flash的A脚数据线直接连DQ脚CE#接片选OE#接读使能WE#接写使能NBL[1:0]接UB#/LB#。这种情况下FMC的外部总线数据宽度一般配成16位存储器类型选NOR Flash或SRAM配置里注意把不相关的NAND、SDRAM选项排除掉。复用接法就有意思了。想让总线复用地址线和数据线是同一组线但NOR Flash本身不支持复用总线怎么办办法是外置一个8位或者16位的地址锁存器。FMC/FSMC在复用模式下会先输出地址、同时拉高ALE让锁存器采样后面的数据阶段AD线就切给数据。锁存器的输出再接NOR Flash的地址脚。这样做虽然多用了一颗逻辑芯片但MCU的地址线可以少接十几根。在寄存器配置层面复用模式和非复用模式的区别主要体现在时序模型上。非复用模式下地址在片选和读使能有效前就稳定了所以AddressSetupTime管的是地址提前于片选的时间。复用模式下地址和数据在AD线上分时出现还要考虑地址锁存使能信号和地址数据切换的时机地址保持时间必须足够否则锁存器还没把地址抓稳AD线就已经切到数据了后患无穷。3.3 时序参数计算从ns换算到HCLK周期这是调总线绕不开的一步。拿STM32F4举例假设HCLK主频168MHz一个HCLK周期就是1/168MHz≈5.95ns。你的NOR Flash手册上通常会写tRC随机读周期时间、tAA地址到数据输出延迟、tOEOE#到数据有效延迟这些参数。以异步读为例控制器访问一个地址时先让地址有效再拉低OE#经过一段时间后数据有效控制器采样数据。你的配置需要保证从地址有效到控制器采样数据之间的总时间大于器件手册要求的tAA。在FMC的时序模型里这个总时间约等于AddressSetupTime DataSetupTime换算成的实际时间不同厂商内部模型略有差异但整体思路一致。实际调试中我不建议严格按照公式死算因为手册上的公式和芯片内部的模型有时不完全一致而且温度、电压变化会影响器件实际时序。我的做法是先查手册拿到最关键的几个时间参数然后转换成周期数再往上加至少2到3个时钟周期的裕量烧进去读写测试。如果工作正常再逐步往下减直到出现错误然后把参数回退两个周期作为最终配置。这种方法叫从大到小收敛既不用背公式又能拿到接近最优的时序配置。有一个反例值得讲。前几年做一款工业产品同事在FSMC配NOR Flash时把DataSetupTime压到了末位常温下读写正常发到北方客户那边低温环境下偶发读回0xFFFF。原因就是器件在低温下时序参数变差而配置的裕量为零。从那以后我所有时序配置都强制留至少2个周期的裕量毕竟主频损失一点换来的稳定性是实实在在的。3.4 初始化代码示例HAL库视角用HAL库举一个典型的异步非复用16位NOR Flash初始化思路FMC_NORSRAM_TimingTypeDef Timing {0}; FMC_NORSRAM_InitTypeDef FMC_NORSRAM_Init {0}; // 先配置控制器基础参数 FMC_NORSRAM_Init.AddressSetupTime 2; // 地址建立时间 FMC_NORSRAM_Init.AddressHoldTime 1; // 地址保持时间 FMC_NORSRAM_Init.DataSetupTime 6; // 数据建立时间 FMC_NORSRAM_Init.BusTurnAroundDuration 0; FMC_NORSRAM_Init.CLKDivision 16; // 同步模式才用但必须给值 FMC_NORSRAM_Init.DataLatency 17; // 同步模式才用 FMC_NORSRAM_Init.AccessMode FMC_ACCESS_MODE_A; FMC_NORSRAM_Init.WriteOperation FMC_WRITE_OPERATION_ENABLE; FMC_NORSRAM_Init.ExtendedMode FMC_EXTENDED_MODE_DISABLE; HAL_SRAM_Init(hsram, FMC_NORSRAM_Init, Timing);HAL库的HAL_SRAM_Init第二三个参数分别是控制器配置和时序配置在ST的例程里经常看到有人把Timing参数留空这样默认值在某些芯片上也能跑但换了型号就说不准了。我的建议是显式配置一下保证可移植性。这里有一个隐藏知识点即使你使用异步模式CLKDivision和DataLatency这两个同步模式参数也不能全填0一些早期库版本对这两个字段有最小值要求。HAL库例程里写16和17是有原因的照着填一般不会出问题。4. 同步复用突发模式PSRAM性能释放的正确姿势4.1 PSRAM/CellularRAM到底是什么PSRAM全称Pseudo SRAM伪静态随机存储器也叫CellularRAM、CRAM。它表面上看是SRAM的接口片选、读写、地址、数据都规规矩矩用起来也像SRAM不需要像SDRAM那样操心刷新。但内核实际上是DRAM单元靠芯片内部的刷新逻辑自动维持数据。好处是DRAM单元密度高同样成本下容量比真SRAM大得多价格也便宜所以常用于MCU外扩缓存、显存、音频缓冲这种需要又大又快又不折腾的场景。PSRAM的接口能力跨度很大。低端PSRAM就是普通异步SRAM接口接法跟接SRAM没区别。但主流CellularRAM大多支持同步突发模式地址只在访问开始时给一次后面的数据连续输出接口上有一个时钟脚有的叫CLK和一个等待信号脚NWAIT。这正是同步-复用-突发的关键基础。4.2 同步读突发的完整时序流程用文字描述一次PSRAM同步突发读的过程这个流程理解了配置就不难了。控制器先把地址放到AD总线上因为复用这一步地址和数据线共用所以同时给出ALE锁存信号把地址固定下来。片选NE#拉低存储器进入工作状态。然后控制器按配置好的等待状态数wait states也叫CAS延迟等待若干时钟周期之后第一个数据出现在总线上每个时钟周期出一个数据。假如你配置了突发长度8那从第一个数据开始连续8拍控制器依次采样。这里有个容易混淆的概念等待状态和突发长度是两个独立参数。等待状态决定从地址锁存到第一个数据之间隔几个时钟周期它由器件访问延迟决定突发长度决定一次突发总共传几个数据它由你对总线上数据量的需求决定。不要混着调。NWAIT信号在PSRAM同步模式下的作用值得多说。有些PSRAM内部访问时间并不是恒定的队列冲突、刷新期间访问都可能让器件临时卡顿这时器件会拉低NWAIT告诉控制器等一下。控制器看到NWAIT有效就插入等待周期直到NWAIT释放才继续采样数据。如果你的设计简化到不接NWAIT那等待状态就必须配置成器件支持的最大值否则极端情况下读回来的数据会错位。4.3 器件模式切换上电默认是异步别急着按同步读这块是最多同行踩坑的地方值得专门写一段。很多PSRAM上电复位后的默认状态是异步SRAM模式不是同步突发模式。你想让它跑同步突发得先通过一种类似于总线配置命令的机制往器件内部的配置寄存器比如CellularRAM的BCR/BAR寄存器全称Bus Configuration Register和Bus Adapter Register写入特定值把这个器件切到同步模式配置突发长度、等待状态、驱动强度等等。不同厂商的PSRAM切换命令细节不完全一样但思路都是往某个特殊地址范围写命令字器件根据命令字修改自己的总线行为。所以如果你一上来就配置FMC为同步模式然后去读PSRAM读回来的很可能是垃圾数据甚至全是0xFF。正确顺序应该分两步先用异步模式把FMC配置好以普通SRAM的方式往器件写模式配置命令把器件切换到同步突发模式然后再把FMC重新配置为同步模式开启突发功能。不少人在这一步上栽跟头拿到开发板例程一跑就通但自己画板子时换了PSRAM型号没有注意这个模式切换就怎么都读不对。判断是不是这个问题最简单的方法把FMC改成异步模式读器件ID能读到就说明硬件连接和基础时序没有问题问题出在同步模式切换环节。4.4 同步模式的FMC参数配置同步模式下FMC/FSMC需要配置的参数比异步多一组。以ST的HAL库为例关键参数包括CLKDivision同步时钟分频系数和DataLatency从地址锁存到第一个数据之间的等待周期。CLKDivision决定FMC_CLK和HCLK的频率比例。FMC_CLK越高突发越快但器件能承受的时钟频率有限。手册上会写最大时钟频率例如某PSRAM支持最高50MHz时钟如果你的HCLK是168MHz分频系数至少要取4因为168/442MHz低于50MHz。如果取3时钟变成56MHz超出器件规格同步突发就会不稳定。DataLatency的配置也要参考PSRAM手册里的等待状态要求。比如器件在42MHz下要求等待状态为3那DataLatency就配置成3意味着地址发出后第4个时钟沿开始有有效数据3拍是潜伏期第4拍才是第一个数据。这个参数配上去了再配合突发长度配置同步读就基本成型了。有一个坑是电子工程师几乎都踩过的改同步参数时不小心把突发长度设置为器件不支持的值比如器件只支持4/8/16字突发你却在寄存器里配了32字突发。这时器件不会报错但读回来你会发现在固定间隔出现重复数据或丢数据因为器件在突发到它自己的上限后就不再继续输出了而控制器还按32个字去采样。5. 实测中容易翻车的几个点配置顺序、时序裕量与排查手段5.1 时序配置留裕量别卡着极限值算前文提过一个低温事故这里再展开讲一下。总线时序的极限状态不是你在实验室常温下测出来的那个值而是一个随温度和电压漂移的区间。芯片手册里的tAA、tRC是某个工作条件下的值实际最坏情况会更差。你的配置如果只卡手册典型值那环境一变就可能翻车。建议的做法是把HCLK周期换算后的结果主动加两个周期作为基础裕量然后按我前面说的从大到小收敛的方法实测一遍最后再把极限值回退两个周期作为生产配置。有人可能会问这样会牺牲一点速度值不值我的答案是在大多数嵌入式应用场景下外扩存储器的访问速度远不是系统瓶颈稳定可靠比那几百纳秒的节约重要得多。当然也不是说裕量越大越好。如果AddressSetupTime和DataSetupTime配得过大每次访问都要等很久FMC总线性能会被严重拖累操作系统里跑内存测试都能明显感觉到慢。所以最终配置应该是保证稳定的前提下尽量小。5.2 数据错位的排查AL地址锁存相关复用模式下最常见的数据错位问题根源基本都在地址锁存环节。我做过一个GD32F303挂PSRAM的项目板子回来之后发现一个怪现象读连续地址返回的数据总是偶数地址和奇数地址对调。当时第一反应是数据线接反了量了一圈硬件没发现问题。后来用逻辑分析仪抓ALE和AD线的时序才发现ALE信号低电平持续时间不够锁存器在地址还没完全稳定时就把地址锁存进去了导致AD线上残留的前一个数据被当成地址的一部分。把AddressHoldTime调大一个周期后问题彻底消失。这类问题有一个共同特征不是每次都错而是偶发性出错且错误地址和正确地址之间存在固定偏移。如果你在调试中发现类似多了半个字的偏移或者某几根数据线总是不对优先检查锁存信号时序而不是怀疑芯片坏了。5.3 常用排查思路一个流程化的定位方法总线问题排查如果东一榔头西一棒子很容易把自己绕进去。我这些年总结了一个相对固定的排查顺序分享出来供参考。第一步先确认物理连接。用万用表逐个量片选、读使能、写使能、地址线、数据线排除虚焊和接反。这一步虽然基础但解决过我至少一半的问题。第二步把FMC/FSMC配置成异步非复用模式PSRAM则用异步模式去读器件ID或者往已知位置写一个固定数再读回来。如果能正常读写说明硬件和控制器基础配置没问题。第三步只有异步模式确认了再去动同步模式依次调CLKDivision、DataLatency、突发长度。第四步每调一个参数就做一轮连续地址扫描比如从0x60000000开始依次写0~0xFFFF并读回比对能一次性通过的才说明配置基本正确。这套流程看起来慢实际上是最快的。因为你把硬件问题和配置问题这两个变量先拆开了。很多人喜欢直接上同步模式调结果硬件和软件问题混在一起光靠猜调一整天也可能毫无进展。5.4 一些容易被忽视的细节最后再列几个我在FAE支持和项目评审中反复强调的细节每一个都对应过真实的返厂案例。第一片选信号不要悬空。没接器件的NE引脚要配成禁用否则浮空电平可能让总线产生意外的读写操作。第二如果需要外扩多个器件各自的片选地址区域不要重叠Nor Flash、PSRAM、LCD控制器各占各的64MB空间逻辑上清清楚楚。第三注意FMC/FSMC引脚的复用功能配置GPIO要切换到AF模式并设置正确的速度档位很多人把时序调了半天发现信号根本没出来就是因为引脚复用没开。第四电源退耦不能省PSRAM突发读时电流瞬态很大如果VDD脚旁边没有足够的去耦电容电源跌落会让总线时序全面劣化表现为偶尔读对、经常读错。我个人的实际体会是总线接口的配置表面看是寄存器操作本质是对时序图的理解。与其到处抄初始化代码不如花一个晚上把数据手册里的读时序图逐段对着控制器框图看明白。一次看懂以后换任何芯片、换任何存储器你都能很快上手。真要遇到问题记得用逻辑分析仪把CLK、ALE、NOE、NWAIT这些关键信号抓下来和手册时序图对照绝大多数问题都能在图上一眼看穿。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑