STM32 Flash详解教程文章

 目录

Flash基本概念理解

Flash编程接口FPEC

Flash擦除/写入流程图

Flash选项字节基本概念理解

Flash电子签名

函数读取地址下存放的数据

Flash的数据处理限制部分


                                            编写不易,请勿搬运,感谢理解!!!

Flash基本概念理解

        STM32的Flash里面包含,程序存储器系统存储器选项字节,通过对Flash闪存的片上外设接口(接口地址在ram地址),可以对程序存储器跟选项字节进行擦除和编程。

·程序存储器:用来下载存放程序的位置

·系统存储器:用来存放BootoLoader芯片出厂自带程序

·选项字节:用来配置Flash读保护,写保护,等功能

        其常见在程序中的用法是,通过程序软件编程利用内部Flash数据掉电不丢失的特性与内部Flash通常不用使用完,将重要的参数存储在Flash的最后几页内

        和使用指针指向系统存储区域内,芯片的ID号地址,读取属于芯片自己的ID号,通过利用芯片不同的ID号,在程序执行时判断芯片ID号,如果不同则不执行程序,来实现程序只能在特定芯片上运行。

        最后一种用法就是通过对Flash的选项字节配置,来完成程序的读保护跟写保护,在程序中来配置。

                                                        flash分配地址详解

        在STM32F1系列芯片中,其中Flash每页的起始地址后三位均以 000 400 800 C00 开头。

Flash编程接口FPEC

        Flash编程控制器 FPEC 全称是 Flash Program/Erase Controller 用于对Flash存储器进行读写和擦除的硬件模块。该模块包含7个32位寄存器。

·FPEC键寄存器(FLASH_KEYR):用于解锁FPEC外设,允许进行Flash编程或擦除操作

·选择字节键寄存器(FLASH_OPTKEYR):用于解锁Flash选项字节的修改操作

·闪存控制寄存器(FLASH_CR):控制Flash编程和擦除操作的核心寄存器

·闪存状态寄存器(FLASH_SR):指示Flash当前状态和操作结果

·闪存地址寄存器(FLASH_AR):指定Flash操作的目标地址

·选择字节寄存器(FLASH_OBR):读取选项字节当前的配置状态

·写保护寄存器(FLASH_WRPR):指示和配置Flash的写保护状态

        7个寄存器中需要注意的是,Flash_SR寄存器的BSY位,当该位为1的时候代表正在进行flash操作,当为0的时候代表操作结束或者发生错误,通常来读取该位的值来判断flash操作是否完成结束。

        Flash_CR寄存器的PG位代表是否选择编程操作,PER位是否选择页擦除操作,MER位是否选择全擦除操作,OPTPG选择字节编程,OPTER擦除选择字节,STRT开始操作,该位为1的时候将触发擦除操作,只能由软件置1并在BSY为1的时候清除为0,LOCK锁为1代表FPEC和Flash_CR被锁住,为0代表解锁成功。

                                                            寄存器地址范围图

        在芯片复位后,FPEC模块是被保护的,Flash_CR寄存器不能被读写操作,首先需要对,Flash_KEYR写入特定键值KEY1  KEY2 来完成解锁操作,如果写入错误会在下次复位之前锁死,FPEC跟Flash_CR寄存器。

·RDPRT键 = 0x000000A5    //解除读保护

·KEY1 = 0x45670123           //KEY1+KEY2解除FPEC锁

·KEY2 = 0xCDEF89AB

        需要注意的是,在解锁FPEC对Flash_CR寄存器操作完成对Flash的读|写之后,需要设置Flash_CR中的LOCK位锁住FPEC跟Flash_CR位,来防止数据的误写入导致对Flash进行操作。

Flash擦除/写入流程图

                                                Flash写入数据流程图

                                                        Flash页擦除流程图

                                                        Flash全擦除流程图

        在三张流程图中只有擦除流程图需要置Flash_CR寄存器的STRT位为1,在上文有介绍过当为1的时候会执行擦除操作,然后当Flash_SR寄存器的BSY为0的是时候会将STRT位置0。

Flash选项字节基本概念理解

       在芯片地址中给选项字节分配了16个字节的地址,其中8个字节的地址用来备份,当前选项字节的数据,只有8个字节用来配置Flash选项字节。其中四个字节用来写保护,1个字节用来读保护,1个字节配置选项,2个字节存储用户数据。  

                                                        分配地址图

       需要注意的是,在完成对FPEC的解锁之后,还需要再次对Flash_OPTKKEYR写入KEY1 KEY2来完成对选项字节的解锁,同事置Flash_CR的OPTWRE位为1才能对选项字节进行写入操作。 

         

