Jiangxiaopi 发表于 2025-3-8 08:01

STM32学习——FLASH闪存

FLASH闪存
简介
STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程

读写FLASH的用途:

利用程序存储器的剩余空间来保存掉电不丢失的用户数据
通过在程序中编程(IAP)类似技术:OTA,实现程序的自我更新
在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序

在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

相关知识:



闪存模块组织



主存储器:程序存储器,用来存放程序代码,是最主要也是容量最大的一块

信息块:分为启动程序代码和用户选择字节,启动程序代码就是系统存储器,存放的是原厂写入的Bootloader,用于串口下载。用户选择字节,也就是选项字节,存放一些独立的参数

闪存存储器接口寄存器:实际上不属于闪存,是普通的外设,这些存储器的存储介质都是SRAM

对于主存储器,进行了分页,分页是为了更好的管理闪存,擦除和写保护,都是以页为单位的,写入前必须擦除等待特性,每页的大小都是1K

FLASH基本结构



如何操作FPEC
第一步:FLASH解锁(防止误操作)

•FPEC共有三个键值:

RDPRT键 = 0x000000A5
KEY1 = 0x45670123
KEY2 = 0xCDEF89AB
•解锁:

复位后,FPEC被保护,不能写入FLASH_CR
在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
•加锁:

设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

使用指针访问存储器
使用指针读指定地址下的存储器:

uint16_t Data = *((__IO uint16_t *)(0x08000000));

第一步:给定要读取的存储器的地址

第二步:在地址前面加上强制类型转换,这里将变量强制转换成了uint16_t的指针类型,volatile译为易变的数据,加上的目的是防止编译器优化,可以去除无用的繁杂代码,降低代码空间,提升运行效率

第三步:指针取内容

使用指针写指定地址下的存储器:

*((__IO uint16_t *)(0x08000000)) = 0x1234;

其中:

#define __IO volatile

程序存储器全擦除



程序存储器页擦除



程序存储器编程



STM32的闪存在写入之前,会检查指定地址有没有擦除,如果没有擦除就写入,STM32就不执行写入操作,除非写入的全是0

字:word,32位数据

半字:HalfWord,16位数据

字节:Byte,8位数据

选项字节



nRDP:在写入RDP数据时,要同时在nRDP写入数据的反码

RDP:读保护配置位,写入RDPRT键(0x000000A5)后解除读保护

USER:配置硬件看门狗和进入停机/待机模式是否产生复位

Data0/1:用户可自定义使用

WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

选项字节编程
检查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

读出被擦除的选择字节并做验证

器件电子签名
电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名

闪存容量寄存器:

基地址:0x1FFF F7E0

大小:16位

产品唯一身份标识寄存器:

基地址: 0x1FFF F7E8

大小:96位

如果程序里有需要频繁执行,且对时间要求严格的中断函数,就要慎用内部闪存来存储用户数据

STM32 ST-LINK Uility基本使用



选项字节的读写





相关库函数
void FLASH_Unlock(void);

用来解锁

void FLASH_Lock(void);

加锁,把CR寄存器的lock位设置为1

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

闪存擦除某一页,参数给一个页的起始地址,之后指定的一页就被擦除了,返回值是这个操作的完成状态

FLASH_Status FLASH_EraseAllPages(void);

全擦除

FLASH_Status FLASH_EraseOptionBytes(void);

擦除选项字节

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

在指定地址写入字和写入半字

代码示例
读写内部FLASH
代码文件:Store.c

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS 0x0800FC00
#define STORE_COUNT                        512

uint16_t Store_Data;

void Store_Init(void)
{
        if(MyFLASH_ReadHalfWord(STORE_START_ADDRESS)!=0xA5A5)
        {
                MyFLASH_ErasePage(STORE_START_ADDRESS);
                MyFLASH_ProgramHalfWord(STORE_START_ADDRESS,0xA5A5);
                for(uint16_t i=1;i<STORE_COUNT;i++)
                {
                        MyFLASH_ProgramHalfWord(STORE_START_ADDRESS+i*2,0x0000);
                }
        }
        //上电的时候,把闪存的数据全部转存到数组里面
        for(uint16_t i=0;i<STORE_COUNT;i++)
        {
                Store_Data=MyFLASH_ReadHalfWord(STORE_START_ADDRESS+i*2);
        }
}

