> 技术文档 > FSMC协议详解

FSMC协议详解

FSMC(Flexible Static Memory Controller,灵活静态存储控制器)是STM32微控制器中用于连接外部存储设备(如SRAM、NOR Flash、LCD等)的重要外设。它通过将外部存储设备映射到处理器的内存地址空间,简化了访问外部存储器的操作。以下是FSMC的详细解析:


​一、FSMC核心功能​

  1. ​支持多种存储器类型​​:

    • ​SRAM/PSRAM​​:静态随机存储器。
    • ​NOR Flash/ROM​​:支持异步读写的Flash或ROM。
    • ​LCD接口​​:通过模拟8080接口驱动TFT-LCD或OLED屏。
    • ​NAND Flash​​(部分型号支持):需要额外配置ECC。
  2. ​多存储区域划分​​:

    • FSMC将地址空间分为多个Bank(如Bank1~Bank4),每个Bank可独立配置为不同存储类型。
    • 例如:
      • ​Bank1​​:通常用于NOR/SRAM,分为4个子区域(NE1~NE4)。
      • ​Bank2/3/4​​:部分型号支持NAND或PC卡。
  3. ​灵活时序配置​​:

    • 可设置地址建立时间、数据保持时间、总线恢复时间等参数,适配不同速度的存储设备。