·RDP:写入RDPRT键(0x000000A5)后解除读保护

·USER:硬件看门狗配置 停机模式|待机模式 是否产生复位

·Data:用户自定义数据

·WRP:该位置用来配置写保护

Flash电子签名

        电子签名是芯片出厂的时候由芯片制造商写入的信息,存放地址实在系统存储区域,用来表示芯片的唯一性跟芯片的flash容量大小。

·0x1FFFF7E0 ~ 0x1FFFF7E3 存放flash大小

·0x1FFFF7E8 ~ 0x1FFFF7EF 存储 UID。

        其中UID Uniqune Device ID 唯一设备标识,共96位12字节改信息用户不可更改,可以利用指针进行访问。

函数读取地址下存放的数据

       上文中提到了在大多数单片机程序中因为自身Flash没有被使用完,因此可以对剩余Flash空间进行读写数据操作函数如下。

uint32_t FlashR_Word(uint32_t address)
{return *((__IO uint32_t *)(address))
}

        函数中__IO来自官方定义库文件定义如下

#define __IO  voliate

        将宏定义带入到函数中就能得到下面代码

(__IO uint32_t *)(address) = (volitate uint32_t *)(address)

        改代码作用是将函数局部变量参数转化为指向该参数的uint32_t,指针类型,同时括号外面的*就是指针解引用的意思,就是返回指针指向该地址下的数值,这里因为声明的指针类型为uint32_t类型所以就能返回一个字的地址。

        也就是32位数据,4个字节的数据,同理可以得到读取16位数据的函数,还有读取一个字节的函数,只要把函数内声明的指针类型给改了就行了

uint16_t FlashR_HWord(uint32_t address)
{return *((__IO uint16_t *)(address))
}uint8_t FlashR_Byte(uint32_t address)
{return *((__IO uint8_t *)(address))
}
//这里因为改变了 内部声明的指针类型 跟返回值类型所以能获取的数据大小也被限制了

Flash的数据处理限制部分

        在对Flash进行数据存储的通常情况下需要再程序中定义一个 uint16_t data[512] 数组,大小是1024也就是一个Flash页面大小,先将数据写入ram的数组里面然后再写入Flash里面进行存储,通常这种情况是因为Flash本身的写入限制问题。

       首先Flash擦除的最小单位是页,同时在Flash进行写入的时候不存在数据覆盖这种写法,也就是将数据0x12345对改地址重新写入数据0x56787是写不进去的是因为Flash中的数据只能从1->0,而不能从0->1,而在擦除完单页Flash之后,该页存储的数据全部为0xFFFF FFFF 也就是全部是1。

        这就导致了如果直接对Flash进行数据写入,会发现在数据写入到最后的时候如果想要更改前面的数据大概率是需要擦除才能进行写入的,但是如果进行擦除其他位的数据又丢失,所以通常常用的数据数据手段也就是。

        将Flash单页数据读出保存在一个Flash页大小的数组里面进行更改数据,然后将整页数据重新写入到Flash数组里面来完成操作,跟将需要写入的数据先在数组里面进行覆盖,最后统一写入Flash里面。

Step 1:先从 Flash 读取数据到 RAM
Step 2:在 RAM 修改数据
Step 3:擦除 整个 Flash Page(Flash 只能整页擦除)。
Step 4:将 RAM 数据重新写入 Flash

