细说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的数据再次读出来,不一定一样。

相关文章:
细说STM32F407单片机以轮询方式读写外部SRAM的方法
目录 一、实例的功能 二、工程配置 1、KEYLED 2、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator 3、FSMC (1) 模式设置 (2) Bank 1子区3参数设置 1) NOR/PSRAM control组,子区控制参数 2) NOR/PSRAM timi…...
【3】安装cyclictest和iperf
cyclictest 安装比较简单,我是直接使用命令行: apt-get install rt-tests 随后,运行 sudo cyclictest 但是这个程序会一直运行,直到你手动中断程序,而且每秒生成一行输出也很烦人,所以可以选择把结果…...
C语言将点分十进制的IP字符串转成4个整数
最近在做lldp的snmp返回值时需要做这样的转换处理:C语言将点分十进制的IP字符串转成4个整数。 这里用两种方式: sscanf格式化处理用 inet_aton函数将ip字符串转成32位的整形,然后再根据bit转成对应的4个整数。 man命令可以确认下sscanf和i…...
go语言学习 笔记 1(变量,语法,数据类型)
1,包管理 一个文件夹可以称为一个包 在一个包里面可以创建多个文件 包中可以创建包 同一个包内的同一级的包的名字要相同 如:包a中的包b.包b中的包得是同一个package,a中和包b同级的包名字也得是一个名字 必须要有一个main包,入口,就像是c必须有一个main函数 如果没有mai…...
无网络时自动切换备用网络环境
目录 背景目标为什么需要做自动网络切换网络切换手段 网络环境实现思路和代码部署脚本开机自动执行附录连接两个网络时的路由问题 背景 目标 学校实验室有两个网络环境,我电脑使用网线连接稳定但低速的网络A,使用WiFi连接高速但不稳定的网络B。因此&am…...
电脑32位和64位之区别(Difference between 32-Bit and 64 Bit Computers)
电脑32位和64位之区别 很多小伙伴还不知道电脑32位和64位是什么意思,今天小编就来普及一下。 32位和64位是指电脑处理器(CPU)和操作系统的架构,决定了电脑如何处理数据、存储信息、运行程序等。 32位和64位是指电脑系统中每个处…...
系统思考—结构影响行为
前段时间,我遇到了一位健康食品初创公司的创始人,产品质量毋庸置疑,但销量却始终打不开局面,资金链也日渐紧绷。他一脸困惑地问我:“我们已经尽力了,为什么结果还是不如人意?”经过深入交流&…...
【算法不挂科】算法期末考试【选择题专项练习】<多单元汇总>
前言 大家好吖,欢迎来到 YY 滴算法不挂科系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 下面是相关传送门 【算法不挂科】算法期末考试题库1(带解析)【选择题53道&填空题36道&算法填空题7道&a…...
2025.1.8(c++对c语言的扩充——堆区空间,引用,函数)
笔记 上一笔记接续(练习2的答案) 练习:要求在堆区连续申请5个int的大小空间用于存储5名学生的成绩,分别完成空间的申请、成绩的录入、升序排序、成绩输出函数以及空间释放函数,并在主程序中完成测试 要求使用new和d…...
如何将Yum源修改为本地挂载的ISO镜像
要将yum源修改为本地挂载的ISO镜像,您可以按照以下步骤进行操作。假设您使用的是CentOS或类似的基于Red Hat的Linux发行版,且已经将ISO镜像文件挂载到系统中。 步骤一:挂载ISO镜像 创建一个挂载点: 首先,您需要创建一个目录来作为ISO镜像的挂载点。例如: sudo mkdir /mnt…...
salesforce如何在系统里保存密码
在 Salesforce 中,保存密码或类似敏感信息时,不应以明文形式存储,而应采用安全的加密和存储机制。以下是一些最佳实践和实现方法: 1. 使用 Salesforce 提供的加密机制 Salesforce 提供了一些内置的加密工具,可以用来加…...
函数提升+上下文+内存清理及释放
文章目录 函数提升上下文函数释放拓展-垃圾回收机制垃圾回收之触发应用 函数提升上下文 函数提升(Hoisting) 概念:在JavaScript中,函数声明会被提升到当前作用域的顶部。这意味着可以在函数声明之前调用函数。例如: sa…...
计算机网络之---计算机网络的性能评估
计算机网络的性能评估是指通过各种标准和指标来衡量网络的工作效率和质量,进而对网络进行优化和改进的过程。评估的目标是确保网络能够满足预期的服务质量(QoS)和性能需求。常见的计算机网络性能评估指标包括带宽、延迟、吞吐量、丢包率等。 …...
Unity学习之UGUI进阶
一、事件监听接口 1、作用 用于实现类型长按、双击、拖拽等基础控件无法实现的功能 所有控件都能够添加更多的事件监听来处理对应的逻辑 2、事件监听接口类型 (1)常用事件接口 (2)不常用事件接口 3、使用事件监听接口 &#…...
深度学习领域创新黑马!频域特征融合新突破
最近,FreqFusion引起了广泛关注,这是一种创新的频率感知特征融合方法,可以提升数据处理的准确性和效率,尤其在语义分割、目标检测、实例分割和全景分割等任务中表现卓越。 通过结合频域分析与特征融合技术,FreqFusion…...
路由器的转发表
【4-24】 已知路由器R₁ 的转发表如表T-4-24 所示。 表T-4-24 习题4-24中路由器R₁的转发表 前缀匹配 下一跳地址 路由器接口 140.5.12.64/26 180.15.2.5 m2 130.5.8/24 190.16.6.2 ml 110.71/16 ----- m0 180.15/16 ----- m2 190.16/16 ----- ml 默认 11…...
用Cline打造你的智能搜索助手:Tavily Search MCP集成指南
引言 本文将详细介绍如何在Cline编辑器中集成Tavily Search智能搜索功能。我们将从MCP(Model Context Protocol)协议基础开始,深入探讨Tavily Search MCP服务器的安装配置、使用方法,以及进阶的二次开发技巧。无论你是AI开发者还…...
HTML+CSS+JS制作中华传统美食主题网站(内附源码,含5个页面)
一、作品介绍 HTMLCSSJS制作一个中华传统文化主题网站,包含首页、菜系页、食材页、名厨页、美食故事页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部横幅导航区 包含网站Logo、搜索栏、主导航菜单࿰…...
黄仁勋CES 2025演讲重点内容
黄仁勋CES 2025演讲重点内容 硬件产品发布 GeForce RTX 50系列GPU: 架构与性能提升:正式发布的新一代GeForce RTX 50系列GPU采用英伟达旗舰的Blackwell架构,这是自25年前引入可编程着色技术以来计算机图形领域最重大的创新。该系列显卡在图形…...
TVbox 手机、智能电视节目一网打尽
文章目录 一、简要介绍二、优点三、下载地址 一、简要介绍 TVbox是目前最火爆的多端、多源的电视影音工具,是一款开源的自定义添加站源的影音工具。TVBox,支持电视频道直播。一款TV端影视工具,软件本身不具有任何影视资源,但可以…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...
