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,发挥同步模式的最大性能。