#include "stm32f10x.h"
#include "Store.h"
#include "MyFlash.h"
//数组内512个数据 每个数据类型是 uint16_t 也就是2个字节 数组能存放1024字节数据 也就是对应Flash一页的容量
uint16_t Store_Data[512];
//在ram 里面定义一个数组 需要备份的时候统一转到闪存里面
void Flash_Init(void)
{if(Flash_WHWord(0x08000000)!=0xA5A5){Earse_FlashPage(0x08000000);Flash_WHWord(0x08000000 ,0xA5A5 );for(uint16_t i = 1;i<512;i++){Flash_WHWord(0x08000000 +i*2 ,0x0000 );}}//将Flash里面数据保存读到ram里面for(uint16_t i = 0; i<512;i++){Store_Data[i] = FlashRH_Word(0x08000000 + i*2);}
}
void Flash_Save(void)
{//先擦除Flash在进行写入,不然会发现写不进去Earse_FlashPage(0x08000000);for(uint16_t i = 0;i<512;i++){Flash_WHWord(0x08000000 + i*2,Store_Data[i] );}
}
//函数用来清除数组 Flash里面的数据
void Flah_Clear(void)
{for(uint16_t i = 0;i<512;i++){Store_Data[i] = 0x0000;}Flash_Save();
}

                                                                代码部分

        这里需要注意的是i*2问题,因为在Flash里面最小写入单元跟最小存储单元室一个半字,也就是16位,4个字节,举个例子就是0x0800 0000是单元起始地址,到0x0800 0001 地址该单元结束,该地址指向存储单元,每个地址指向的存储单元能够存储 2个字节。

        所以在数据数据进行写入的时候,地址的起始地址都是用2的倍数开头的,因为像0x0800 0001这种是不合法地址,是没有办法进行写入半字数据。

        这种用法通常是用在单片机程序很小,自身Flash占用不完的情况下使用,而有没有办法知道自己的程序占用多少字节,同时改变程序烧录的位置,无论程序大小都不能烧录到自己保存数据的Flash数据部分这种方法也是有的。

                                        查看程序自身大小方法图

        魔术棒里面有程序的起始地址还有结束地址,只要把在Keil5里面可以更改这两个地址从而达到更改程序的烧录地址的效果。

                                欢迎指正,希望对你,有所帮助!!!

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

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

相关文章

WPF 设置宽度为 父容器 宽度的一半

方法1&#xff1a;使用 绑定和转换器 实现 创建类文件 HalfWidthConverter public class HalfWidthConverter : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if (value is double width){return width / 4…

【Ubuntu VScode Remote SSH 问题解决】Resolver error: Error: XHR failed

1. 问题描述 VScode使用remote ssh 远程服务器&#xff0c;报错类似&#xff1a; [12:06:01.219] Downloading VS Code server locally... [12:06:01.310] Resolver error: Error: XHR failedat k.onerror (vscode-file://vscode-app/private/var/folders/g1/cvs2rnpx60qc3b4…

32单片机学习记录1之GPIO

32单片机学习记录1之GPIO 前置 GPIO口在单片机中扮演着什么角色&#xff1f; 在单片机中&#xff0c;GPIO口&#xff08;General Purpose Input/Output&#xff09; 是一种通用输入/输出接口&#xff0c;扮演着连接单片机与外部设备的桥梁角色。具体来说&#xff0c;它在单片…

第三十二周:Informer学习笔记

目录 摘要Abstract1 Informer1.1 预备知识1.2 模型框架1.3 实验分析 总结 摘要 本周学习的主要内容是Informer模型&#xff0c;Informer是一种专为长序列时间序列预测&#xff08;LSTF&#xff09; 设计的Transformer模型。相较于传统的Transformer&#xff0c;Informer采用Pr…

绩效归因概述

绩效归因概述 1. 分类2. 基于净值的归因方法2.1 发展背景2.2 择时选股模型 T-M模型2.3 择时选股模型 H-M模型2.4 择时选股模型 C-L模型2.5 风格配置模型-Sharpe2.6 多因子模型 Fama-French32.7 多因子模型 Carhart42.8 多因子模型 Fama-French5 3. 基于持仓的归因方法3.1 发展背…

MambaMorph brain MR-CT

loss代码实现了几种用于医学图像配准(Registration)和分割(Segmentation)任务的损失函数,主要包括以下几种: NCC (Normalized Cross-Correlation): 功能: 计算局部归一化互相关损失,用于衡量两个图像之间的相似性。 应用场景: 通常用于图像配准任务,通过最大化图像之间…

C++ ——从C到C++

1、C的学习方法 &#xff08;1&#xff09;C知识点概念内容比较多&#xff0c;需要反复复习 &#xff08;2&#xff09;偏理论&#xff0c;有的内容不理解&#xff0c;可以先背下来&#xff0c;后续可能会理解更深 &#xff08;3&#xff09;学好编程要多练习&#xff0c;简…

<tauri><rust><GUI>基于rust和tauri的图片显示程序(本地图片的加载、显示、保存)

前言 本文是基于rust和tauri,由于tauri是前、后端结合的GUI框架,既可以直接生成包含前端代码的文件,也可以在已有的前端项目上集成tauri框架,将前端页面化为桌面GUI。 环境配置 系统:windows 10 平台:visual studio code 语言:rust、javascript 库:tauri2.0 概述 …

Arrays工具类详解

