0%

FSMC—扩展外部SRAM

FSMC—扩展外部SRAM

STM32F407ZGT6 自带了 192K 字节的 SRAM,对一般应用来说,已经足够了,不过在一 些对内存要求高的场合,STM32F4 自带的这些内存就不够用了。比如跑算法或者跑 GUI 等, 就可能不太够用,所以探索者 STM32F4 开发板板载了一颗 1M 字节容量的 SRAM 芯片: XM8A51216。

1、SRAM控制原理

拓展内存时一般选择SRAM和SDRAM两种,但是stm32F407不支持拓展SDRAM(stm32F429系列支持),芯片使用FSMC外设拓展SRAM。

SRAM(静态随机存储器)和SDRAM(同步动态随机存储器)。

  • SRAM:静态存储器,使用触发器来存储数据。只要保持通电,数据就不会丢失,无需刷新。
  • SDRAM:动态随机存储器。使用电容器存储数据,与系统时钟同步操作,需要定期的刷新以保持数据。
  • SRAM:使用六个晶体管称为一个存储单元,通过触发器保持状态。
  • SDRAM:每个存储由一个电容器和一个晶体管组成,依靠电容器存储的电荷来存储数据,需要定期充电保持数据。

XM8A51216简介

在我们使用的是XM8A51216的SRAM芯片内部结构框图。以他为模型学习。

  • 国产SRAM芯片具有最高10/12ns的访问速度。
  • 具备低功耗
  • TTL电容兼容
    • TTL:晶体管-晶体管逻辑。一种逻辑电平标准。
      TTL定义逻辑高和逻辑低电平的范围。高电平是2.4-5V,低电平是0-0.8V。
  • 三态输出。
    • 三态表示:高电平、低电平、高阻抗(既不输出高、也不输出低)。
    • 作用:当多个设备连接一天总线时,通过控制三态输出端使能端,当一个设备输出时,其他设备设置高阻抗,不干扰总线数据传输。
    • 这允许多个存储器芯片或其他设备共享同一组数据线,从而减少了电路板上的布线数量。
  • 全静态操作,不需要刷新
  • 字节控制功能,可以读取16位、8位数据。

1、SRAM信号线

第一张图是芯片内部结构框图,其说明表如下图。

SRAM的控制比较简单,只要使能控制信号,从地址从线输入要访问的地址,就可以读写数据。

2、存储矩阵

框图中标记号表示存储矩阵,这个SRAM芯片的内部空间大小为512*16(bits),即1M字节容量16位宽的静态内存芯片。

SRAM内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格, 这是SRAM芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储矩阵。

3、地址编译器、列I/o及I/O数据电路

地址编译器把N条地址线转换为2N根信号线,每根地址线对应着一列或着一行存储单元,通过地址线找到具体的存储单元。如果存储矩阵比较大,地址会分为列地址线、行地址线,或者行、列地址线会复用一条地址线,访问数据寻址时会先用地址线传输行地址再传输列地址。

我们这次使用的SRAM比较小,没有列地址线,只有行地址线。再图中我们看到数据线为0-15,传输16位数据,则就是2字节。地址线是0-18,经过转换就是2^18=2^8x1024=512K行存储单元。所以总共是512*16bits空间大小。

例如,当要访问宽度为16位的数据时,使用行地址线指出地址,然后把UB#和LB#线都设置为低电平, 那么I/O0-I/O15线都有效,它们一起输出该地址的16位数据(或者接收16位数据到该地址);当要访问宽度为8位的数据时,使用行地址线指出地址, 然后把UB#或LB#其中一个设置为低电平,I/O会对应输出该地址的高8位和低8位数据,因此它们被称为数据掩码信号。

4、控制电路

控制电路包括了片选、读写使能、上面提到的数据宽度控制信号UB、LB。

利用片选信号可以把多个SRAM组成一个大容量的内存条。

OE、WE可以控制读写使能,防止误操作。

5、SRAM的读写流程

对SRAM进行读写数据时,它各个信号线的时序流程图如图。

读写时序的流程:

  • 主机使能地址信号线,发送要访问的存储器地址。
  • 控制片选信号CS1、CS2,使能存储芯片
  • 若进行读操作,则控制读使能信号OE;若进行写操作,则控制写使能信号WE。
  • 使用掩码LB与UB指示访问目标的高、低字节。
  • 若读取过程中,存储去会通过数据线像主机输出目标数据;若写入过程,主要使用数据像存储器传输木目标。

2、FSMC简介

STM32F407系列芯片使用FSMC外设来管理扩展的存储器, FSMC是Flexible Static Memory Controller的缩写,译为灵活的静态存储控制器。 它可以用于驱动包括SRAM、NOR FLASH以及NANDFLSAH类型的存储器, 不能驱动如SDRAM这种动态的存储器而在STM32F429系列的控制器中, 它具有FMC外设,支持控制SDRAM存储器。

FSMC 具有四个芯片选择输出(NE1-NE4),允许 STM32F407 与最多四个外部存储设备接口。FSMC 为外部存储器提供了一个专用的地址空间,通常从 0x60000000 开始,

FSMC 的内存映射特性简化了对外部存储器的访问,因为可以像访问内部存储器位置一样,使用微控制器的标准加载和存储指令来处理它

FSMC框图解析

1、通讯引脚

在框图的右侧是FSMC外设相关的控制引脚,由于控制不同类型存储器的时候会有一些不同的引脚,看起来有非常多,其中地址线FSMC_A和数据线FSMC_D是所有控制器都共用的。 这些FSMC引脚具体对应的GPIO端口及引脚号可在《STM32F4xx规格书》中搜索查找到,不在此列出。针对本示例中的SRAM控制 器,我们整理出以下的FSMC与SRAM引脚对照表 FSMC中的SRAM控制信号线

其中比较特殊的FSMC_NE是用于控制SRAM芯片的片选控制信号线,STM32具有FSMC_NE1/2/3/4号引脚,不同的引脚对应STM32内部不同的地址区域。 例如,当STM32访问0x6C000000-0x6FFFFFFF地址空间时,FSMC_NE3引脚会自动设置为低电平,由于它连接到SRAM的CE#引脚, 所以SRAM的片选被使能,而访问0x60000000-0x63FFFFFF地址时,FSMC_NE1会输出低电平。当使用不同的FSMC_NE引脚连接外部存储器时, STM32访问SRAM的地址不一样,从而达到控制多块SRAM芯片的目的。各引脚对应的地址会在后面“FSMC的地址映射”小节讲解。

2、存储器控制器

上面不同类型的引脚是连接到FSMC内部对应的存储控制器中的。NOR/PSRAM/SRAM设备使用相同的控制器,NAND/PC卡设备使用相同的控制器,不同的控制器有专用的寄存器用于配置其工作模式。

控制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寄存器控制的参数类似,它专门用于控制写时序的时间参数。

3、时钟控制逻辑

FSMC外设挂载在AHB总线上,时钟信号来自于HCLK(默认168MHz),控制器的同步时钟输出就是由它分频得到。例如,NOR控制器的FSMC_CLK引脚输出的时钟,它可用于与同步类型的SRAM芯片进行同步通讯,它的时钟频率可通过FSMC_BTR寄存器的CLKDIV位配置,可以配置为HCLK的1/2或 1/3,也就是说,若它与同步类型的SRAM通讯时,同步时钟最高频率为84MHz。本示例中的SRAM为异步类型的存储器,不使用同步时钟信号,所以时钟分频配置不起作用。

4、FSMC地址映射

FSMC连接好外部的存储器并初始化后,就可以直接通过访问地址来读写数据,这种地址访问与I2C EEPROM、SPI FLASH的不一样, 后两种方式都需要控制I2C或SPI总线给存储器发送地址,然后获取数据;在程序里,这个地址和数据都需要分开使用不同的变量存储, 并且访问时还需要使用代码控制发送读写命令。而使用FSMC外接存储器时,其存储单元是映射到STM32的内部寻址空间的;在程序里, 定义一个指向这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FSMC外设会自动完成数据访问过程, 读写命令之类的操作不需要程序控制。FSMC的地址映射见图 FSMC的地址映射

图中左侧的是Cortex-M4内核的存储空间分配,右侧是STM32 FSMC外设的地址映射。 可以看到FSMC的NOR/PSRAM/SRAM/NAND FLASH以及PC卡的地址都在ExternalRAM地址空间内。 正是因为存在这样的地址映射,使得访问FSMC控制的存储器时,就跟访问STM32的片上外设寄存器一样(片上外设的地址映射即图中左侧的“Peripheral”区域)。

FSMC把整个External RAM存储区域分成了4个Bank区域,并分配了地址范围及适用的存储器类型, 如NOR及SRAM存储器只能使用Bank1的地址。在每个Bank的内部又分成了4个小块, 每个小块有相应的控制引脚用于连接片选信号,如FSMC_NE[4:1]信号线可用于选择BANK1内部的4小块地址区域, 见图 Bank1内部的小块地址分配 , 当STM32访问0x6C000000-0x6FFFFFFF地址空间时,会访问到Bank1的第4小块区域, 相应的FSMC_NE3信号线会输出控制信号。

