STM32的FLASH学习笔记

news2024/9/21 0:34:59

        不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了1024K 字节。大容量产品的闪存模块组织如图所示:

         STM32 的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。 ​

①主存储器:该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是 0X08000000, Boot0、Boot1 都接 GND 的时候,就是从 0X08000000开始运行代码的。 ​

②信息块:该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 Boot0 接 V3.3,Boot1 接 GND 的时候,运行的就是这部分代码。用户选择字节则一般用于配置写保护、读保护等功能。 ​

③闪存存储器接口寄存器:该部分用于控制闪存读写等,是整个闪存模块的控制机构。 ​

        对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;即在进行写或擦除操作时,不能进行代码或数据的读取操作

闪存的读取

        任何 32 位数据的读操作都能访问闪存模块的内容并得到相应的数据。例如,我们要从地址 addr,读取一个半字(半字为 16 位,字为 32 位),可以通过如下的语句读取:

data=*(vu16*)addr;

        将 addr 强制转换为 vu16 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。类似的,将上面的 vu16 改为 vu8,即可读取指定地址的一个字节。

闪存的编程和擦除

        STM32 的闪存编程是由 FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含 7 个32 位寄存器,他们分别是:FPEC 键寄存器(FLASH_KEYR),选择字节键寄存器(FLASH_OPTKEYR),闪存控制寄存器(FLASH_CR),闪存状态寄存器(FLASH_SR),闪存地址寄存器(FLASH_AR),选择字节寄存器(FLASH_OBR),写保护寄存器(FLASH_WRPR)。

FLASH编程注意事项:

①FPEC 键寄存器总共有 3 个键值:RDPRT 键=0X000000A5,KEY1=0X45670123,KEY2=0XCDEF89AB 。STM32 复位后,FPEC 模块是被保护的,不能写入 FLASH_CR 寄存器;通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和 KEY2),只有在写保护被解除后,我们才能操作相关寄存器,这一步叫做Unlock

②STM32 闪存的编程每次必须写入 16 位(不能写入 8 位),当 FLASH_CR 寄存器的 PG 位为’1’时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据,FPEC 都会产生总线错误。在编程过程中(BSY 位为’1’)不能进行其他读写擦除操作

③STM32 的 FLASH 在编程的时候,必须要求其写入地址的 FLASH 是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入,在 FLASH_SR 寄存器的 PGERR 位将得到一个警告。

STM23 的 FLASH 编程过程如图:

 从上图可以得到闪存的编程顺序如下: ​

①检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁 ​

②检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的编程操作 ​

③设置 FLASH_CR 寄存器的 PG 位为’1’ ​ 在指定的地址写入要编程的半字 ​

④等待 BSY 位变为’0’ ​ ,读出写入的地址并验证数据

        在FLASH编程前要先确定目标地址是否已经被擦除,有两种擦除方式:页擦除整片擦除。页擦除过程如图:

 从上图可以看出,STM32 的页擦除顺序为: ​

①检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁 ​

②检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作 ​

③设置 FLASH_CR 寄存器的 PER 位为’1’,选择为页擦除 ​

④用 FLASH_AR 寄存器选择要擦除的页 ​

⑤设置 FLASH_CR 寄存器的 STRT 位为’1’ ​,开始擦除

⑥等待 BSY 位变为’0’ ​ 读出被擦除的页并做验证

常用库函数

锁定解锁函数

void FLASH_Unlock(void);//解锁函数
void FLASH_Lock(void);//锁定函数

写操作函数

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//32位字写入,实际上是写两次16字节
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//16位半字写入
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//用户选择字节写入

擦除函数

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);//页擦除
FLASH_Status FLASH_EraseAllPages(void);//全片擦除
FLASH_Status FLASH_EraseOptionBytes(void);//用户选择字节擦除

获取flash状态

FLASH_Status FLASH_GetStatus(void);
//返回值是通过枚举类型定义的:
 typedef enum
{ 
 FLASH_BUSY = 1,//忙
 FLASH_ERROR_PG,//编程错误
 FLASH_ERROR_WRP,//写保护错误
 FLASH_COMPLETE,//操作完成
 FLASH_TIMEOUT//操作超时
}FLASH_Status;   

等待操作完成函数

       在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。使用的函数是:

FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);

正点原子函数解析

STMFLASH_ReadHalfWord

//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}

STMFLASH_Write_NoCheck

//不检查的写入,在使用此函数前需要检查要写入的地址是否已经被擦除
//WriteAddr:起始地址
//pBuffer:要写入的数据指针
//NumToWrite:半字(16位)数   
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);//循环写入半字
	    WriteAddr+=2;//地址增加2.
	}  
} 

        计算机中最小的信息单位是bit,也就是一个二进制位,8个bit组成一个Byte,也就是1个字节,1个存储单元存放1个字节,每个存储单元对应一个32位(bit)地址,对于32bit的ARM CPU一个32位地址指向1个字节!所以上面写完一个半字后地址要+2。

STMFLASH_Read

