细说STM32F407单片机以轮询方式读写外部SRAM的方法

目录

一、实例的功能

二、工程配置

1、KEYLED 

2、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator

3、FSMC

(1) 模式设置

(2) Bank 1子区3参数设置

1) NOR/PSRAM control组,子区控制参数

2) NOR/PSRAM timing组,读写操作时序参数

3) DMA 

三、软件设计 

1、KEYLED

2、fsmc.h、fsmc.c

3、main.h

4、main.c

四、下载运行 


        本文介绍STM32F407单片机以轮询方式读写外部SRAM IS61LV25616AL的方法。继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。

        关于  IS61LV25616AL,可以详见参考文章1。

        参考文章1:细说STM32F407单片机FSMC连接外部SRAM的方法及HAL驱动-CSDN博客  https://wenchm.blog.csdn.net/article/details/144930868

        参考文章2:细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV_stm32f407 spiflash驱动程序-CSDN博客  https://wenchm.blog.csdn.net/article/details/144587209

一、实例的功能

        连接外部SRAM的FSMC接口设置以及轮询方式读写外部SRAM的方法,并用HAL函数读写数据和直接用指针读写数据。本示例需要引用参考文章2的KEYLED的4个按键和对应的LED。

        本示例还用到RNG,在组件面板Security分组里有RNG模块,启用RNG即可。RNG需要用到48MHz时钟。

[S2]KeyUp   = Write by HAL functions   LED1 ON
[S3]KeyDown = Read by HAL functions    LED2 ON
[S4]KeyLeft = Write by pointer         LED3 ON
[S5]KeyRight= Read by pointer          LED4 ON

二、工程配置

1、KEYLED 

        本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章1相同。

2、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator

        外部时钟,25MHz,设置到HCLK=168MHz,PCLK1=42MHz,PCLK2=84MHz,其它,都设置成168MHz。

        DEBUG,选择serial wire,USART6、NVIC、CodeGenerator的设置同参考文章1。        

3、FSMC

        开发板上使用Bank 1子区3连接外部SRAM,所以对NOR Flash/PSRAM/SRAM/ROM/LCD 3进行配置。

(1) 模式设置

  • Chip Select设置为NE3,也就是使用FSMC_NE3作为SRAM芯片的片选信号。
  • Memory type设置为SRAM。
  • Address设置为19bits,因为用到了FSMC_A0至FSMC_A18共19根地址线。
  • Data设置为16bits,因为使用了16位数据线。
  • Wait设置为Disable。Wait是PSRAM芯片发给FSMC的等待输入信号,本示例电路中IS62WV51216芯片没有这个输出信号。
  • Byte enable需要勾选,表示允许字节访问。允许字节访问时,将通过芯片的UB和LB信号控制访问高位字节和低位字节。

        这样设置后,在引脚视图上将自动标出使用的各FSMC引脚。其中,FSMC_D0至FSMC_D15是LCD和SRAM共用的16位数据线,FSMC_NOE和FSMC_NWE是共用的控制信号线。

        FSMC_NE3用于SRAM片选。FSMC_NBL1和FSMC_NBL0是SRAM的高低字节选择信号。        

        FSMC_A0至FSMC_A18共19根地址线的GPIO引脚分配,自动分配的GPIO引脚与实际电路的引脚是一致的,所以无须更改。

  /** FSMC GPIO ConfigurationPF0   ------> FSMC_A0PF1   ------> FSMC_A1PF2   ------> FSMC_A2PF3   ------> FSMC_A3PF4   ------> FSMC_A4PF5   ------> FSMC_A5PF12   ------> FSMC_A6PF13   ------> FSMC_A7PF14   ------> FSMC_A8PF15   ------> FSMC_A9PG0   ------> FSMC_A10PG1   ------> FSMC_A11PE7   ------> FSMC_D4PE8   ------> FSMC_D5PE9   ------> FSMC_D6PE10   ------> FSMC_D7PE11   ------> FSMC_D8PE12   ------> FSMC_D9PE13   ------> FSMC_D10PE14   ------> FSMC_D11PE15   ------> FSMC_D12PD8   ------> FSMC_D13PD9   ------> FSMC_D14PD10   ------> FSMC_D15PD11   ------> FSMC_A16PD12   ------> FSMC_A17PD13   ------> FSMC_A18PD14   ------> FSMC_D0PD15   ------> FSMC_D1PG2   ------> FSMC_A12PG3   ------> FSMC_A13PG4   ------> FSMC_A14PG5   ------> FSMC_A15PD0   ------> FSMC_D2PD1   ------> FSMC_D3PD4   ------> FSMC_NOEPD5   ------> FSMC_NWEPG10   ------> FSMC_NE3PE0   ------> FSMC_NBL0PE1   ------> FSMC_NBL1*//* GPIO_InitStruct */

