FSMC——扩展外部SRAM && 实现对内存的动态管理(STM32F1战舰板)

本文介绍了如何使用STM32的FSMC扩展外部SRAM,特别是IS62WV51216芯片。FSMC允许方便地访问外部内存,其时序配置与硬件连接详细讲解了SRAM的读写过程。此外,文章还讨论了内存管理,特别是动态内存分配的分块式方法,以避免内存泄漏问题。
摘要由CSDN通过智能技术生成



1 概述

  STM32控制器芯片内部有一定大小的SRAM及FLASH作为内存和程序存储空间,代码存放在FLASH里,而代码运行过程的一些变量及缓存会放在SRAM里(可以把SRAM理解成电脑的内存条,内存条实质是由多个内存颗粒(即SDRAM芯片)组成的通用标准模块),当程序较大、比如跑算法或者跑 GUI 等,内存和程序空间不足时,就需要在STM32芯片的外部扩展存储器了。STM32F103ZE系列芯片可以扩展外部SRAM用作内存。

1.1 自带的IS62WV51216芯片

  STM32F103ZET6 自带了 64K 字节的 SRAM(在芯片里),战舰 STM32 开发板板载了一颗 1M 字节容量的外部 SRAM 芯片:IS62WV51216,它是一颗 16 位宽 512K(16 * 512K,即 1M 字节;ps:16=2个字节)容量的 CMOS 静态内存芯片。“16”表示16位数据总线,即表示每个地址上的数据是16位(2个字节)。PS:1M字节 = 1024 * 1024字节 = 512K * 2字节 = (512 * 1024)字节 * 2字节

1.2 FSMC概述

  FSMC是Flexible StaticMemory Controller的缩写,就是灵活的静态存储控制器。它可以用于驱动包括SRAM、NOR FLASH(如W25Q128)以及NAND FLSAH类型的存储器。其他我们不用管,我们只要知道,stm32雇佣FSMC这个管家来管理我们的IS62WV51216。

  介绍之前,先描述一下用FSMC来管理IS62WV51216后的美好样子:已知C代码在定义变量时,编译器最终会将变量名替换成内存的实际地址,以后每次访问该变量,就相当于直接访问了该地址里的数据,还有比如指针的解引用。这个也一样,配置好FSMC一些相关的参数后(相当于让外部SRAM与它进行绑定),然后我们就可以直接使用外部SRAM上的内存了,以前的变量对应的是芯片内部内存的地址,而现在则对应的是外部SRAM里的地址,并且和对内部内存地址操作的方式都一样,有两种方式:

  • 使用指针对SRAM进行读写:定义一个指针直接指向SRAM里的地址即可
#define SRAM_BASE_ADDR 				(0x68000000)
uint8_t *p;
p = (uint8_t *)(SRAM_BASE_ADDR+0x40);
*p = 0xAB; //写入数据到外部SRAM,同样也可读出
  • 使用绝对地址的方式访问SRAM:直接访问到SRAM里的实际地址
#define SRAM_BASE_ADDR 				(0x68000000)
//使用__attribute__定义变量时,需要定义为全局变量(MDK提供的)
//指定一个变量的地址
uint8_t testValue __attribute__ ((at (SRAM_BASE_ADDR+0x40))) ;
testValue = 0xAB; //写入数据到外部SRAM,同样也可读出

  看到这里惊呆了,居然还能这样子方便,即只要你知道这个外部SRAM里的内存地址了,你就可以很方便去访问了。这里应该有个疑问,使用内部内存的地址和这个外部的内存的地址会不会有冲突?不会。下面是cotex-m3内核的地址映射和FSMC相关的地址映射:可以知道芯片自带的片上SRAM的地址在0x20000000到0x3FFFFFFF,而划分给FSMC管理的地址从0x60000000到0x9FFFFFFF,即供外部SRAM拓展的最大空间有1G这么大。

  code区访问片上SRAM的数据和访问外部SRAM的数据一摸一样,只要有了内存地址,并且你的内存芯片已经做好了准备(比如初始化FSMC),那么就可以直接读写了。

  
  

2 IS62WV51216芯片介绍

先介绍IS62WV51216芯片的相关知识:

芯片图
内部功能框架

  图中 A0 ~ 18 为地址线,总共 19 根地址线(即 2^19bit=512K,1K=1024bit);IO0 ~ 15 为数据线,总共 16 根数据线(分高低两部分)。CS2 和 CS1 都是片选信号,不过 CS2 是高电平有效 CS1 是低电平有效;OE是输出使能信号(读信号,如果要读取该芯片,则需要使能该引脚);WE 为写使能信号(写入数据时,则使能该引脚);UB 和 LB 分别是高字节控制和低字节控制信号(数据掩码信号);

  实际的内存大小为1024 * 1204个字节,但是19根地址线只能访问到512K(512*1024字节)的范围,那怎么办呢?注意IS62WV51216里的”16“代表的是16bit,也就是只要给一个地址,其实就能够访问到16bit的数据(即2字节,比如输入地址0,实际可以访问到第0和第1个字节),因此19根地址线实际可寻址的范围是1024个字节这么大。只不过有时候如果我只想读写一个字节的时候怎么做呢?那就用到了上一段的UB 和 LB数据掩码信号。

  比如,我要往第0字节写入数据,而不影响第1字节里原有的数据,则我需要将UB线拉高,LB线拉低(低位字节有效),数据通过16根数据线传过来,IS62WV51216就知道自己只要接收 IO0 ~ 7的数据,自动屏蔽忽略了IO8 ~ 15的数据。
  

2.1 SRAM通信时序

  外部SRAM(IS62WV51216)的读写时序介绍,采用的是异步方式:

下面是STM32读取SRAM时的时序:
  • 先使能片选引脚指明设备,然后开始发送地址;
  • 一段时间后使能OE引脚,再经过tDOE时间后开始采集数据;
  • 数据是内存芯片通过16个IO引脚传回STM32的,在整个读取的过程地址线要保持不变tRC时间(即每根地址线上的高低电平要保持住);
  • 三个时间比较重要:tRC,tAA,tODE。

 STM32写入数据到SRAM的时序如下:

读写时序的流程很类似,过程如下:
 (1) 主机使用地址信号线发出要访问的存储器目标地址;
 (2) 控制片选信号CS1#及CS2#使能存储器芯片;
 (3) 若是要进行读操作,则控制读使能信号OE#表示要读数据,若进行写操作则控制写使能信号WE#表示要写数据;
 (4) 使用掩码信号LB#与UB#指示要访问目标地址的高、低字节部分;
 (5) 若是读取过程,存储器会通过数据线向主机输出目标数据,若是写入过程,主要使用数据线向存储器传输目标数据。

3 FSMC的介绍

  关于FSMC的介绍,如前面提到的,FSMC是Flexible StaticMemory Controller的缩写,就是灵活的静态存储控制器。它是STM32集成的一个外设,顾名思义它只能控制静态存储器,类似于SDRAM这样的动态内存它无法控制,可以把它看成是一个衔接CPU与外部存储的桥梁,它的功能呢就是你往相应的地址里写数据时候,你不需用软件来模拟外部存储芯片的读写时序,而只需配置好FSMC相关的时序寄存器,配置好相关寄存器之后,你只管往相应存储块中的地址里写数据就可以了。

  下面是FSMC的控制框图,本文讲到的IS62WV51216这个外部SRAM归属于NOR存储控制器来管理,因此我们暂时先学习这部分即可。

  然后接着是FSMC NOR存储控制器的信号线,CLK即HCLK,FSMC这个外设挂载在HCLK时钟线下(默认72MHz,用于内核与FSMC的时钟同步,也可用于输出时钟信号),接着是地址总线共有26个,但是这次的SRAM只用到19个,数据线和SRAM一样都是16个,NE是个很有趣的东西,它决定了FSMC可以控制多个存储器,下文再接着介绍,先继续往下看,NOE和NWE分别为读/写使能,NL和NWAIT没有用到,暂时不理;最后的NBL则是掩码信号引脚,对应的是SRAM的LB#和UB#

  上面讲到的FSMC引脚与SRAM的引脚相匹配总结如下,发现其高度统一:

  接着将上面的FSMC_NE[1:4],是用于控制SRAM芯片的控制信号线,STM32具有FSMC_NE1/2/3/4号引脚,不同的引脚对应STM32内部不同的地址区域。啥不同的地址区域呢?

