STM32CubeMX教程23 FSMC - IS62WV51216(SRAM)驱动

发布时间:2024年01月11日

目录

1、准备材料

2、实验目标

3、轮询方式读写SRAM

3.0、前提知识

3.1、CubeMX相关配置

3.1.1、时钟树配置

3.1.2、外设参数配置

3.1.3、外设中断配置

3.2、生成代码

3.2.1、外设初始化调用流程

3.2.2、外设中断调用流程

3.2.3、添加其他必要代码

4、常用函数

5、烧录验证

5.1、实验具体流程

5.2、实验现象

6、DMA方式读写SRAM

6.1、前提知识

6.2、CubeMX相关配置

6.3、DMA中断调用流程

6.4、添加其他必要代码

6.5、实验现象

参考资料


1、准备材料

开发板(正点原子stm32f407探索者开发板V2.4

STM32CubeMX软件(Version 6.10.0

野火DAP仿真器

keil μVision5 IDE(MDK-Arm

CH340G Windows系统驱动程序(CH341SER.EXE

XCOM V2.6串口助手

2、实验目标

使用STM32CubeMX软件配置STM32F407开发板的FSMC实现以轮询或DMA的方式读写IS62WV51216(SRAM)芯片

3、轮询方式读写SRAM

3.0、前提知识

关于FSMC的内容读者可阅读“STM32CubeMX教程22 FSMC - 8080并行接口TFT-LCD驱动”实验“3.0、前提知识”小节

本实验使用的SRAM芯片为IS62WV51216,其为16位宽512K容量的静态随机存取存储器,开发板使用FSMC Bank 1-NOR/PSRAM3,片选信号为NE3(PG10),地址线A[0:18],数据线D[0:15]来控制该存储芯片,如下图所示为该存储芯片的硬件原理图

FSMC的地址线是按照字节寻址的,因此19根地址线的寻址范围应该为0x0 0000~0x7 FFFF(2^19=524288=0x8 0000)

但是由于该存储器为16位(2个字节),因此其实际容量应该为2*512KB=1024KB,其需要的寻址范围为0x0 0000~0xF FFFF

再考虑到FSMC Bank 1-NOR/PSRAM3起始地址为0x6800 0000,因此如果要访问IS62WV51216芯片全部的1024KB数据的地址范围应该为0x6800 0000~0x680F FFFF

19根地址线做不到,那怎么办?

解决方法就是将16位宽分为高字节和低字节,通过FSMC的字节控制引脚FSMC_NBL0/1实现全部1024KB存储空间的访问

3.1、CubeMX相关配置

请先阅读“STM32CubeMX教程1 工程建立”实验3.4.1小节配置RCC和SYS

3.1.1、时钟树配置

系统时钟树配置均设置为STM32F407总线能达到的最高时钟频率,具体如下图所示

3.1.2、外设参数配置

本实验需要需要初始化开发板上KEY1和KEY0两个用户按键,具体配置步骤请阅读“STM32CubeMX教程3 GPIO输入 - 按键响应

本实验需要需要初始化USART1作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信

?本实验写入SRAM数据时使用到了STM32的随机数RNG功能,读者直接在Security/RNG中将其激活即可

单击Pinout & Configuration页面左边Connectivity/FSMC选项,在右边的Mode下点开NORFIash/PSRAM/SRAM/ROM/LCD3选项卡(因为SRAM使用的是使用FSMC Bank 1-NOR/PSRAM3),然后按照下面顺序配置

  1. 选择片选信号NE3
  2. 内存类型为SRAM
  3. FSMC地址线19位
  4. 数据Data为16位宽度(D0-D15)
  5. 使能字节控制Byte enable

下方的读写时序参数配置可阅读“STM32CubeMX教程22 FSMC - 8080并行接口TFT-LCD驱动”实验“3.0、前提知识”小节,具体配置如下图所示

配置完成之后请读者对照STM32CubeMX中配置的默认功能引脚是否和开发板硬件原理图每个引脚对应,防止出现默认功能引脚与开发板设计的引脚不一致的现象

3.1.3、外设中断配置

轮询方式读写SRAM无需配置任何中断

3.2、生成代码

请先阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节配置Project Manager

单击页面右上角GENERATE CODE生成工程

3.2.1、外设初始化调用流程

请阅读“STM32CubeMX教程22 FSMC - 8080并行接口TFT-LCD驱动”实验3.2.1小节

3.2.2、外设中断调用流程

轮询方式读写SRAM无需配置任何中断

3.2.3、添加其他必要代码

在main.c文件中添加SRAM读写测试函数,具体函数源代码如下所示

/*用HAL函数写入数据*/
void SRAM_WriteByFunc(void)
{
    //1.写入字符串
    uint32_t *pAddr = (uint32_t *)(SRAM_ADDR_BEGIN);	//给指针赋值
    uint8_t strIn[] = "Moment in UPC";
    uint16_t dataLen = sizeof(strIn); //数据长度,字节数,包括最后的结束符'\0’
    if(HAL_SRAM_Write_8b(&hsram3, pAddr, strIn, dataLen) == HAL_OK)
    {
        printf("Write string at 0x6800 0000:");
        printf("%s\r\n",strIn);
    }

    //2.写入一个随机数
    uint32_t num=0;
    pAddr=(uint32_t *)(SRAM_ADDR_BEGIN+256);	//指针重新赋值
    HAL_RNG_GenerateRandomNumber(&hrng, &num);	//产生32位随机数
    if(HAL_SRAM_Write_32b(&hsram3, pAddr, &num, 1) == HAL_OK)
    {
        printf("Write 32b number at 0x6800 0100");
        printf("0x%x\r\n", num);	
    }
    printf("-----------------------------------------\r\n");
}

/*用HAL函数读取数据*/
void SRAM_ReadByFunc(void)
{
    //1.读取字符串
    uint32_t *pAddr = (uint32_t *)(SRAM_ADDR_BEGIN);	//给指针赋值
    uint8_t strOut[30];
    uint16_t dataLen = 30;
    if(HAL_SRAM_Read_8b(&hsram3, pAddr, strOut, dataLen) == HAL_OK)
    {
        printf("Read string at 0x6800 0000:");
        printf("%s\r\n", strOut); 
    }

    //2.读取一个uint32_t数
    uint32_t num=0;
    pAddr=(uint32_t *)(SRAM_ADDR_BEGIN+256);	//指针重新赋值,指向一个新的地址
    if(HAL_SRAM_Read_32b(&hsram3, pAddr, &num, 1) == HAL_OK)
    {
        printf("Read 32b number at 0x6800 0100:");
        printf("0x%x\r\n", num);
    }
    printf("-----------------------------------------\r\n");
}

?在main.c文件中添加使用到的SRAM起始/中间/结束地址,并对定义的函数声明,源代码如下所示

/*定义SRAM地址*/
#define SRAM_ADDR_BEGIN		0x68000000UL //Bank1 子区3的 SRAM起始地址
#define SRAM_ADDR_HALF		0x68080000UL //SRAM 中间地址 512K字节
#define SRAM_ADDR_END	    0x680FFFFFUL //SRAM 结束地址 1024K字节

/*读写测试函数声明*/
void SRAM_ReadByFunc(void);
void SRAM_WriteByFunc(void);

最后在主函数主循环while(1)中使用用户按键调用SRAM读写测试函数即可,源代码如下所示?

/*KEY1被按下*/
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
    {
        //SRAM写测试函数
        SRAM_WriteByFunc();
        while(!HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin));
    }
}

/*KEY0被按下*/
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
    {
        //SRAM读测试函数
        SRAM_ReadByFunc();
        while(!HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin));
    }
}

