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

news2025/1/9 13:45:44

目录

一、实例的功能

二、工程配置

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 Configuration
  PF0   ------> FSMC_A0
  PF1   ------> FSMC_A1
  PF2   ------> FSMC_A2
  PF3   ------> FSMC_A3
  PF4   ------> FSMC_A4
  PF5   ------> FSMC_A5
  PF12   ------> FSMC_A6
  PF13   ------> FSMC_A7
  PF14   ------> FSMC_A8
  PF15   ------> FSMC_A9
  PG0   ------> FSMC_A10
  PG1   ------> FSMC_A11
  PE7   ------> FSMC_D4
  PE8   ------> FSMC_D5
  PE9   ------> FSMC_D6
  PE10   ------> FSMC_D7
  PE11   ------> FSMC_D8
  PE12   ------> FSMC_D9
  PE13   ------> FSMC_D10
  PE14   ------> FSMC_D11
  PE15   ------> FSMC_D12
  PD8   ------> FSMC_D13
  PD9   ------> FSMC_D14
  PD10   ------> FSMC_D15
  PD11   ------> FSMC_A16
  PD12   ------> FSMC_A17
  PD13   ------> FSMC_A18
  PD14   ------> FSMC_D0
  PD15   ------> FSMC_D1
  PG2   ------> FSMC_A12
  PG3   ------> FSMC_A13
  PG4   ------> FSMC_A14
  PG5   ------> FSMC_A15
  PD0   ------> FSMC_D2
  PD1   ------> FSMC_D3
  PD4   ------> FSMC_NOE
  PD5   ------> FSMC_NWE
  PG10   ------> FSMC_NE3
  PE0   ------> FSMC_NBL0
  PE1   ------> 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 on
  LED1_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 functions
	    LED1_ON();
	    LED2_OFF();
	    LED3_OFF();
	    LED4_OFF();
	  }
	  	break;

	  case KEY_DOWN:
	  {
	  	SRAM_ReadByFunc();		//Read by HAL functions
	    LED1_OFF();
	    LED2_ON();
	    LED3_OFF();
	    LED4_OFF();
	  }
	  	break;

	  case KEY_LEFT:
	  {
		SRAM_WriteByPointer();	//Write by pointer
	    LED1_OFF();
	    LED2_OFF();
	    LED3_ON();
	    LED4_OFF();
		break;
	  }

	  case KEY_RIGHT:
	  {
		SRAM_ReadByPointer();	//Read by pointer
	    LED1_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.coloradmin.cn/o/2273815.html

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

相关文章

下载ffmpeg执行文件

打开网址&#xff1a;Download FFmpeg 按下面步骤操作 解压文件就可以看到ffmpeg的执行文件了&#xff0c;需要通过命令行进行使用&#xff1a; ffmpeg命令行使用参考&#xff1a; ffmpeg 常用命令-CSDN博客

嵌入式系统 (2.嵌入式硬件系统基础)

2.嵌入式硬件系统基础 2.1嵌入式硬件系统的组成 嵌入式硬件系统以嵌入式微处理器为核心&#xff0c;主要由嵌入式微处理器、总线、存储器、输入/输出接口和设备组成。 嵌入式微处理器 嵌入式微处理器采用冯诺依曼结构或哈佛结构&#xff1a;前者指令和数据共享同一存储空间…

【合作原创】使用Termux搭建可以使用的生产力环境(九)

前言 在上一篇【合作原创】使用Termux搭建可以使用的生产力环境&#xff08;八&#xff09;-CSDN博客中我们讲到了如何安装IDEA社区版&#xff0c;并在Termux中安装VNC服务器&#xff0c;在proot-distro的Debian中启动xfce桌面&#xff0c;并通过这个方式解决了IDEA社区版中无…

计算机网络--UDP和TCP课后习题

【5-05】 试举例说明有些应用程序愿意采用不可靠的UDP, 而不愿意采用可靠的TCP。 解答&#xff1a; 这可能有以下几种情况。 首先&#xff0c;在互联网上传输实时数据的分组时&#xff0c;有可能会出现差错甚至丢失。如果利用 TCP 协议对这些出错或丢失的分组进行重传&…

STM32学习(十)

I2C模块内部结构 I2C&#xff08;Inter-Integrated Circuit&#xff09;模块是一种由Philips公司开发的二线式串行总线协议&#xff0c;用于短距离通信&#xff0c;允许多个设备共享相同的总线‌。 ‌硬件连接简单‌&#xff1a;I2C通信仅需要两条总线&#xff0c;即SCL&…

Flutter:吸顶效果

在分页中&#xff0c;实现tab吸顶。 TDNavBar的screenAdaptation: true, 开启屏幕适配。 该属性已自动对不同手机状态栏高度进行适配。我们只需关注如何实现吸顶。 view import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import p…

数据结构基础之《(13)—前缀树》

一、前缀树 1、前缀树&#xff08;Trie&#xff09;&#xff0c;又称字典树或单词查找树&#xff0c;是一种用于存储字符串集合的数据结构。 2、前缀树的操作 &#xff08;1&#xff09;单个字符串中&#xff0c;字符从前到后的加到一棵多叉树上 &#xff08;2&#xff09;字…

道品科技智慧农业与云平台:未来农业的变革之路

随着全球人口的不断增长&#xff0c;农业面临着前所未有的挑战。如何在有限的土地和资源上提高农业生产效率&#xff0c;成为了各国政府和农业从业者亟待解决的问题。智慧农业的兴起&#xff0c;结合云平台的应用&#xff0c;为农业的可持续发展提供了新的解决方案。 ## 一、智…

【Linux基础指令】第一期

一、Linux的介绍 Linux是一个开源的操作系统&#xff0c;性能、稳定性、安全性方面上都是很优秀的&#xff0c;所以它一直是企业后端系统的首选。所以其图形化界面并不是Linux的必需品&#xff0c;所以我们避免不了要使用命令行的形式来使用Linux&#xff0c;也就离不开…

​​​​​​芯盾时代以数据为核心的车联网业务安全解决方案

芯盾时代车联网业务安全建设聚焦智能网联业务运行过程产生的多维度、多模态、多视角数据以及因业务需求产生的过程数据和业务衍生数据&#xff0c;以网络安全等级保护为基础&#xff0c;坚持网络安全管理体系和技术体系并重的原则&#xff0c;加强网络安全体系化、实战化、常态…

剖析 Claim-Check 模式:以小传大,赋能分布式系统与微服务

1. 前言 1.1 写作背景与目的 在当今分布式系统与微服务架构盛行的时代&#xff0c;服务间的消息传递与数据交换越来越频繁。传统的消息传输在面对海量数据时&#xff0c;往往会遇到以下痛点&#xff1a; 消息体过大&#xff1a;直接通过消息队列或服务间接口发送大体量数据&…

VS2022引入sqlite数据库交互

法一&#xff1a;用官网编译好的动态库(推荐) 下载所需文件 sqlite官网地址&#xff1a;https://www.sqlite.org/howtocompile.html 下载以下的2个压缩包 第一个压缩包 sqlite-amalgamation-xxxx.zip&#xff0c;xxxx是版本号,保持一致即可&#xff0c;这里面有sqite3.h 第…

计算机的错误计算(二百零五)

摘要 基于一位读者的问题&#xff0c;提出题目&#xff1a;能用数值计算证明 吗&#xff1f;请选用不同的点&#xff08;即差别大的数&#xff09;与不同的精度。实验表明&#xff0c;大模型理解了题意。但是&#xff0c;其推理能力值得商榷。 例1. 就摘要中问题&#xff0…

设计形成从业务特点到设计模式的关联

规范和指引在应用架构、数据架构等各架构方向上形成规范性约束指导。同一个决策要点、架构单元在统一的架构原则指导下&#xff0c;会因业务特点差异有不同的实现&#xff0c;经过总结形成了最佳实践。在开展新应用的设计时&#xff0c;根据决策要点以及相关的业务特点&#xf…

深度学习blog-深刻理解线性变换和矩阵

深度学习中避免不了矩阵运算&#xff0c;或者张量&#xff08;其实是矩阵数组&#xff09;运算。卷积是矩阵加、乘法&#xff0c;注意力也是一样。本质都一样&#xff0c;所谓注意力&#xff0c;卷积、滤波&#xff0c;是对不必了解数学的人说的&#xff0c;底层都是矩阵运算&a…

C/C++程序性能测试方法综述

摘要 性能测试是软件开发中不可或缺的一部分&#xff0c;特别是在对性能要求较高的C/C程序中。本文将详细介绍多种C/C程序性能测试方法&#xff0c;包括时间复杂度分析、事后统计方法、事前分析估算方法、使用性能测试工具&#xff08;如Google Benchmark、gprof、Valgrind等&…

jmeter 中 BeanShell 预处理程序、JSR223后置处理程序使用示例

1. 各个组件如何新建的&#xff1f; 2. "http请求" 组件内容样例&#xff1a; "消息体数据" 源码&#xff1a; {"task_tag": "face_detect","image_type": "base64","extra_args": [{"model"…

电脑32位和64位之区别(Difference between 32-Bit and 64 Bit Computers)

电脑32位和64位之区别 很多小伙伴还不知道电脑32位和64位是什么意思&#xff0c;今天小编就来普及一下。 32位和64位是指电脑处理器&#xff08;CPU&#xff09;和操作系统的架构&#xff0c;决定了电脑如何处理数据、存储信息、运行程序等。 32位和64位是指电脑系统中每个处…

vue -关于浏览器localstorge数据定期清除的实现

1.实现背景 用户登录时的信息存在了localstorge中&#xff0c;但它会一直存在。一般来说&#xff0c;我们希望这个数据能够定期被清除掉&#xff0c;以下一个定时清除的实现。 2.实现原理 在用户登录时&#xff0c;将用户信息存入localstorge的同时&#xff0c;将当前时间作…

【JavaEE进阶】获取Cookie/Session

&#x1f340;Cookie简介 HTTP协议自身是属于 "⽆状态"协议. "⽆状态"的含义指的是: 默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信,和下次通信之间没有直接的联系.但是实际开发中,我们很多时候是需要知道请求之间的关联关系的. 例如登陆⽹站成…