内核地址映射
FSMC地址映射

  cotex-m3将4GB的地址空间中的0x60000000到0x9FFFFFFF共1GB的空间分给外部内存,这1GB的空间供用户自由使用,然后强势的FSMC就接管了这1GB的空间。FSMC把整个External RAM存储区域分成了4个Bank区域,并分配了地址范围及适用的存储器类型,如NOR及SRAM存储器只能使用Bank1的地址(0x60000000到0x6FFFFFFF)。

  Bank内部的256MB空间又被分成4个小块,每块64M,各自有相应的控制引脚用于连接片选信号,如FSMC_NE[4:1]信号线可用于选择BANK1内部的4小块地址区域,当STM32访问0x68000000-0x6BFFFFFF地址空间时,会访问到Bank1的第3小块区域,相应的FSMC_NE3信号线会输出控制信号。

  例如,当STM32访问0x68000000-0x6BFFFFFF地址空间时,FSMC_NE3引脚会自动设置为低电平,由于它连接到SRAM的CE#引脚,所以SRAM的片选被使能,而访问0x60000000-0x63FFFFFF地址时,FSMC_NE1会输出低电平。当使用不同的FSMC_NE引脚连接外部存储器时,STM32访问SRAM的地址不一样,从而达到控制多块SRAM芯片的目的。

3.1 FSMC时序

  另外,FSMC外设支持输出多种不同的时序以便于控制不同的存储器,它具有ABCD四种模式,以模式A为例,当内核发出访问某个指向外部存储器地址时,FSMC外设会根据配置控制信号线产生时序(由硬件产生时序)访问存储器,下图是访问外部SRAM时FSMC外设的读写时序:

  以读时序为例,该图表示一个存储器操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)以及2个HCLK周期组成。在地址建立周期中,地址线发出要访问的地址,数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片;地址建立周期结束后读使能信号线发出读使能信号,接着存储器通过数据信号线把目标数据传输给FSMC,FSMC把它交给内核。

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

3.2 FSMC相关的配置结构体

IS62WV51216为异步类型的SRAM存储器

时序结构体: FSMC_NORSRAMTimingInitTypeDef,定义SRAM读写时序中的各项时间参数,与FSMC_BRT及FSMC_BWTR寄存器配置对应。

//HCLK的时钟频率为72MHz,即一个HCLK周期为1/72微秒
typedef struct{
   
	uint32_t FSMC_AddressSetupTime;/*ADDSET 地址建立时间,0-0xF个HCLK周期*/
	uint32_t FSMC_AddressHoldTime ;/*ADDHLD 地址保持时间,0-0xF个HCLK周期*/
	uint32_t FSMC_DataSetupTime;/*DATAST 地址建立时间,0-0xF 个HCLK周期*/ 
	uint32_t FSMC_BusTurnAroundDuration; /*BUSTURN 总线转换周期, 0-0xE个HCLK周期,在NOR FLASH */
	uint32_t FSMC_CLKDivision; /*CLKDIV 时钟分频因子, 1-0xF,若控制异步存储器,本参数无效*/
	uint32_t FSMC_DataLatency; /*数据延迟/保持时间,若控制异步存储器,本参数无效*/
	uint32_t FSMC_AccessMode;/*设置访问模式,可选FSMC_AccessMode_A/B/C/D模式。一般来说控制SRAM时使用A模式。
*/
} FSMC_NORSRAMTimingInitTypeDef;

//对于本示例中只用到以下三个参数:
//FSMC_AddressSetupTime、FSMC_DataSetupTime、FSMC_AccessMode

初始化结构体: FSMC_NORSRAMInitTypeDef,对应到FSMC_BCR中的寄存器位。

