STM32H7 FMC同步模式驱动NOR Flash:时序参数计算与实测校准全解析
1. 为什么需要同步模式?
STM32H7的FMC(Flexible Memory Controller)支持异步和同步两种NOR Flash访问模式。异步模式简单但速度受限,而同步模式利用FMC_CLK时钟输出,配合NOR Flash的突发读特性,可大幅提升吞吐率。例如,使用同步突发读,连续数据可在每个时钟周期输出,理论带宽可达异步模式的数倍。
但同步模式对时序要求严格,参数配置不当会导致数据采样错误。本文将手把手带你计算并校准这些参数。
2. 硬件连接与时钟树
同步模式需要连接以下信号:
- FMC_CLK:时钟输出,需配置为复用推挽
- FMC_NWAIT:Flash的等待信号,用于插入等待周期
- 地址/数据总线、片选、读写使能等
FMC时钟源来自HCLK3,通过RCC配置。例如HCLK3=200MHz,则FMC_CLK可设置为HCLK3/2=100MHz或分频。注意:FMC_CLK频率不能超过NOR Flash的最大同步时钟频率。
3. 关键时序参数解析
STM32H7的FMC同步模式寄存器包括:
- FMC_BTRx(SRAM/NOR Flash时序寄存器):包含CLKDIV、DATLAT、CLK等位域。
- FMC_BWTRx(写时序寄存器):同步写通常不用,但需配置。
主要参数:
- CLKDIV:FMC_CLK分频系数,实际时钟 = FMC_CLK输入 / (CLKDIV+1)。
- DATLAT:数据延迟,即从时钟边沿到数据有效的等待周期数(0~15)。
- CLK:时钟边沿选择,0=上升沿采样,1=下降沿采样。
- BUSTURN:总线周转时间,用于读写切换。
- WAITEN:等待使能,配合NWAIT信号。
4. 时序参数计算步骤
以某NOR Flash(如S29GL系列)为例,数据手册给出:
- tCLK:最小时钟周期(如10ns,即100MHz)
- tDATAV:数据有效时间(如5ns)
- tSU:数据建立时间(如3ns)
- tH:数据保持时间(如2ns)
步骤1:确定FMC_CLK频率 假设HCLK3=200MHz,选择CLKDIV=1,则FMC_CLK=100MHz,周期10ns,满足tCLK。
步骤2:计算DATLAT
DATLAT表示从时钟有效边沿到FMC采样数据的延迟周期数。需满足:
DATLAT * T_clk >= tDATAV + tSU
代入:tDATAV=5ns,tSU=3ns,T_clk=10ns,则DATLAT >= 0.8,取1。
步骤3:检查保持时间 采样点后数据需保持tH=2ns,由于采样在时钟边沿,下一个边沿前数据应稳定。通常DATLAT=1即可满足。
步骤4:配置等待信号 若Flash需要插入等待,使能WAITEN,并设置WAITPOL(极性)和WAITCFG(等待时序)。
5. 完整代码示例
#include "stm32h7xx_hal.h"
FMC_NORSRAM_HandleTypeDef hnor;
void MX_FMC_Init(void)
{
FMC_NORSRAM_TimingTypeDef Timing = {0};
hnor.Instance = FMC_NORSRAM_DEVICE;
hnor.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
// 配置NOR Flash控制参数
hnor.Init.NSBank = FMC_NORSRAM_BANK1;
hnor.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
hnor.Init.MemoryType = FMC_MEMORY_TYPE_NOR;
hnor.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
hnor.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_ENABLE; // 同步突发
hnor.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
hnor.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
hnor.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
hnor.Init.WaitSignal = FMC_WAIT_SIGNAL_ENABLE;
hnor.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
hnor.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
hnor.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
hnor.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
// 同步读时序
Timing.ClockDivision = 1; // CLKDIV=1 -> 100MHz
Timing.DataLatency = 1; // DATLAT=1
Timing.BusTurnAroundDuration = 0; // BUSTURN=0
Timing.CLKDivision = 1; // 与ClockDivision相同?注意:H7中CLKDivision用于同步模式
Timing.DataSetupTime = 0; // 同步模式忽略
Timing.AddressSetupTime = 0;
Timing.AddressHoldTime = 0;
Timing.AccessMode = FMC_ACCESS_MODE_B; // 模式B
HAL_FMC_NORSRAM_Init(&hnor);
HAL_FMC_NORSRAM_Timing_Init(&hnor, &Timing, FMC_NORSRAM_BANK1);
// 使能FMC时钟
__HAL_RCC_FMC_CLK_ENABLE();
}
注意:STM32H7的HAL库中,同步模式参数在FMC_NORSRAM_TimingTypeDef中,ClockDivision对应CLKDIV,DataLatency对应DATLAT。
6. 实测校准方法
理论计算后必须实测校准,因为PCB走线、Flash特性差异会影响时序。
步骤1:编写测试代码 向Flash写入已知模式(如0xAA55),然后连续读取,比较数据。
步骤2:示波器测量
- 探头连接FMC_CLK和FMC_D0。
- 触发CLK上升沿,观察数据有效窗口。
- 调整DATLAT,直到数据在采样点稳定。
步骤3:动态调整 若读取错误,逐步增加DATLAT(0→1→2...),直到稳定。同时检查CLKDIV是否过高,可降低频率测试。
步骤4:压力测试 全地址范围读写,并提高环境温度,确保余量。
7. 注意事项
- 时钟极性:确保FMC_CLK与Flash要求一致,必要时反相。
- NWAIT处理:若Flash未就绪,正确配置等待极性,否则会死锁。
- 电源噪声:高速同步模式下,电源去耦至关重要,建议在Flash电源引脚加0.1uF和10uF电容。
- PCB走线:时钟线等长,减少 skew。
- HAL库版本:不同版本HAL对同步模式支持有差异,建议使用最新版。
- Cache一致性:若使用DMA或CPU缓存,注意维护一致性。
通过以上计算与校准,你的STM32H7可以稳定高效地驱动NOR Flash,发挥同步模式的最大性能。