//从指定地址开始读出指定长度的数据,存储在pBuffer
//ReadAddr:起始地址
//pBuffer:读取到的数据存储的位置
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}

STMFLASH_Write

//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:要写入的数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256//判断FLASH容量,小于256K是中小容量,一页FLASH有1K字节
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048//大于等于256K为大容量,一页FLASH有2K字节
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节,用来存放一页读出的数据,因为写之前要擦除,为了避免数据丢失,先将数据读出,擦除FLASH,修改数据后再写回去。2k字节就是1k个半字,所以需要除以2
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //基于0X08000000的偏移地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址,要写入的地址相对于flash起始地址0x08000000的偏移量.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址,扇区号  0~127 for STM32F103RCT6,计算要写入的地址在哪个扇区
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.),取模后算出的是偏移的字节数,除以2得到半字数
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小,   STM_SECTOR_SIZE是扇区中的字节数,除以2是半字数,减去偏移就是剩下的半字数
	if(NumToWrite<=secremain)secremain=NumToWrite;//如果要写的大小不大于扇区剩余大小,也就是不需要写到下一个扇区,就令剩余扇区大小等于要写的大小,后面有用。
	while(1) 
	{	STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容,secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE是扇区起始地址,STMFLASH_BUF是读取结果存储的数组,STM_SECTOR_SIZE/2是要读取的半字个数
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//如果要写入的地址遍历到扇区结束遇到了不是0XFFFF的数据,表示需要擦除  	  
		}
		if(i<secremain)//如果是通过break跳出循环,需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	//将要写入的数据拷贝到扇区数据缓冲数组的相应位置  
			}	
          STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//如果不需要擦除就直接写数据		   
		if(NumToWrite==secremain)break;//如果NumToWrite==secremain,表示不需要跨扇区,写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1,写下一个扇区
			secoff=0;				//偏移位置为0 ,从下一个扇区开头开始写	 
		   	pBuffer+=secremain;  	//指针偏移,要写的数据指针偏移已经写过的部分
			WriteAddr+=secremain;	//写地址偏移,要写的地址也要偏移已经写过的部分	   
		   	NumToWrite-=secremain;	//字节(16位)数递减,还需要写的半字个数减去已经写完的部分
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//如果要写的数据大于一个扇区的个数,下一个扇区还是写不完,就把剩余扇区大小等于整个扇区的半字数,即下一个扇区写满
			else secremain=NumToWrite;//下一个扇区可以写完了,再循环一次就会跳出了
		}	 
	};	
	FLASH_Lock();//上锁
}

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

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

相关文章

【LInux】<基础IO> 文件操作 | 文件描述符 | 重定向

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

OpenHarmony 实战开发——ArkUI容器类API介绍

容器类&#xff0c;顾名思义就是存储的类&#xff0c;用于存储各种数据类型的元素&#xff0c;并具备一系列处理数据元素的方法。在 ArkUI 开发框架中&#xff0c;容器类采用了类似静态的语言来实现&#xff0c;并通过 NAPI 框架对外提供。通过对存储位置以及属性的限制&#x…

如何找到MySQL中存储引擎所对应的表空间并且打开?

在上节课我们学习了数据库&#xff08;MySQL&#xff09;进阶&#xff1a;存储引擎&#xff0c;有不少同学产生疑惑&#xff0c;到底要怎么找到表空间并且打开啊&#xff1f;这节课我们就来探讨。 首先&#xff0c;根据这个路径&#xff1a;C:\ProgramData\MySQL\MySQL Server…

k8s的网路配置

目录 1、k8s相关网络类型 1.1 K8S中Pod网络通信 1.2 Overlay Network 1.3 VXLAN 1.3.1 vlan和vxlan的区别 2、Flannel 2.1 简介 2.2 Flannel工作原理 2.3 ETCD之Flannel提供说明 2.4 Flannel部署 2.4.1 在node节点上操作 2.4.2 在master01节点上操作 2.4.2.1 安装f…

SVDD(Singing Voice Deepfake Detection,歌声深度伪造检测)挑战2024

随着AI生成的歌声快速进步&#xff0c;现在能够逼真地模仿自然人类的歌声并与乐谱无缝对接&#xff0c;这引起了艺术家和音乐产业的高度关注。歌声与说话声不同&#xff0c;由于其音乐性质和强烈的背景音乐存在&#xff0c;检测伪造的歌声成为了一个特殊的领域。 SVDD挑战是首个…

Android实践:查看Activity信息

问题&#xff1a;本地Android SDK的monitor无法正常运行&#xff0c;看不了进程相关信息&#xff0c;确认当前显示Activity十分不便 解决办法&#xff1a;使用adb shell指令可以快速查看 命令&#xff1a; adb shell dumpsys activity activities 这个命令用于获取Android设…

精酿啤酒:品质的保障与消费者的信赖

在啤酒市场中&#xff0c;Fendi club啤酒以其卓着的品质和消费者的信赖赢得了广泛的认可。作为精酿啤酒的品牌&#xff0c;Fendi club啤酒始终坚持对品质的严格把控&#xff0c;为消费者带来放心的口感体验。 品质的保障源于Fendi club啤酒对原料的严谨挑选和加工。他们深知&a…

