STM32学习笔记13-FLASH闪存

news2024/11/24 1:59:37

FLASH简介

  • STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
  • 读写FLASH的用途:

                利用程序存储器的剩余空间来保存掉电不丢失的用户数据

                通过在程序中编程(IAP),实现程序的自我更新

  • 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAGSWD协议或系统加载程序(Bootloader)下载程序
  • 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

闪存模块组织

FLASH基本结构

FLASH解锁 

FPEC共有三个键值:

  • 1)RDPRT= 0x000000A5
  • 2)KEY1 = 0x45670123
  • 3)KEY2 = 0xCDEF89AB
  • 解锁的方式:
  • 复位后,FPEC被保护,不能写入FLASH_CR,也就是复位后FLASH默认是锁着的,然后在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。错误的操作序列会在下次复位前锁死FPECFLASH_CR。

  • 解锁之后如何加锁呢?我们操作完成之后要尽快把FLASH重新加锁,以防止意外情况。

  • 加锁:

  • 设置FLASH_CR中的LOCK位锁住FPECFLASH_CR。

  • 接下来看一下如何使用C语言指针访问存储器:

 

程序存储器全擦除

程序存储器擦除

 

程序存储器编程

擦除之后我们就可以执行写入的流程了,STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就写入STM32则不执行写入操作,除非写入的全是0,这一个数据是个例外。

 

选项字节

简单介绍一下,了解即可。

  • RDP:写入RDPRT键(0x000000A5)后解除读保护
  • USER:配置硬件看门狗和进入停机/待机模式是否产生复位
  • Data0/1:用户可自定义使用
  • WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

选项字节编程

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的编程操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTPG位为1
  4. 写入要编程的半字到指定的地址
  5. 等待BSY位变为0
  6. 读出写入的地址并验证数据

选项字节擦除

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的闪存操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTER位为1
  4. 设置FLASH_CRSTRT位为1
  5. 等待BSY位变为0
  6. 读出被擦除的选择字节并做验证

器件电子签名

电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名。

1)闪存容量寄存器:

          基地址:0x1FFF F7E0

          大小:16

2)产品唯一身份标识寄存器:

          基地址: 0x1FFF F7E8

          大小:96

FLASH应用

读写内部FLASH

代码整体规划如下:

 

接下来看一下库函数:

void FLASH_Unlock(void);//用来解锁
void FLASH_Lock(void);//加锁
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);//页擦除
FLASH_Status FLASH_EraseAllPages(void);//全擦除
FLASH_Status FLASH_EraseOptionBytes(void);//擦除选项字节
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//指定地址写入字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//指定地址写入半字

 完整代码:

MyFLASH.c:

#include "stm32f10x.h"                  // Device header


/*读取字(32位)
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
	return *((__IO uint32_t *)(Address));
}

/*读取半字(16位)
*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
	return *((__IO uint16_t *)(Address));
}

/*读取字节(8位)
*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
	return *((__IO uint8_t *)(Address));
}

/*全擦除
*/
void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_EraseAllPages();//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*页擦除
* 参数 PageAddress:要擦除的页地址
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ErasePage(PageAddress);//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*编程,写入一个字
* 参数1 Address:要写入的地址
* 参数2 Data:32位的数据
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ProgramWord(Address, Data);//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*编程,写入半字
* 参数1 Address:要写入的地址
* 参数2 Data:16位的数据
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ProgramHalfWord(Address, Data);//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}





Store.c:

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS		0x0800FC00
#define STORE_COUNT 			512

uint16_t Store_Data[STORE_COUNT];

void Store_Init(void)
{
	/* 初始化闪存,最后一页第一个半字是标志位A5A5,剩下数据全是0 */
	//0xA5A5是随便定义的标志位,如果第一个半字不是A5A5就说明是第一次使用
	if(MyFLASH_ReadHalfWord(0x08000000) != 0xA5A5)
	{
		MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);//在第一个半字的位置写入规定的标志位
		for(uint16_t i = 1;i < STORE_COUNT;i++)//把剩余的存储空间全都置为默认值0
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, 0x0000);
		}
	}
	
	/* 上电时把闪存数据转存到SRAM数组 */
	for(uint16_t i = 0;i < STORE_COUNT;i++)
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i*2);
	}
}

/*SRAM数组备份保存到闪存
*/
void Stort_Save(void)
{
	/* 第一步擦除最后一页 */
	MyFLASH_ErasePage(STORE_START_ADDRESS);
	
	/* 第二步把数组完全备份保存到闪存最后一页 */
	for(uint16_t i = 0;i < STORE_COUNT;i++)
	{
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, Store_Data[i]);
	}
}

/*数据清零
*/
void Store_Clear(void)
{
	for(uint16_t i = 1;i < STORE_COUNT;i++)
	{
		Store_Data[i] = 0x0000;
	}
	Stort_Save();//把更改更新到闪存
}

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"

uint8_t KeyNum;

int main(void)
{
	OLED_Init();
	Key_Init();
	Store_Init();//第一次使用的时候初始化闪存,把闪存备份的数据加载回SRAM数组
	
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");
	
	while(1)
	{
		KeyNum = Key_GetNum();
		if(KeyNum == 1)
		{
			Store_Data[1] ++;//第0个位置是标志位,不能用
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Stort_Save();//把SRAM数组备份到闪存
		}
		if(KeyNum == 2)
		{
			Store_Clear();
		}
		
		OLED_ShowHexNum(1, 6, Store_Data[0], 4);//显示标志位
		OLED_ShowHexNum(3, 1, Store_Data[1], 4);
		OLED_ShowHexNum(3, 6, Store_Data[2], 4);
		OLED_ShowHexNum(4, 1, Store_Data[3], 4);
		OLED_ShowHexNum(4, 6, Store_Data[4], 4);
	}
}

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

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

相关文章

【瑞芯微RV1126(板端摄像头图像数据采集)】②使用v4l2视频设备驱动框架采集图像数据,按键拍照并显示

RV1126开发板&#xff1a;使用v4l2视频设备驱动框架采集图像数据 前言一、按键二、LCD显示三、V4L2 摄像头应用编程四、完整代码 前言 本系列的目的是&#xff0c;不仅仅将能够进行图片推理的模型部署于板端&#xff0c;还提供了两种摄像头数据采集的方法&#xff0c;集成到自…

【python】OpenCV—Optical Flow

文章目录 1、光流2、Opencv 中光流的实现3、稀疏光流4、密集光流4.1、farneback4.2、lucaskanade_dense4.3、rlof 5、涉及到的库5.1、cv2.goodFeaturesToTrack5.2、cv2.calcOpticalFlowPyrLK5.3、cv2.optflow.calcOpticalFlowSparseToDense5.4、cv2.calcOpticalFlowFarneback5.…

超赞!墙裂推荐这款开箱即用、永久免费的运维监控平台

文章目录 简介一、初次印象&#xff1a;直观而强大的界面二、深入体验&#xff1a;6个重点功能模块1、告警管理2、综合监控3、业务服务4、网络拓扑5、可视化管理6、知识库7、报表管理 Lerwee AI 在当今这个数字化时代&#xff0c;企业依赖于强大的IT基础设施来支持其日常运营。…

微软Detours Hook库编译与使用

Detours 是微软开发的一个强大的Windows API钩子库&#xff0c;用于监视和拦截函数调用。它广泛应用于微软产品团队和众多独立软件开发中&#xff0c;旨在无需修改原始代码的情况下实现函数拦截和修改。Detours 在调试、监控、日志记录和性能分析等方面表现出色&#xff0c;已成…

java之如何爬取本地数据(利用正则表达式)