目录 1. Arrays.toString() 方法 2. Arrays.deepToString() 方法 3. Arrays.equals(int[ ] arr1, int[ ] arr2) 方法 4. Arrays.equals(Object[] arr1, Object[] arr2) 方法 5. Arrays.deepEquals(Object[] arr1, Object[] arr2) 方法 6. Arrays.sort(int[] arr) 方法 7…

设计高效的测试用例:从需求到验证

在现代软件开发过程中&#xff0c;测试用例的设计一直是质量保证&#xff08;QA&#xff09;环节的核心。有效的测试用例不仅能够帮助发现潜在缺陷&#xff0c;提升软件质量&#xff0c;还能降低后期修复成本&#xff0c;提高开发效率。尽管如此&#xff0c;如何从需求出发&…

基于YoloV11和驱动级鼠标模拟实现Ai自瞄

本文将围绕基于 YoloV11 和驱动级鼠标实现 FPS 游戏 AI 自瞄展开阐述。 需要着重强调的是&#xff0c;本文内容仅用于学术研究和技术学习目的。严禁任何个人或组织将文中所提及的技术、方法及思路应用于违法行为&#xff0c;包括但不限于在各类游戏中实施作弊等违规操作。若因违…

三角测量——用相机运动估计特征点的空间位置

引入 使用对极约束估计了相机运动后&#xff0c;接下来利用相机运动估计特征点的空间位置&#xff0c;使用的方法就是三角测量。 三角测量 和对极几何中的对极几何约束描述类似&#xff1a; z 2 x 2 R ( z 1 x 1 ) t z_2x_2R(z_1x_1)t z2​x2​R(z1​x1​)t 经过对极约束…

如何本地部署DeepSeek

第一步&#xff1a;安装ollama https://ollama.com/download 打开官网&#xff0c;选择对应版本 第二步&#xff1a;选择合适的模型 https://ollama.com/ 模型名称中的 1.5B、7B、8B 等数字代表模型的参数量&#xff08;Parameters&#xff09;&#xff0c;其中 B 是英文 B…

Git生成公钥和私钥的方式

因为需要访问远程Git服务器&#xff0c;需要使用公钥&#xff1a; 1、先检测电脑上是否已经有.ssh目录 像我这就是没有的 2、开始生成一个新的SSH密钥&#xff08;RSA&#xff09; 打开Git Bash, 然后运行ssh-keygen -t rsa -b 4096 -C "注释" -t rsa是密匙类型…

常用的python库-安装与使用

常用的python库函数 yield关键字openslide库openslide库的安装-linuxopenslide的使用openslide对象的常用属性 cv2库numpy库ASAP库-multiresolutionimageinterface库ASAP库的安装ASAP库的使用 concurrent.futures.ThreadPoolExecutorxml.etree.ElementTree库skimage库PIL.Image…

【Oracle专栏】本地 expdp 导出远程库

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 近期需要在远程备份机器上远程导出数据库,之前用expdp数据泵只导出过本服务器的,本文跨服务器使用expdp 。 2. 测试 2.1 本机装完整oracle时,执行expdp导出远端数据库 实验说明:以下12为本机,14…

Flink KafkaConsumer offset是如何提交的

一、fllink 内部配置 client.id.prefix&#xff0c;指定用于 Kafka Consumer 的客户端 ID 前缀partition.discovery.interval.ms&#xff0c;定义 Kafka Source 检查新分区的时间间隔。 请参阅下面的动态分区检查一节register.consumer.metrics 指定是否在 Flink 中注册 Kafka…

【leetcode】双指针:移动零 and 复写零

文章目录 1.移动零2.复写零 1.移动零 class Solution { public:void moveZeroes(vector<int>& nums) {for (int cur 0, dest -1; cur < nums.size(); cur)if (nums[cur] ! 0)swap(nums[dest], nums[cur]);} };class Solution { public:void moveZeroes(vector&l…

网络安全工程师逆元计算 网络安全逆向

中职逆向题目整理合集 逆向分析&#xff1a;PE01.exe算法破解&#xff1a;flag0072算法破解&#xff1a;flag0073算法破解&#xff1a;CrackMe.exe远程代码执行渗透测试天津逆向re1 re22023江苏省re12023年江苏省赛re2_easygo.exe2022天津市PWN 逆向分析&#xff1a;PE01.exe …

string类(二)

目录 前言 string类的常用接口说明 3、string类对象的容量操作 3.1 size&#xff0c;length和capacity 3.2 empty和clear 3.3 reserve 3.4 resize 4、string类的修改操作 4.1 operator 4.2 c_str 4.3 findnpos 5、string类非成员函数 5.1 operator>>和opera…