4、常用函数

/*向SRAM中写8位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Write_8b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize)

/*从SRAM中读8位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Read_8b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize)

/*向SRAM中写16位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Write_16b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize)

/*从SRAM中读16位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Read_16b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize)

/*向SRAM中写32位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Write_32b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize)

/*从SRAM中读32位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Read_32b(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize)

/*向SRAM中写32位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Write_DMA(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pSrcBuffer, uint32_t BufferSize)

/*从SRAM中读32位缓存数据*/
HAL_StatusTypeDef HAL_SRAM_Read_DMA(SRAM_HandleTypeDef *hsram, uint32_t *pAddress, uint8_t *pDstBuffer, uint32_t BufferSize)

5、烧录验证

5.1、实验具体流程

“初始化用户按键 -> 初始化串口 -> 配置FSMC模式 -> 配置FSMC参数 -> 添加SRAM测试函数 -> 主函数中实现使用按键调用测试函数”

5.2、实验现象

烧录程序,开发板上电后打开串口助手,按下KEY0按键从SRAM中指定位置读取数据,发现读取到错误乱码信息;按下KEY1按键将准备好的数据写入SRAM指定位置,再次按下KEY0按键从该指定位置读取数据,发现和我们写入的数据一致,可重复多次尝试,发现读取的信息均和写入信息一致,如下图所示为串口输出详细信息

6、DMA方式读写SRAM

6.1、前提知识

读写SRAM还可以使用DMA的方式进行,但是由于SRAM属于存储设备,因此读写SRAM的DMA方向应该为内存到内存

在“STM32CubeMX教程12 DMA 直接内存读取”我们提到只有DAM2的8个通道可以实现从存储器到存储器这种传输模式,由于该模式比较特殊,因此读者可以发现不同于其他外设DMA的配置,在FSMC选项卡我们配置SRAM的页面中根本没有DMA选项卡可以设置,因此想要设置从存储器到存储器这种传输模式的DMA必须在System Core/DMA选项卡中设置

另外在其他外设设置DMA时,DMA流与外设关联的函数__HAL_LINKDMA()会被自动生成在HAL_xxx_MspInit()函数中

但是如果DMA为存储器到存储器这种传输模式,则生成的工程代码中不会自动关联DMA流与外设(他也不知道该如何关联),因此在DMA及外设均初始化完毕之后,需要用户手动增加__HAL_LINKDMA()函数将外设与DMA流关联

6.2、CubeMX相关配置

