stm32F407读写SRAM
STM32F407ZGT6的开发板,MCU自带的flash容量是1024kB(byte下同),RAM的容量是192kB,其实容量还是可以的。
但是我这个开发板外扩了flash和RAM,其中外扩的flash是16MB(128Mb),RAM的容量是1MB(8Mb)。
本节是练习下怎么使用外扩的RAM读写方法,网上有很多介绍如何读写的方法,基本都不是使用标准的HAL库方式,要么使用寄存器方式,手写配置;要么自己写了垃圾ram驱动而且只公布几个函数,非常不适合入门。所以本文不介绍寄存器,反正我也不懂^_^
一、前情提要
- 外扩的SRAM是IS62WV51216,是一颗 16 位宽 512K(512*16,即 1M 字节)容量的 CMOS 静态内存芯片,详细的参数就不写了
- 将printf函数重定向至USART1串口
- 需要了解FSMC用法,后面会介绍
二、SRAM引脚
SRAM一共有44个引脚,主要分为地址线、数据线等,在后面的配置中会要用到(但是不需要在代码中操作),具体如下:
- A0~18 为地址线,总共 19 根地址线;
- IO0~15 为数据线,总共 16 根数据线;
- OE是输出使能信号(读信号);
- WE 为写使能信号
- UB 和 LB 分别是高字节控制和低字节控制信号
- CS2 和 CS1 都是片选信号,原理图上实际上就是CE引脚(只有一个片选引脚),此引脚本开发板中外接的是NE3(这个后面会要在FSMC中配置一个参数,NE3这个非常重要,涉及到FSMC配置和内存起始地址)
- 以上一共40个引脚,另有4个引脚是VCC和GND,即一共44个引脚
三、FSMC介绍
FSMC全称Flexible static memory controller,
- STM32 的FSMC 将外部存储器划分为固定大小为 256M 字节的四个存储块Bank,总共管理1GB的空间。
- 4个bank各有不同的用途,如bank1是NOR/PSRAM,bank2和bank3是Nand flash,bank4是pc card;
- 因此SRAM是在bank1块,而且本开SRAM芯片的片选引脚(CE引脚)外接的是NE3,即在bank1的第三区,不同的区,RAM的地址是不同的。如NE3的内存起始地址是0x68000000~0x6BFFFFFF,NE2起始地址是0x64000000~0x67FFFFFF(可以计算出每个区是64M,每个bank分为4个区,即每个bank是256M);
- FSMC可以在网上找框图了解引脚功能,本SRAM的引脚接法如下:(配置时,系统自动配置好了,如果没有,就手动配置下)
PF0 ------> FSMC_A0 PF1 ------> FSMC_A1 PF2 ------> FSMC_A2 PF3 ------> FSMC_A3 PF4 ------> FSMC_A4 PF5 ------> FSMC_A5 PF12 ------> FSMC_A6 PF13 ------> FSMC_A7 PF14 ------> FSMC_A8 PF15 ------> FSMC_A9 PG0 ------> FSMC_A10 PG1 ------> FSMC_A11 PE7 ------> FSMC_D4 PE8 ------> FSMC_D5 PE9 ------> FSMC_D6 PE10 ------> FSMC_D7 PE11 ------> FSMC_D8 PE12 ------> FSMC_D9 PE13 ------> FSMC_D10 PE14 ------> FSMC_D11 PE15 ------> FSMC_D12 PD8 ------> FSMC_D13 PD9 ------> FSMC_D14 PD10 ------> FSMC_D15 PD11 ------> FSMC_A16 PD12 ------> FSMC_A17 PD13 ------> FSMC_A18 PD14 ------> FSMC_D0 PD15 ------> FSMC_D1 PG2 ------> FSMC_A12 PG3 ------> FSMC_A13 PG4 ------> FSMC_A14 PG5 ------> FSMC_A15 PD0 ------> FSMC_D2 PD1 ------> FSMC_D3 PD4 ------> FSMC_NOE PD5 ------> FSMC_NWE PD6 ------> FSMC_NWAIT PG10 ------> FSMC_NE3 PE0 ------> FSMC_NBL0 PE1 ------> FSMC_NBL1
四、配置
- 启用USART1串口,即PA9和PA10接口;
- RCC和Sys,参照前面的同样配置
- FSMC配置:
(一)启用FSMC
FSMC配置看起来复杂,但是Cube基本把大部分都已经配置好了,只要开发板不是太奇葩,基本不用太多的手动配置。
STM32CubeIDE好像有点问题,配置了FSMC后,无法点击其他配置,需要关掉ioc文件再次打开才能配置其他文件。
- Connectivity-->FSMC
(二)配置FSMC
- Mode:NOR Flash/PSRAM/SRAM/LCD 1
- chip select:NE3
- memory type:sram
- address:19(即sram的地址引脚数量,见SRAM介绍)
- data:16bit(即sram的数据位宽)
- wait:异步
- byte enable:启用
- Configuration-->NOR/PSRAM1-->NOR/PSRAM control(为Configuration下级选项,基本保持默认配置即可)
- memory type:sram
- bank:bank1 nor flash/psram 3
- write operation:enable
- extend mode:enable(只有启用,才会有下面的nor timing for write accesses选框)
- 优先级:low
- Configuration-->NOR/PSRAM1-->NOR/PSRAM timing和NOR/PSRAM timing for write accesses
- 这两个均为时钟周期选项,保持一致即可,数据越小,读写速度越快;
- access mode:选A(可选项有A/D)
- Configuration-->GPIO Settings
- 根据上面FSMC的引脚进行配置
- 这里需要注意的就是片选(CS)的配置,开发板上只有一个片选引脚CE,外接开发板的NE3引脚。
五、实现
FSMC的配置和初始化在fsmc.c文件中
SRAM的初始化和操作函数,在stm32f4xx_hal_sram.c文件中,sram的读写操作有8、16和32位几种方式。
前面提到SRAM使用的bank1的NE3区,在内存中的映射起始地址时0x68000000,因此需要代码中定义一个地址变量,供后面的内存读写函数使用。
/* USER CODE BEGIN PV */
static uint32_t start_addr=0x68000000;
/* USER CODE END PV */
(一)字符串一次性读写
- 代码
定义一个hello[]变量的字符串;
使用HAL_SRAM_Write_8b函数向SRAM写入
使用HAL_SRAM_Read_8b函数读取内容
uint32_t p_addr=start_addr;
uint8_t hello[]="welcome stm32 world,www.bytetoy.cn\r\n";
uint8_t txt_buf[26];
uint8_t tmp_buf[64];
printf("write sram hello[]:%s\r\n",hello);
HAL_Delay(100);
HAL_SRAM_Write_8b(&hsram1, (uint32_t *)start_addr, (uint8_t *)hello, sizeof(hello));
p_addr=start_addr+sizeof(hello);
printf("log ram address:start=%X,pointer=%X\r\n",start_addr,p_addr);
HAL_SRAM_Read_8b(&hsram1, (uint32_t *)start_addr, (uint8_t *)tmp_buf, 64);
printf("read from sram tmp_buf:%s\r\n",tmp_buf);
- 输出结果
与代码中的预期一致
write sram hello[]:welcome stm32 world,www.bytetoy.cn
log ram address:start=68000000,pointer=68000025
read from sram tmp_buf:welcome stm32 world,www.bytetoy.cn
(二)字符串单个读写
sram中内容的读写,函数是通过操作指针实现的,除了上面的一次性读写操作,也可以逐个字节来读取。 本代码是一次性写入一个字符串(26个小写字母),然后逐个读取字符串
代码
uint32_t p_addr=start_addr; uint8_t hello[]="welcome stm32 world,www.bytetoy.cn\r\n"; uint8_t txt_buf[26]; uint8_t tmp_buf[64]; for(int i=0;i<26;i++) { txt_buf[i]='a'+i; } //数组写入,单个读出测试 printf("write sram txt_buf:%s\r\n",txt_buf); HAL_SRAM_Write_8b(&hsram1, (uint32_t *)p_addr, (uint8_t *)txt_buf, sizeof(txt_buf)); uint8_t t=0; HAL_Delay(500); for(int i=0;i<26;i++) { HAL_SRAM_Read_8b(&hsram1, (uint32_t *)p_addr, (uint8_t *)&t, 1); p_addr++; printf("read sram txt_buf[%d]=%c\r\n",i,t); }
输出结果
write sram txt_buf:abcdefghijklmnopqrstuvwxyz read sram txt_buf[0]=a read sram txt_buf[1]=b read sram txt_buf[2]=c read sram txt_buf[3]=d read sram txt_buf[4]=e read sram txt_buf[5]=f read sram txt_buf[6]=g read sram txt_buf[7]=h read sram txt_buf[8]=i read sram txt_buf[9]=j read sram txt_buf[10]=k read sram txt_buf[11]=l read sram txt_buf[12]=m read sram txt_buf[13]=n read sram txt_buf[14]=o read sram txt_buf[15]=p read sram txt_buf[16]=q read sram txt_buf[17]=r read sram txt_buf[18]=s read sram txt_buf[19]=t read sram txt_buf[20]=u read sram txt_buf[21]=v read sram txt_buf[22]=w read sram txt_buf[23]=x read sram txt_buf[24]=y read sram txt_buf[25]=z
六、注意事项
- SRAM的读写操作函数的参数,内存地址参数是32整型(arm是32位处理器,因此内存地址长度是32位),如HAL_SRAM_Write_8b的第二个参数
- SRAM在FSMC中的内存起始地址是0x68000000,需要手动设置;
- 内存的读写操作要手动记录当前内存地址,如第一次写入数组后,需要手动记录内存地址移动到哪个位子,如:
p_addr=start_addr+sizeof(hello);
,才能保证后面写入内容,不会覆盖前面的内容。 - STM32CubeIDE已经提供了SRAM的读写操作函数,无需自己实现内存的读写操作函数。
log ram address:start=68000000,pointer=68000025
计算方法:hello字符串数组的长度是38,指针地址偏移了25(实际是26,从0开始计算),16进制转换为10进制就是38。- 两个有用的参考网址: