STM32复习笔记(五):FSMC连接外部SRAM

这篇具有很好参考价值的文章主要介绍了STM32复习笔记(五):FSMC连接外部SRAM。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

Preface:

(一)原理相关

(二)CUBEMX配置

(三)轮询方式读写

(四)DMA方式读写


Preface:

STM32F4有一个FSMC(Flexible Static Memory Controller,可变静态存储控制器),可以用来驱动8080接口的TFT LCD,我之前就写过一篇blog,是用FSMC来驱动4.3寸液晶屏;此外,还可以用FSMC来连接外部的各种存储器,比如说SRAM、NOR FLASH、PSRAM等等;但是每个区(Bank)的功能是不一样的;Bank1可以连接多达4个NOR FLASH或PSRAM/SRAM存储器件(通过片选);Bank2和Bank3只能用于访问NAND FLASH,且每个Bank只能连一个设备;Bank4只能用于连接PC Card设备。


(一)原理相关

STM32F4的FSMC控制器的存储区分为4个区,分别为Bank 1~Bank 4,每个Bank大小为2e28个字节,即256MB,因此总共管理的内存可达到1GB;而每个Bank又分成4个子区,每个子区64MB;Bank1的地址范围为0x6000 0000h~6FFF FFFFh,Bank2的地址范围为0x7000 0000h~7FFF FFFFh,Bank3的地址范围为0x8000 0000h~8FFF FFFFh,Bank4的地址范围为0x9000 0000h~9FFF FFFFh;如下图:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

对于STM32F407ZGT6来说,其内部SRAM为192kB,一般的应用程序是足够用了,但是在使用GUI(特别是要做得很炫酷那种)等需要大量内存的功能时,192kB是不太够的,可能就需要扩展SRAM了。FSMC连接PSRAM/SRAM设备时,接口线的功能如下表所示: 

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

根据开发板的原理图(如下图)可知,FSMC的NE3线连接到了外部SRAM的片选,而由于只有Bank1才能连接SRAM,所以可知板子用的是FSMC的Bank1的子区3来连接外部SRAM;

该SRAM芯片为IS62WV51216,这是一个16位宽512K容量(512K×16位,即1024KB)的静态内存芯片。它与MCU的连接电路如下图所示。芯片几个主要管脚的功能,以及与MCU的连接原理如下:

  • A0至A18是19根地址线,连接FSMC的19根地址线,即FSMC_A0至FSMC_A18;
  • I/O0至I/O15是16位数据线,连接FSMC的FSMC_D0至FSMC_D15数据线;
  • CE是芯片的片选信号,连接MCU的FSMC_NE3(PG10引脚),也就是Bank1子区3的片选信号;
  • OE是输出使能信号,连接MCU的FSMC_NOE(PD4引脚),是读数据时的使能信号;
  • WE是写使能信号,连接MCU的FSMC_NWE (PD5引脚),是写数据使能信号;
  • UB是高字节使能信号,连接MCU的FSMC_NBL[1](PE1引脚);LB是低字节使能信号,连接MCU的FSMC_NBL[0](PE0引脚);通过UB和LB的控制可以只读取一个地址的高字节(I/O8~I/O15)或低字节(I/O0~I/O7),或读取16位数据;

IS62WV51216有19根地址线,能表示的地址范围是512K,而数据宽度是16位(2B),因此实际存储容量是1024KB,偏移地址范围是0x00000~0x7FFFF。又因为Bank1子区3的起始地址是0x68000000,所以IS62WV51216的全部1024KB的地址范围是 0x68000000~0x680FFFFF。FSMC_NBL[1]和FSMC_NBL[0]分别控制高位字节和低位字节访问,实现全部1024KB存储空间的按字节访问。

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件


(二)CUBEMX配置

cubemx中的FSMC模式配置如下(选择子区3,片选为NE3;Mem类型SRAM;地址19位;数据16位;Wait是PSRAM芯片发送给FSMC的等待输入信号,IS62WV51216没有该线,所以disable掉;最后勾上Byte enable,允许字节访问):

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

开启之后再对照原理图看一遍发现引脚刚好与开发板上的一致,因此无需更改引脚重映射:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接下来进行参数配置;首先是控制参数:Memory type只能选SRAM;Bank只能选Bank1子区3,这两项与模式设置部分是一一对应的;Write operation设置为Enabled,表示使能写操作;Extended mode设置为Disabled,FSMC自动使用模式A对SRAM进行操作,SRAM的读写操作速度基本相同,所以读写操作可以使用相同的时序参数,无需使用扩展模式单独设置读时序和写时序;接下来是时序参数:地址建立时间ADDSET,设置范围为0~15,设置为0即可;数据建立时间DATASET,设置范围为1~255,设置为8;总线翻转时间,设置范围为0~15,设置为0即可;

