STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
读写FLASH的用途:
- 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
- 通过在程序中编程(IAP),实现程序的自我更新
在线编程(In-Circuit Programming -- ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
在程序中编程(In-Application Programming -- IAP)可以使用微控制器支持的任一种通信接口下载程序
内存映射
小容量产品(16K 至 32K)、中容量产品(64k 至 128k)、大容量产品(256K 至 512K)。
![](https://i-blog.csdnimg.cn/direct/9813cf68893d49c0b7b689d982f3eb34.png)
闪存存储器接口(外设)不属于闪存
FLASH基本结构
可以看出 闪存存储器接口(外设)整体结构
使用指针访问存储器
- 使用指针读指定地址下的存储器: uint16_t Data = *((__IO uint16_t *)(0x08000000));
- 使用指针写指定地址下的存储器: *((__IO uint16_t *)(0x08000000)) = 0x1234;
- #define __IO volatile
易读不易写 可以直接读或者像下面代码一样封装一个函数读
cpp
/**
* 函 数:FLASH读取一个32位的字
* 参 数:Address 要读取数据的字地址
* 返 回 值:指定地址下的数据
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
return *((__IO uint32_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
/**
* 函 数:FLASH读取一个16位的半字
* 参 数:Address 要读取数据的半字地址
* 返 回 值:指定地址下的数据
*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
return *((__IO uint16_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
/**
* 函 数:FLASH读取一个8位的字节
* 参 数:Address 要读取数据的字节地址
* 返 回 值:指定地址下的数据
*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
return *((__IO uint8_t *)(Address)); //使用指针访问指定地址下的数据并返回
}
程序存储器使用方法
![](https://i-blog.csdnimg.cn/direct/4fbb4b94b8b44c35a846a74ea23a683e.png)
FLASH锁
**擦除控制器(FPEC)**共有三个键值:
- RDPRT键 = 0x000000A5
- KEY1 = 0x45670123
- KEY2 = 0xCDEF89AB
解锁: 复位后,FPEC被保护,不能写入FLASH_CR 在FLASH_KEYR先写入KEY1,再写入KEY2,解锁错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
加锁: 设置FLASH_CR中的LOCK位锁住 FPEC 和 FLASH_CR
该环节库函数两行代码即可完成,实际操作并不复杂,只是这样侧面说明FLASH的复杂及不易出现问题导致丢失。
cpp
FLASH_Unlock(); //解锁
FLASH_Lock(); //加锁
程序存储器全擦除
流程图
![](https://i-blog.csdnimg.cn/direct/a65d1bc5e2a74b60888cb8db44809dae.png)
cpp
/**
* 函 数:FLASH全擦除
* 参 数:无
* 返 回 值:无
* 说 明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在
*/
void MyFLASH_EraseAllPages(void)
{
FLASH_Unlock(); //解锁
FLASH_EraseAllPages(); //全擦除
FLASH_Lock(); //加锁
}
程序存储器页擦除
流程图
![](https://i-blog.csdnimg.cn/direct/852a13d4f61147c58f1a43fc753fcbea.png)
cpp
/**
* 函 数:FLASH页擦除
* 参 数:PageAddress 要擦除页的页地址
* 返 回 值:无
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
FLASH_Unlock(); //解锁
FLASH_ErasePage(PageAddress); //页擦除
FLASH_Lock(); //加锁
}
程序存储器编程
流程图
![](https://i-blog.csdnimg.cn/direct/95e038bbd78e4eba8e3ebdc8390c97e3.png)
cpp
/**
* 函 数:FLASH编程字
* 参 数:Address 要写入数据的字地址
* 参 数:Data 要写入的32位数据
* 返 回 值:无
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Unlock(); //解锁
FLASH_ProgramWord(Address, Data); //编程字
FLASH_Lock(); //加锁
}
/**
* 函 数:FLASH编程半字
* 参 数:Address 要写入数据的半字地址
* 参 数:Data 要写入的16位数据
* 返 回 值:无
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Unlock(); //解锁
FLASH_ProgramHalfWord(Address, Data); //编程半字
FLASH_Lock(); //加锁
}
选项字节
- RDP:写入RDPRT键(0x000000A5)后解除读保护
- USER:配置硬件看门狗和进入停机/待机模式是否产生复位
- Data0/1:用户可自定义使用
- WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)
nUSER及前面+ n 是反码
![](https://i-blog.csdnimg.cn/direct/299bf4d2564348c8ab284ce731d3f658.png)
对于产品,选择字节WRPx 中的每一个比特位用于保护主存储器中 4 个存储页 (1K 字节 /
页 ) :
- 0:实施写保护
- 1:不实施写保护
中容量四个用户选择字节用于保护总共128K 字节的主存储器。
- WRP0:第0~31页的写保护
- WRP1:第32~63页的写保护
- WRP2:第64~95页的写保护
- WRP3:第96~127页的写保护
大容量产品 四个用户选择字节用于保护总共512K 字节的主存储器。
- WRP0:第0~15页的写保护
- WRP1:第16~31页的写保护
- WRP2:第32~47页的写保护
- WRP3:位0~6提供第48~61页的写保护;位7提供第62~255页的写保护
选项字节编程
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
- 解锁FLASH_CR的OPTWRE位
- 设置FLASH_CR的OPTPG位为1
- 写入要编程的半字到指定的地址
- 等待BSY位变为0
- 读出写入的地址并验证数据
选项字节擦除
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作
- 解锁FLASH_CR的OPTWRE位
- 设置FLASH_CR的OPTER位为1
- 设置FLASH_CR的STRT位为1
- 等待BSY位变为0
- 读出被擦除的选择字节并做验证