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;如下图:

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

根据开发板的原理图(如下图)可知,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存储空间的按字节访问。


(二)CUBEMX配置

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

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

接下来进行参数配置;首先是控制参数: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;

如下图所示:

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

 

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


(三)轮询方式读写

首先加入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;
}

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

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

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

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

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

除此之外,因为这个扩展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++;}
}

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

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


(四)DMA方式读写

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

然后进入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;
  • 源存储器和目标存储器都应开启地址自增;

配置如下图所示: 

此外,还要在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对象:

接下来,封装一下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窗口中,首先加入窗口:

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

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

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

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

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

大功告成!

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

完~


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

THE END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/94801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

侯捷 C++ STL标准库和泛型编程 —— 9 STL周围

最后一篇&#xff0c;完结辽&#xff01;&#x1f60b; 9 STL周围 9.1 万用Hash Function Hash Function的常规写法&#xff1a;其中 hash_val 就是万用Hash Function class CustumerHash { public:size_t operator()(const Customer& c) const{ return hash_val(c.fna…

BASH shell脚本篇5——文件处理

这篇文章介绍下BASH shell中的文件处理。之前有介绍过shell的其它命令&#xff0c;请参考&#xff1a; BASH shell脚本篇1——基本命令 BASH shell脚本篇2——条件命令 BASH shell脚本篇3——字符串处理 BASH shell脚本篇4——函数 在Bash Shell脚本中&#xff0c;可以使用…

【C++】String -- 详解

⚪C语言中的字符串 C 语言中&#xff0c;字符串是以 \0 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C 标准库中提供了一些 str 系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合 OOP 的思想&#xff0c;而且底层空间需要用户自己…

Is This The Intelligent Model(这是智能模型吗)

Is This The Intelligent Model 这是智能模型吗 Ruoqi Sun Academy of Military Science Defense Innovation Institute, Beijing, 100091, China E-mail: ruoqisun7163.com The exposed models are called artificial intelligent models[1-3]. These models rely on knowled…

讲讲项目里的仪表盘编辑器(二)

应用场景 正常来说&#xff0c;编辑器应用场景应该包括&#xff1a; 编辑器-预览 编辑器 最终运行时 怎么去设计 上一篇推文&#xff0c;我们已经大概了解了编辑器场景。接下来&#xff0c;我们来看预览时的设计 编辑器-预览 点击预览按钮&#xff0c;执行以…

[Unity][VR]Oculus透视开发图文教程1-Passthrough应用XR项目设置

Oculus现在已向开发者公布了如何使用自己的设备Camera,本系列课程就来手把手地告诉你如何在Unity中使用这个特性。 第一步,既然用的是Quest的特性,那就需要先引入Quest的Unity开发SDK。并且完成基本的VR开发项目设置。 新建Unity项目后,在编辑器界面先点击Window,打开资…

虹科分享 | 为工业机器人解绑,IO-Link wireless无线通讯技术可实现更加轻量灵活的机器人协作

背景 机器人是一种能够半自主或全自主工作的智能机器。中国电子学会组织发布的《中国机器人产业发展报告&#xff08;2022年&#xff09;显示&#xff0c;近些年&#xff0c;我国机器人市场规模持续快速增长&#xff0c;“机器人”应用不断拓展深入&#xff0c;预计五年年均增…

YOLOv5、YOLOv8改进:RepVGG结构

1.简介 论文参考&#xff1a;最新RepVGG结构: Paper 我们所说的“VGG式”指的是&#xff1a; 没有任何分支结构。即通常所说的plain或feed-forward架构。 仅使用3x3卷积。 仅使用ReLU作为激活函数。 主要创新点为结构重参数化。在训练时&#xff0c;网络的结构是多分支进…

Visopsys 0.92 发布

