STM32与FPGA通讯——FSMC

STM32与FPGA通讯——FSMC

STM32与FPGA的FSMC通讯,STM32作为主机,FPGA作为从机。本文在STM32采用FSMC拓展SRAM的程序基础上编写FPGA代码及STM32访问FPGA的代码。将FPGA视为SRAM进行STM32与FPGA的通讯。

一、FMSC简介

FSMC译为灵活的静态存储控制器,STM32F1系列芯片使用FSMC外设来管理扩展的存储器。

FSMC框图剖析

在这里插入图片描述
1.通讯引脚:本实验采用的引脚为:

FSMC引脚名称对应的FPGA引脚名说明
FSMC_A[2:0]addr[2:0]行地址信号
FSMC_D[15:0]data[15:0]数据线
FSMC_NWEWR写入使能
FSMC_NOERD读出使能
FSMC_NE[1:4]CS片选信号

本实验采用FSMC_NE[1]。
2.存储器控制器:NOR/PSRAM/SRAM 设备使用相同的控制器,NAND/PC 卡设备使用相同的控制器。
控制 SRAM 的有以下3种寄存器:
FSMC_BCR 控制寄存器可配置要控制的存储器类型、数据线宽度以及信号有效极性能参数。
FMC_BTR 时序寄存器用于配置 SRAM 访问时的各种时间延迟,如数据保持间、地址保持时间等。
FMC_BWTR 写时序寄存器它专门用于控制写时序的时间参数。
每种寄存器都有 4 个,分别对应于 4 个不同的存储区域.
3、时钟控制逻辑
FSMC 外设挂载在 AHB总线上,时钟信号来自于 HCLK(默认 72MHz),控制器的同步时钟输出就是由它分频得到。本实验不采用同步时钟信号,无需时钟分频。

FSMC的地址映射

FSMC连接好外部存储器并初始化后,就可以直接通过访问地址来读写数据。因为外接的存储单元是映射到STM32的内部寻址空间的。FSMC的地址映射如下所示:
在这里插入图片描述
FSMC将整个 External RAM 存储区域分成了 4 个 Bank 区域,并分配了地址范围及适用的存储器类型,每个Bank的内部又分为4个小块。本实验将FPGA视为SRAM,则只能使用Bank1中的地址,Bank1内部四块地址分配如下图:
在这里插入图片描述

FSMC控制SRAM的时序

FSMC外设支持输出多种不同的时序以便控制不同的存储器,共有ABCD四种模式,下图为FSMC模式A的读时序:
FSMC模式的读时序
FSMC模式A的写时序如下:

在这里插入图片描述

FSMC_NORSRAMTimingInitTypeDef 时序结构体

在这里插入图片描述

FSMC_NORSRAMInitTypeDef初始化结构体

在这里插入图片描述

二、STM32代码编写

STM32中初始化FSMC外设代码如下:

/**  
  * @brief  初始化FSMC外设
  * @param  None. 
  * @retval None.
  */
