stm32F407读写SRAM

STM32F407ZGT6的开发板,MCU自带的flash容量是1024kB(byte下同),RAM的容量是192kB,其实容量还是可以的。

但是我这个开发板外扩了flash和RAM,其中外扩的flash是16MB(128Mb),RAM的容量是1MB(8Mb)。

本节是练习下怎么使用外扩的RAM读写方法,网上有很多介绍如何读写的方法,基本都不是使用标准的HAL库方式,要么使用寄存器方式,手写配置;要么自己写了垃圾ram驱动而且只公布几个函数,非常不适合入门。所以本文不介绍寄存器,反正我也不懂^_^

一、前情提要

  1. 外扩的SRAM是IS62WV51216,是一颗 16 位宽 512K(512*16,即 1M 字节)容量的 CMOS 静态内存芯片,详细的参数就不写了
  2. 将printf函数重定向至USART1串口
  3. 需要了解FSMC用法,后面会介绍

二、SRAM引脚

SRAM一共有44个引脚,主要分为地址线、数据线等,在后面的配置中会要用到(但是不需要在代码中操作),具体如下:

  1. A0~18 为地址线,总共 19 根地址线;
  2. IO0~15 为数据线,总共 16 根数据线;
  3. OE是输出使能信号(读信号);
  4. WE 为写使能信号
  5. UB 和 LB 分别是高字节控制和低字节控制信号
  6. CS2 和 CS1 都是片选信号,原理图上实际上就是CE引脚(只有一个片选引脚),此引脚本开发板中外接的是NE3(这个后面会要在FSMC中配置一个参数,NE3这个非常重要,涉及到FSMC配置和内存起始地址
  7. 以上一共40个引脚,另有4个引脚是VCC和GND,即一共44个引脚

三、FSMC介绍

FSMC全称Flexible static memory controller,

  1. STM32 的FSMC 将外部存储器划分为固定大小为 256M 字节的四个存储块Bank,总共管理1GB的空间。
  2. 4个bank各有不同的用途,如bank1是NOR/PSRAM,bank2和bank3是Nand flash,bank4是pc card;
  3. 因此SRAM是在bank1块,而且本开SRAM芯片的片选引脚(CE引脚)外接的是NE3,即在bank1的第三区,不同的区,RAM的地址是不同的。如NE3的内存起始地址是0x68000000~0x6BFFFFFF,NE2起始地址是0x64000000~0x67FFFFFF(可以计算出每个区是64M,每个bank分为4个区,即每个bank是256M);
  4. 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
    

四、配置

  1. 启用USART1串口,即PA9和PA10接口;
  2. RCC和Sys,参照前面的同样配置
  3. FSMC配置:

(一)启用FSMC

FSMC配置看起来复杂,但是Cube基本把大部分都已经配置好了,只要开发板不是太奇葩,基本不用太多的手动配置。

STM32CubeIDE好像有点问题,配置了FSMC后,无法点击其他配置,需要关掉ioc文件再次打开才能配置其他文件。

  1. Connectivity-->FSMC

(二)配置FSMC

  1. Mode:NOR Flash/PSRAM/SRAM/LCD 1
    1. chip select:NE3
    2. memory type:sram
    3. address:19(即sram的地址引脚数量,见SRAM介绍)
    4. data:16bit(即sram的数据位宽)
    5. wait:异步
    6. byte enable:启用
  2. Configuration-->NOR/PSRAM1-->NOR/PSRAM control(为Configuration下级选项,基本保持默认配置即可)
    1. memory type:sram
    2. bank:bank1 nor flash/psram 3
    3. write operation:enable
    4. extend mode:enable(只有启用,才会有下面的nor timing for write accesses选框)
    5. 优先级:low
  3. Configuration-->NOR/PSRAM1-->NOR/PSRAM timing和NOR/PSRAM timing for write accesses
    1. 这两个均为时钟周期选项,保持一致即可,数据越小,读写速度越快;
    2. access mode:选A(可选项有A/D)
  4. Configuration-->GPIO Settings
    1. 根据上面FSMC的引脚进行配置
    2. 这里需要注意的就是片选(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 */

(一)字符串一次性读写

  1. 代码

    定义一个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);
  1. 输出结果

    与代码中的预期一致

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个小写字母),然后逐个读取字符串

  1. 代码

    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);
    }
    
  2. 输出结果

    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
    

六、注意事项

  1. SRAM的读写操作函数的参数,内存地址参数是32整型(arm是32位处理器,因此内存地址长度是32位),如HAL_SRAM_Write_8b的第二个参数
  2. SRAM在FSMC中的内存起始地址是0x68000000,需要手动设置;
  3. 内存的读写操作要手动记录当前内存地址,如第一次写入数组后,需要手动记录内存地址移动到哪个位子,如:p_addr=start_addr+sizeof(hello);,才能保证后面写入内容,不会覆盖前面的内容。
  4. STM32CubeIDE已经提供了SRAM的读写操作函数,无需自己实现内存的读写操作函数。
  5. log ram address:start=68000000,pointer=68000025计算方法:hello字符串数组的长度是38,指针地址偏移了25(实际是26,从0开始计算),16进制转换为10进制就是38。
  6. 两个有用的参考网址:

results matching ""

    No results matching ""

    results matching ""

      No results matching ""