5、FSMC控制SRAM的时序

FSMC外设支持输出多种不同的时序以便于控制不同的存储器,它具有ABCD四种模式, 下面我们仅针对控制SRAM使用的模式A进行讲解,见图 FSMC模式A的读时序 及图 FSMC模式A的写时序

当内核发出访问某个指向外部存储器地址时,FSMC外设会根据配置控制信号线产生时序访问存储器,上图中的是访问外部SRAM时FSMC外设的读写时序。

以读时序为例,该图表示一个存储器操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)以及2个HCLK周期组成。

  • 在地址建立周期中,地址线发出要访问的地址,
  • 数据掩码信号线指示出要读取地址的高、低字节部分,
  • 片选信号使能存储器芯片;
  • 地址建立周期结束后读使能信号线发出读使能信号,
  • 接着存储器通过数据信号线把目标数据传输给FSMC,FSMC把它交给内核。

写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)和数据建立周期(DATAST)组成, 且在数据建立周期期间写使能信号线发出写信号,接着FSMC把数据通过数据线传输到存储器中。

6、代码
  • FSMC初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
void FSMC_SRAM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;

XmRamInit(); //初始化XMRAM 20191024
delay_us(100);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推挽输出,控制背光
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 //PB15 推挽输出,控制背光

GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(0XFF<<8);//PD0,1,4,5,8~15 AF OUT
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化

GPIO_InitStructure.GPIO_Pin = (3<<0)|(0X1FF<<7);//PE0,1,7~15,AF OUT
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化

GPIO_InitStructure.GPIO_Pin = (0X3F<<0)|(0XF<<12); //PF0~5,12~15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化

GPIO_InitStructure.GPIO_Pin =(0X3F<<0)| GPIO_Pin_10;//PG0~5,10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化


GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,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);//PD15,AF12

GPIO_PinAFConfig(GPIOE,GPIO_PinSource0,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource1,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
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);//PE15,AF12

GPIO_PinAFConfig(GPIOF,GPIO_PinSource0,GPIO_AF_FSMC);//PF0,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource1,GPIO_AF_FSMC);//PF1,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource2,GPIO_AF_FSMC);//PF2,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource3,GPIO_AF_FSMC);//PF3,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource4,GPIO_AF_FSMC);//PF4,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource5,GPIO_AF_FSMC);//PF5,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource13,GPIO_AF_FSMC);//PF13,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource14,GPIO_AF_FSMC);//PF14,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource15,GPIO_AF_FSMC);//PF15,AF12

GPIO_PinAFConfig(GPIOG,GPIO_PinSource0,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource1,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource2,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource3,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource4,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource5,GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource10,GPIO_AF_FSMC);


readWriteTiming.FSMC_AddressSetupTime = 0; //地址建立时间(ADDSET)为1个HCLK 1/36M=27ns
readWriteTiming.FSMC_AddressHoldTime = 0; //地址保持时间(ADDHLD)模式A未用到
readWriteTiming.FSMC_DataSetupTime = 12; //数据保持时间(DATAST)为13个HCLK 6*13=117ns
readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
readWriteTiming.FSMC_CLKDivision = 0x00;
readWriteTiming.FSMC_DataLatency = 0x00;
readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A



FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM3;// 这里我们使用NE3 ,也就对应BTCR[4],[5]。
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; //SRAM
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
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); // 使能BANK1区域3
}
  • GPIO初始化:配置了多个 GPIO 引脚的工作模式、输出类型、速度和上下拉状态。这些引脚将用作 SRAM 的地址线、数据线和控制线。

  • GPIO复用功能配置:将配置好的 GPIO 引脚连接到 FSMC 外设的相应功能。

  •   readWriteTiming.FSMC_AddressSetupTime = 0;                 //地址建立时间(ADDSET)为1个HCLK 1/36M=27ns
          readWriteTiming.FSMC_AddressHoldTime = 0;                //地址保持时间(ADDHLD)模式A未用到
          readWriteTiming.FSMC_DataSetupTime = 12;                 //数据保持时间(DATAST)为13个HCLK 6*13=117ns
          readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
          readWriteTiming.FSMC_CLKDivision = 0x00;
          readWriteTiming.FSMC_DataLatency = 0x00;
          readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;     //模式A
      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

    * 配置FSMC的读写时序。定义了在惊醒SRAM读写时,地址线和数据线和控制信号之间的时序。
    * FSMC_AddressSetupTime = 0;
    * 设置地址建立时间为0个HCLK周期。
    * 地址建立时间是指在使能控制信号之前,地址信号必须保持稳定。
    * `FSMC_AddressHoldTime = 0;`:
    * 设置地址保持时间为0个HCLK周期。
    * 地址保持时间是指在禁用控制信号之后,地址信号必须保持稳定的时间。
    * FSMC_DataSetupTime = 12;
    * 设置数据建立时间 (DATAST) 为 12 个 HCLK 周期。
    * 数据建立时间是指在读取操作期间,数据信号在被微控制器采样之前必须保持稳定的时间。
    * `FSMC_BusTurnAroundDuration = 0x00;`: 设置总线周转时间,在非复用总线模式下通常设置为 0。
    * `FSMC_CLKDivision = 0x00;`: 设置时钟分频因子,在异步模式下通常设置为 0。
    * `FSMC_DataLatency = 0x00;`: 设置数据延迟,在异步模式下通常设置为 0。
    * `FSMC_AccessMode = FSMC_AccessMode_A;`: 选择 FSMC 的访问模式为模式 A。不同的访问模式定义了不同的时序行为。

    * ```
    FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM3;//  这里我们使用NE3 ,也就对应BTCR[4],[5]。
    FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
    FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM;  //SRAM
    FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit
    FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
    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);  // 使能BANK1区域3
    }
    * 选择 FSMC Bank 1 的第三个 NOR/SRAM 区域 (NE3)。 * `FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;`: 禁用数据线和地址线的复用模式,因为 SRAM 通常使用独立的地址线和数据线。 * `FSMC_MemoryType = FSMC_MemoryType_SRAM;`: 设置存储器类型为 SRAM。 * `FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;`: 设置存储器的数据宽度为 16 位 * `FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;`: 禁用突发访问模式。 `FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;`: 设置等待信号的极性(如果使用)。 `FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;`: 禁用异步等待模式。 `FSMC_WrapMode = FSMC_WrapMode_Disable;`: 禁用回绕模式。 `FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;`: 设置等待信号激活的时间(如果使用)。 `FSMC_WriteOperation = FSMC_WriteOperation_Enable;`: 使能对 SRAM 的写操作。 `FSMC_WaitSignal = FSMC_WaitSignal_Disable;`: 禁用等待信号。 `FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;`: 禁用扩展模式,这意味着读写操作使用相同的时序配置。 `FSMC_WriteBurst = FSMC_WriteBurst_Disable;`: 禁用写突发模式。 `FSMC_ReadWriteTimingStruct = &readWriteTiming;`: 将之前配置的读写时序参数结构体赋值给 FSMC 初始化结构体。 `FSMC_WriteTimingStruct = &readWriteTiming;`: 由于禁用了扩展模式,写操作也使用相同的时序配置。 `FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);`: 使用配置好的参数初始化 FSMC 外设。 `FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, ENABLE);`: 使能 FSMC Bank 1 的第三个区域,激活与该区域连接的外部 SRAM 芯片,使其可以被微控制器访问。

流程

一、硬件连接准备:

  1. 连接地址线: 将 STM32F407 的 FSMC 地址线引脚(例如 A0-A18)与 IS62WV51216 SRAM 芯片的对应地址引脚连接。IS62WV51216 是 512K x 16 位,需要 19 根地址线。
  2. 连接数据线: 将 STM32F407 的 FSMC 数据线引脚(D0-D15)与 IS62WV51216 SRAM 芯片的对应数据引脚连接,实现 16 位的数据传输。
  3. 连接片选信号: 将 STM32F407 的一个 FSMC Bank 选择引脚(例如 NE3)与 IS62WV51216 SRAM 芯片的片选信号引脚(CS# 或 CE#)连接。
  4. 连接读使能信号: 将 STM32F407 的 FSMC 读使能引脚(NOE)与 IS62WV51216 SRAM 芯片的读使能引脚(OE# 或 RD#)连接。
  5. 连接写使能信号: 将 STM32F407 的 FSMC 写使能引脚(NWE)与 IS62WV51216 SRAM 芯片的写使能引脚(WE# 或 WR#)连接。
  6. 连接字节控制信号: 将 STM32F407 的 FSMC 字节控制引脚(NBL0 和 NBL1)与 IS62WV51216 SRAM 芯片的字节控制引脚(LB# 和 UB#)连接,以支持按字节读写。
  7. 电源和地: 确保 STM32F407 和 IS62WV51216 SRAM 芯片的电源和地线连接正确。

二、软件初始化流程 (概念性描述):

  1. 使能时钟: 在 STM32F407 的初始化代码中,需要使能 FSMC 外设的时钟以及所有与 SRAM 连接的 GPIO 端口的时钟。

  2. 配置 GPIO: 对于所有连接到 SRAM 的 STM32F407 引脚,将其配置为“复用功能 (Alternate Function)”模式。同时设置合适的输出类型(通常为推挽)、输出速度(建议设置为高速或极高速以满足 SRAM 的时序要求)和上拉/下拉电阻(通常使用上拉)。选择与 FSMC 外设对应的复用功能(一般是 AF12)。

  3. 配置 FSMC Bank

    选择一个 FSMC Bank(例如 Bank 1 的第三个子 Bank,对应 NE3 引脚)来连接 IS62WV51216。对该 Bank 进行如下配置:

    • 存储器类型: 设置为 SRAM。
    • 数据/地址复用: 禁用,因为 SRAM 使用独立的地址线和数据线。
    • 存储器数据宽度: 设置为 16 位,与 IS62WV51216 的数据宽度匹配。
    • 突发访问模式: 通常禁用,因为 IS62WV51216 是异步 SRAM。
    • 等待信号: 禁用,因为这是一个异步 SRAM。
    • 写使能: 启用,允许向 SRAM 写入数据。
    • 扩展模式: 通常禁用,以便读写操作使用相同的时序配置。
  4. 配置 FSMC 时序:

    这是驱动 SRAM 的关键步骤,需要根据 IS62WV51216 的数据手册进行配置。主要配置以下参数:

    • 地址建立时间 (Address Setup Time): 设置地址信号在读/写使能信号有效之前需要保持稳定的时间,以 HCLK 时钟周期为单位。
    • 地址保持时间 (Address Hold Time): 设置地址信号在读/写使能信号无效之后需要保持稳定的时间,以 HCLK 时钟周期为单位。
    • 数据建立时间 (Data Setup Time):
      • 读取时: 设置在读使能信号有效后,SRAM 输出的数据需要保持稳定的时间,以便 FSMC 正确采样,以 HCLK 时钟周期为单位。
      • 写入时: 设置在写使能信号有效后,STM32F407 输出的数据需要保持稳定的时间,以确保 SRAM 正确写入,以 HCLK 时钟周期为单位。
    • 根据 IS62WV51216 的访问速度(最高 10/12ns),结合 STM32F407 的时钟频率,合理设置这些时序参数。
  5. 初始化 FSMC: 使用配置好的参数初始化 FSMC 外设。

  6. 使能 FSMC Bank: 启用所选的 FSMC Bank,使得外部 SRAM 可以通过 STM32F407 的内存映射地址空间进行访问。

三、读写数据流程:

  1. 内存映射: 完成 FSMC 初始化后,外部 SRAM 会被映射到 STM32F407 的一块特定的内存地址空间。例如,如果使用 Bank 1 的第三个子 Bank,起始地址通常是 0x68000000
  2. 写入数据: 要向 SRAM 写入数据,只需要像操作 STM32F407 内部内存一样,向 SRAM 映射的地址执行写操作。例如,要向地址 0x68000000 + 0x1000 写入一个 16 位的数据 0xABCD,您只需要在代码中执行类似于 *(volatile uint16_t*)(0x68000000 + 0x1000) = 0xABCD; 的操作。FSMC 硬件会自动产生相应的控制信号(片选、写使能)和时序来完成写入过程。
  3. 读取数据: 要从 SRAM 读取数据,同样只需要像操作内部内存一样,从 SRAM 映射的地址执行读操作。例如,要从地址 0x68000000 + 0x1000 读取一个 16 位的数据,您可以执行类似于 uint16_t data = *(volatile uint16_t*)(0x68000000 + 0x1000); 的操作。FSMC 硬件会自动产生相应的控制信号(片选、读使能)和时序来完成读取过程,并将读取到的数据返回给您的程序。
  4. 字节控制: 如果需要进行字节级别的读写,您可以使用 8 位的数据类型(uint8_t)并配合 FSMC 的字节控制信号(NBL0 和 NBL1)。当您访问奇数地址或偶数地址时,FSMC 会自动激活相应的字节使能信号,从而实现对 SRAM 特定字节的操作。