【STM32】读写内部Flash初步使用

news/2025/2/11 14:12:00/

基于stm32f103,作为个人学习记录使用

STM32 芯片内部有一个 FLASH 存储器,它主要用于存储代码,在紧急状态下常常会使用内部 FLASH 存储关键记录;
在这里插入图片描述

内部 FLASH 的构成

STM32 的内部 FLASH 包含主存储器系统存储器以及选项字节区域

大容量产品内部 FLASH 的构成(摘自《STM32F10x 闪存编程参考手册》
在这里插入图片描述

主存储器

一般我们说 STM32 内部 FLASH 的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的 256K FLASH、512K FLASH 都是指这个区域的大小。

主存储器分为 256 页,每页大小为 2KB,共 512KB。这个分页的概念,实质就是FLASH 存储器的扇区,与其它 FLASH 一样,在写入数据前,要先按页(扇区)擦除

不同容量的芯片,Flash的主存储器的页数量、页大小均有不同

系统存储区

系统存储区是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB 以及 CAN 等 ISP 烧录功能。

选项字节

选项字节用于配置 FLASH 的读写保护、待机/停机复位、软件/硬件看门狗等功能,这部分共 16 字节。可以通过修改 FLASH 的选项控制寄存器修改。

查看keil工程的空间分布,确定空余空间

内部 FLASH 本身存储有程序数据,若不是有意删除某段程序代码,一般不应修改程序空间的内容。所以在使用内部 FLASH 存储其它数据前需要了解哪一些空间已经写入了程序代码,存储了程序代码的扇区都不应作任何修改。

通过.map文件,可以了解程序存储到了哪些区域。
打开方式
打开 map 文件后,查看文件最后部分的区域,可以看到一段以“Memory Map of the image”开头的记录。
在这里插入图片描述
这一段是某工程的 ROM 存储器分布映像,在 STM32芯片中,ROM 区域的内容就是指存储到内部 FLASH 的代码

1. 计算程序 ROM 的加载与执行空间

例子中
Load Region LR_ROM1:程序的加载空间 :Base 0x800 0000,Size:0x0000 17a8
Execution Region ER_IROM1:程序的执行空间: Base 0x0800 0000,Size:0x0000 177c

在芯片刚上电运行时,会加载程序及数据,例如它会从程序的存储区域加载到程序的执行区域,还把一些已初始化的全局变量从 ROM 复制到 RAM 空间,以便程序运行时可以修改变量的内容。加载完成后,程序开始从执行区域开始执行。

在上面 map 文件的描述中

加载及执行空间的基地址 (Base)都是0x08000000,它正好是 STM32 内部 FLASH 的首地址,也是 STM32 的程序存储空间就直接是执行空间;
它们的大小(Size)分别为 0x000017a8 及 0x0000177c,执行空间的 ROM 比较小的原因就是因为部分 RW-data 类型的变量被拷贝到 RAM 空间了;
拷贝RW到RAM中

最大空间(Max):0x00080000,即 512K 字节,此款STM32内部 FLASH 的最大空间。

用大的那个

计算程序占用的空间时,需要使用加载区域的大小进行计算,本例子中应用程序使用的内部 FLASH 是从 0x08000000 至(0x08000000+0x000017a8)地址的空间区域。

2. ROM 空间分布表

在加载及执行空间总体描述之后,紧接着一个ROM详细地址分布表,它列出了工程中的各个段(如函数、常量数据)所在的地址 Base Addr 及占用的空间 Size。在这里插入图片描述
Type 说明了该段的类型;
CODE 表示代码;
DATA 表示数据;
PAD 表示段之间的填充区域,它是无效的内容,PAD 区域往往是为了解决地址对齐的问题。
在这里插入图片描述
观察表中的最后一项,它的基地址是 0x0800175c,大小为 0x00000020,可知它占用的最高的地址空间为 0x0800177c,跟执行区域的最高地址 0x0000177c 一样,但它们比加载区域说明中的最高地址 0x80017a8 要小,所以我们以加载区域的大小为准。

所以这边一共使用了4k不到的空间,那么从第三页开始就可以作为其他功用了。

对内部 FLASH 的写入的一般过程

1.解锁

由于内部 FLASH 空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会给控制寄存器 FLASH_CR 上锁,这个时候不允许设置FLASH 的控制寄存器,不能修改 FLASH 中的内容。
所以对 FLASH 写入数据前,需要先给它解锁。解锁的操作步骤如下:
(1) 往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY1 = 0x45670123
(2) 再往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY2 = 0xCDEF89AB

#define FLASH_KEY1 ((uint32_t)0x45670123)
#define FLASH_KEY2 ((uint32_t)0xCDEF89AB)//对 FLASH 控制寄存器解锁,使能访问
void FLASH_Unlock(void){if ((FLASH->CR & FLASH_CR_LOCK) != RESET) {/* 写入确认验证码 */FLASH->KEYR = FLASH_KEY1;FLASH->KEYR = FLASH_KEY2;}}

顺带一提,给flash上锁的方法

void FLASH_Lock(void)
{FLASH->CR |= FLASH_CR_LOCK;/* 设置 FLASH 寄存器的 LOCK 位 */
}

2. 页擦除

在写入新的数据前,需要先擦除存储区域,STM32 提供了页(扇区)擦除指令和整个FLASH 擦除(批量擦除)的指令,批量擦除指令仅针对主存储区。
页擦除的过程:
(1) 检查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY”,以确认当前未执行任何Flash 操作;
(2) 在 FLASH_CR 寄存器中,将“激活页擦除寄存器位 PER ”置 1;
(3) 用 FLASH_AR 寄存器选择要擦除的页;
(4) 将 FLASH_CR 寄存器中的“开始擦除寄存器位 STRT ”置 1,开始擦除;
(5) 等待 BSY 位被清零时,表示擦除完成。

/**
* @brief 擦除指定的页
* @param Page_Address: 要擦除的页地址.
* @retval FLASH Status:
可能的返回值: FLASH_BUSY, FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ErasePage(uint32_t Page_Address)
{FLASH_Status status = FLASH_COMPLETE;/* 检查参数 */assert_param(IS_FLASH_ADDRESS(Page_Address));/*...此处省略 XL 超大容量芯片的控制部分*//* 等待上一次操作完成 */status = FLASH_WaitForLastOperation(EraseTimeout);if (status == FLASH_COMPLETE) {/* 若上次操作完成,则开始页擦除 */FLASH->CR|= CR_PER_Set;FLASH->AR = Page_Address;FLASH->CR|= CR_STRT_Set;/* 等待操作完成 */status = FLASH_WaitForLastOperation(EraseTimeout);/* 复位 PER 位 */FLASH->CR &= CR_PER_Reset;}return status; /* 返回擦除结果 */
}