void FSMC_SRAM_Init(void)
{	
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;

	/*初始化SRAM相关的GPIO*/
	SRAM_GPIO_Config();
		
	/*使能FSMC外设时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);

	//地址建立时间(ADDSET)为1个HCLK 1/72M=14ns
	readWriteTiming.FSMC_AddressSetupTime = 0x00;	

	//地址保持时间(ADDHLD)模式A未用到
	readWriteTiming.FSMC_AddressHoldTime = 0x00;	 

	//数据保持时间(DATAST)+ 1个HCLK = 3/72M=42ns(对EM的SRAM芯片)	
	readWriteTiming.FSMC_DataSetupTime = 0x02;		  
	
	//设置总线转换周期,仅用于复用模式的NOR操作
	readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
	
	//设置时钟分频,仅用于同步类型的存储器
	readWriteTiming.FSMC_CLKDivision = 0x00;	

	//数据保持时间,仅用于同步型的NOR
	readWriteTiming.FSMC_DataLatency = 0x00;		
	
	//选择匹配SRAM的模式
	readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 
    

	// 选择FSMC映射的存储区域: Bank1 sram2
	FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
	
	//设置地址总线与数据总线是否复用,仅用于NOR
	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; 
	
	//设置要控制的存储器类型:SRAM类型
	FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;   
	
	//存储器数据宽度:16位
	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 = &readWriteTiming;
	
	//读写同样时序,使用扩展模式时这个配置才有效
	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming; 

	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC配置

	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);  // 使能BANK										  
											
}

配置好FSMC后,,可直接采用绝对地址访问FPGA内部的寄存器数值。本实验才采用的是FSMC_NE[1],对应的内核地址为((uint32_t)(0x60000000)),宏定义如下:

#define Bank1_SRAM1_ADDR    ((uint32_t)(0x60000000))	
*( uint16_t*) (Bank1_SRAM1_ADDR ) = (uint16_t)0xAA;//将0XAA写入FPGA的ARM_FPGA_REG0中

测试程序

/**
  * @brief  测试SRAM是否正常 
  * @param  None
  * @retval 正常返回1,异常返回0
  */
uint8_t SRAM_Test(void)
{
  /*写入数据计数器*/
  uint32_t counter=0;
  
  /* 16位的数据 */
  uint16_t uhWritedata_16b = 0, uhReaddata_16b = 0; 
  
  SRAM_INFO("正在检测SRAM,以16位的方式读写sram...");

  /*按16位格式读写数据,并检测*/
  
  /* 把SRAM数据全部重置为0 */
  for (counter = 0x00; counter < FPGA_3_16_SIZE/2; counter++)
  {
    *(__IO uint16_t*) (Bank1_SRAM1_ADDR + 2*counter) = (uint16_t)0x00;
  }
  
  /* 向整个SRAM写入数据  16位 */
  for (counter = 0; counter < FPGA_3_16_SIZE/2; counter++)
  {
    *(__IO uint16_t*) (Bank1_SRAM1_ADDR + 2*counter) = (uint16_t)(uhWritedata_16b + counter);
		//SRAM_INFO("%d",*(__IO uint16_t*) (Bank1_SRAM1_ADDR + 2*counter));
  }
	
	SRAM_INFO("counter = %d",counter);
    /* 读取 SRAM 数据并检测*/
  for(counter = 0; counter<FPGA_3_16_SIZE/2;counter++ )
  {
    uhReaddata_16b = *(__IO uint16_t*)(Bank1_SRAM1_ADDR + 2*counter);  //从该地址读出数据
		
    
    if(uhReaddata_16b != (uint16_t)(uhWritedata_16b + counter))      //检测数据,若不相等,跳出函数,返回检测失败结果。
    {
      SRAM_ERROR("16位数据读写错误!");
      return 0;
    }
  }
  SRAM_INFO("SRAM读写测试正常!"); 
  /*检测正常,return 1 */
  return 1;

}

主函数

int main(void)
 {	     
	 
	LED_GPIO_Config();
	//串口初始化  
	USART_Config();		 	                       
  //初始化外部SRAM  
  FSMC_SRAM_Init();	
	SysTick_Init(); 
	 
	printf ( "\r\n野火外部 SRAM 测试\r\n" );

	  	
  /*蓝灯亮,表示正在读写SRAM测试*/
 LED_RED;
	
  /*对SRAM进行读写测试,检测SRAM是否正常*/
 if(SRAM_Test()==1)
  {
		//测试正常 绿灯亮
    LED_GREEN;			  
  }
	else
	{
		//测试失败 红灯亮
		LED_RED;
	}
	
	/*指针方式访问SRAM*/
	{	
	 uint32_t temp;
	 printf("\r\n指针方式访问SRAM\r\n");
	 /*写/读 16位数据*/
	 *( uint16_t*) (Bank1_SRAM1_ADDR ) = (uint16_t)0xAA;
	 printf("指针访问SRAM,写入数据0xAA \r\n");
	 
	 temp =  *( uint16_t*) (Bank1_SRAM1_ADDR );
	 printf("读取数据:0x%X\r\n",temp);
	}
	
	/*绝对定位方式访问SRAM,这种方式必须定义成全局变量*/
	{
		testValue = 0xaa;
		printf("\r\n绝对定位访问SRAM,写入数据0xaa,读出数据0x%X,变量地址为%X\r\n",testValue,(uint32_t )&testValue);	 
	}
}

三、FPGA代码编写

本实验将FPGA视为3根地址线、16根数据线的SRAM,即FPGA内部具有8个存储单元,每个从存储单元可存放16位数据。可视为存储大小为16B的SRAM。
所以FPGA内部要设置8个16位的寄存器,用于存放数据。
代码如下:

/* STM32与FPGA之间的FSMC通讯 */
module STM32_FPGA(
					input  	sys_clk,
					input	sys_rst_n,
					  
					input [2:0] WR,
					inout [15:0] data,
					input FPGA_CS0,//FPGA片选段
					input RD,//ARM从FPGA读数据
					input WR,//ARM写数据到FPGA
					
					output led,
					output led1
					 );


reg [24:0] cnt ;

always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
		cnt <= 25'd0;
else 
		cnt <= cnt + 1'b1;			 

assign led = cnt[24];
	
//FPGA内部8个数据存放的寄存器

reg [15:0] ARM_FPGA_REG0;					 
reg [15:0] ARM_FPGA_REG1;
reg [15:0] ARM_FPGA_REG2;					 
reg [15:0] ARM_FPGA_REG3;					 
reg [15:0] ARM_FPGA_REG4;
reg [15:0] ARM_FPGA_REG5;					 
reg [15:0] ARM_FPGA_REG6;					 
reg [15:0] ARM_FPGA_REG7;					 
					 

wire rd_en;
wire wr_en;

assign	wr_en = ~FPGA_CS0 && ~WR;//写使能
assign	rd_en = ~FPGA_CS0 && ~RD;//读使能

reg [15:0] data_reg;					 
//根据地址线,ARM从FPGA中读取对应寄存器数据放在寄存器data_reg中
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
	data_reg <= 16'd0;                                                
else   if(rd_en)                                 
     begin                                           
        case(addr[2:0])                              
         3'd0   :  data_reg <= ARM_FPGA_REG0;        
         3'd1   :  data_reg <= ARM_FPGA_REG1;        
         3'd2   :  data_reg <= ARM_FPGA_REG2;        
         3'd3   :  data_reg <= ARM_FPGA_REG3;        
         3'd4   :  data_reg <= ARM_FPGA_REG4;        
         3'd5   :  data_reg <= ARM_FPGA_REG5;        
         3'd6   :  data_reg <= ARM_FPGA_REG6;        
         3'd7   :  data_reg <= ARM_FPGA_REG7;        
         default:  ;                                 
        endcase 		     		                         
     end                                                 		                                             
//根据地址线,ARM将都数据写入到对应的就寄存器中		 
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin	
	ARM_FPGA_REG0 <= 16'd0;
	ARM_FPGA_REG1 <= 16'd0;
	ARM_FPGA_REG2 <= 16'd0;
	ARM_FPGA_REG3 <= 16'd0;
	ARM_FPGA_REG4 <= 16'd0;
	ARM_FPGA_REG5 <= 16'd0;
	ARM_FPGA_REG6 <= 16'd0;
	ARM_FPGA_REG7 <= 16'd0;	
end
else if(wr_en)	                                                                
    case(addr[2:0])                       
     3'd0   :  ARM_FPGA_REG0 <= data;     
     3'd1   :  ARM_FPGA_REG1 <= data;     
     3'd2   :  ARM_FPGA_REG2 <= data;     
     3'd3   :  ARM_FPGA_REG3 <= data;      
     3'd4   :  ARM_FPGA_REG4 <= data;     
     3'd5   :  ARM_FPGA_REG5 <= data;     
     3'd6   :  ARM_FPGA_REG6 <= data;     
     3'd7   :  ARM_FPGA_REG7 <= data;     
     default:  ;                          
    endcase 		     		                                                        		                                        
assign data = 	rd_en ? data_reg : 16'hzzzz;//将数据传给ARM
assign led1 = (ARM_FPGA_REG0 == 16'haa)? 1'b1:1'b0;//判断接收到的数据是否正确(STM32发送的数据是16'haa)					 
endmodule

四、实验结果

STM32依次给FPGA发送数据0xaa并接收FPGA发送过来的数据,通过串口打印出。FPGA接收的数据为0xaa,则点亮led1灯。
实验结果如图所示:
在这里插入图片描述
在这里插入图片描述

  • 6
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值