Visopsys 是一个 PC 机的操作系统&#xff0c;系统小型、快速而且开源。有着丰富的图形界面、抢先式多任务机制以及支持虚拟内存。Visopsys 视图兼容很多操作系统&#xff0c;但并不是他们的克隆版本。Visopsys 0.92 现已发布&#xff0c;此维护版本引入了多任务处理程序、文件…

二叉树题目:路径总和 II

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;路径总和 II 出处&#xff1a;113. 路径总和 II 难度 4 级 题目描述 要求 给你二叉树的根结点 root \tex…

我的第一个react.js 的router工程

react.js 开发的时候&#xff0c;都是针对一个页面的&#xff0c;多个页面就要用Router了&#xff0c;本文介绍我在vscode 下的第一个router 工程。 我在学习react.js 前端开发&#xff0c;学到router 路由的时候有点犯难了。经过1-2天的努力&#xff0c;终于完成了第一个工程…

使用Pytorch构建神经网络

构建神经网络的典型流程 定义一个拥有可学习参数的神经网络遍历训练数据集处理输入数据使其流经神经网络计算损失值将网络参数的梯度进行反向传播以一定的规则更新网络的权重 我们首先定义一个Pytorch实现的神经网络: # 导入若干工具包 import torch import torch.nn as nn …

亲,您的假期余额已经严重不足了......

引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 转眼八天长假已经接近尾声了&#xff0c;今天来总结一下大家的假期&#xff0c;聊一聊假期关于学习的看法&#xff0c;并预估一下大家节后大家上班时的样子。 1.放假前一天 即将迎来八天…

基于Web安全的Python编程(1)

目录 一、http协议基础知识介绍 1、http协议分类 2、请求方法 3、什么是URL 4、请求头 5、响应状态码 二、常用Python库、函数、操作 三、http常用请求方法 1、不带参请求 2、带参数请求&#xff08;get和post存在细微区别&#xff09; 四、http响应属性获取 1、获取…

计算机网络(六):应用层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 应用层概述 应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 早期基于文本的应用 (电子邮件、远程登…

分布式架构篇

1、微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个服务运行在自己的进程中&#xff0c;并使用轻量级机制通信&#xff0c;通常是 HTTP API。这些服务围绕业务能力来构建&#xff0c;并通过完全自动化部署机制来独立部署。这些…

Spring 原理

它是一个全面的、企业应用开发一站式的解决方案&#xff0c;贯穿表现层、业务层、持久层。但是 Spring仍然可以和其他的框架无缝整合。 1 Spring 特点 轻量级控制反转面向切面容器框架集合 2 Spring 核心组件 3 Spring 常用模块 4 Spring 主要包 5 Spring 常用注解 bean…

第十七章:Java连接数据库jdbc(java和myql数据库连接)

1.进入命令行&#xff1a;输入cmd&#xff0c;以管理员身份运行 windowsr 2.登录mysql 3.创建库和表 4.使用Java命令查询数据库操作 添加包 导入包的快捷键 选择第四个 找到包的位置 导入成功 创建java项目 二&#xff1a;连接数据库&#xff1a; 第一步&#xff1a;注册驱动…

设计模式 - 策略模式

目录 一. 前言 二. 实现 一. 前言 策略模式 (Strategy Pattern) 是指对一系列的算法定义&#xff0c;并将每一个算法封装起来&#xff0c;而且使它们还可以相互替换。此模式让算法的变化独立于使用算法的客户。 与状态模式的比较 状态模式的类图和策略模式类似&#xff0c;并…

VUE3照本宣科——内置指令与自定义指令及插槽

VUE3照本宣科——内置指令与自定义指令及插槽 前言一、内置指令1.v-text2.v-html3.v-show4.v-if5.v-else6.v-else-if7.v-for8.v-on9.v-bind10.v-model11.v-slot12.v-pre13.v-once14.v-memo15.v-cloak 二、自定义指令三、插槽1.v-slot2.useSlots3.defineSlots() 前言 &#x1f…