另外,因为FSMC参数设置部分没有DMA设置页面,如果要用DMA的话需要去System Core的DMA里面手动创建,并且在代码里要手动LINK DMA;

如下图所示:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

然后,因为代码里会使用随机数生成器,所以打开Security分组下的RNG模块,启用RNG;RNG需要用到48MHz时钟,时钟树上可能会提示错误;单击时钟树界面上的Resolve Clock Issues,让cubemx自动解决即可:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件 fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

配置好后直接生成代码即可


(三)轮询方式读写

首先加入3个宏,分别表示Bank1子区3的SRAM起始地址、中间地址、结束地址,如下所示:

#define SRAM_ADDR_BEGIN     0x68000000UL    //Bank1子区3的SRAM起始地址
#define SRAM_ADDR_HALF      0x68080000UL    //SRAM中间地址,一共512KB
#define SRAM_ADDR_END       0x680FFFFFUL    //SRAM结束地址,一共1024KB

然后封装一下读取、写入数据;如下:

#include "fsmc_func.h"
#include "fsmc.h"
#include "rng.h"

/*
 * 用HAL函数写入数据
 * */
HAL_StatusTypeDef SRAM_WriteByFunc() {
    HAL_StatusTypeDef status = HAL_OK;
    uint8_t str[] = "Input Data";   //待写入字符
    uint16_t length = sizeof (str); //数据长度(注意是字节数),包括'\0'
    auto *paddr = (uint32_t*)(SRAM_ADDR_BEGIN); //目标地址

    //写入字符串
    if (HAL_OK == HAL_SRAM_Write_8b(&hsram3, paddr, str, length)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    //写入数字
    uint32_t num = 0;
    paddr = (uint32_t*)(SRAM_ADDR_HALF); //修改目标地址
    HAL_RNG_GenerateRandomNumber(&hrng, &num);  //生成随机数
    if (HAL_OK == HAL_SRAM_Write_32b(&hsram3, paddr, &num, 1)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    return status;
}
/*
 * 用HAL函数读取数据
 * */
HAL_StatusTypeDef SRAM_ReadByFunc() {
    HAL_StatusTypeDef status = HAL_OK;
    auto *paddr = (uint32_t*)(SRAM_ADDR_BEGIN);
    uint8_t str[30];
    uint16_t length = 30;   //读取字节数

    //读取字符
    if (HAL_OK == HAL_SRAM_Read_8b(&hsram3, paddr, str, length)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    //读取数字
    uint32_t num = 0;
    paddr = (uint32_t*)(SRAM_ADDR_HALF);
    if (HAL_OK == HAL_SRAM_Read_32b(&hsram3, paddr, &num, 1)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    return status;
}

 然后在主函数中输入测试代码,测试是否正确写入、读出:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

进入调试,跑了上半部分,status为HAL_OK,说明成功写入字符串:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

 跑完下半部分,status为HAL_OK,说明成功写入随机数num:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接下来是读出调试;重新读出SRAM开始出的字符,发现前面部分与刚刚写入的字符串一模一样,且status仍为HAL_OK,表示成功写入,读出字符串:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接着重新读出SRAM中间部分的一个32位数字,发现status为HAL_OK,说明读取成功,并且能看到num中的数字与刚刚写入的随机数一模一样,表示成功写入、读出数字:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

除此之外,因为这个扩展RAM本质上还是存储器,所以还可以不使用HAL库函数,直接使用指针读取指定地址的内容;STM32是32位机器,最大能够管理的地址空间为2e32 = 4GB,只要在0x0000 0000h~0xFFFF FFFFh中实际存在的地址,STM32都能访问;下面代码是通过指针直接访问对应地址中的内容:

/*
 * 用指针写入数据
 * */
void SRAM_WriteByPointer() {
    uint16_t num = 100;
    uint16_t *paddr_16b = (uint16_t*)(SRAM_ADDR_BEGIN); //uint16_t类型的指针
    for (int i = 0; i < 5; ++i) {
        num += 10;
        *paddr_16b = num;   //指定地址写入数据
        paddr_16b++;    //每次自增2B
    }
}

/*
 * 用指针读出数据
 * */
void SRAM_ReadByPointer() {
    uint16_t num[5] = {0};
    uint16_t *paddr_16b = (uint16_t*)(SRAM_ADDR_BEGIN); //uint16_t类型的指针
    for (int i = 0; i < 5; ++i) {
        num[i] = *paddr_16b;
        paddr_16b++;
    }
}

通过调试可以发现,用指针来读写数据也无误:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

PS.注意,在使用HAL函数读写外部SRAM数据时,传递的目的地址必须是uint32_t类型的指针;而在使用指针直接访问SRAM时,指针的类型需要与实际访问的数据类型一致,比如说要读一个16位的数据,就要指定读取地址为一个uint16_t的指针(因为指针只是一个数,指针的类型就是表示该指针所指向的地址中的数据的类型)


(四)DMA方式读写

前面说了,要用FSMC的DMA需要去System Core的DMA里面手动创建,并且在代码里要手动LINK DMA;为了使用DMA,重新打开项目中的cubemx,如下:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

然后进入DMA设置页面,在MenToMem栏新建一个DMA流,发现DMA2中出现了一个同样的DMA流,这是因为只有DMA2控制器支持mem到mem的传输,DMA1不支持;设置属性如下:

  • DMA的工作模式只能设置为Normal模式,没有Circular模式;
  • DMA流自动使用FIFO(DMA流队列),且不能关闭,Burst Size保持默认Single即可;
  • 源存储器和目标存储器的数据宽度设置为Word,这是因为HAL_SRAM_Write_DMA()和HAL_SRAM_Read_DMA()函数只支持uint32_t类型的数据buffer;
  • 源存储器和目标存储器都应开启地址自增;

配置如下图所示: 

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

此外,还要在NVIC中开启该DMA流的中断,否则系统不会调用中断回调函数;然后生成代码即可;

首先添加几个定义,主要是定义需要用得的宏、变量;如下:

#define     COUNT   5           //缓冲区数据个数
uint32_t    txbuf[COUNT];       //DMA发送缓冲区
uint32_t    rxbuf[COUNT];       //DMA接收缓冲区
bool        direction = true;   //DMA传输方向:ture表示MCU向外部SRAM传,false则相反
bool        is_busy = false;    //DMA状态:true表示正忙,false表示idle

还有,在主函数初始化FSMC后,需要加上LINK,将DMA流对象连接到SRAM对象:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接下来,封装一下DMA数据读、写函数;如下:

/*
 * DMA发送函数
 * */
HAL_StatusTypeDef SRAM_WriteDMA() {
    HAL_StatusTypeDef status = HAL_OK;
    uint32_t val = 1000;
    //准备数据
    for (int i = 0; i < COUNT; ++i) {
        txbuf[i] = val;
        val += 100;
    }

    direction = true;
    dma_is_busy = true; //指示传输方向以及状态

    uint32_t *paddr = (uint32_t*)(SRAM_ADDR_BEGIN);
    if (HAL_OK == HAL_SRAM_Write_DMA(&hsram3, paddr, txbuf, COUNT)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    return status;
}

/*
 * DMA读取函数
 * */
HAL_StatusTypeDef SRAM_ReadDMA() {
    HAL_StatusTypeDef status = HAL_OK;
    uint32_t *paddr = (uint32_t*)(SRAM_ADDR_BEGIN);

    direction = false;
    dma_is_busy = true; //指示传输方向以及状态

    if (HAL_OK == HAL_SRAM_Read_DMA(&hsram3, paddr, rxbuf, COUNT)) {
        HAL_Delay(1);
    } else {
        status = HAL_ERROR;
    }

    return status;
}

/*
 * DMA传输结束中断回调函数
 * */
volatile uint8_t test = 0;
/*
* 测试变量 test
* 当MCU向外部SRAM写入成功时,该变量赋值为1
* 当MCU从外部SRAM读取成功时,该变量赋值为2
* */
void HAL_SRAM_DMA_XferCpltCallback(DMA_HandleTypeDef *hdma)
{
    if (direction) {    //方向为
        test = 1;
    } else {
        test = 2;
    }

    dma_is_busy = false;    //表示dma传输结束
}

在主函数中调用这两个函数,打个断点,然后进入快乐的debug环节;一开始发现几个全局变量不在watch窗口中,首先加入窗口:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接着检查一下txbuf和rxbuf中的值,看是否正确:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

然后在中断回调函数中打一个断点,看发送完成是否会进入回调;并注意发送数据前两个标志以及test变量的值:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

接着走一步,发现发送成功了,进入了HAL_Delay()函数,然后再走一步,果然进入了回调函数;如下:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

说明理论与实际情况一致,DMA发送成功。

接下来进入接收环节;一样的调试方法,最后发现依然进入回调函数,test被赋为2,此时查看rxbuf的值,可以看到与刚刚发送的5个数据一模一样,说明DMA接收也成功;如下:

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

fsmc接口扩展sram和驱动lcd,STM32复习笔记,stm32,笔记,嵌入式硬件

大功告成!

工程链接:https://pan.baidu.com/s/18AJoG1epClGWzjHQkf6SRQ 
提取码:0xFF

完~


以上均为个人学习心得,如有错误,请不吝赐教~

THE END文章来源地址https://www.toymoban.com/news/detail-772044.html

到了这里,关于STM32复习笔记(五):FSMC连接外部SRAM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32CubeMX教程22 FSMC - 8080并行接口TFT-LCD驱动

    开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) 野火DAP仿真器 keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 XCOM V2.6串口助手 使用STM32CubeMX软件配置STM32F407开发板 FSMC接口驱动8080并行接口TFT-LCD显示 ,具体为使用FSMC Bank 1-NOR/PSRAM4,片选信号为NE4(PG12),TFT-L

    2024年01月23日
    浏览(52)
  • STM32CubeMX学习笔记(48)——USB接口使用(MSC基于外部Flash模拟U盘)

    USB(Universal Serial BUS)通用串行总线 ,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。USB 接口支持设备的即插即用和热插拔功能。USB 是在 1994 年底由英特尔、康柏、IBM、Microsoft 等多家公司联合提出的。 USB 发展到现在已经有 US

    2024年01月16日
    浏览(50)
  • 【STM32】STM32学习笔记-EXTI外部中断(11)

    中断 :在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行 中断优先级 :当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加

    2024年02月04日
    浏览(40)
  • STM32的FSMC

    大容量,且引脚数在 100 脚以上的 STM32F103 芯片都带有 FSMC 接口。 FSMC,即灵活的静态存储控制器flexible static memory controller。 STM32 的 FSMC 接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。 何为PSRAM FSMC就是一个MCU与外部存储器(SRAM,FLASH等)读写数据的一个接口 NE[4-1] 片选

    2024年02月02日
    浏览(57)
  • STM32 FSMC机制

    FSMC介绍 FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储器卡连接,STM32的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存储器。FSMC的框图如下图所示: FSMC驱动LCD原理  FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0~A25)、数据线(如D0~D15)

    2024年02月08日
    浏览(45)
  • 【STM32学习笔记】(13)——外部中断详解

            EXTI(External interrupt/event controller)—外部中断/事件控制器 ,管理了控制器的 20 个中断/事件线。每个输入线可以独立地配置输入类型(脉冲 或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置

    2024年02月05日
    浏览(41)
  • 使用STM32的SPI接口实现与外部传感器的数据交互

    一、引言 外部传感器是嵌入式系统中常用的外设,用于检测环境参数、采集数据等。通过STM32微控制器的SPI接口,可以与外部传感器进行数据交互,从而实现数据的采集和控制。 本文将介绍如何使用STM32的SPI接口实现与外部传感器的数据交互,并提供示例代码。 ✅作者简介:

    2024年01月23日
    浏览(42)
  • stm32——hal库学习笔记(外部中断)

    一、什么是中断?(了解) 打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断 中断的作用和意义 中断的意义:高效处理紧急程序,不会一直占用CPU资源 STM32 GPIO外部中断简图 二、NVIC(熟悉) 2.1,NVIC基本概念 2.2,NVIC相关寄存器介绍

    2024年02月22日
    浏览(55)
  • stm32学习笔记-5EXIT外部中断

    注:笔记主要参考B站 江科大自化协 教学视频“STM32入门教程-2023持续更新中”。 注:工程及代码文件放在了本人的Github仓库。 图5-1 中断及中断嵌套示意图 中断 是指在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理

    2024年02月09日
    浏览(36)
  • 【STM32】STM32学习笔记-定时器定时中断 定时器外部时钟(14)

    1.1 TIM_InternalClockConfig 1.2 TIM_TimeBaseInit 1.3 TIM_TimeBaseInitTypeDef 1.4 TIM_ClearFlag 1.5 TIM_ITConfig 1.6 TIM_Cmd 1.7 中断服务函数 参考程序 1.8 TIM_ETRClockMode2Config timer.h timer.c main.c timer.h timer.c main.c 09-定时器定时中断.rar 10-定时器外部时钟.rar 参考: 【STM32】江科大STM32学习笔记汇总

    2024年02月03日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包