STM32快速复习系列之十二:FLASH闪存读写详解

文章目录

  • 一、FLASH是什么?FLASH的结构?
  • 二、使用步骤
  • 1.标准库函数
  • 2.示例函数
  • 总结

  • 一、FLASH是什么?FLASH的结构?

    1、FLASH简介

    (1)STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
    (2)读写FLASH的用途:
    利用程序存储器的剩余空间来保存掉电不丢失的用户数据
    通过在程序中编程(IAP),实现程序的自我更新
    (3)在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
    (4)在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

    stm32寄存器地址介绍

    闪存模块组织


    FLASH基本结构

    FLASH解锁

    (1)FPEC共有三个键值:
    RDPRT键 = 0x000000A5(解除读保护的秘钥)
    KEY1 = 0x45670123
    KEY2 = 0xCDEF89AB
    (2)解锁:
    复位后,FPEC被保护,不能写入FLASH_CR
    在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
    错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
    (3)加锁:
    设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

    使用指针访问存储器
    (1)使用指针读指定地址下的存储器:
    uint16_t Data = *((__IO uint16_t *)(0x08000000));
    (2)使用指针写指定地址下的存储器:
    *((__IO uint16_t *)(0x08000000)) = 0x1234;
    (3)其中:
    #define __IO volatile (易变的数据,防止编译器优化)


    程序存储器

    编程


    页擦除

    全擦除

    选项字节
    (1)选项字节
    a.RDP:写入RDPRT键(0x000000A5)后解除读保护
    b.USER:配置硬件看门狗和进入停机/待机模式是否产生复位
    c.Data0/1:用户可自定义使用
    d.WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

    2)选项字节编程
    a.检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
    b.解锁FLASH_CR的OPTWRE位
    c.设置FLASH_CR的OPTPG位为1(即将写入选项字节)
    d.写入要编程的半字到指定的地址(指针写入操作)
    e.等待BSY位变为0
    f.读出写入的地址并验证数据

    (3)选项字节擦除
    a.检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作
    b.解锁FLASH_CR的OPTWRE位(选项字节里面有一个单独的解锁)
    c.设置FLASH_CR的OPTER位为1(即将擦除选项字节)
    d.设置FLASH_CR的STRT位为1(触发芯片,开始干活)
    e.等待BSY位变为0
    f.读出被擦除的选择字节并做验证

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

    (2)闪存容量寄存器:
    基地址:0x1FFF F7E0
    大小:16位

    (3)产品唯一身份标识寄存器:
    基地址: 0x1FFF F7E8
    大小:96位

    二、使用步骤

    1.标准库函数

    2.示例函数

    代码如下(示例):

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Store.h"
    #include "button.h"
     
    uint8_t KeyNum;
     
    int main(void)
    {
    	OLED_Init();
    	Button_Init();
    	Store_Init();//第一次使用时,初始化闪存,把闪存备份的数据加载回SRAM数组,实现SRAM数组上电不丢失
    	
    	OLED_ShowString(1,1,"Flag:");
    	OLED_ShowString(2,1,"Data:");
     
    	while(1)
    	{
    		KeyNum = Button_GetNum();
    		
    		if(KeyNum ==1)
    		{
    			Store_Data[1] ++;
    			Store_Data[2] +=2;
    			Store_Data[3] +=3;
    			Store_Data[4] +=4;
    			Store_Save();
    		}
    		if(KeyNum == 2)
    		{
    			Store_Clear();
    		}
    		OLED_ShowHexNum(1,6,Store_Data[0],4);
    		OLED_ShowHexNum(3,1,Store_Data[1],4);
    		OLED_ShowHexNum(3,6,Store_Data[2],4);
    		OLED_ShowHexNum(4,1,Store_Data[3],4);
    		OLED_ShowHexNum(4,6,Store_Data[4],4);
    	}
    }
    

    最底层:MyFLASH层

    #include "stm32f10x.h"                  // Device header
     
    /*
    	读取32位的字
    */
    uint32_t MyFLASH_ReadWord(uint32_t Address)//地址必须是32位的
    {
    	return *((__IO uint32_t *)(Address));
    }
    	
    /*
    	读取16位的半字
    */
    uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
    {
    	return *((__IO uint16_t *)(Address));
    }
     
    /*
    	读取8位的字节
    */
    uint8_t MyFLASH_ReadByte(uint32_t Address)
    {
    	return *((__IO uint8_t *)(Address));
    }
     
    /*
    	全擦除
    */
    void MyFLASH_EraseAllPages(void)
    {
    	FLASH_Unlock();//解锁
    	FLASH_EraseAllPages();//全擦除
    	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();//锁上
    }
    

    Store.c

    #include "stm32f10x.h"                  // Device header
    #include "MyFLASH.h"
     
    #define STORE_START_ADDRESS 0x0800FC00
    #define STORE_COUNT  		512
     
    uint16_t Store_Data[STORE_COUNT];
     
    void Store_Init(void)
    {
    	if(MyFLASH_ReadHalfWord(STORE_START_ADDRESS) !=0xA5A5)
    	{
    		MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
    		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS,0xA5A5);//写入规定的标志位0xA5A5
    		for(uint16_t i=1;i<STORE_COUNT;i++)
    		{
    			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS +i*2,0x0000);//写0
    		}
    	}
    	
    	for(uint16_t i=0;i<STORE_COUNT;i++)//在上电的时候,把闪存备份的地址的数据,恢复到SRAM数组里
    	{
    		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS +i*2);//读闪存数据并存到SRAM数组里
    	}
     
    }
     
    /*
    	把SRAM的所有内容备份到闪存
    */
    void Store_Save(void)
    {
    	MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
    	for(uint16_t i=0;i<STORE_COUNT;i++)//在上电的时候,把闪存备份的地址的数据,恢复到SRAM数组里
    	{
    		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS +i*2,Store_Data[i]);//把SRAM的所有内容备份到闪存的最后一页
    	}
    }	
     
    /*
    	把SRAM的所有内容备份到闪存
    */
    void Store_Clear(void)
    {
    	for(uint16_t i=1;i<STORE_COUNT;i++)
    	{
    		Store_Data[i] = 0x0000;
    	}
    	Store_Save();//把这个更改更新到闪存里
    }
    

    偷个懒,用了别人的图片,还是要自己敲一下代码。才算学会


    总结

    偷个懒。。。。。。
    存在问题
    (1)目前闪存的前面部分存储的是程序文件,最后一页存储的是用户数据,如果程序较大,触及到了最后一页,那程序和用户数据存储的位置就冲突了,这时就可以给程序文件限定一个存储范围,不让它分配到后面我们用户数据的空间来

    (2)配置下载选项,擦除扇区,用到多少就擦除多少,下载速度更快

    (3)想知道目前程序编译后占多大空间
    全部编译一下

    or
    或者双击Target1查看.map文件

    作者:典则

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32快速复习系列之十二:FLASH闪存读写详解

    发表回复