STM32学习笔记:读写内部Flash

首先我们需要了解一个内存映射:

stm32的flash地址起始于0x0800 0000 , 结束地址是0x0800 0000加上芯片实际的flash大小 , 不同的芯片flash大小不同 。
RAM起始地址是0x2000 0000 , 结束地址是0x2000 0000加上芯片的RAM大小 。不同的芯片RAM也不同 。
Flash中的内容一般用来存储代码和一些定义为const的数据 , 断电不丢失 , 
RAM可以理解为内存 , 用来存储代码运行时的数据 , 变量等等 。掉电数据丢失 。
【STM32学习笔记:读写内部Flash】STM32将外设等都映射为地址的形式 , 对地址的操作就是对外设的操作 。
stm32的外设地址从0x4000 0000开始 , 可以看到在库文件中 , 是通过基于0x4000 0000地址的偏移量来操作寄存器以及外设的 。
一般情况下 , 程序文件是从 0x0800 0000 地址写入 , 这个是STM32开始执行的地方 , 0x0800 0004是STM32的中断向量表的起始地址 。
在使用keil进行编写程序时 , 其编程地址的设置一般是这样的:

程序的写入地址从0x08000000(数好零的个数)开始的 , 其大小为0x80000也就是512K的空间 , 换句话说就是告诉编译器flash的空间是从0x08000000-0x08080000 , RAM的地址从0x20000000开始 , 大小为0x10000也就是64K的RAM 。这与STM32的内存地址映射关系是对应的 。
M3复位后 , 从0x08000004取出复位中断的地址 , 并且跳转到复位中断程序 , 中断执行完之后会跳到我们的main函数 , main函数里边一般是一个死循环 , 进去后就不会再退出 , 当有中断发生的时候 , M3将PC指针强制跳转回中断向量表 , 然后根据中断源进入对应的中断函数 , 执行完中断函数之后 , 再次返回main函数中 。大致的流程就是这样 。
内部Flash的构成:STM32 的内部 FLASH 包含主存储器、系统存储器、 OTP 区域以及选项字节区域 , 它们的地址分布及大小如下:

主存储器:一般我们说 STM32 内部 FLASH 的时候 , 都是指这个主存储器区域它是存储用户应用程序的空间 , 芯片型号说明中的 1M FLASH、 2M FLASH 都是指这个区域的大小 。与其它 FLASH 一样 , 在写入数据前 , 要先按扇区擦除 , 
系统存储区:系统存储区是用户不能访问的区域 , 它在芯片出厂时已经固化了启动代码 , 它负责实现串口、 USB 以及 CAN 等 ISP 烧录功能 。
OTP 区域:OTP(One Time Program) , 指的是只能写入一次的存储区域 , 容量为 512 字节 , 写入后数据就无法再更改 ,  OTP 常用于存储应用程序的加密密钥 。
选项字节:选项字节用于配置 FLASH 的读写保护、电源管理中的 BOR 级别、软件/硬件看门狗等功能 , 这部分共 32 字节 。可以通过修改 FLASH 的选项控制寄存器修改 。
对内部Flash的写入过程:1. 解锁
(1) 往 Flash 密钥寄存器 FLASH_KEYR 中写入 KEY1 = 0x45670123
(2) 再往 Flash 密钥寄存器 FLASH_KEYR 中写入 KEY2 = 0xCDEF89AB
2. 数据操作位数
最大操作位数会影响擦除和写入的速度 , 其中 64 位宽度的操作除了配置寄存器位外 , 还需要在 Vpp 引脚外加一个 8-9V 的电压源 , 且其供电间不得超过一小时 , 否则 FLASH可能损坏 , 所以 64 位宽度的操作一般是在量产时对 FLASH 写入应用程序时才使用 , 大部分应用场合都是用 32 位的宽度 。
3. 擦除扇区
在写入新的数据前 , 需要先擦除存储区域 ,  STM32 提供了扇区擦除指令和整个FLASH 擦除(批量擦除)的指令 , 批量擦除指令仅针对主存储区 。
扇区擦除的过程如下:
(1) 检查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY” , 以确认当前未执行任何
Flash 操作;
(2) 在 FLASH_CR 寄存器中 , 将“激活扇区擦除寄存器位 SER ”置 1 , 并设置“扇
区编号寄存器位 SNB” , 选择要擦除的扇区;
(3) 将 FLASH_CR 寄存器中的“开始擦除寄存器位 STRT ”置 1 , 开始擦除;
(4) 等待 BSY 位被清零时 , 表示擦除完成 。
4. 写入数据
擦除完毕后即可写入数据 , 写入数据的过程并不是仅仅使用指针向地址赋值 , 赋值前还还需要配置一系列的寄存器 , 步骤如下:
(1) 检查 FLASH_SR 中的 BSY 位 , 以确认当前未执行任何其它的内部 Flash 操作;
(2) 将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
(3) 针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作;
(4) 等待 BSY 位被清零时 , 表示写入完成 。
查看工程内存的分布:由于内部 FLASH 本身存储有程序数据 , 若不是有意删除某段程序代码 , 一般不应修改程序空间的内容 , 所以在使用内部 FLASH 存储其它数据前需要了解哪一些空间已经写入了程序代码 , 存储了程序代码的扇区都不应作任何修改 。通过查询应用程序编译时产生的“ *.map”后缀文件 , 
打开 map 文件后 , 查看文件最后部分的区域 , 可以看到一段以“ Memory Map of the
image”开头的记录(若找不到可用查找功能定位) , 

这一段是某工程的 ROM 存储器分布映像 , 在 STM32 芯片中 ,  ROM 区域的内容就是 指存储到内部 FLASH 的代码 。
在上面 map 文件的描述中 , 我们了解到加载及执行空间的基地址(Base)都是0x08000000 , 它正好是 STM32 内部 FLASH 的首地址 , 即 STM32 的程序存储空间就直接是执行空间;它们的大小(Size)分别为 0x00000b50 及 0x00000b3c , 执行空间的 ROM 比较小的原因就是因为部分 RW-data 类型的变量被拷贝到 RAM 空间了;它们的最大空间(Max)均为 0x00100000 , 即 1M 字节 , 它指的是内部 FLASH 的最大空间 。
计算程序占用的空间时 , 需要使用加载区域的大小进行计算 , 本例子中应用程序使用
的内部 FLASH 是从 0x08000000 至(0x08000000+0x00000b50)地址的空间区域 。
所以从扇区 1(地址 0x08004000)后的存储空间都可以作其它用途 , 使用这些存储空间时不会篡改应用程序空间的数据 。
具体可参考原子的例程:实验四十一:FLASH 模拟 EEPROM 实验


    推荐阅读