(2) Bank 1子区3参数设置

        在模式设置中启用Bank 1子区3之后,在参数设置部分会出现NOR/PSRAM3参数设置页面,在这个页面设置SRAM的控制和时序参数。

1) NOR/PSRAM control组,子区控制参数
  • Memory type只能选择SRAM,因为在模式配置部分已设置为SRAM。
  • Bank只能选择为Bank 1 NOR/PSRAM3,是与模式设置部分对应的。
  • Write operation设置为Enabled,表示允许写操作。
  • Extended mode设置为Disabled。FSMC自动使用模式A对SRAM进行操作。SRAM的读操作和写操作的速度基本相同,所以读写操作可以使用相同的时序参数,无须使用扩展模式单独设置读时序和写时序。
2) NOR/PSRAM timing组,读写操作时序参数
  • Address setup time in HCLK clock cycles,即地址建立时间参数ADDSET,设置范围为0~15,设置为0即可。
  • Data setup time in HCLK clock cycles,即数据建立时间参数DATAST,设置范围为1~255,设置为8。
  • Bus turn around time in HCLK clock cycles,总线翻转时间,设置范围为0~15,设置为0即可。
3) DMA 

        FSMC参数设置部分没有DMA设置页面,但是SRAM的HAL驱动程序中有DMA方式进行数据读写的函数,其DMA方式的设置与其他外设稍有不同的。 

三、软件设计 

1、KEYLED

        本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章1相同。

2、fsmc.h、fsmc.c

         IDE自动生成。

3、main.h

/* USER CODE BEGIN Private defines */
void SRAM_WriteByFunc();
void SRAM_ReadByFunc();
void SRAM_WriteByPointer();
void SRAM_ReadByPointer();
/* USER CODE END Private defines */

4、main.c

