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_NWE | WR | 写入使能 | FSMC_NOE | RD | 读出使能 | 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模式A的写时序如下:
FSMC_NORSRAMTimingInitTypeDef 时序结构体
FSMC_NORSRAMInitTypeDef初始化结构体
二、STM32代码编写
STM32中初始化FSMC外设代码如下:
void FSMC_SRAM_Init(void)
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
SRAM_GPIO_Config();
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);
readWriteTiming.FSMC_AddressSetupTime = 0x00;
readWriteTiming.FSMC_AddressHoldTime = 0x00;
readWriteTiming.FSMC_DataSetupTime = 0x02;
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
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 = &readWriteTiming;
FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming;
FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);
}
配置好FSMC后,,可直接采用绝对地址访问FPGA内部的寄存器数值。本实验才采用的是FSMC_NE[1],对应的内核地址为((uint32_t)(0x60000000)),宏定义如下:
#define Bank1_SRAM1_ADDR ((uint32_t)(0x60000000))
*( uint16_t*) (Bank1_SRAM1_ADDR ) = (uint16_t)0xAA;
测试程序
uint8_t SRAM_Test(void)
{
uint32_t counter=0;
uint16_t uhWritedata_16b = 0, uhReaddata_16b = 0;
SRAM_INFO("正在检测SRAM,以16位的方式读写sram...");
for (counter = 0x00; counter < FPGA_3_16_SIZE/2; counter++)
{
*(__IO uint16_t*) (Bank1_SRAM1_ADDR + 2*counter) = (uint16_t)0x00;
}
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("counter = %d",counter);
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;
}
主函数
int main(void)
{
LED_GPIO_Config();
USART_Config();
FSMC_SRAM_Init();
SysTick_Init();
printf ( "\r\n野火外部 SRAM 测试\r\n" );
LED_RED;
if(SRAM_Test()==1)
{
LED_GREEN;
}
else
{
LED_RED;
}
{
uint32_t temp;
printf("\r\n指针方式访问SRAM\r\n");
*( 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);
}
{
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位的寄存器,用于存放数据。 代码如下:
module STM32_FPGA(
input sys_clk,
input sys_rst_n,
input [2:0] WR,
inout [15:0] data,
input FPGA_CS0,
input RD,
input WR,
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];
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;
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
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;
assign led1 = (ARM_FPGA_REG0 == 16'haa)? 1'b1:1'b0;//判断接收到的数据是否正确(STM32发送的数据是16'haa)
endmodule
四、实验结果
STM32依次给FPGA发送数据0xaa并接收FPGA发送过来的数据,通过串口打印出。FPGA接收的数据为0xaa,则点亮led1灯。 实验结果如图所示:
|