请读者先按照本实验“3.1、CubeMX相关配置” 小节配置RCC、SYS、用户按键、串口USART1和FSMC模式和基本参数

如果读者对DMA参数不理解,请阅读“STM32CubeMX教程12 DMA 直接内存读取”实验,然后按下下方顺序配置DMA参数

  1. 单击Pinout & Configuration页面左边System Core/DMA选项卡
  2. 在Configuration中选择MemToMem
  3. 单击下方的增加按钮选择增加DMA请求
  4. 单击增加的DMA请求
  5. 在下方对其参数进行配置,模式为Normal
  6. 源/目标内存地址均递增
  7. 数据宽度选择word(因为HAL库提供的DMA写入SRAM函数数据为32位的)

其他参数默认,具体配置参看下图

在Pinout & Configuration页面左边System Core/NVIC中勾选DMA2 Stream0 全局中断,然后选择合适的中断优先级即可

6.3、DMA中断调用流程

DMA触发中断,其回调函数是一个函数指针的形式,在外设使用DMA启动传输的时候会将外设对应的中断回调函数赋值给DMA中断回调函数指针,具体请阅读“STM32CubeMX教程12 DMA 直接内存读取”实验实验3.2.2小节,流程大致一致

这里读者只需知道,在STM32CubeMX开启FSMC配置的SRAM DMA中断之后,在工程代码中使用HAL_SRAM_Read_DMA()/HAL_SRAM_Write_DMA()函数传输完毕之后,都会调用HAL_SRAM_DMA_XferCpltCallback()虚函数,用户重新实现该虚函数即可

由于以DMA方式读和写SRAM传输完成的回调函数为同一个,因此用户可以自己设定标志位,从而可以在中断回调函数中判断是读完成还是写完成

6.4、添加其他必要代码

配置工程并单击页面右上角GENERATE CODE生成工程

在main.c文件中FSMC初始化完毕之后添加DMA流与外设关联的函数

//将外设与DMA流关联
__HAL_LINKDMA(&hsram3, hdma, hdma_memtomem_dma2_stream0);

在main.c文件中增加以DMA方式读写SRAM的测试函数,源代码如下

/*以DMA方式写入数据*/
void SRAM_WriteDMA(void)													
{
    printf("Write 32bit array by DMA:");
    uint32_t Value=3000;	
    for(uint8_t i=0; i<COUNT; i++)
    {
        txBuffer[i] = Value;
        printf("%d,",Value);
        Value += 6;
    }
    printf("\r\n");
    //DMA传输方向,1=write, 0=read
    DMA_Direction=1;					
    //表示DMA正在传输,1=working, 0=idle	
    DMA_Busy=1;																			
    //给指针赋值
    uint32_t *pAddr_32b=(uint32_t *)(SRAM_ADDR_BEGIN);			
    //DMA方式写入SRAM	
    HAL_SRAM_Write_DMA(&hsram3, pAddr_32b, txBuffer, COUNT);	
}

/*以DMA方式读取数据*/
void SRAM_ReadDMA(void)														
{
    printf("Read 32bit array by DMA\r\n");
    DMA_Direction=0;													
    DMA_Busy=1;			
    uint32_t *pAddr_32b=(uint32_t *)(SRAM_ADDR_BEGIN);	
    //以DMA方式读取SRAM
    HAL_SRAM_Read_DMA(&hsram3, pAddr_32b, rxBuffer, COUNT);
}

在main.c文件头部增加函数声明及使用到的一些变量定义,源代码如下

/*函数声明*/
void SRAM_WriteDMA(void);
void SRAM_ReadDMA(void);

/*变量定义*/
#define COUNT 5                      //缓存区数据个数
uint32_t txBuffer[COUNT];            //DMA发送缓存区
uint32_t rxBuffer[COUNT];            //DMA接收缓存区
uint8_t DMA_Direction = 1;           //DMA传输方向,1=write, 0=read
uint8_t DMA_Busy = 0;                //DMA工作状态,1=busy, 0=idle

最后在主循环中使用用户按键调用SRAM读写测试函数即可,源代码如下所示

/*KEY1被按下*/
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_RESET)
    {
        //SRAM DMA写测试函数
        SRAM_WriteDMA();
        while(!HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin));
    }
}

/*KEY0被按下*/
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
{
    HAL_Delay(50);
    if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_RESET)
    {
        //SRAM DMA读测试函数
        SRAM_ReadDMA();
        while(!HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin));
    }
}

6.5、实验现象

烧录程序,开发板上电后打开串口助手,按下KEY0按键以DMA方式从SRAM中指定位置读取数据,发现读取到错误的5个数据;按下KEY1按键将3000,3006,3012,3018,3024五个数据以DMA方式写入SRAM指定位置,再次按下KEY0按键以DMA方式从SRAM中指定位置读取数据,发现和我们写入的数据一致,整个过程读者也可以发现每次以DMA方式写入/读取SRAM完成之后都会进入DMA传输完成回调函数中,如下图所示为整个过程串口输出的详细信息

参考资料

STM32Cube高效开发教程(基础篇)

文章来源:https://blog.csdn.net/lc_guo/article/details/135505927
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。