STM32H745外挂SDRAM参考设计:FMC接口、硬件布线及初始化

📅 发布时间:2026/8/31 22:53:55
STM32H745外挂SDRAM参考设计:FMC接口、硬件布线及初始化
最近整理了一块板子的核心参考设计STM32H745IIT6 外挂一颗 IS42S83200J 的 SDRAM容量 16MB数据总线 32bit。可能有人会想H745 已经是双核 480MHz 240MHz内部还带 1MB SRAM为什么还要再挂 SDRAM这个问题的答案恰恰是这类参考设计真正有价值的地方当你要跑 LVGL 界面、做音频缓存、或者放一帧摄像头图像时1MB 内部 SRAM 根本不够用而外挂 SDRAM 是性价比最高、驱动最成熟的大内存方案。这篇记录面向正在画 H745 板卡、准备扩展外部内存的硬件工程师也适合把 H7 工程从内部 RAM 迁移到外部 SDRAM 的嵌入式开发参考。我会把方案选型、FMC 接口原理、原理图与布线要点、初始化配置、以及实际调通时踩过的坑一次性写清楚尽量做到能直接照着做。1. 方案选型为什么是 H745 配 IS42S83200J1.1 STM32H745IIT6 的核心价值STM32H745IIT6 属于 STM32H7 系列里的双核型号Cortex-M7 跑 480MHz、Cortex-M4 跑 240MHzFlash 有 2MB内部 SRAM 加起来有 1MB。I 后缀表示工业级温度范围T 是 LQFP 封装6 代表 176 pin。这颗料能同时跑两个核M7 负责重负载算法M4 可以单独处理通讯或者实时性要求高的任务架构上非常适合处理器密集和 I/O 密集混合的项目。不过很多人容易忽略一点H745 的内部 SRAM 虽然看着有 1MB但它被分成了 AXI SRAM、SRAM1、SRAM2、SRAM3、SRAM4 好几块大块内存分配并不是完全自由的。真要放一个 1280x800 的 RGB565 显存一张就要 2MB内部 RAM 直接爆掉。所以外挂 SDRAM 不是可选而是这个方案的刚需。1.2 IS42S83200J 这颗 SDRAM 的真实定位IS42S83200J 是 ISSI 家的一颗 128Mb 同步动态随机存储器组织方式是 4M x 32bit换算下来正好 16MB。它内部有 4 个 bank行地址 12 位列地址 8 位供电 3.3V工作频率一般能到 143MHz 或 166MHz不同速度等级对应不同尾缀。要说性能它在现在这个年头不算快但胜在稳定、成熟、便宜而且 32bit 位宽配合 FMC 接口访问效率很高。选这颗颗粒还有一个现实原因H745 的 FMC 控制器原生支持 16bit 和 32bit SDRAM而 IS42S83200J 的 32bit 总线正好能把 FMC 的数据总线宽度用满。同样跑 50MHz 到 100MHz 的 SDRAM 时钟32bit 位宽比 16bit 位宽带宽直接翻一倍这对要拿作显存或者摄像头的缓冲区的场景来说差别非常大。1.3 没有外部内存时1MB 内部 SRAM 够不够用如果只是做简单控制逻辑内部 1MB SRAM 永远用不完。可一旦涉及图像、音频、界面框架内存就立刻变成瓶颈。比如一个 240x240 的 RGB565 屏幕缓冲一帧就要 112KB摄像头 VGA 分辨率 YUV422一帧也要 300KB 以上做音频算法如果需要几十毫秒的延迟缓冲几个缓冲区叠下来1MB 根本不够。外挂 SDRAM 后可以把 LVGL 的显存、片外 framebuffer、DMA 缓冲区、音频 FIFO、甚至日志缓冲区全放进去。SDRAM 虽然延迟比内部 SRAM 高但顺序访问带宽足够用好了完全能撑起 GUI 刷新和摄像头抓取这类场景。还有一个隐性好处外挂 SDRAM 还能作为 M7 和 M4 之间的共享内存双核跑数据交换时不用占用昂贵的内部 SRAM省心不少。2. FMC 接口深度拆解SDRAM 控制器的原理与配置2.1 FMC 是怎么把 SDRAM 变成一段内存的H745 的 FMCFlexible Memory Controller负责对外部存储器做访问控制。它不仅仅管 SDRAM还能接 NOR Flash、SRAM、NAND Flash但 SDRAM 部分有独立的命令控制器。简单理解FMC 把 CPU 发出的地址和数据请求翻译成 SDRAM 需要的行激活、列读写、预充电、刷新这些时序操作。也就是说CPU 只需要像读内部内存一样读 0xC0000000 这个地址段FMC 会自动完成 SDSDRAM 的复杂时序。SDRAM 和静态存储器不一样它需要周期性刷新否则内部存储的电荷会丢失。FMC 的 SDRAM 控制器里有刷新计数器可以硬件自动刷新不需要 CPU 干预。这也是为什么同样是外部内存SDRAM 比 SRAM 便宜得多但初始化流程和时序要求更挑剔。实际使用中只要初始化序列没有严格执行后面读写就会出现各种随机错误。2.2 地址线、数据线与 Bank 映射STM32H745 的 FMC 可以接两片 SDRAM分别用 FMC_SDNE0 和 FMC_SDNE1 做片选对应地址段是 0xC0000000 和 0xD0000000。我们的参考设计只用一片挂在 Bank1也就是 0xC0000000 起始地址。数据线 FMC_D0 到 FMC_D31 共 32 根地址线这里用到 FMC_A0 到 FMC_A11以及 bank 选择线 FMC_BA0、FMC_BA1控制线包括 FMC_SDCLK、FMC_SDCKE0、FMC_NRAS、FMC_NCAS、FMC_NWE、FMC_SDNWE另外还有字节屏蔽 FMC_NBL0 到 FMC_NBL3。这里有一个很常见的误读地址线 FMC_A0-A11 在 SDRAM 里是行地址和列地址复用的不是像 SRAM 那样一次性把所有地址高低电平给出。FMC 先发行地址再发列地址通过 RAS 和 CAS 信号的先后顺序区分。所以不要用容量等于 2 的地址线次方去硬套SDRAM 的寻址能力要看行数、列数和 bank 数的乘积。IS42S83200J 是 4 个 bank、每 bank 1M 个字数据总线 32bit整体就是 4M x 32bit 16MB。2.3 时序参数和刷新机制必须搞懂的两个细节FMC 时序初始化时有一堆参数比如 RCDDelay、RPDelay、RowCycleDelay、WriteRecoveryTime、ExitSelfRefreshDelay、LoadToActiveDelay。这些参数的本质是把 SDRAM 数据手册里的 tRCD、tRP、tRC、tWR、tXSR、tMRD 转换成 SDCLK 周期个数。转换方法是物理时间除以时钟周期向上取整再留一点余量。比如 SDCLK 跑 100MHz周期 10nstRCD 是 15ns那么至少需要 1.5 个时钟周期实际取 2 个周期。刷新机制是另一个容易出错的地方。IS42S83200J 数据手册会给刷新周期数/刷新时间常见是 4096 行/64ms也就是每 64ms 要把所有行至少刷一遍。FMC 的刷新计数器会匀速发出自动刷新命令刷新周期寄存器里的计数值要按实际 SDCLK 频率计算。如果算错刷新过密会浪费带宽刷新过疏则会出现数据保持错误。初始化时一定要对照数据手册确认行数不要照抄别的型号配置。3. 硬件设计实操从原理图到 PCB 的关键约束3.1 原理图设计要点与信号连接原理图阶段的重点是把 FMC 信号和 SDRAM 一一对上同时给每个电源引脚配上足够去耦。IS42S83200J 是 3.3V 器件H745 的 FMC 引脚如果配置为 3.3V 电平可以直接连接不用加电平转换。连接时注意 FMC_NBL0-NBL3 要接到 SDRAM 的 DQM0-DQM3这个在原理图检查时经常被漏看一旦漏接16bit 或 8bit 写操作就会出错。电源去耦方面SDRAM 的供电引脚和 I/O 供电引脚最好都就近放一个 100nF 陶瓷电容然后板级再放一个 10uF 左右的钽电容或者多层陶瓷电容。H745 的供电要严格按数据手册来特别注意 VCAP 引脚上的电容值和 ESR 要求这是内部 LDO 稳压的关键很多自制板跑不稳都是 VCAP 电容选错导致的。原理图评审时我一般还会核对复位引脚的上拉、BOOT 引脚默认状态以及 SWD 调试接口的完整性这些和 SDRAM 看似无关但实际上影响调试效率。3.2 布局布线经验别让 SDRAM 死在信号完整性上PCB 布局上SDRAM 要尽量靠近 MCU走线距离越短越好。H745 的 FMC 总线工作在几十到一百多 MHz对于 SDRAM 这种并行总线数据线和地址线如果走得太长反射和串扰会非常明显。我的经验是SDRAM 和 MCU 放在同一面两者中间不要穿插其他连接器或者高噪声器件FMC 信号尽量集中在一个区域。布线约束建议分三个层次处理信号组约束建议说明数据线 D[31:0] 和 NBL组内等长误差控制在 50mil 以内数据线是双向的时序裕量最紧张地址线 A[11:0]、BA[1:0] 和控制线组内等长相对时钟线误差控制在 100-150mil地址和控制线是单向的要求略宽松SDCLK 时钟线单独走包地或与地间距保持 2 倍线宽时钟是所有信号的参考基准最不能含糊布线时我会尽量保持整组信号在同一层减少过孔数量因为过孔会带来不连续的阻抗变化。如果有条件用 4 层板保证有一个完整的地平面和一个完整的电源平面。串阻方面每个 FMC 信号串 22 到 33 欧姆的电阻放在驱动端附近能有效抑制过冲。对于 100MHz 级别的 SDRAM 接口这些措施不是玄学而是能不能稳定过温度测试的分水岭。3.3 电源和去耦设计容易被忽略的坑H745 这类高性能 MCU电源设计比 SDRAM 部分更容易踩坑。MCU 的 VDD 电源平面要足够宽并在每个电源引脚旁放 100nF 电容大容量的 bulk 电容也要跟上。SDRAM 对电源纹波比较敏感如果 3.3V 纹波过大可能表现为偶发读写错误而且这种错误很难复现。示波器测 SDRAM 电源引脚时纹波最好控制在 50mV 以内。还有一点SDRAM 的数据总线在翻转时会产生较大的瞬态电流这个瞬态电流会在地平面上造成噪声。如果 MCU 的地和 SDRAM 的地分开铺反而容易形成电位差。正确做法是保证整个板子地平面连续让回流路径最短。省地层是绝对不可取的会直接导致信号完整性问题。另外H745 的 LQFP176 封装底部有裸露焊盘一定要良好接地并做好散热满载运行时内核温度控制全靠这个焊盘。4. 软件初始化与验证让 SDRAM 真正跑起来4.1 CubeMX 中 FMC SDRAM 参数怎么填用 CubeMX 配置时在 FMC 里启用 SDRAM Bank1然后把颗粒参数按 IS42S83200J 填写。列地址 8bit行地址 12bit内部 bank 数 4数据位宽 32bitCAS latency 根据你的时序余量选 2 或 3。SDCLK 周期一般选 HCLK 分频比如 HCLK 200MHz 时选除以 2得到 100MHz 的 SDCLK。这里不要贪快SDRAM 本身能跑 166MHz但 FMC 控制器和 PCB 走线未必跟得上。CubeMX 还会要求填写刷新率这个值是根据颗粒刷新周期算出来的时间值。IS42S83200J 按 64ms/4096 行计算刷新间隔约 15.6us。在 CubeMX 里写刷新率时系统会按你的 SDCLK 频率换算成刷新计数。我的习惯是先按 15.6us 填入跑内存测试确认稳定后再考虑是否需要调整。如果发现刷新过于频繁导致带宽下降可以在满足数据手册要求的前提下适当放宽。4.2 初始化时序计算与代码落实SDRAM 上电后必须严格按顺序执行初始化命令否则颗粒无法进入正常读写状态。首先是上电等待约 100us然后发 NOP 命令接着执行 PRECHARGE ALL把所有 bank 预充电再发两个以上 AUTO REFRESH 命令最后是 LOAD MODE REGISTER把 CAS latency、burst length、write burst 模式写进颗粒的模式寄存器。HAL 库的 HAL_SDRAM_Init 之后需要手动调用 HAL_SDRAM_SendCommand 按这个顺序走一遍。以 100MHz SDCLK 为例时序寄存器可以这样估算tRCD 约 15ns 记 2 个周期tRP 约 15ns 记 2 个周期tRC 约 63ns 记 7 个周期tWR 约 14ns 记 2 个周期tXSR 约 70ns 记 7 个周期tMRD 约 12ns 记 2 个周期。这些数值不要照抄要打开颗粒数据手册对着换算。模式寄存器的典型值CAS2、顺序突发、burst length 选 1 或者 4/8需要结合你的访问模式决定。跑 GUI 时我一般选较短的 burst避免突发太长占用总线。初始化完成后建议在 0xC0000000 地址段做一次基础读写。比如往里面写 0x00000000 和 0xFFFFFFFF 两个极端值再写 0x5A5A5A5A 和 0xA5A5A5A5读回校验。如果能通过说明 SDRAM 已经能响应基本读写后面再上压力测试。不要急着跑应用先把这块内存的底摸清楚。4.3 内存自检与带宽评估实测数据说话内存自检不能只写一种 pattern我习惯做三类测试。第一类是数据线测试用 walking 1 和 walking 0 逐位翻转用来找出数据线短路、虚焊或者交叉连接。第二类是地址线测试在地址值编码进数据例如在地址 0x100 写入 0x100然后全片扫描可以发现地址线的高位或低位有无粘连。第三类是随机数据压力测试用伪随机序列反复写读跑足够长时间。对应代码如下可以直接在工程里调用int sdram_self_test(uint32_t base, uint32_t len) { uint32_t i; volatile uint32_t *p (volatile uint32_t *)base; // walking 1 测试 for (i 0; i len / 4; i) { p[i] 0x1UL (i % 32); } for (i 0; i len / 4; i) { if (p[i] ! (0x1UL (i % 32))) return -1; } // 地址相关性测试 for (i 0; i len / 4; i) { p[i] i; } for (i 0; i len / 4; i) { if (p[i] ! i) return -2; } // 翻转 pattern 测试 for (i 0; i len / 4; i) { p[i] 0x55555555; } for (i 0; i len / 4; i) { if (p[i] ! 0x55555555) return -3; } return 0; }带宽评估可以用 DWT 计数器来测批量 memcpy 的时间。比如复制 1MB 数据到 SDRAM读 DWT-CYCCNT 计算周期数再根据 CPU 频率换算带宽。我实测在 100MHz SDCLK、32bit 总线时连续读写的有效带宽大概在 300MB/s 到 380MB/s 之间比纯理论值低因为还有刷新和行切换开销。这个数据对评估 GUI 刷屏是否流畅有直接参考价值。5. 问题排查与稳定性优化实录5.1 数据偶发错误先从这几个方向查SDRAM 出现偶发数据错误时先不要急着怀疑颗粒按照概率排序排查第一是时序寄存器配置过紧第二是 SDCLK 频率过高第三是供电纹波第四是 PCB 等长和参考平面最后才是颗粒本身。多数情况下把 RCDDelay 或 RowCycleDelay 加一个周期现象就消失了。这说明时序裕量不足不是颗粒坏了。如果只在温度升高后出现错误优先检查电源散热和 SDCLK 信号质量。用示波器看 SDCLK、DQS如果有和数据的建立保持时间比较麻烦但最基本的方法是看波形有没有明显过冲、振铃和上升沿塌陷。没有示波器的话就把系统时钟降档试试比如 SDCLK 从 100MHz 降到 66MHz如果错误消失基本可以判断是信号完整性或时序裕量问题。5.2 刷新与 CPU 访问冲突用什么手段规避SDRAM 刷新操作会占用总线周期如果 CPU 正在连续读取刷新请求会把当前访问打断造成额外的等待周期。对于大多数应用这种延迟影响不大但如果你在跑实时性要求高的 DMA 传输刷新带来的等待可能导致 DMA 带宽抖动。FMC 的刷新计数器是均匀插入刷新的问题通常不会集中在某一刻所以实际影响一般可控。不过有一个常见冲突值得注意在低功耗模式下如果关闭了 FMC 时钟或者进入 Stop 模式SDRAM 会停止刷新数据就可能丢失。需要保留 SDRAM 内容时要么让系统进入 SDRAM Self-Refresh 模式要么避免在持有 SDRAM 数据时进入低功耗。H745 双核架构下还要特别注意 M7 和 M4 同时访问 SDRAM 时的总线仲裁建议把共享数据区域做好互斥访问防止逻辑上的竞争。5.3 M7 的 D-Cache 与 SDRAM 一致性最容易踩的坑这个坑我必须单独拿出来说。H745 的 Cortex-M7 带 D-Cache默认情况下 CPU 写到 SDRAM 的数据可能先停在 Cache 里并不会立刻写回 SDRAM。如果这时 DMA 外设直接去 SDRAM 读数据读到的可能是旧数据。反过来DMA 往 SDRAM 写数据后M7 再去读可能读到 Cache 里的旧缓存导致数据不一致。解决办法有两种。第一种是给映射到 SDRAM 的内存区域配置 MPU设置为 non-cacheable这样所有访问都直接穿透到 SDRAM逻辑最简单。第二种是保留 Cache但在 DMA 传输前后手动执行 Clean 和 Invalidate 操作比如调用 SCB_CleanDCache_by_Addr 或 SCB_InvalidateDCache_by_Addr。如果这块内存要兼顾 GUI 刷新和摄像头 DMA我更推荐用 MPU 划分区域按需 cacheable避免每次 DMA 都要手动刷缓存。5.4 高温和长时间稳定性测试方法SDRAM 常见的问题往往不是一开始就出现的而是在温度升高或者长时间运行后暴露。我的习惯是先把板子在常温下跑内存自检 30 分钟然后放进温箱做 60 度到 85 度的热循环测试每个温度点停留至少 30 分钟期间持续跑随机读写。如果温度升高后出错率明显上升优先怀疑 SDCLK 时序裕量不足因为温度会影响信号传播延迟和芯片内部时序。长时间稳定性测试还要包括反复进入和退出低功耗模式的场景。比如系统每 5 秒进一次 Stop 模式唤醒后立即读写 SDRAM跑几个小时看是否出现数据损坏。这类问题在常温下很难复现但一旦发生就非常难查。提前在自检代码里加入 CRC 校验连续检测数据完整性能快速定位是刷新问题还是总线竞争问题。最后再分享一点个人体会我在实际做 H745 IS42S83200J 这个方案时最大的感触是这类老接口的 SDRAM 虽然技术上不新但稳定性和性价比依旧能打。画板时把信号完整性放在心上初始化时序认真换算内存自检跑足基本不会翻车。反而是那些看起来和 SDRAM 无关的问题比如 D-Cache 一致性、低功耗刷新、双核共享内存才是真正费时间的地方。如果你正准备搞类似设计建议先拿最小系统把 SDRAM 验证透再往上叠 GUI 和算法这样后面排查起来会轻松很多。