LeetCode刷题笔记第1800题:最大升序子数组和

LeetCode刷题笔记第1800题&#xff1a;最大升序子数组和 题目&#xff1a; 想法&#xff1a; 遍历数组的同时记录当前最大升序子数组和&#xff0c;最终返回最大升序子数组和 class Solution:def maxAscendingSum(self, nums: List[int]) -> int:result 0i 0n len(num…

前端之电力系统SVG图低代码

其实所有的图形都是由点&#xff0c;线&#xff0c;面组成的。点线面可以组成一个设备。下面就简单讲讲点线面是怎么画的吧 对于线&#xff0c;可以用path <g><path:d"M ${beginX},${beginY} L ${endX},${endY}":stroke-width"lineWidth":strok…

Java面试题:Spring框架除了IOC和AOP,还有哪些好玩的设计模式?

Spring是一个基于Java的企业级应用程序开发框架&#xff0c;它使用了多种设计模式来实现其各种特性和功能。本文将介绍一些在Spring中使用的常见设计模式以及相应的代码示例和说明。 单例模式 单例模式是Spring中最常用的设计模式之一。在ApplicationContext中&#xff0c;Bean…

DiskANN数据布局

_mem.index.data&#xff1a;和sift_base.fbin一模一样。0-3字节是总向量数&#xff0c;4-7是每个向量的特征数。后面就是依次放置的每个向量。 _disk.index&#xff1a;是存储的图&#xff0c;但是不光包含图也包含原始向量。前4KB不知道存的是啥。从第0x1000开始存放的是原始…

浙大博士毕业2年后发表观点:读博一定要慎重!

已经博士毕业两年了&#xff0c;非常高兴和大家分享自己研究生的学习和生活经历&#xff0c;给准备读研的同学和考研的同学一点点小帮助。 首先我简单介绍下自己&#xff0c;本科非 985&#xff0c;非 211&#xff0c;研究生非 985&#xff0c;非 211&#xff0c;就不说学校的名…

【FPGA、maltab】基于FPGA的SOQPSK调制解调技术的设计与实现

基于FPGA的SOQPSK调制解调技术的设计与实现 SOQPSK一、QPSK、OQPSK、SOQPSK之间的关系二、SOQPSK调制原理 matlab 仿真FPGA 实现顶层设计发射模块接收模块顶层调制解调FPGA代码 SOQPSK 一、QPSK、OQPSK、SOQPSK之间的关系 SOQPSK&#xff08;Shaped Offset Quadrature Phase …

fl studio试用版文件保存无法打开??一个方法教你免费打开!

前言 当下&#xff0c;各款编曲软件五花八门&#xff0c;而这其中最有声誉的必为FL Studio莫属 这个软件呢国人习惯叫他水果&#xff0c;拥有强大的录音、编曲、混音等功能&#xff0c;所以广受音乐圈欢迎。如今&#xff0c;大部分水果一旦有编曲所需&#xff0c;一般都要使用…

一文搞懂CPU是如何进行计算的?

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

优秀测试的核心能力!2招高效定位分析BUG!

之所以写这一篇文章&#xff0c;是突然想起来曾经在测试过程中被开发嘲讽过&#xff0c;事情是这样的&#xff0c;当时发现了一个疑似前端的Bug就草草提交到了禅道&#xff0c;结果刚来的女前端看到了就有点生气地问我为啥不查清到底是前后端问题就直接派给她前端了&#xff0c…

React框架-Next 学习-1

创建一个 Next.js 应用,node版本要高&#xff0c;16.5以上 npm淘宝镜像切为https://registry.npmmirror.com npm config set registry https://registry.npmmirror.com npx create-next-applatest//安装后 使用npm run dev 启动 Next.js 是围绕着 页面&#xff08;pages&am…

uac驱动之const修饰的变量和const修饰的指针

const int*p // p所指向的空间是常量 不可修改 ,但p可以修改 int*const p // p所指向的空间是可以修改 ,p不可以修改 #include <stdio.h> #include <string.h>struct usb_string {char id;const char *s; };enum {STR_ASSOC,STR_AC_IF,STR_USB_OUT_IT,STR_USB_O…

单位个人怎样向报社的报纸投稿?

作为一名单位的信息宣传员,我肩负着每月定期在媒体上投稿发表文章的重任。然而,在投稿的道路上,我经历了不少波折和挫折。 一开始,我天真地以为只要将稿件发送到报社的投稿邮箱,就能轻松完成任务。然而,现实却远比我想象的复杂。邮箱投稿的竞争异常激烈,编辑们会在众多稿件中挑…

ROS2系统与px4通信测试

参考文章&#xff1a; No communication with ROS2 using MicroXRCEAgent with px4 board ROS2官方安装及测试程序 概要 新安装的ROS2与PixHawk开发板进行通信。 操作步骤 启动示例程序&#xff0c;在&#xff5e;/ws_sensor_combined/src路径下执行&#xff1a; ros2 l…