ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32外部总线FMC/FSMC/EXMC实战:NOR Flash与PSRAM配置排障全攻略

STM32外部总线FMC/FSMC/EXMC实战:NOR Flash与PSRAM配置排障全攻略 前阵子帮一个项目组救火他们用STM32F407外扩一颗8Mbit PSRAM做GUI缓冲区代码怎么调都会花屏。后来定位到问题根本不是代码而是FMC的同步突发模式一直没配出来总线老老实实按异步单字节模式跑速度上不去时序也卡在临界状态。这类问题我见过不止一次而且一旦牵扯到FMC、FSMC、EXMC这几兄弟很多人就晕了。所以这篇想把NOR Flash和PSRAM挂在外部总线上的事一次说透从三个缩写的来龙去脉到异步/同步、复用/非复用、突发/不突发的选择再到实际配置和排障一次性讲清楚。这篇内容适合正在用STM32F1/F4/H7或者GD32F30x做外部存储器扩展的工程师也适合那些刚把一颗NOR Flash或PSRAM焊上板子、却发现怎么都读不出数据的同学。全文不绕弯子直接按实际调试的路径走。1. 三个缩写其实是一家人FMC、FSMC与EXMC的定位1.1 从FSMC到FMC一次“改名”而非“换代”很多新人刚接触时会以为FSMC是老的、FMC是新的EXMC又是什么别家的东西感觉三个不一样。实际上它们解决的是同一个问题让MCU像访问内部内存一样访问外部并行存储器。你写一个地址控制器帮你生成片选、读使能、写使能、地址线、数据线上的时序然后把数据读回来或者写出去。至于NOR Flash、SRAM、PSRAM、NAND甚至TFT-LCD屏只要能接受这种并行接口都能挂上去。FSMC全称是Flexible Static Memory Controller最早在STM32F1/F2上出现支持NOR/PSRAM/SRAM、NAND和PC卡。FMC是FSMC的演进版主要出现在STM32F4/F7/H7系列上全称Flexible Memory Controller在原来NOR/PSRAM控制器之外又加了SDRAM控制器同时对时序配置做了一些扩展。EXMC则是GD32系列上的叫法全称External Memory Controller功能定位和FSMC几乎一样GD32F303这种Cortex-M3内核的芯片上就带EXMC。所以这三者不是竞争关系也不是三个截然不同的硬件而是同一类外设在不同产品线里的不同名字。从寄存器层面看STM32F1的FSMC和STM32F4的FMC在NOR/PSRAM这一块的操作逻辑非常接近很多老代码稍作修改就能移植。GD32的EXMC则是在寄存器命名和库函数上和STM32的FSMC保持高度相似理解一个另外两个基本就通了。1.2 NOR Flash与PSRAM为什么需要这么一套专用接口有人会问现在MCU内部Flash和RAM那么大干嘛还要外扩答案是不同场景的需求。NOR Flash能直接映射到MCU地址空间代码可以在上面原地执行XIP所以很多需要大容量代码存储或者掉电保存数据的场合会外挂一颗NOR Flash上电后CPU直接从外部Flash取指不用先拷到内部RAM。PSRAM是另外一个方向的产物。它的存储核心本质上是DRAM1T或者2T单元结构容量大、成本低但DRAM需要周期性刷新普通MCU要是自己去管刷新就会很痛苦。PSRAM的做法是在芯片内部集成刷新电路对外接口做得和SRAM一模一样CPU只要像访问普通RAM一样读和写就行不需要关心行激活、预充电、刷新这些底层操作。这也是“伪静态”这个名字的由来内部是动态的外部看起来是静态的。如果没有总路线控制器想接这些器件就得用GPIO去模拟时序读一个字节要拉半天引脚速度慢不说CPU也被完全占死。FMC/FSMC/EXMC这类控制器的价值就是把CPU从位操作里解放出来你只需要一条指令访问地址控制器自动处理所有总线的电平切换和时序关系。1.3 三款外部总线控制器的能力差异对比为了直观看清三者的关系我按实际工程中常用的维度做了一个简单对比对比项FSMCSTM32F1/F2FMCSTM32F4/F7/H7EXMCGD32F30x外部存储映射基址0x600000000x600000000x60000000NOR/PSRAM片选数量444NOR/PSRAM数据位宽8/16位8/16位部分型号32位8/16位是否支持SDRAM不支持F4系列部分型号支持部分型号支持是否支持NAND Flash支持支持支持配置寄存器风格BCR/BTR/BWTRSNCR/SNTR或BCR/BTR类似FSMC从表里能看出三者在NOR/PSRAM这块的核心能力几乎一致尤其是前4个片选都是从0x60000000开始映射。这也是为什么很多STM32F103的FSMC代码能直接照着改到STM32F407的FMC上GD32F303的EXMC例程也经常能在STM32板子上参考。2. 选型之前先搞懂三种维度异步/同步、复用、突发标题里那串“异步-复用-不突发 / 同步-复用-突发”看着吓人其实是在描述三个正交的维度控制时序是异步还是同步地址线和数据线是复用还是分离数据传输是突发还是非突发。这三个维度各自独立但实际产品里又经常组合成两种典型配置这也是市面上NOR Flash和PSRAM最常见的两种工作形态。2.1 异步读写的本质一切由“时间参数”决定异步模式是NOR Flash和PSRAM最基础的工作方式。在这种模式下总线上没有外部时钟读操作时控制器先给出地址然后拉低片选和读使能信号存储器经过一段访问时间后把数据放到数据线上控制器再把它采回来写操作则是控制器在写使能有效期间把数据稳定在数据线上。整个过程完全靠控制信号和数据的相对时序来保证时钟不是必需的。配置异步模式时关键就是那几个时间参数地址建立时间Address Setup Time、地址保持时间Address Hold Time、数据建立时间Data Setup Time、总线翻转周期Bus Turnaround。它们直接对应到FMC的BTR/BWTR寄存器或者HAL库里的Timing结构体。地址建立时间的意思是地址线上已经稳定、但还没有开始真正读写的一段时间给存储器一个准备数据建立时间则是读使能或写使能有效到数据被采样/锁存之间的时间这段如果太短从设备数据还没准备好就被读走或者写数据还没写稳就被撤销。用生活化的类比说异步模式下你每读一次数据就相当于发一次快递先写地址填收货单等地址稳定确认地址没写错再拉读信号通知发货然后等数据上线等包裹送到最后读走。每访问一个字节都走一遍完整流程所以速度不快但兼容性好、实现简单。NOR Flash早期的读操作基本都是这种模式低速访问完全够用。FMC/FSMC在异步模式下还细分成好几种访问协议比如模式A、模式1、模式2等等区别在于读和写时序的细节比如OE与WE的相位关系、地址保持时间是否复用。实际配置时不需要死记只要从对应芯片的数据手册里查出读周期、写周期、地址建立时间等参数再填入对应的寄存器字段就行。2.2 复用与非复用地址线不够时才需要考虑复用英文叫Multiplexed指的是地址线和数据线共用同一组引脚。比如一颗PSRAM有16根数据线和若干根地址线如果MCU引脚不够可以把低16位地址和16位数据放到同一组线上先发地址再切到数据分时使用。这种方式能省不少PCB空间和MCU引脚但代价是每个访问周期都要多出地址锁存的环节速度有所下降时序配置也更复杂。非复用就是常见的做法地址线归地址线数据线归数据线两者互不干扰。访问时地址直接给出去数据线独立承载数据时序更简单速度通常也更高。对于引脚富裕的MCU方案默认选非复用就好。标题里的“异步-复用-不突发”和“同步-复用-突发”两种组合实际上反映了低引脚数产品里更常见的设计地址线不够的情况下用复用模式省引脚同时通过同步突发把分时复用带来的性能损失补回来。反过来如果你引脚够用非复用模式会省心很多很多新人上来就纠结复用模式结果把自己绕进去了其实先跑通非复用才是正路。2.3 同步模式与突发把带宽真正跑满的关键同步模式与异步最大的区别是总线上多了一个时钟信号所有地址、数据和控制信号的采样/更新都以这个时钟为基准。同步模式能跑很高的频率但它的真正威力不在单个访问而在突发Burst。突发模式的意思是控制器只发送一次起始地址后面连续若干个时钟周期每个周期都传输一个数据。比如设置突发长度为8一次地址发起后连续读/写8个数据中间的地址切换完全省掉这对于顺序访问的场景比如从PSRAM连续读取一帧图像数据到LCD控制器或者从NOR Flash顺序读取一段代码效率提升非常明显。要配置好同步突发有几个参数必须搞清楚FMC_CLK分频系数这是外部总线的时钟频率由HCLK分频得到。比如HCLK是168MHz分频系数配置为1FMC_CLK就是84MHz这个值不能超过外设数据手册允许的最高时钟。数据等待周期同步读时数据从存储器出来可能需要一定的等待周期数通常用DataLatency或DATLAT字段设置。页面边界CRAM page size有些PSRAM内部按页面组织突发不能跨页控制器需要知道页面大小在跨页时自动插入额外周期。连续突发使能部分控制器支持连续写突发避免写操作被打断。从波形上理解突发读就是“发一次地址然后等着数据一排一排出来”就像公交车只停一站后面乘客排队上车而不是每个乘客都单独叫一辆车。2.4 PSRAM与DDR DRAM的区别同名伪静态难度差很多很多人搜“dram和ddr psram的区别”说明在选型时看到两个字很像就混了。PSRAM和DDR DRAM虽然内核都基于动态存储单元但使用难度完全不同。PSRAM对外是SRAM接口没有行/列复用没有预充电没有自动刷新配置甚至不需要初始化训练上电后只要配置好时序就能直接读写。DDR DRAM则完全不同它必须由DDR控制器完成初始化、训练、定期刷新、Bank管理、突发长度配置接口上还分命令线和DQ数据线时序极其严格。单片机上的FMC/FSMC/EXMC根本不是用来直接驱动DDR颗粒的如果你要做DDR内存条需要的是带DDR PHY的MPU或FPGA方案。从应用场景看MCU外扩中大容量缓存、GUI帧缓冲、语音缓冲选PSRAM就对了而DDR基本是应用处理器、FPGA、高性能MCU比如带MIPI和DDR接口的型号才会用到的方案。选型时知道这一点能少走很多弯路。3. 实战落地从原理图到FMC/EXMC初始化代码理论上讲再多最终都要落到板子上。这部分我按自己实际调试的流程走一遍从硬件连接开始到CubeMX配置再到时序参数换算最后提一下GD32的库函数怎么适配。3.1 原理图连接地址线偏移和字节使能硬件连接是FMC/FSMC最容易栽跟头的地方而且栽了很难查出来。先说最经典的一个问题地址线偏移。当总线位宽是16位时芯片内部地址是“按字节”编址的而外部存储器地址线通常按“字”或者“半字”组织。如果接一颗16位NOR FlashMCU的A0这一根地址线就得空着不接外部存储器的A0要接到MCU的A1。也就是说你在软件里访问地址0x60000002硬件上真正送到外部器件地址线的其实是2号半字对应字节地址0x60000004。这是硬件设计决定的软件里改变指针类型并不能弥补接线错误。8位存储器不存在这个偏移A0对A0。但如果你的存储器是8位芯片而控制器配置成了16位访问结果会非常怪异甚至只能读到一半数据。所以原理图阶段就要先想清楚数据位宽再确定地址线到底要不要错位。字节使能信号也是很多人忽略的。16位总线下FMC会输出NBL0和NBL1两个字节使能信号分别对应低字节和高字节。如果这两个信号没有接到存储器的UB/LB引脚16位访问时就会变成“整个半字被当成低字节或高字节写入”数据错乱是必然的。PSRAM和NOR Flash一般都有BYTE引脚或UB/LB引脚一定要按数据手册接对。另外一个容易被忽略的点是存储器的片选和控制信号极性。大多数外设的CS、OE、WE都是低有效FMC默认也是低有效这个基本没问题。但有些NOR Flash还带有WP写保护、RESET复位、BYTE字节/字模式选择等引脚这些引脚如果悬空可能导致写操作被屏蔽或者进入错误的位宽模式。我的习惯是WP接高电平RESET通过电阻到高电平BYTE按需要的位宽接固定电平绝不能什么都不接。3.2 STM32CubeMX配置FMC NOR/PSRAM用STM32CubeMX配置FMC是我目前觉得最不容易出错的方式。以STM32F407为例打开CubeMX后在Pinout Configuration面板里找到FMC勾选NOR/PSRAM选项然后选择使用哪个片选比如NE1。之后需要设置Memory type可选NOR Flash或PSRAM。如果是NOR Flash还要打开FACCEN如果是PSRAM则通常不需要这个使能位。数据宽度按硬件设计选择8位或16位。关键是Timing这块。新版CubeMX里可以直接选择按ns还是按cycles填写这就方便很多。对于异步读需要填AddressSetupTime、AddressHoldTime、DataSetupTime、BusTurnPhaseDuration如果配置了复用模式AddressHoldTime也会变得重要。对于同步模式则要填ClockDivideFactor和DataLatency。这里我建议一个保守的初始配置异步模式下ADDSET填1~2个周期DATAST填8~12个周期ADDHLT和BUSTURN先填0。多数慢速NOR Flash和PSRAM都能在这种宽松时序下跑通。等第一次读写验证通过后再逐步压缩DATAST看示波器波形再决定最终值。CubeMX生成代码后记得检查一下GPIO的复用功能是否都正确配置尤其是FMC的数据线和地址线一不留神就会少配一两根引脚。我见过的很多“FMC不工作”案例最后查出来都是某根地址线没有切到AF模式。3.3 时序参数从手册到寄存器一个具体换算案例光说参数名还是虚的我拿一个实际例子走一遍换算过程。假设系统HCLK168MHz则每个时钟周期T 1/168MHz ≈ 5.95ns。外接一颗PSRAM数据手册上给出的异步读周期时间tRC最小是70ns地址建立时间可以取0输出使能到数据有效时间大约是50ns。如果采用非复用异步模式一个读周期至少要满足从地址稳定开始到数据被采样之间的总时间不小于70ns。我们把AddressSetupTime设为1个HCLK周期约5.95nsAddressHoldTime设为0DataSetupTime设为X。整个读周期大约是ADDSET DATAST BUSTURN这里BUSTURN先设为0那么需要 (1 X) * 5.95 70计算得到 X 10.76向上取整为11。所以DATAST填11总访问时间约71.4ns满足70ns要求。写时序通常比读时序快。这颗PSRAM手册给的写周期最小可能只要45ns同样算法下DATAST只需7个周期左右。但实际使用时我不会把写时序压得比读时序激进太多否则板子布线寄生参数一上来就容易偶发错误。CubeMX里读时序和写时序是分开配置的我不建议直接把写的DATAST填成6、7就完事最好留出10%~20%余量。同步突发模式下的参数换算又是另一套思路。如果想把FMC_CLK跑到84MHzCLKDIV1即可因为FMC_CLK HCLK / (CLKDIV 1) 168MHz / 2 84MHz。接下来查PSRAM手册看它从地址发送到第一个数据有效需要多少个时钟周期这个值通常就是DATLAT常见配置是2~6个FMC_CLK周期。如果颗粒要求第一个数据在总线时钟第3个上升沿后才能出现那DATLAT就写3。这个参数配小了会丢第一个数据配大会浪费等待时间需要仔细对手册。3.4 GD32F303 EXMC的配置方法很多人在搜索引擎里直接搜“gd32f303 fmc例程”因为用惯了STM32叫法。实际上GD32F303的对应外设叫EXMC库函数里也都是exmc_前缀。思路和STM32的FSMC完全一样只是函数名、结构体字段会有差异。以GD32F30x标准外设库为例初始化EXMC的NOR/PSRAM区域大致分三步先定义一个EXMC norsram参数结构体填写存储区域号、存储器类型、数据宽度、时序参数然后调用初始化函数写入寄存器最后使能对应的区域。典型代码骨架长这样exmc_norsram_parameter_struct norsram_init; exmc_norsram_timing_parameter_struct norsram_timing; norsram_timing.address_setup_time 0; norsram_timing.address_hold_time 0; norsram_timing.data_setup_time 11; norsram_timing.bus_turnaround_delay 0; norsram_init.norsram_region EXMC_BANK0_NORSRAM_REGION0; norsram_init.memory_type EXMC_MEMORY_TYPE_PSRAM; norsram_init.data_width EXMC_NORSRAM_DATA_WIDTH_16B; /* 其余字段按库头文件定义填写 */ exmc_norsram_init(norsram_init, norsram_timing); exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION0, ENABLE);不同版本的GD32固件库字段命名会有些出入比如有的版本把memory_type写成memory_select有的版本把使能函数叫exmc_norsram_enable有的叫exmc_norsram_bank_enable。遇到编译报错不要慌打开头文件看一眼结构体定义对照着改就行。核心的地址映射和STM32一样Bank0区域地址也是从0x60000000开始软件访问方式没有任何区别。4. 现场排障FMC/FSMC/EXMC常见问题与排查技巧配置完成不等于万事大吉实际调试中踩坑才是常态。这一节我把这些年遇到的高频问题整理出来每个都附上排查思路。4.1 地址错位16位位宽下的经典翻车如果发现代码里写的数组读写地址对不上比如在0x60000000写一个数然后从0x60000002读出来发现数据根本对不上或者写入地址总是只能写偶数地址十有八九是原理图阶段地址线没有做偏移。16位存储器要求外部地址线A0接到MCU的A1A1接到A2以此类推。如果直接A0接A0软件里看到的地址就会“压缩”一半你访问0x60000002硬件上送出去的却是外部地址1号半字对应的地址你访问0x60000004硬件送的是外部地址2号半字。对于NOR Flash这种要按地址发命令的器件这个问题尤其严重命令序列几乎不可能成功。排查方法很简单读一次NOR Flash的ID如果读出来的是乱码且规律性地只差一位或几位基本就是地址偏移没做对。这种问题改软件没用只能改硬件转接板或者飞线。4.2 读出来全是0xFF或0x00问题多半不在代码这是最让人抓狂的现象之一。芯片读回来全是0xFF通常意味着数据线没有真正被驱动可能原因有片选没选中、读使能没生效、电源没供上、芯片处于复位状态、字节使能被拉错。建议第一步不要碰代码先用示波器或逻辑分析仪抓CS、OE、WE和数据线的波形。手动发一个读操作观察CS有没有按预期拉低OE有没有在访问期间拉低数据线上有没有出现有效输出。如果CS根本没反应检查片选地址是否对应了正确的Bank区比如用了NE2但软件访问的是NE1的地址CS当然不动。还有一类特殊情况是NOR Flash的复位引脚被拉低了导致芯片一直处于复位状态读任何地址都是0xFF。查一下RESET引脚电平通常要高电平才能正常工作。另外WP写保护引脚如果悬空可能导致写操作静默失败表现出来就是读出来正常但写不进去。4.3 PSRAM同步突发模式的“假死”与花屏PSRAM在异步模式下通常很稳定但一旦切到同步突发模式问题就多了。最常见的是GUI花屏或者数据偶发错乱而这种问题不是每次都能复现很考验耐心。先检查同步时钟频率是否超过PSRAM规格。比如某颗PSRAM最高只能跑84MHz你把FMC_CLK配到104MHz甚至更高短期测试可能没反应跑一段时间就数据错乱。用逻辑分析仪量一下FMC_CLK的实际频率确认和配置一致。再检查DATLAT等待周期。同步突发读最关键的是从地址发送到第一个数据有效之间的等待周期这个值偏小会导致第一个数据被提前采样后续所有数据都会错位一格表现出来就是花屏、图像整体偏移。遇到这种情况把DATLAT加1~2试试往往立刻好转。还有页面边界问题。PSRAM突发传输不能跨页比如页面大小128KB你在页面末尾附近做长突发控制器自动分页后如果参数配置不对就会在这个位置附近出现零星错误。CubeMX里CRAM page size选项要按你的PSRAM实际页面大小填填错了就会出现“只在某个地址附近出错”的诡异现象。4.4 突发模式开了为什么速度还是上不去有人配置了同步突发测试下来速度并没有提升多少甚至和异步模式差不多。这种情况下先确认你是否真的跑进了突发模式。用逻辑分析仪看FMC_CLK如果发现访问时CLK没有持续翻转或者每个数据之间都夹着地址切换周期说明控制器并未按连续突发运行。常见原因是写突发没使能有些FMC配置里读突发是默认使能的但写突发需要一个单独的控制位比如CBURSTRW没使能的话写操作就会退化成非突发模式。另外代码里的访问方式也很关键。如果你每次只读4个字节就跳转到不连续地址即使控制器支持突发内存访问的连续地址跨度也不足以触发长突发。要提高吞吐应该尽量用指针递增连续访问比如批量拷贝PSRAM数据时用memcpy或者for循环连续遍历而不是碎片化访问。还有一个很容易忽略的地方代码编译优化等级。如果调试时用了-O0CPU生成的取指和访存指令可能会插入大量额外访问总线突发效率无法充分发挥。调到-O2再看性能数据才比较真实。5. 一些调试经验小结如果非要用一句话总结我这几年调外部总线的体会那就是先把异步模式跑稳再谈同步突发先看波形再改参数。很多人一上来就想直接上最高性能模式结果被各种诡异问题砸懵其实FMC/FSMC/EXMC这类外设最难的不是初始化而是找到那一组既满足手册要求又贴合板子实际布线的时序参数。我自己的习惯是新板子第一次调试FMC时先在异步模式下用最简单的循环读写把所有地址区域测一遍比如向0x60000000到0x6000FFFF写入伪随机数再读回来比对确认地址线、数据线、控制线全部正确之后再切换同步突发模式优化速度。这个基础如果没打牢后面出现的任何问题都会让人分不清是硬件问题还是配置问题。如果你现在也卡在某个总线问题上建议先拿示波器把CS/OE/WE的波形抓出来对照数据手册一条一条看大多数问题都能在波形里找到答案。
返回列表