一、为什么同步模式时序需要“反推”?

STM32H7的FMC(Flexible Memory Controller)支持同步模式访问PSRAM、NOR Flash等器件。与异步模式直接填写“等待周期数”不同,同步模式下FMC的时钟(FMC_CLK)由HCLK3分频得到,而NOR Flash手册给出的却是纳秒级的时序要求(如tACC、tOE、tOH等)。因此必须根据FMC_CLK周期,将ns转换为寄存器中的周期数(DATAST、ADDHLD、BUSTURN等),并向上取整留余量。

二、关键时序参数与寄存器映射

同步模式下,一次读/写操作被划分为多个阶段,每个阶段由FMC时序寄存器(FMC_BTRx/FMC_BWTRx)中的字段控制:

  • ADDSET:地址建立阶段,对应NOR的地址有效到片选/时钟有效时间。
  • ADDHLD:地址保持阶段(同步模式特有),用于满足地址保持时间。
  • DATAST:数据阶段,决定数据采样/输出窗口,对应tACC、tOE等。
  • BUSTURN:总线周转阶段,两次访问之间的空闲周期,对应tOH、tHZ等。
  • CLKDIV:FMC_CLK分频系数,决定时钟周期T_clk = (CLKDIV+1) * HCLK3周期。

三、从ns到周期数的计算步骤

假设HCLK3 = 200MHz(周期5ns),目标FMC_CLK = 50MHz(T_clk = 20ns),NOR Flash手册给出:

  • tACC = 70ns(地址到数据有效)
  • tOE = 25ns(输出使能到数据有效)
  • tOH = 5ns(数据保持)
  • tHZ = 10ns(片选无效到数据高阻)

步骤1:确定CLKDIV

CLKDIV = round(HCLK3周期 / T_clk) - 1 = round(5/20) - 1 = -1?不对,实际是分频系数:FMC_CLK = HCLK3 / (CLKDIV+1)。要得到50MHz,CLKDIV+1 = 200/50 = 4,所以CLKDIV = 3。

步骤2:计算DATAST

数据阶段需覆盖tACC和tOE的最大值。同步读时,数据在时钟上升沿采样,因此DATAST(以T_clk为单位)应满足:

DATAST ≥ ceil( max(tACC, tOE) / T_clk ) - 1

代入:max(70,25)=70ns,70/20=3.5,向上取整4,DATAST = 4 - 1 = 3。

注意:FMC中DATAST字段实际表示“数据阶段周期数-1”,因此写3表示4个时钟周期。

步骤3:计算ADDHLD

ADDHLD用于地址保持,通常取0~1个周期。若Flash要求地址保持时间tAH=5ns,则:

ADDHLD ≥ ceil(tAH / T_clk) = ceil(5/20)=1,字段值 = 1-1 = 0。

步骤4:计算BUSTURN

总线周转需满足tOH和tHZ。BUSTURN ≥ ceil( max(tOH, tHZ) / T_clk ) - 1 = ceil(10/20)-1 = 0。

若计算结果为负,则取0。

四、完整配置代码示例

以下代码基于STM32H7 HAL库,配置FMC Bank1 NOR Flash同步模式。

#include "stm32h7xx_hal.h"

FMC_NORSRAM_TimingTypeDef Timing = {0};
FMC_NORSRAM_InitTypeDef Init = {0};

void FMC_NOR_Init(void) {
    // 1. 使能FMC时钟和GPIO(略)
    __HAL_RCC_FMC_CLK_ENABLE();

    // 2. 配置时序(同步模式)
    Timing.AddressSetupTime = 2;      // ADDSET,地址建立
    Timing.AddressHoldTime = 0;       // ADDHLD,地址保持
    Timing.DataSetupTime = 3;         // DATAST,数据阶段
    Timing.BusTurnAroundDuration = 0; // BUSTURN,总线周转
    Timing.CLKDivision = 3;           // CLKDIV,分频系数
    Timing.DataLatency = 0;           // 同步模式数据延迟,通常0
    Timing.AccessMode = FMC_ACCESS_MODE_B; // 模式B

    // 3. 初始化NORSRAM控制器
    Init.NSBank = FMC_NORSRAM_BANK1;
    Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
    Init.MemoryType = FMC_MEMORY_TYPE_NOR;
    Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
    Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_ENABLE; // 同步突发
    Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
    Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
    Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
    Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
    Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
    Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
    Init.WriteBurst = FMC_WRITE_BURST_DISABLE;
    Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
    Init.WriteFifo = FMC_WRITE_FIFO_ENABLE;

    HAL_FMC_NORSRAM_Init(FMC_NORSRAM_DEVICE, &Init);
    HAL_FMC_NORSRAM_Timing_Init(FMC_NORSRAM_DEVICE, &Timing, FMC_NORSRAM_BANK1);

    // 4. 使能FMC控制器
    __HAL_FMC_NORSRAM_ENABLE(FMC_NORSRAM_DEVICE, FMC_NORSRAM_BANK1);
}

五、注意事项与验证

  • 向上取整:所有计算必须向上取整,宁可多给1个周期,不可少给,否则高温或低压下易出错。
  • 时钟极性:同步模式需正确配置FMC_CLK的极性(上升沿/下降沿采样),与Flash手册一致。
  • 数据延迟:部分Flash需要DataLatency(如2个周期),需根据手册设置。
  • 实测验证:用示波器测量FMC_CLK与数据线,确认数据窗口是否满足tACC和tOH。
  • 写时序:写操作使用FMC_BWTRx寄存器,计算方法相同,但需注意tWP、tWH等参数。
  • HCLK3频率:确保HCLK3频率与计算时一致,否则所有周期数失效。

通过以上“ns→周期数→寄存器值”的反推流程,可快速完成STM32H7 FMC同步模式NOR Flash的时序配置,兼顾性能与可靠性。