3. 写入数据

擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还还需要配置一系列的寄存器,步骤如下:
(1) 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
(2) 将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
(3) 向指定的 FLASH 存储器地址执行数据写入操作,每次只能以 16 位的方式写入;
(4) 等待 BSY 位被清零时,表示写入完成。

/**
* @brief 向指定的地址写入一个字的数据(32 位)
* @param Address: 要写入的地址
* @param Data: 要写入的数据
* @retval FLASH Status:
可能的返回值: FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data){FLASH_Status status = FLASH_COMPLETE;__IO uint32_t tmp = 0;/* 检查参数 */assert_param(IS_FLASH_ADDRESS(Address));/*...此处省略 XL 超大容量芯片的控制部分*//* Wait for last operation to be completed */status = FLASH_WaitForLastOperation(ProgramTimeout);if (status == FLASH_COMPLETE) {/* 若上次操作完成,则开始页入低 16 位的数据(输入参数的第 1 部分) */FLASH->CR |= CR_PG_Set;*(__IO uint16_t*)Address = (uint16_t)Data;/* 等待上一次操作完成 */status = FLASH_WaitForLastOperation(ProgramTimeout);if (status == FLASH_COMPLETE) {/* 若上次操作完成,则开始页入高 16 位的数据(输入参数的第 2 部分) */tmp = Address + 2;*(__IO uint16_t*) tmp = Data >> 16;/* 等待操作完成 */status = FLASH_WaitForLastOperation(ProgramTimeout);/* 复位 PG 位 */FLASH->CR &= CR_PG_Reset;} else {/* 复位 PG 位 */FLASH->CR &= CR_PG_Reset;}}return status; /* 返回写入结果 */}