​二、FSMC工作原理​

  1. ​地址映射​​:

    • 外部存储器的物理地址被映射到STM32的​​固定内存地址​​(如Bank1起始地址为0x60000000)。
    • 通过指针直接访问这些地址,即可操作外部设备。
  2. ​主要控制信号​​:

    • ​片选信号(NE)​​:选择对应的存储区域(如NE1对应Bank1的子区域1)。
    • ​写使能(NWE)​​:低电平时写入数据。
    • ​读使能(NOE)​​:低电平时读取数据。
    • ​地址线(A0~A25)​​:用于寻址。
    • ​数据线(D0~D15/D31)​​:传输数据(宽度可配置为8/16/32位)。
  3. ​数据宽度与地址对齐​​:

    • 若外部设备为16位(如多数SRAM和LCD),需将STM32的地址线右移1位(Address << 1),因为每个16位数据占两个字节地址。

    三、FSMC时序讲解

    • FSMC读操作时序图

    • FSMC写操作时序图

     


    四、FSMC在MCU和FPGA系统中的应用

            FSMC通讯在MCU+FPGA系统中,主要负责芯片间的通讯工作,主要流程就是MCU接收到上位机数据,然后解析数据包之后,对FPGA的某个子模块的某个寄存器进行配置或者读取操作,如果是写操作不需要返回数据,如果是读操作,FPGA会通过FSMC返回数据给MCU,MCU接收到返回数据后将相关信息打包发给上位机;

     


    五、Verilog代码示例

    //-----------------------------------------------------------------------------------//-----------------------------------------------------------------------------------// stm32程序2字节对齐(字对齐),fsmc接口每次读写2两字节;//-----------------------------------------------------------------------------------//-----------------------------------------------------------------------------------module fsmc_test ( input wire clk ,//系统时钟 input wire rst ,//复位信号,高电平有效 //fsmc input wire fsmc_ren  ,//FSMC_NOE input wire fsmc_wen  ,//FSMC_NWE input wire [ 15: 0] fsmc_add  ,//double bytes address inout wire [ 15: 0] fsmc_data  ,//FSMC_D input wire fsmc_csn  ,//FSMC_NEx output reg fsmc_waitn  ,//FSMC_NWAIT output reg [ 15: 0] fsmc_fpga_add  ,//操作的寄存器地址 output reg [ 15: 0] fsmc_fpga_wen  ,//对应外部16路模块写使能 output reg [ 15: 0] fsmc_fpga_ren  ,//对应外部16路模块读使能 input wire [ 15: 0] fsmc_fpga_rdata ,//读使能对应模块返回数据 output reg [ 15: 0] fsmc_fpga_wdata //写对应地址数据); reg  csn_d1  ; reg  csn_d2  ; reg  csn_d3  ; reg  csn_d4  ; reg  csn_d5  ; reg  csn_d6  ; reg  csn_d7  ; reg  csn_d8  ; reg  csn_d9  ; reg  csn_d10  ; reg  csn_d11  ; reg [ 15: 0] fsmc_rdata  ; reg  fsmc_ren_d1 ; reg  fsmc_ren_d2 ; //对CS信号进行同时钟域处理,同时进行延时打拍,为后面操作提供参考时间标准 always @(posedge clk or posedge rst) begin if(rst) begin csn_d1 <= 1\'b1; csn_d2 <= 1\'b1; csn_d3 <= 1\'b1; csn_d4 <= 1\'b1; csn_d5 <= 1\'b1; csn_d6 <= 1\'b1; csn_d7 <= 1\'b1; csn_d8 <= 1\'b1; csn_d9 <= 1\'b1; csn_d10 <= 1\'b1; csn_d11 <= 1\'b1; end else begin csn_d1 <= fsmc_csn; csn_d2 <= csn_d1; csn_d3 <= csn_d2; csn_d4 <= csn_d3; csn_d5 <= csn_d4; csn_d6 <= csn_d5; csn_d7 <= csn_d6; csn_d8 <= csn_d7; csn_d9 <= csn_d8; csn_d10 <= csn_d9; csn_d11 <= csn_d10; end end //地址同时钟域处理 always @(posedge clk or posedge rst) begin if(rst) fsmc_fpga_add <=\'b0; else if(~csn_d2 && csn_d3) fsmc_fpga_add <= fsmc_add; else fsmc_fpga_add <= fsmc_fpga_add; end //读使能同时钟域处理 always @(posedge clk or posedge rst) begin if(rst) begin fsmc_ren_d1 <= 1\'b0; fsmc_ren_d2 <= 1\'b0; end else begin fsmc_ren_d1 <= fsmc_ren; fsmc_ren_d2 <= fsmc_ren_d1; end end //wait信号处理 always @(posedge clk or posedge rst) begin if(rst) fsmc_waitn <= 1\'b1; else if(fsmc_waitn == 1\'b1) if(~csn_d1 && csn_d2)  fsmc_waitn <= 1\'b0; else  fsmc_waitn <= 1\'b1; else if(~csn_d9 && csn_d10)  fsmc_waitn <= 1\'b1; else  fsmc_waitn <= 1\'b0; end //根据基地址,将读写使能分配到对应的外部子模块 genvar i; generate for (i = 0; i < 16; i = i + 1) begin : gen_fsmc_ctr_block always @(posedge clk or posedge rst) begin if(rst)  fsmc_fpga_wen[i] <= 1\'b0; else  if(~csn_d4 && csn_d5 && fsmc_ren_d2 && (fsmc_fpga_add[14:11] == i)) fsmc_fpga_wen[i] <= 1\'b1;  else fsmc_fpga_wen[i] <= 1\'b0; end always @(posedge clk or posedge rst) begin if(rst)  fsmc_fpga_ren[i] <= 1\'b0; else  if(~csn_d4 && csn_d5 && ~fsmc_ren_d2 && (fsmc_fpga_add[14:11] == i)) fsmc_fpga_ren[i] <= 1\'b1;  else fsmc_fpga_ren[i] <= 1\'b0; end end endgenerate //数据同时钟域处理 always @(posedge clk or posedge rst) begin if(rst) fsmc_fpga_wdata <= \'b0; else fsmc_fpga_wdata <= fsmc_data; end //读取操作,返回读取值 always @(posedge clk or posedge rst) begin if(rst) fsmc_rdata <= 16\'d0; else if(~csn_d7 && csn_d8) fsmc_rdata <= fsmc_fpga_rdata; else fsmc_rdata <= fsmc_rdata; end assign fsmc_data = (fsmc_ren_d1) ? {16{1\'bz}} : fsmc_rdata;endmodule