public class RegexDemo4 {public static void main(String[] args) {String s"程序员学习java&#xff0c;""电话&#xff1a;181512516758&#xff0c;18512508907" "或者联系邮箱&#xff1a;boniuitcast.cn&#xff0c;""座机电话&…

基于vue框架的RTY个人记账管理系统03jc1(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,收入分类,支出分类,收入信息,支出信息,开支预算,负债信息 开题报告内容 基于Vue框架的RTY个人记账管理系统 开题报告 一、研究背景与意义 随着社会经济的快速发展和人们生活水平的不断提升&#xff0c;个人财务管理成为越来越…

centos7.9 内核升级至5.4

一、修改yum源 查看现有系统内核版本&#xff1a; 备份系统自带的yum源&#xff1a; 一、修改CentOS-Base.repo ## cat CentOS-Base.repo # CentOS-Base.repo # # The mirror system uses the connecting IP address of the client and the # update status of each mirror t…

详细解读keepalived高可用集群

一.高可用集群 1.1 集群类型 LB&#xff1a;Load Balance 负载均衡LVS/HAProxy/nginx&#xff08;http/upstream, stream/upstream&#xff09;HA&#xff1a;High Availability 高可用集群数据库、RedisSPoF: Single Point of Failure&#xff0c;解决单点故障HPC&#xff1…

docker容器挂载USB串口设备

1、在容器所在宿主机确认USB串口设备 有两种方法可以将USB设备挂载到容器中: 使用--privileged参数或者使用--device参数 --prvleged参数可以让容器拥有主机的所有特权&#xff0c;包括所有可以访问USB设备。--device参数可以针对特定的设备挂载到容器中。 [rootdocker40 ~]…

【经验总结】ShardingSphere+Springboot-01模式参数配置

文章目录 详细配置&#xff08;boot&#xff09;一、模式配置&数据源配置1.1 模式配置1.2 数据源配置1.3 默认数据源的配置默认数据源配置结论 二、基础属性配置2.1 在日志中打印 SQL2.2 在程序启动和更新时&#xff0c;是否检查分片元数据的结构一致性 详细配置&#xff0…

JavaScript高阶笔记总结第三天:(JavaScript高阶完结)

Xmind鸟瞰图&#xff1a; 简单文字总结&#xff1a; js高阶笔记总结&#xff1a; 严格模式&#xff1a; 1.开启严格模式&#xff1a;"use strict" 2.不使用var关键字声明会报错 3.严格模式下普通函数的this指向undefined 高阶函数&#xff1a; 满足…

浅谈C/C++指针和引用在Linux和Windows不同环境下的编码风格

目录 0. 前言 1. 代码块、函数体上的 { } 的规范 2. 指针和引用中的 * 和 & 符号的位置 1. Linux 环境下编码风格(gcc) 2. Windows 环境下编码风格(Visual Studio) 3. 简单总结 0. 前言 C/C因为高度的自由性&#xff0c;并没有对一些常见的编码风格进行限制&#…

https://registry.nlark.com/无法访问

先上问题&#xff1a; own up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. npm ERR! code ENOTFOUND npm ERR! syscall getaddrinfo npm ERR! errno ENOTFOU…

电动自行车出海黑马Avento独立站拆解(上)丨出海笔记

这次我们来拆解一个电动自行车的独立站 为什么选电动自行车&#xff1f; 因为全球疫情&#xff0c;带来出行问题——避免聚集&#xff0c;大家都减少了公共交通工具&#xff0c;而改为自行车&#xff0c;电动自行车...... 君不见疫情之后无论是出行自行车&#xff0c;还是健…

NMAP扫描器的使用

NMAP 一 概述 nmap是一个网络探测和安全扫描工具,系统管理者和个人可以使用这个软件扫描大型的网络&#xff0c;获取 哪台主机正在运行以及提供什么服务等信息。 nmap可用于 检测活在网络上的主机(主机发现)。 检测主机上开放的端口(端口发现)。 检测到相应的端口(服务发现…

cisp-pte考试复盘

考试客户端下载-->点击进入考试>输入密码->进去可以看见一个qax的登录框【监考员会告知账户密码】->进入考试页面 里面的操作题,点击开始答题,能看见一个题有一个攻击机,这时我们需要点击鼠标左键,看见一个控制台,点击控制台才能进入答题【双击是没有用的】 …

C/C++开发,opencv内置背景减除算法与运动检测

目录 一、c opencv 背景减除算法内置算法 1.1 MOG2算法 1.2 KNN算法 二、完整案例实现 2.1 程序代码 2.2 程序编译及输出 一、c opencv 背景减除算法内置算法 背景减除算法的目标是将视频帧中的背景与前景&#xff08;通常是移动的对象&#xff09;分离。OpenCV提供MOG2&…

数据结构-排序的概念、应用及其算法实现1(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序)

本篇文章主要讲解直接插入排序、希尔排序、选择排序、堆排序、冒泡排序算法实现以及时间复杂度&#xff0c;稳定性分析。将在接下来的文章讲解快速排序、归并排序和计数排序。 本文全部代码在文章最后 目录 一、常见的排序算法 a.排序实现的接口 1.1插入排序 1.1.1基本思想…

【C++】BFS解决边权唯一的最短路径问题

目录 介绍 迷宫中离入口最近的出口 算法思路 代码实现 最小基因变化 算法思路 代码实现 单词接龙 算法思路 代码实现 为高尔夫比赛砍树 算法思路 代码实现 介绍 最短路问题是图论中非常经典的一种问题,其实就是通过代码找到两点之间的最优路径(往往是距离最短),最…

用MobaXterm,TightVNC和secure SSH实现两台windows电脑之间的连接和通信

今天给大家分享一个非常有趣的技术&#xff0c;那便是如何使用MobaXterm来实现两台电脑之间的通信。实验成功&#xff0c;保证能跑。 首先&#xff0c;给大家介绍我们今天最重要的工具&#xff1a;那便是MobaXterm&#xff08;以下由ChatGPT生成&#xff09;&#xff1a; Moba…