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

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

STM32 芯片内部有一个 FLASH 存储器,它主要用于存储代码,在紧急状态下常常会使用内部 FLASH 存储关键记录;
【STM32】读写内部Flash初步使用_第1张图片

内部 FLASH 的构成

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

大容量产品内部 FLASH 的构成(摘自《STM32F10x 闪存编程参考手册》
【STM32】读写内部Flash初步使用_第2张图片

主存储器

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

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

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

系统存储区

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

选项字节

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

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

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

通过.map文件,可以了解程序存储到了哪些区域。
【STM32】读写内部Flash初步使用_第3张图片
打开 map 文件后,查看文件最后部分的区域,可以看到一段以“Memory Map of the image”开头的记录。
【STM32】读写内部Flash初步使用_第4张图片
这一段是某工程的 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。【STM32】读写内部Flash初步使用_第5张图片
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; /* 返回写入结果 */
 }

你可能感兴趣的:(STM32,stm32,嵌入式硬件,单片机)