FPGA与STM32通过FSMC总线进行通信的实验探究

FPGA与STM32_FSMC总线通信实验

  • FSMC总线通信简介
  • FSMC协议分析
  • 内部存储器IP核的参数设置
  • 创建IP核
  • FPGA代码
  • STM32标准库的程序
  • FSMC总线通信简介

    FSMC是STM32系列采用的一种新型存储器扩展技术。在外部存储器扩展方面具有独特的优势,可根据系统的应用需要,方便进行不
    同类型大容量静态存储器的扩展。
    FSMC的特点:
    (1)支持不同位宽的异步读写操作。
    (2)不同的BANK在映射地址空间中是独立的,可用于扩展不同的存储器。
    (3)支持代码从FSMC扩展的外部存储器中直接运行。
    两种工作方式:地址独立模式和地址复用模式

    FSMC协议分析

    FSMC协议的几个主要信号:

    1. CSn(片选信号):低电平片选信号有效,高电平失能(默认状态为高:失能)
    2. NADV(地址有效信号):上升沿锁存地址的低位
    3. RDn(读信号):低电平读信号有效,上升沿锁存数据,高电平失能(默认状态为高:失能)
    4. WRn(写信号):低电平写信号有效,上升沿锁存数据,高电平失能(默认状态为高:失能)

    FSMC读/写操作时序:

    STM32F407 上自带 FSMC 控制器,通过 FSMC 总线的地址复用模式实现STM32 与 FPGA 之间的通信,FPGA 内部建立 RAM 块,FPGA 桥接 STM32 和 RAM 块,通过 FSMC 总线从 STM32 向 RAM 块中写入数据(数据为 0 到 511),然后读取 RAM出来的数据并进行验证。原理图如下图所示:

    内部存储器IP核的参数设置

    单端口RAM参数介绍

    创建IP核

    请添加图片描述
    ①框是设置输出数据端口的位宽,②框是设置存储器容量的大小,这两个选项大家可根据实际的设计进行设置。这里我们设置数据位宽 16bit,存储容量为 512words,即我们设置的 RAM 的最大存储量为 512 个 16bit 数据。
    ③框是存储单元类型的选择,这里我们保持 Auto(软件自动选择)即可。
    ④框中是选择使用的时钟模式,可选择单时钟或双时钟。选择单时钟时:用一个时钟信号控制存储块的所有寄存器。选择双时钟时:输入时钟控制地址寄存器,输出时钟控制数据输出寄存器。大家可根据设计需求进行选择,这里我们选择默认选项 Single clock(单时钟)。

    设置完之后点击 Next 进入下一界面:
    请添加图片描述
    ①框是选择是否输出“q”输出寄存器。这里把输出端口的寄存器去掉(如果不去掉的话,就会使输出延迟一拍。如果没有特别的需求的话我们是不需要延迟这一拍的,所以这里我们把它去掉)。
    ②框是询问我们是否选择为时钟信号创建响应的使能信号,这里我们不需要,不勾选。
    ③框是询问我们为端口A创建字节使能,这里我们不需要,不勾选。
    ④框是选择是否创建“aclr”异步复位信号以及是否创建“rden”读使能信号,大家可根据实际的设计需求进行勾选,这里我们把它们都勾选上。设置完之后点击 Next 进入下一界面:

    请添加图片描述
    如上图所示,是进行 Read During Write Operation 项配置,选择某个地址即将被写入数据时读该地址的数据输出类型:有 Don’t Care(不关心)、 New Data(写入的新数据)和Old Data(原有数据),我们保持默认的 New Data 即可,也就是说,某个地址将被写入新数据时,同时进行读操作会读出新的数据。点击 Next 进入下一页面:
    请添加图片描述
    ①框是选择是否为存储器配置初始化文件,与 ROM 不同的是,RAM 可以选择不配置初始化文件,这里我们选择不配置初始化文件。
    ②框是选择是否允许系统内存储器内容编辑器在于与系统时钟无关的情况下捕获和更新存储器的内容,这里我们不勾选。
    设置完后点击 Next 进入下一界面:

    请添加图片描述
    如上图所示,该界面没有什么要配置的参数,但显示了我们在仿真 ROM IP 核时所需要的 Altera 仿真库,这里提示了我们单独使用第三方仿真工具时需要添加名为“altera_mf”的库。这里保持默认,直接点击“Next”。

    请添加图片描述
    RAM 输出的文件,除了灰色必选文件,默认还勾选上了rom_256x8_bb.v,这里我们去掉 rom_256x8_bb.v 文件,加入my_ram_ints.v(例化模板文件)即可。最后点击“Finish”完成整个 IP 核的创建。接下来 Quartus II 软件会在我们创建的 IP 核文件目录下生成 RAM IP 文件。

    FPGA代码

    使用RAM IP核构成FSMC,可以从示意图中看出 我们恰好对得上,需要地址线和数据线、读写使能线以及,将IP核的分配到实在的引脚
    模块

    FSMC模块

     module FSMC_Ctrl(
    	ab,
    	db,
    	wrn,
    	rdn,
    	csn,
    	PLL_100M,
    	RST_n,
    	nadv
     	);
    	
    	input [8:0]ab;	//地址
    	inout [15:0]db;	//数据
    	input wrn;		//写使能
    	input rdn;		//读使能
    	input csn;		//使能
    	input PLL_100M;	//时钟
    	input RST_n; 	//复位
    	input nadv; 	//复用功能
    	
    	wire rd;
    	wire wr;
    	
    	assign rd = (csn | rdn);//使能和读使能共同有效时
    	assign wr = (csn | wrn);//使能和写使能共同有效时
    	
    	wire [15:0]DB_OUT;
    	
    	assign db = !rd ? DB_OUT : 16'hzzzz;//在读数据的时候,将端口全部设置成高阻态
    
    	reg wr_clk1,wr_clk2;
    	
    	always @(posedge PLL_100M or negedge RST_n)
    		begin
    			if(!RST_n)
    				begin
    					wr_clk1 <= 1'd1;
    					wr_clk2 <= 1'd1;
    				end
    			else
    				{wr_clk2,wr_clk1} <= {wr_clk1,wr};	//提取写时钟
    		end
    		
    	wire clk = (!wr_clk2 | !rd);
    						
    my_ram      u1(											//ram块例化
    					.address(ab),
    					.clock(clk),
    					.data(db),
    					.wren(!wr),
    					.rden(!rd),
    					.q(DB_OUT),
    					);
    	
    	
    endmodule
    

    复位模块

    //--------------------Timescale------------------------------//
    `timescale 1 ns / 1 ps
    
    //--------------------RST_Ctrl------------------------//
    module RST_Ctrl(
    				input FPGA_CLK,	//输入板载晶振FPGA_CLK,25M
    				output RST_n			//输出全局复位信号
    				);
    //--------------------RST_n----------------------------------//
    	reg [3:0] cnt_rst = 4'd0;
    	always @(posedge FPGA_CLK)
    		if (cnt_rst == 4'd10)
    			cnt_rst <= 4'd10;
    		else
    			cnt_rst <= cnt_rst + 1'd1;
    
    	assign RST_n = (cnt_rst == 4'd10);//复位信号,10个周期后RST_n为1
    
    //--------------------endmodule------------------------------//
    endmodule
    

    顶层文件

    //-------------------------Timescale----------------------------//
    `timescale 1 ns / 1 ps
    //--------------------FSMC_SIG---------------------//
    module FSMC_INDEP(
    	FPGA_CLK,	//输入板载晶振GPGA_CLK,25M
    	FPGA_LEDR,
    	FPGA_LEDG,
    	FPGA_LEDB,
    	WR,			//FSMC写信号
    	RD,			//FSMC读信号
    	CS0,			//FSMC片选
    	A,				//FSMC地址总线
    	DB,			//FSMC数据总线
    	NADV,			//FSMC的NADV
    	);
    
    	input FPGA_CLK,NADV;
    	input WR,RD,CS0;
    	inout [15:0]DB;
    	input [24:16]A;
    	output FPGA_LEDB,FPGA_LEDG,FPGA_LEDR;
    	
    	assign FPGA_LEDR = 1'd1;
    	assign FPGA_LEDG = 1'd0;
    	assign FPGA_LEDB = 1'd1;
    //-------------------------MY_PLL-------------------------------//
    	wire PLL_100M;
    	MY_PLL 				U1(
    								.inclk0(FPGA_CLK),
    								.c0(PLL_100M)
    								);//例化MY_PLL模块,输出50M时钟
    //------------------------RST_Ctrl-----------------------------//
    	wire RST_n;
    	RST_Ctrl				U2(
    								.FPGA_CLK(FPGA_CLK),
    								.RST_n(RST_n)
    								);	//例化RST_Ctrl模块,输出全局复位信号RST_n
    
    //-------------------------FSMC_Ctrl ------------------------------//
    	FSMC_Ctrl			U3(									//FSMC总线测试模块
    								.ab(A[24:16]),
    								.db(DB),
    								.wrn(WR),
    								.rdn(RD),
    								.csn(CS0),
    								.PLL_100M(PLL_100M),
    								.RST_n(RST_n),
    								.nadv(NADV)
    								);
    								
    //------------------------enmodule ---------------------------//
    
    endmodule
    

    STM32标准库的程序

    fsmc.h

    //------------------------define---------------------------//
    #ifndef __fsmc_h__
    #define __fsmc_h__
    
    //---------------------Include files-----------------------//
    
    //----------------------- Define --------------------------//
    #define fpga_write(offset,data)	*((volatile unsigned short int *)(0x60000000 + (offset << 17))) = data
    
    #define fpga_read(offset)	*((volatile unsigned short int *)(0x60000000 + (offset << 17)))
    
    //----------------- Typedef -----------------------------//
    typedef struct{
    	int (* initialize)(void);
    }FSMC_T;
    
    //---------------- Extern -------------------------------//
    
    extern FSMC_T fsmc;
    
    #endif //__fsmc_h__
    

    fsmc.c

    /*
     * FILE								: fsmc.c
     * DESCRIPTION				: This file is iCore3 fsmc driver.
     * Author							: XiaomaGee@Gmail.com
     * Copyright					:
     *
     * History
     * --------------------
     * Rev								: 0.00
     * Date								: 01/03/2016
     *
     * create.
     * --------------------
     */
    //---------------- Include files ------------------------//
    #include "..\include\fsmc.h"
    #include "..\fwlib\inc\stm32f4xx_rcc.h"
    #include "..\fwlib\inc\stm32f4xx_gpio.h"
    #include "..\fwlib\inc\stm32f4xx_fsmc.h"
    
    //---------------- Function Prototype -------------------//
    static int initialize(void);
    
    //---------------- Variable -----------------------------//
    FSMC_T fsmc = {
    	.initialize = initialize
    };
    
    //-----------------Function------------------------------//
    /*
     * Name										: initialize
     * Description						: ---
     * Author									: XiaomaGee.
     *
     * History
     * ----------------------
     * Rev										: 0.00
     * Date										: 01/03/2016
     *
     * create.
     * ----------------------
     */
    static int initialize(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
      FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
      FSMC_NORSRAMTimingInitTypeDef  p;
    	
    	//时钟使能
    	RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOG, ENABLE);
    	
    	//IO初始化
    	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_FSMC);
    	
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource3, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource7, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 |
    																GPIO_Pin_6 | GPIO_Pin_7 |	GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |
    																GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    	GPIO_Init(GPIOD, &GPIO_InitStructure);
    
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource2 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource3 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource4 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource5 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource6 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FSMC);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FSMC);
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |
    																GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    	GPIO_Init(GPIOE, &GPIO_InitStructure);
    	
    	GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_FSMC);
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    	GPIO_Init(GPIOG, &GPIO_InitStructure);
    	
    	//FSMC初始化
      p.FSMC_AddressSetupTime = 1;
      p.FSMC_AddressHoldTime = 0;
      p.FSMC_DataSetupTime = 4;
      p.FSMC_BusTurnAroundDuration = 0;
      p.FSMC_CLKDivision = 0;
      p.FSMC_DataLatency = 0;
      p.FSMC_AccessMode = FSMC_AccessMode_A;
    
      FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
      FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Enable;
      FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;
      FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
      FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
      FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;  
      FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
      FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
      FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
      FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
      FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
      FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
      FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
      FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
      FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
    
      FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 
    
      //FSMC Bank1_SRAM1 Bank使能
    	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE); 
    
    	return 0;
    }
    
    

    led.c

    //--------------------------- Include ---------------------------//
    #include "..\include\led.h"
    #include "..\fwlib\inc\stm32f4xx_gpio.h"
    #include "..\fwlib\inc\stm32f4xx_rcc.h"
    
    //--------------------- Function Prototype ----------------------//
    static int initialize(void);
    
    //--------------------------- Variable --------------------------//
    LED_T led = {
    	.initialize = initialize
    };
    //--------------------------- Function --------------------------//
    /*
     * Name                : initialize
     * Description         : ---
     * Author              : ysloveivy.
     *
     * History
     * --------------------
     * Rev                 : 0.00
     * Date                : 01/03/2016
     * 
     * create.
     * --------------------
     */
    static int initialize(void)
    {
    	GPIO_InitTypeDef   GPIO_uInitStructure;
    
    	//LED IO初始化
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI,ENABLE);
    	GPIO_uInitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;    //设置连接三色LED灯的IO端口
    	GPIO_uInitStructure.GPIO_Mode = GPIO_Mode_OUT;                          //设置端口为输出模式
    	GPIO_uInitStructure.GPIO_OType = GPIO_OType_PP;                         //推挽输出
    	GPIO_uInitStructure.GPIO_PuPd = GPIO_PuPd_UP;                           //上拉
    	GPIO_uInitStructure.GPIO_Speed = GPIO_Speed_100MHz;                     //设置速度为第三级
    
    	GPIO_Init(GPIOI,&GPIO_uInitStructure);
    
    	//PI5、PI6、PI7接三色LED灯,PI5、PI6、PI7置高电位,灯熄灭
    	GPIO_SetBits(GPIOI,GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
    
    	return 0;
    }
    

    led.h

    #ifndef __led_h__
    #define __led_h__
    
    //--------------------------- Define ---------------------------//
    //红灯<----->PI5
    #define LED_RED_OFF GPIO_SetBits(GPIOI,GPIO_Pin_5)
    #define LED_RED_ON  GPIO_ResetBits(GPIOI,GPIO_Pin_5)
    //绿灯<----->PI6
    #define LED_GREEN_OFF GPIO_SetBits(GPIOI,GPIO_Pin_6)
    #define LED_GREEN_ON  GPIO_ResetBits(GPIOI,GPIO_Pin_6)
    //蓝灯<----->PI7
    #define LED_BLUE_OFF GPIO_SetBits(GPIOI,GPIO_Pin_7)
    #define LED_BLUE_ON  GPIO_ResetBits(GPIOI,GPIO_Pin_7)
    
    //----------------------- Include files ------------------------//
    
    //-------------------------- Typedef----------------------------//
    typedef struct {
    	int (* initialize)(void);
    }LED_T;
    
    //--------------------------- Extern ---------------------------//
    extern LED_T led;
    
    #endif //__led_h__
    

    主函数

    int main(void)
    {
    	int i;
    	unsigned short int fsmc_read_data;
    	/*初始化*/
    	led.initialize();
    	fsmc.initialize();
    	LED_GREEN_ON;
    	
    	/*绿色led亮,表示测试正常
    	红色led亮,表示测试失败,测试结束*/
    	while(1){
    		for(i = 0;i < 512;i++){
    		 fpga_write(i,i);                  //向FPGA写入数据
    		}	
    		for(i = 0;i < 512;i++){
    			fsmc_read_data = fpga_read(i);   //从FPGA读数据
    			if(fsmc_read_data != i){
    				LED_GREEN_OFF;
    				LED_RED_ON;
    				while(1);
    			}			
    		}
    	}
    }
    

    复用模式说明

    地址和数据线复用时,NADV是访问地址和数据的区别信号,你只需要配合时序参数,芯片会自动识别的。

    物联沃分享整理
    物联沃-IOTWORD物联网 » FPGA与STM32通过FSMC总线进行通信的实验探究

    发表评论