/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PD */
// SRAM的容量不同,该处的定义就不同,更改SRAM就得修改此处的定义
#define SRAM_ADDR_BEGIN	 0x68000000UL //Bank1子区3的SRAM起始地址
#define SRAM_ADDR_HALF	 0x6801FFFFUL //SRAM容量256K*16bit,中间地址128K字节
#define SRAM_ADDR_END	 0x6803FFFFUL //SRAM容量256K*16bit,结束地址512K字节
//#define SRAM_ADDR_HALF 0x68080000UL //SRAM容量512K*16bit,中间地址512K字节
//#define SRAM_ADDR_END	 0x680FFFFFUL //SRAM容量512K*16bit,结束地址1024K字节
/* USER CODE END PD */
/* USER CODE BEGIN 2 */printf("Demo19_1_FSMC: External SRAM\r\n");printf("Read/Write SRAM by polling\r\n");//显示菜单printf("[S2]KeyUp   = Write by HAL functions.\r\n");printf("[S3]KeyDown = Read by HAL functions.\r\n");printf("[S4]KeyLeft = Write by pointer.\r\n");printf("[S5]KeyRight= Read by pointer.\r\n");// MCU output low level LED light is onLED1_OFF();LED2_OFF();LED3_OFF();LED4_OFF();/* USER CODE END 2 */
/* USER CODE BEGIN 3 */KEYS curKey=ScanPressedKey(KEY_WAIT_ALWAYS);switch(curKey){case KEY_UP:{SRAM_WriteByFunc();		//Write by HAL functionsLED1_ON();LED2_OFF();LED3_OFF();LED4_OFF();}break;case KEY_DOWN:{SRAM_ReadByFunc();		//Read by HAL functionsLED1_OFF();LED2_ON();LED3_OFF();LED4_OFF();}break;case KEY_LEFT:{SRAM_WriteByPointer();	//Write by pointerLED1_OFF();LED2_OFF();LED3_ON();LED4_OFF();break;}case KEY_RIGHT:{SRAM_ReadByPointer();	//Read by pointerLED1_OFF();LED2_OFF();LED3_OFF();LED4_ON();}default:{LED1_OFF();LED2_OFF();LED3_OFF();LED4_OFF();}}printf("** Reselect menu or reset **\r\n");HAL_Delay(500);				//延时,消除按键抖动}/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
/* 用HAL函数写入数据 */
void SRAM_WriteByFunc()
{
//1. 写入字符串uint32_t *pAddr = (uint32_t *)(SRAM_ADDR_BEGIN);	//给指针赋值uint8_t strIn[] = "Test FSMC";uint16_t dataLen = sizeof(strIn); //数据长度,字节数,包括最后的结束符'\0'if (HAL_SRAM_Write_8b(&hsram3,pAddr,strIn,dataLen) == HAL_OK){printf("Write string at %p = %s\r\n",pAddr,strIn);}//2. 写入1个随机数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 %p = %lx\r\n",pAddr,num);//十六进制显示,显示前缀0x}
}/* 用HAL函数读取数据 */
void SRAM_ReadByFunc()
{
//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 %p = %s\r\n",pAddr,strOut);//显示自动以'\0'结束}//2. 读取1个uint32_t类型的数uint32_t num = 0;pAddr=(uint32_t *)(SRAM_ADDR_BEGIN+256);	//指针重新赋值指向1个新的地址if (HAL_SRAM_Read_32b(&hsram3, pAddr, &num, 1) == HAL_OK){printf("Read 32b number at %p = %lx\r\n",pAddr,num);}
}/* 直接通过指针写数据 */
void SRAM_WriteByPointer()
{printf("Write five uint16_t numbers.\r\n");printf("start from 0x6801 FFFF.\r\n");uint16_t num = 100;uint16_t *pAddr_16b = (uint16_t *)(SRAM_ADDR_HALF);	//uint16_t 类型数据指针for(uint8_t i=0;i<5;i++) //连续写入5个16位整数{num += 3;*pAddr_16b = num;	//直接向指针所指的地址写入数据pAddr_16b ++;		//++1次,地址+2, 因为是uint16_t类型printf("The data of Add %p = %d\r\n",pAddr_16b,num);}
}/* 直接通过指针读取数据 */
void SRAM_ReadByPointer()
{printf("Read five uint16_t numbers.\r\n");printf("start from 0x6801 FFFF.\r\n");uint16_t num = 0;uint16_t *pAddr_16b = (uint16_t *)(SRAM_ADDR_HALF); //uint16_t 类型数据指针for(uint8_t i=0; i<5; i++){num = *pAddr_16b;	//直接从指针所指的地址读数pAddr_16b ++;		//++1次,地址+2, 因为是uint16_t类型printf("The data of Add %p = %d\r\n",pAddr_16b,num);}
}//串口打印
int __io_putchar(int ch)
{HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);return ch;
}
/* USER CODE END 4 */

        使用HAL函数读写外部SRAM的数据,就是使用参考文章2中的函数读写SRAM的数据。给这些函数传递的SRAM目标地址必须是uint32_t类型指针,如下所示:

uint32_t *pAddr = (uint32_t*)(SRAM_ADDR_BEGIN);	//给指针赋值
pAddr = (uint32_t*)(SRAM_ADDR_BEGIN+256);		//指针重新赋值,指向新的地址

        而在使用指针直接访问SRAM时,指针类型需要与实际访问的数据类型一致,例如,访问的数据是uint16_t类型,就应该定义如下的指针:

uint16_t *pAddr_16b = (uint16_t*)(SRAM_ADDR_HALE);	//uint16_t类型数据指针

四、下载运行 

        测试过程中发现一个有趣的现象,当num的值增加到超过1024以后,num值会被置0,余数仍然有效,比如某一时刻,num=1023,++3,新的num=2。然后从2开始自增下去直到再次超过1024。这可能是一个BUG,也可能是哪一处设置的不对,请感兴趣的网友,把解决办法贴上来。

        还发现一个BUG,写进SRAM的数据再次读出来,不一定一样。

 

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

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

相关文章

Eclipse配置Tomcat服务器(最全图文详解)

前言&#xff1a; 本章使用图文讲解如何在Eclipse开发工具中配置Tomcat服务器、如何创建和启动JavaWeb工程&#xff0c;欢迎童鞋们互相交流。觉得不错可以三连订阅喔。 目标&#xff1a; 一、配置Tomcat服务器 1. 切换Eclipse视图 2. 打开菜单 3. 找到服务选项 4. 选择…

uniapp vue2版本如何设置i18n

如何设置i18n在该软件设置过语言的情况下优先选择所设置语言&#xff0c;在没有设置的情况下&#xff0c;获取本系统默认语言就&#xff0c;将系统默认语言设置为当前选择语言。 1、下载依赖&#xff1a; npm install vue-i18n --save 2、创建相关文件&#xff08;在最外层&…

QPS和TPS 的区别是什么?QPS 大了会有什么问题,怎么解决?

QPS 和 TPS 的区别是什么&#xff1f;QPS 大了会有什么问题&#xff0c;怎么解决&#xff1f; QPS&#xff08;Queries Per Second&#xff09;和 TPS&#xff08;Transactions Per Second&#xff09;都是衡量系统性能的重要指标&#xff0c;尤其是在 Web 服务、数据库和分布…

《Spring Framework实战》4:Spring Framework 文档

欢迎观看《Spring Framework实战》视频教程 概述 历史&#xff0c; 设计理念&#xff0c; 反馈&#xff0c; 开始。 核心技术 IoC 容器、事件、资源、i18n、 验证、数据绑定、类型转换、SpEL、AOP、AOT。 测试 Mock 对象、TestContext 框架、 Spring MVC 测试&#xff0c;…

经典多模态模型CLIP - 直观且详尽的解释

对比语言-图像预训练&#xff08;CLIP&#xff09;&#xff0c;这是一种创新的多模态建模策略&#xff0c;能够创建视觉和语言的联合表示。CLIP 的效果非常出色&#xff0c;可以用于构建高度特定且性能卓越的分类器&#xff0c;而无需任何训练数据。本文将深入探讨其理论基础&a…

vulnhub靶场【DC系列】之7

前言 靶机&#xff1a;DC-7&#xff0c;IP地址为192.168.10.13 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.2 都采用VMWare&#xff0c;网卡为桥接模式 对于文章中涉及到的靶场以及工具&#xff0c;我放置在网盘中&#xff0c;链接&#xff1a;https://pan.quark…

内网基础-防火墙-隧道技术

内网对抗-网络通讯篇&防火墙组策略&入站和出站规则&单层双层&C2正反向上线 关闭第一个防火墙&#xff1a; 第一个上线就走反向或者正向 第二个上线走反向&#xff08;第二个防火墙阻止入站&#xff09; 关闭第二个防火墙&#xff1a; 第一个上线就走反向&a…

BGP的local_preference本地优先级属性

一、BGP的local preference属性简介 1、local preference公认任意属性 当一条BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由属性进行比较&#xff0c;从而筛选出最佳到达目标网络的通达路径。本地优先属性&#xff0c;只在IBGP对等体之间…

从零手写线性回归模型:PyTorch 实现深度学习入门教程

系列文章目录 01-PyTorch新手必看&#xff1a;张量是什么&#xff1f;5 分钟教你快速创建张量&#xff01; 02-张量运算真简单&#xff01;PyTorch 数值计算操作完全指南 03-Numpy 还是 PyTorch&#xff1f;张量与 Numpy 的神奇转换技巧 04-揭秘数据处理神器&#xff1a;PyTor…

如何让用户在网页中填写PDF表格?

在网页中让用户直接填写PDF表格&#xff0c;可以大大简化填写、打印、扫描和提交表单的流程。通过使用复选框、按钮和列表等交互元素&#xff0c;PDF表格不仅让填写过程更高效&#xff0c;还能方便地在电脑或移动设备上访问和提交数据。 以下是在浏览器中显示可填写PDF表单的四…

【和春笋一起学C++】文本输入与读取(二)

目录 使用cin.eof()或cin.fail()检测EOF 使用cin.get(ch)的返回结果检测EOF 使用cin.get()的返回结果检测EOF 上篇文章《文本输入与读取》学习了遇到某个字符时停止读取&#xff0c;那如果要读取键盘输入的所有字符该怎么做呢。答案是检测文件尾&#xff08;EOF&#xff09;…

Ungoogled Chromium127 编译指南 MacOS篇(八)- 开始编译

1. 引言 完成了所有依赖包的安装后&#xff0c;我们终于来到了最关键的编译阶段。在开始编译之前&#xff0c;有一些重要的配置信息需要了解。本文将指导您完成整个编译过程。 2. 签名相关说明 虽然在我们的测试编译中不需要进行签名操作&#xff0c;但了解官方的签名要求仍…

Jenkins内修改allure报告名称

背景&#xff1a; 最近使用Jenkins搭建自动化测试环境时&#xff0c;使用Jenkins的allure插件生成的报告&#xff0c;一直显示默认ALLURE REPORT&#xff0c;想自定义成与项目关联的名称&#xff0c;如图所示&#xff0c;很明显自定义名称显得高大上些 本地修改方法&am…

关于物联网的基础知识(二)——物联网体系结构分层

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网的基础知识&#xff08;二&a…

应急响应——Windows / Linux 排查笔记

一、基本思路流程 收集信息&#xff1a;收集客户信息和中毒主机信息&#xff0c;包括样本判断类型&#xff1a;判断是否是安全事件&#xff0c;何种安全事件&#xff0c;勒索、挖矿、断网、DoS 等等抑制范围&#xff1a;隔离使受害⾯不继续扩⼤深入分析&#xff1a;日志分析、…

Git撤销指定commit并更新远端仓库

Git撤销指定commit并更新远端仓库 一、撤销指定commit 1.首先执行git log 命令&#xff0c;查看git历史提交以及commit信息&#xff1a; 由于需要脱敏&#xff0c;所以截图可能看得马赛克比较多&#xff0c;需要关注的就是上面的commit后跟的id&#xff0c;以及HEAD当前指定…

C 语言奇幻之旅 - 第16篇:C 语言项目实战

目录 引言1. 项目规划1.1 需求分析与设计1.1.1 项目目标1.1.2 功能需求1.1.3 技术实现方案 2. 代码实现2.1 模块化编程2.1.1 学生信息模块2.1.2 成绩管理模块 2.2 调试与测试2.2.1 调试2.2.2 测试2.2.4 测试结果 3. 项目总结3.1 代码优化与重构3.1.1 代码优化3.1.2 代码重构 3.…

2025最新解决方案:新买的mac鼠标和这个触控板反向

solution1 &#xff1a;1.打开设置&#xff0c;搜索 触控 点击 自然滚动 ----->解决的是 触控板 但是还是解决不了鼠标反向的问题 solution1 ultra&#xff1a; 下载一个免费 且纯净的 软件 Scroll Reverser for macOS 这是给出的链接&#xff0c;非常简单&#xff0c;…

黄仁勋CES 2025演讲重点内容

黄仁勋CES 2025演讲重点内容 硬件产品发布 GeForce RTX 50系列GPU&#xff1a; 架构与性能提升&#xff1a;正式发布的新一代GeForce RTX 50系列GPU采用英伟达旗舰的Blackwell架构&#xff0c;这是自25年前引入可编程着色技术以来计算机图形领域最重大的创新。该系列显卡在图形…

机器学习免费使用的数据集及网站链接

机器学习领域存在许多可以免费使用的数据集&#xff0c;这些数据集来自于学习、研究、比赛等目的。 一、综合性数据集平台 1.Kaggle 网址&#xff1a;Kaggle 数据集https://www.kaggle.com/datasets Kaggle是一个数据科学竞赛和社区平台&#xff0c;提供了大量的数据集供用…