typedef struct{
   
	uint32_t FSMC_Bank;/*设置要控制的Bank区域(1~4)*/
	uint32_t FSMC_DataAddressMux; /*设置地址总线与数据总线是否复用,nor flash使用*/
	uint32_t FSMC_MemoryType;/*设置存储器的类型,本示例SRAM*/
	uint32_t FSMC_MemoryDatawidth;/*设置存储器的数据宽度,可选8/16*/
	uint32_t FSMC_BurstAccessMode; /*设置是否支持突发访问模式(访问地址自增加),只支持同步类型的存储器*/
	uint32_t FSMC_AsynchronousWait;/*设置是否使能在同步传输时的等待信号,*/
	uint32_t FSMC_WaitsignalPolarity; /* 设置等待信号的极性*/
	uint32_t FSMC_WrapMode;/*设置是否支持对齐的突发模式*/
	uint32_t FSMC_WaitsignalActive; /* 配置等待信号在等待前有效还是等待期间有效*/
	uint32_t FSMC_Writeoperation;/*设置是否写使能(保护存储器,禁止写使能的话FSMC只能从存储器中读取数据,不能写入)*/
	uint32_t FSMC_Waitsignal;/*设置是否使能等待状态插入*/
	uint32_t FSMC_ExtendedMode ;/*设置是否使能扩展模式*/
	uint32_t FSMC_WriteBurst;/*设置是否使能写突发操作*/
	/* 当不使用扩展模式时,本参数用于配置读写时序,否则用于配置读时序*/
	FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadwriteTimingStruct;
	/*当使用扩展模式时,本参数用于配置写时序*/
	FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;
} FSMC_NORSRAMInitTypeDef;

/*
在非扩展模式下,对存储器读写的时序都只使用FSMC_BCR寄存器中的配置,即下面的
FSMC_ReadWriteTimingStruct结构体成员;在扩展模式下,对存储器的读写时序可以分开配置,
读时序使用FSMC_BCR寄存器,写时序使用FSMC_BWTR寄存器的配置,即下面的FSMC_WriteTimingStruct结构体。
*/

//对于SRAM,只要关注这几个:
//FSMC_Bank、FSMC_DataAddressMux、FSMC_MemoryType、FSMC_MemoryDatawidth
//FSMC_Writeoperation、FSMC_ExtendedMode
//FSMC_ReadwriteTimingStruct、FSMC_WriteTimingStruct

4 扩展外部SRAM硬件部分

  SRAM与FSMC的硬件连接图如下,用到的引脚很多很多,,我就不一一列出来了,反正有DEFG引脚,直接在代码里看吧。

SRAM连接的引脚
芯片FSMC引脚

5 扩展外部SRAM代码部分

 使用 FSMC 的 BANK1 区域 3 来控制 IS62WV51216

sram.c

#include "sram.h"	  
#include "usart.h"

/*
	A[0:18]接FMSC_A[0:18]
	D[0:15]接FSMC_D[0:15]
	UB接FSMC_NBL1 引脚PE1
	LB接FSMC_NBL0 引脚PE0
	OE接FSMC_NOE  引脚PD4
	WE接FSMC_NWE  引脚PD5
	CS接FSMC_NE3  引脚PG10
*/
					
#define Bank1_SRAM3_ADDR    ((u32)(0x68000000))	
  						   
//初始化外部SRAM
void FSMC_SRAM_Init(void)
{
   	
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;
	GPIO_InitTypeDef  GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOF|RCC_APB2Periph_GPIOG,ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);//开启外设时钟
 
	//所有引脚都配置成复用推挽输出、50MHz
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	
//	/*写法一:将所用到的引脚的地址相或,得到最终的地址,这样做可以简化代码*/
	
//	/*---------------------------------------------------------------*/
//	GPIO_InitStructure.GPIO_Pin = 0xFF33; 			 	//PORTD
// 	GPIO_Init(GPIOD, &GPIO_InitStructure);
//  /*---------------------------------------------------------------*/
//	GPIO_InitStructure.GPIO_Pin = 0xFF83; 			 	//PORTE
// 	GPIO_Init(GPIOE, &GPIO_InitStructure);
//  /*---------------------------------------------------------------*/
// 	GPIO_InitStructure.GPIO_Pin = 0xF03F; 			 	//PORTF
// 	GPIO_Init(GPIOF, &GPIO_InitStructure);
//  /*---------------------------------------------------------------*/
//	GPIO_InitStructure.GPIO_Pin = 0x043F; 			 	//PORTG 
// 	GPIO_Init(GPIOG, &GPIO_InitStructure);
//  /*---------------------------------------------------------------*/
	 			  
					
  /*写法二:常规写法*/
	
	/*---------------------------------------------------------------*/
	/*A地址信号线 针对引脚配置*/
	//A0 ~ 9 分别对应 PF0 ~ PF5 和 PF12 ~ PF15
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5 \
																|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; 
  GPIO_Init(GPIOF, &GPIO_InitStructure);
	//A10 ~ 15 分别对应 PG0 ~ PG5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
	GPIO_Init(GPIOG
  • 18
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值