void Store_Save(void)
{
        //第一步:擦除最后一页
        MyFLASH_ErasePage(STORE_START_ADDRESS);
        //第二步:把数组完全备份保存到闪存
        for(uint16_t i=0;i<STORE_COUNT;i++)
        {
                MyFLASH_ProgramHalfWord(STORE_START_ADDRESS+i*2,Store_Data);
        }
}

void Store_Clear(void)
{
        for(uint16_t i=1;i<STORE_COUNT;i++)
        {
                Store_Data=0x0000;
        }
        Store_Save();
}




代码文件:MyFLASH.c

#include "stm32f10x.h"                  // Device header

uint32_t MyFLASH_ReadWord(uint32_t Address)
{
        return *((__IO uint32_t *)(Address));
}

uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
        return *((__IO uint16_t *)(Address));
}

uint8_t MyFLASH_ReadByte(uint32_t Address)
{
        return *((__IO uint8_t *)(Address));
}

//全擦除
void MyFLASH_EraseAllPages(void)
{
        //第一步:对FLASH进行解锁
        FLASH_Unlock();
        //第二步:直接调用库函数
        FLASH_EraseAllPages();
        //第三步:执行后再锁上FLASH
        FLASH_Lock();
}

//页擦除,参数需要给定指定页的地址
void MyFLASH_ErasePage(uint32_t PageAddress)
{
        FLASH_Unlock();
        FLASH_ErasePage(PageAddress);
        FLASH_Lock();
}

void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data)
{
        FLASH_Unlock();
        FLASH_ProgramWord(Address,Data);
        FLASH_Lock();
}

void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
        FLASH_Unlock();
        FLASH_ProgramHalfWord(Address,Data);
        FLASH_Lock();
}




代码文件:main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Store.h"
#include "Key.h"

uint8_t KeyNum;

int main(void)
{
        OLED_Init();
        Key_Init();
        //第一次使用的时候,初始化闪存,然后把闪存备份的数据,加载回SRAM数组,实现SRAM数组的掉电不丢失
        Store_Init();
       
        OLED_ShowString(1,1,"Flag:");
        OLED_ShowString(2,1,"Data:");
       
        while(1)
        {
                KeyNum=Key_GetNum();
                if(KeyNum==1)
                {
                        Store_Data++;
                        Store_Data+=2;
                        Store_Data+=3;
                        Store_Data+=4;
                        //把SRAM数组备份到闪存
                        Store_Save();
                }
                if(KeyNum==2)
                {
                        Store_Clear();
                }
                OLED_ShowHexNum(1,6,Store_Data,4);
                OLED_ShowHexNum(3,1,Store_Data,4);
                OLED_ShowHexNum(3,6,Store_Data,4);
                OLED_ShowHexNum(4,1,Store_Data,4);
                OLED_ShowHexNum(4,6,Store_Data,4);
        }
}




读取芯片ID
代码文件:main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"


int main(void)
{
        OLED_Init();
       
        OLED_ShowString(1,1,"F_SIZE");
        OLED_ShowHexNum(1,8,*((__IO uint16_t *)(0x1FFFF7E0)),4);
        OLED_ShowString(2,1,"U_ID");
        OLED_ShowHexNum(2,6,*((__IO uint16_t *)(0x1FFFF7E8)),4);
        OLED_ShowHexNum(2,11,*((__IO uint16_t *)(0x1FFFF7E8+0x02)),4);
        OLED_ShowHexNum(3,1,*((__IO uint32_t *)(0x1FFFF7E8+0x04)),8);
        OLED_ShowHexNum(4,1,*((__IO uint16_t *)(0x1FFFF7E8+0x08)),8);
        while(1)
        {
               
        }
}




前三个数相加,就是得到程序占用闪存的大小,后两个数相加,得到的是占用SRAM的大小
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_46403759/article/details/145951933

页: [1]
查看完整版本: STM32学习——FLASH闪存