STM32-FSMC-SRAM扩展
STM32-FSMC-SRAM扩展
1、SRAM芯片
IS62WV51216BLL-55TLI相关芯片资料下载
1.1 信号线
SRAM的控制比较简单,只要控制信号线使能了访问,从地址线输入要访问的地址,即可从I/O数据线写入或读出数据。
1.2 SRAM芯片的存储矩阵
SRAM内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格,这是SRAM芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储矩阵。
1.3 地址译码器、列I/O及I/O数据电路
地址译码器把N根地址线转换成2N根信号线,每根信号线对应一行或一列存储单元,通过地址线找到具体的存储单元,实现寻址。
本实例中的SRAM比较小,没有列地址线,它的数据宽度为16位,即一个行地址对应2字节空间,框图中左侧的A0-A18是行址信号,19根地址线一共可以表示219=29x1024=512K行存储单元,所以它一共能访问512Kx16bits = 8 Mb = 8MB大小的空间。访问时,使用UB#或LB#线控制数据宽度,
1.4 控制电路
控制电路主要包含了片选、读写使能以及上面提到的宽度控制信号UB#和LB#。利用CS2或CS1#片选信号,可以把多个SRAM芯片组成一个大容量的内存条。OE#和WE#可以控制读写使能,防止误操作。
1.5 读写流程
1.5.1 读流程
1.5.2 写流程
2、STM32的FSMC
2.1 FSMC功能描述
FSMC(Flexible Static Memory Controller,灵活的静态存储控制器)具有下列主要功能:
● 具有静态存储器接口的器件包括:
─ 静态随机存储器(SRAM)
─ 只读存储器(ROM)
─ NOR闪存
─ PSRAM(4个存储器块)
● 两个NAND闪存块,支持硬件ECC并可检测多达8K字节数据
● 16位的PC卡兼容设备
● 支持对同步器件的成组(Burst)访问模式,如NOR闪存和PSRAM
● 8或16位数据总线
● 每一个存储器块都有独立的片选控制
● 每一个存储器块都可以独立配置
● 时序可编程以支持各种不同的器件:
─ 等待周期可编程(多达15个周期)
─ 总线恢复周期可编程(多达15个周期)
─ 输出使能和写使能延迟可编程(多达15周期)
─ 独立的读写时序和协议,可支持宽范围的存储器和时序
● PSRAM和SRAM器件使用的写使能和字节选择输出
● 将32位的AHB访问请求,转换到连续的16位或8位的,对外部16位或8位器件的访问
● 具有16个字,每个字32位宽的写入FIFO,允许在写入较慢存储器时释放AHB进行其它操作。在开始一次新的FSMC操作前, FIFO要先被清空。
2.2 框图
其中比较特殊的FSMC_NE是用于控制SRAM芯片的控制信号线,STM32具有FSMC_NE1/2/3/4号引脚,不同的引脚对应STM32内部不同的地址区域。
例如,当STM32访问0x68000000-0x6BFFFFFF地址空间时,FSMC_NE3引脚会自动设置为低电平,由于它连接到SRAM的CE#引脚,所以SRAM的片选被使能,而访问0x60000000-0x63FFFFFF地址时,FSMC_NE1会输出低电平。当使用不同的FSMC_NE引脚连接外部存储器时,STM32访问SRAM的地址不一样,从而达到控制多块SRAM芯片的目的。
2.3 存储器控制器
控制SRAM的有FSMC_BCR1/2/3/4控制寄存器、FSMC_BTR1/2/3/4片选时序寄存器以及FSMC_BWTR1/2/3/4写时序寄存器。每种寄存器都有4个,分别对应于4个不同的存储区域,各种寄存器介绍如下:
-
FSMC_BCR控制寄存器可配置要控制的存储器类型、数据线宽度以及信号有效极性能参数。
-
FMC_BTR时序寄存器用于配置SRAM访问时的各种时间延迟,如数据保持时间、地址保持时间等。
-
FMC_BWTR写时序寄存器与FMC_BTR寄存器控制的参数类似,它专门用于控制写时序的时间参数。
2.4 时钟控制逻辑
FSMC外设挂载在AHB总线上,时钟信号来自于HCLK(默认72MHz),控制器的同步时钟输出就是由它分频得到。例如,NOR控制器的FSMC_CLK引脚输出的时钟,它可用于与同步类型的SRAM芯片进行同步通讯,它的时钟频率可通过FSMC_BTR寄存器的CLKDIV位配置,可以配置为HCLK的1/2或1/3,也就是说,若它与同步类型的SRAM通讯时,同步时钟最高频率为36MHz。
本示例中的SRAM为异步类型的存储器,不使用同步时钟信号,所以时钟分频配置不起作用。
2.5 FSMC控制SRAM时序
FSMC外设支持输出多种不同的时序以便于控制不同的存储器,它具有ABCD四种模式
A模式
3、FSMC控制SRAM芯片
3.1 FSMC时序结构体
- FSMC_NORSRAMTimingInitTypeDef
typedef struct
{
uint32_t FSMC_AddressSetupTime; /*设置地址建立时间,它可以被设置为0-0xF个HCLK周期数,按STM32标准库的默认配置, HCLK的时钟频率为72MHz,即一个HCLK周期为1/72微秒。*/
uint32_t FSMC_AddressHoldTime; /*设置地址保持时间,它可以被设置为0-0xF个HCLK周期数。*/
uint32_t FSMC_DataSetupTime; /*设置数据建立时间,它可以被设置为0-0xF个HCLK周期数。*/
uint32_t FSMC_BusTurnAroundDuration; /*设置总线转换周期,在NOR FLASH存储器中,地址线与数据线可以分时复用,总线转换周 期就是指总线在这两种状态间切换需要的延时,防止冲突。控制其它存储器时这个参数无 效,配置为0即可。*/
uint32_t FSMC_CLKDivision; /*设置时钟分频,它以HCLK时钟作为输入,经过FSMC_CLKDivision分频后输出FSMC_CLK 引脚作为通讯使用的同步时钟。控制其它异步通讯的存储器时这个参数无效,配置为0即 可。*/
uint32_t FSMC_DataLatency; /*设置数据保持时间,它表示在读取第一个数据之前要等待的周期数,该周期指同步时钟的周 期,本参数仅用于同步NOR FLASH类型的存储器,控制其它类型的存储器时,本参数无 效。*/
uint32_t FSMC_AccessMode; /*设置存储器访问模式,不同的模式下FSMC访问存储器地址时引脚输出的时序不一样,可选 FSMC_AccessMode_A/B/C/D模式。一般来说控制SRAM时使用A模式。*/
}FSMC_NORSRAMTimingInitTypeDef;
3.2 FSMC的SRAM初始化结构体
- FSMC_NORSRAMInitTypeDef
typedef struct
{
uint32_t FSMC_Bank; /*用于选择FSMC映射的存储区域,它的可选参数以及相应的内核地址映射范围见上面的表格*/
uint32_t FSMC_DataAddressMux; /*用于设置地址总线与数据总线是否复用*/
uint32_t FSMC_MemoryType; /*用于设置要控制的存储器类型,它支持控制的存储器类型为SRAM、PSRAM以及NOR FLASH(FSMC_MemoryType_SRAM/PSRAM/NOR)。*/
uint32_t FSMC_MemoryDataWidth; /*用于设置要控制的存储器的数据宽度,可选择设置成8或16位(FSMC_MemoryDataWidth_8b /16b)。*/
uint32_t FSMC_BurstAccessMode; /*用于设置是否使用突发访问模式(FSMC_BurstAccessMode_Enable/Disable),突发访问 模式是指发送一个地址后连续访问多个数据,非突发模式下每访问一个数据都需要输入一个地 址,仅在控制同步类型的存储器时才能使用突发模式。*/
uint32_t FSMC_AsynchronousWait; /*本成员用于设置是否使能在同步传输时使用的等待信号 (FSMC_AsynchronousWait_Enable/Disable),在控制同步类型的NOR或PSRAM时,存储 器可以使用FSMC_NWAIT引脚通知STM32需要等待。*/
uint32_t FSMC_WaitSignalPolarity; /*本成员用于设置等待信号的有效极性,即要求等待时,使用高电平还是低电平 (FSMC_WaitSignalPolarity_High/Low)。*/
uint32_t FSMC_WrapMode; /*本成员用于设置是否支持把非对齐的AHB突发操作分割成2次线性操作 (FSMC_WrapMode_Enable/Disable),该配置仅在突发模式下有效。*/
uint32_t FSMC_WaitSignalActive; /*本成员用于配置在突发传输模式时,决定存储器是在等待状态之前的一个数据周期有效还是在 等待状态期间有效 (FSMC_WaitSignalActive_BeforeWaitState/DuringWaitState)。*/
uint32_t FSMC_WriteOperation; /*这个成员用于设置是否写使能(FSMC_WriteOperation_ Enable /Disable),禁止写使能 的话FSMC只能从存储器中读取数据,不能写入。*/
uint32_t FSMC_WaitSignal; /*本成员用于设置当存储器牌突发传输模式时,是否允许通过NWAIT信号插入等待状态 (FSMC_WaitSignal_Enable/Disable)。*/
uint32_t FSMC_ExtendedMode; /*本成员用于设置是否使用扩展模式(FSMC_ExtendedMode_Enable/Disable),在非扩展模 式下,对存储器读写的时序都只使用FSMC_BCR寄存器中的配置,即下面的 FSMC_ReadWriteTimingStruct结构体成员;在扩展模式下,对存储器的读写时序可以分开 配置,读时序使用FSMC_BCR寄存器,写时序使用FSMC_BWTR寄存器的配置,即下面的 FSMC_WriteTimingStruct结构体。*/
uint32_t FSMC_WriteBurst; /*是否使能写突发操作*/
FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct; /*是一个指针,赋值时使用上一小节中讲解的时序结构体 FSMC_NORSRAMInitTypeDef设置,当不使用扩展模式时, 读写时序都使用本成员的参数配置。*/
FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct; /*一个时序结构体的指针,只有当使用扩展模式时,本配置才有 效,它是写操作使用的时序。*/
}FSMC_NORSRAMInitTypeDef;
- FSMC_Bank:用于选择FSMC映射的存储区域,它的可选参数以及相应的内核地址映射范围见上面的表格
3、代码实操
3.1 GPIO配置
- 以野火霸道的开发板为例(部分原理图),其他的开发板可根据自己的原理图。
/*A地址信号线*/
#define FSMC_A0_GPIO_PORT GPIOF
#define FSMC_A0_GPIO_CLK RCC_APB2Periph_GPIOF
#define FSMC_A0_GPIO_PIN GPIO_Pin_0
#define FSMC_A1_GPIO_PORT GPIOF
#define FSMC_A1_GPIO_CLK RCC_APB2Periph_GPIOF
#define FSMC_A1_GPIO_PIN GPIO_Pin_1
#define FSMC_A2_GPIO_PORT GPIOF
#define FSMC_A2_GPIO_CLK RCC_APB2Periph_GPIOF
#define FSMC_A2_GPIO_PIN GPIO_Pin_2
#define FSMC_A3_GPIO_PORT GPIOF
#define FSMC_A3_GPIO_CLK RCC_APB2Periph_GPIOF
#define FSMC_A3_GPIO_PIN GPIO_Pin_3
………………
/*D 数据信号线*/
#define FSMC_D0_GPIO_PORT GPIOD
#define FSMC_D0_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_D0_GPIO_PIN GPIO_Pin_14
#define FSMC_D1_GPIO_PORT GPIOD
#define FSMC_D1_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_D1_GPIO_PIN GPIO_Pin_15
#define FSMC_D2_GPIO_PORT GPIOD
#define FSMC_D2_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_D2_GPIO_PIN GPIO_Pin_0
#define FSMC_D3_GPIO_PORT GPIOD
#define FSMC_D3_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_D3_GPIO_PIN GPIO_Pin_1
…………
/*控制信号线*/
/*CS片选*/
/*NE3 ,对应的基地址0x68000000*/
#define FSMC_CS_GPIO_PORT GPIOG
#define FSMC_CS_GPIO_CLK RCC_APB2Periph_GPIOG
#define FSMC_CS_GPIO_PIN GPIO_Pin_10
/*WE写使能*/
#define FSMC_WE_GPIO_PORT GPIOD
#define FSMC_WE_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_WE_GPIO_PIN GPIO_Pin_5
/*OE读使能*/
#define FSMC_OE_GPIO_PORT GPIOD
#define FSMC_OE_GPIO_CLK RCC_APB2Periph_GPIOD
#define FSMC_OE_GPIO_PIN GPIO_Pin_4
/*LB数据掩码*/
#define FSMC_UDQM_GPIO_PORT GPIOE
#define FSMC_UDQM_GPIO_CLK RCC_APB2Periph_GPIOE
#define FSMC_UDQM_GPIO_PIN GPIO_Pin_1
/*UB数据掩码*/
#define FSMC_LDQM_GPIO_PORT GPIOE
#define FSMC_LDQM_GPIO_CLK RCC_APB2Periph_GPIOE
#define FSMC_LDQM_GPIO_PIN GPIO_Pin_0
- 引脚配置
/* 通用 GPIO 配置 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //配置为复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
根据手册:
3.2 FSMC配置
3.2.1 时序结构体
FSMC_NORSRAMTimingInitTypeDef
//地址建立时间(ADDSET)为1个HCLK 1/72M=14ns
readWriteTiming.FSMC_AddressSetupTime = 0x00;
//地址保持时间(ADDHLD)模式A未用到
readWriteTiming.FSMC_AddressHoldTime = 0x00;
//数据保持时间(DATAST)为3个HCLK 4/72M=55ns(对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;
ADDSET和DATASET的单位是HCLK一个时钟周期
T = 1/72 MHz = 1/72000000 Hz = 1.38 * 10^(-8) s = 13.8 ns
- 写
readWriteTiming.FSMC_DataSetupTime = 2
==> DATASET = 2
==> DATASET + 1 = 2 + 1 = 3 = 3 * 13.8 = 41.4 ns > 40ns
==> 1 + 1 = 27.6ns > 25ns
readWriteTiming.FSMC_AddressSetupTime = 0;
==> ADDSET = 0
==> ADDSET + 1 > 0 ns
==> 0 + 1 = 13.8 ns > 0
(ADDSET + 1) + (DATASET + 1)
==> 0 + 1 + 2 + 1 = 4
==> 4 * 13.8 = 55.2ns > 55ns
- 读
readWriteTiming.FSMC_DataSetupTime = 1
==> DATASET = 1
==> DATASET + 1 > 25ns
==> 1 + 1 = 27.6ns > 25ns
readWriteTiming.FSMC_AddressSetupTime = 0;
==> ADDSET = 0
==> ADDSET + 1 > 0 ns
==> 0 + 1 = 13.8 ns > 0
(ADDSET + 1) + (DATASET + 1) + 2
==> 1 + 1 + 1 +2 = 5
==> 69ns > 55ns
3.2.2 初始化结构体
FSMC_NORSRAMInitTypeDef
// 选择FSMC映射的存储区域: Bank1 sram3
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM3;
//设置地址总线与数据总线是否复用,仅用于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_NORSRAM3, ENABLE); // 使能BANK
3.3 测试
// 选择FSMC映射的存储区域: Bank1 sram3
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM3;
// 定义基地址
#define SRAM_BASE_ADDR (0x68000000)
// 1M字节
#define SRAM_SIZE (1*1024*1024)
// 结束地址
#define SRAM_END_ADDR (SRAM_BASE_ADDR + SRAM_SIZE)
uint8_t testValue __attribute__((at(SRAM_BASE_ADDR + 0x40)));
uint8_t testValue1;
void setup(void)
{
SRAM_Init();
Debug_USART_Config();
printf("SRAM 程序初始化\r\n");
uint8_t *p;
uint8_t *temp;
p = (uint8_t*)SRAM_BASE_ADDR;
*p = 0xDE;
temp = (uint8_t*)SRAM_BASE_ADDR;
printf("读出 8 位数据 ==> 0x%X, 地址 = %p\r\n", *temp, temp);
uint16_t *p16;
uint16_t *temp16;
p16 = (uint16_t*)(SRAM_BASE_ADDR + 10);
*p16 = 0xDE12;
temp16 = (uint16_t*)(SRAM_BASE_ADDR + 10);
printf("读出 16 位数据 ==> 0x%X, 地址 = %p\r\n", *temp16, temp16);
float *pF;
float *tempF;
pF = (float*)(SRAM_BASE_ADDR + 20);
*pF = 0xABCD;
tempF = (float*)(SRAM_BASE_ADDR + 20);
printf("读出 Float 数据 ==> 0x%f, 地址 = %p\r\n", *tempF, tempF);
testValue = 0xDD;
printf("testValue = %d, testValue 地址 = %x\r\n", testValue, (unsigned int)&testValue);
testValue1 = 0xCC;
printf("testValue1 = %d, testValue 地址 = %x", testValue1, (unsigned int)&testValue1);
}