http://www.ppmy.cn/news/1125994.html

相关文章

OpenCV项目开发实战--使用 EigenFaces 进行人脸重建 (含C++/Python源码)

在这篇文章中,我们将学习如何使用 EigenFaces 重建面部。这篇文章是为初学者写的。如果您不了解主成分分析 (PCA) 或 EigenFaces。 什么是特征脸? 特征脸是可以添加到平均(平均)脸部以创建新的面部图像的图像。我们可以用数学方式将其写为: 在哪里,

timm模型无法联网下载采用本地读取

最新的timm版本(0.9.x)默认使用huggingface hub作为权重,优先于torch hub缓存,许多权重已经为模型更改重新映射,所以最好通过HF hub下载。Kaggle真的应该支持通过HF hub,或者至少正确地缓存它,但他们似乎没有兴趣让事情…

【PostgreSQL内核学习(十七)—— (AutoAnalyze)】

AutoAnalyze 概述AutoAnaProcess 类AutoAnaProcess 函数AutoAnaProcess::executeSQLCommand 函数AutoAnaProcess::runAutoAnalyze 函数AutoAnaProcess::run 函数AutoAnaProcess::check_conditions 函数AutoAnaProcess::cancelAutoAnalyze 函数AutoAnaProcess::~AutoAnaProcess …

TensorFlow入门(七、检查点)

保存检查点 在实际的模型训练中,TensorFlow难免会出现中断的情况,使得到的中间参数丢失,因此需要在模型训练过程中及时将模型保存下来。并将这种在训练中保存模型的操作,称为保存检查点 通过设置saver的另一个参数max_to_keep,指定生成检查点文件的个数,代码示例如下: saver…

Linux系统编程——进程间通信的学习

学习参考博文: 进程间的五种通信方式介绍Linux 信号介绍 Linux系统编程学习相关博文 Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——线程的学习Linux系统编程——网络编程的学习 Linux系统编程——进程间通信的学习 一、概述1. 无…

给你两个集合,要求{A} + {B}

先看题&#xff1a; 看完题后你会觉得&#xff1a;哇&#xff0c;好简单&#xff0c;STL一下就出来啦。 #include <iostream> #include <set>using namespace std;int main() {int n, m;while (cin >> n >> m) {set<int> set_a;for (int i 0;…

分享从零开始学习网络设备配置--任务3.8 使用动态路由OSPF实现网络连通

任务描述 某公司随着规模的不断扩大&#xff0c;路由器的数量在原有的基础上有所增加。网络管理员发现原有的路由协议已经不适合现有的网络环境&#xff0c;可实施动态路由OSPF协议配置&#xff0c;实现网络中所有主机之间互相通信。因为动态路由OSPF协议可以实现快速收敛&…

[git] 撤销 git reset –hard

要撤销 git reset –hard origin/master&#xff0c;我们可以使用 Git 的 reflog 功能。Git reflog 记录了仓库中每一个 HEAD 的更改历史。通过查看 reflog&#xff0c;我们可以找到之前的 HEAD 和分支位置&#xff0c;从而找回丢失的提交。 首先&#xff0c;使用以下命令查看…