打印
[应用相关]

STM32单片机-FLASH闪存

[复制链接]
874|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-6-25 09:16 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、FLASH简介
STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
读写FLASH的用途:
  利用程序存储器的剩余空间来保存掉电不丢失的用户数据
  通过在程序中编程(IAP),实现程序的自我更新

在线编程(ICP)用于更新存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
在程序中编程(IAP)可以使用微控制器支持的任一种通信接口下载程序
  下图为中容量闪存模块组织
  主存储器:用来存放程序代码,主要且容量最大的部分,基本单位是1K的页,C8T6只有64K
  信息块:启动程序代码-系统存储器,存放原厂写入的Bootloader,用于串口下载。用户选择字节-存放独立的参数
  闪存存储器接口寄存器:普通外设,存储介质是SRAM,控制擦除和编程



二、FLASH工作原理
  下图为FLASH基本结构图
  整个闪存分为程序存储器、系统存储器和选项字节
  以C8T6为例,程序存储器为64K,最后一页地址是0x0800 FC00。左边控制器是闪存管理院,可以擦除和编程程序存储器和选项字节。选项字节配置程序存储器的读写保护



FPEC擦除和编程程序存储器和选项字节时需要前置操作

FLASH解锁:FLASH操作前需要解锁,在键寄存器写入指定的键值来实现
  FPEC有三个键值:RDPRT键 = 0x000000A5(解除读保护)、KEY1 = 0x45670123、KEY2 = 0xCDEF89AB
  复位后,FPEC被保护,不能写入FLASH_CR。在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。错误的操作序列会在下次复位前锁死FPEC和LASH_CR
  加锁:设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

  下图为闪存页擦除过程
  判断LOCK锁没锁,需要解锁,随后置寄存器、判断页地址、等待状态寄存器标志位,擦除



  下图为闪存全擦除过程
  判断LOCK锁没锁,需要解锁,随后置寄存器、等待状态寄存器标志位,擦除



  下图为闪存写入过程
  判断LOCK锁没锁,需要解锁,随后置寄存器、在指定地址写入数据、等待状态寄存器标志位



三、读写内部FLASH
  读内部FLASH指定地址的数据,只需要调用读函数即可uint16_t Data = *((__IO uint16_t *)(0x8000000)),其中数据位数可以更改,地址可以更改
  指定地址写数据时,需要先擦除再写
  下面为MyFLASH.c,其中包括读、擦除和写数据

#include "stm32f10x.h"                  // Device header

/*
@brief:读取指定地址FLASH里的地址-32、16和8位
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
        return *((__IO uint32_t *)(Address));//读取32位数据
}
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
        return *((__IO uint16_t *)(Address));//读取16位数据
}
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
        return *((__IO uint8_t *)(Address));//读取8位数据
}

/*
@brief:全擦除
*/
void MyFLASH_EraseAllPages()
{
        FLASH_Unlock();//解锁
        FLASH_EraseAllPages();
        FLASH_Lock();//锁上
}
/*
@brief:指定页擦除
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
        FLASH_Unlock();//解锁
        FLASH_ErasePage(PageAddress);
        FLASH_Lock();//锁上
}
/*
@brief:指定地址写32位数据
*/
void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data)
{
        FLASH_Unlock();//解锁
        FLASH_ProgramWord(Address,Data);
        FLASH_Lock();//锁上
}
/*
@brief:指定地址写16位数据
*/
void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
        FLASH_Unlock();//解锁
        FLASH_ProgramHalfWord(Address,Data);
        FLASH_Lock();//锁上
}




  由于FLASH是擦除后再写入,擦除后还容易丢数据,可以在RSAM里新建一个数组,FLASH的数据复制到SRAM,实现掉电不丢失,往FLASH里写入SRAM里的数据,实现参数的任意读写和保存
  首次上电,FLASH数据复制给SRAM数组时,需要判断第一个数据-标志位(任意设置),之后断电上电或者复位时,存在FLASH的数据(不丢失)继续复制给SRAM
  下面为Store.c

uint16_t Store_Data[512];

void Store_Init()
{
        //第一次需要判断标志位
        if(MyFLASH_ReadHalfWord(0x0800FC00) != 0xA5A5)
        {
                MyFLASH_ErasePage(0x0800FC00);
                MyFLASH_ProgramHalfWord(0x0800FC00,0xA5A5);
                for(uint16_t i = 1;i<512;i++)
                {
                        MyFLASH_ProgramHalfWord(0x0800FC00+i*2,0x0000);
                }
        }//设置闪存最后一页第一个半字为A5A5,剩下全部为0
       
        for(uint16_t i = 0;i<512;i++)
        {
                Store_Data = MyFLASH_ReadHalfWord(0x0800FC00+i*2);
        }//上电时保证数据不丢失,将FLASH数据复制到SRAM数组中
}
/*
@brief:往闪存里面写数据(SRAM数组-FLASH)
*/
void Store_Save()
{
        MyFLASH_ErasePage(0x0800FC00);
        for(uint16_t i = 0;i<512;i++)
        {
                MyFLASH_ProgramHalfWord(0x0800FC00+i*2,Store_Data);
        }
}
/*
@brief:清除FLASH
*/
void Store_Clear()
{
        for(uint16_t i = 1;i<512;i++)//标志位不擦除
        {
                Store_Data = 0x0000;
        }
        Store_Save();
}



  下面为main.c,按键1-SRAM数组值加1,按键2-清零数组

uint8_t KeyNum;

int main(void)
{
        OLED_Init();
        Key_Init();
        Store_Init();
       
        OLED_ShowString(1,1,"FLAG:");
        OLED_ShowString(2,1,"Data:");
        while(1)
        {
                KeyNum = Key_GetNum();
                if(KeyNum == 1)
                {
                        Store_Data[1] ++;
                        Store_Data[2] += 2;
                        Store_Data[3] += 3;
                        Store_Data[4] += 4;
                        Store_Save();//SRAM数组写入到FLASH
                }
                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);
        }
}




四、读取芯片ID
  读取ID只需要读取指定地址的数据即可

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 uint32_t *)(0x1FFFF7E8+0x08)),8);

        while(1)
        {
               
        }
}
————————————————

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

原文链接:https://blog.csdn.net/m0_51319492/article/details/139832584

使用特权

评论回复
沙发
LOVEEVER| | 2024-6-25 19:16 | 只看该作者
整个闪存分为程序存储器、系统存储器和选项字节

使用特权

评论回复
板凳
为你转身| | 2024-6-30 15:15 | 只看该作者
在STM32微控制器中,LPUART(低功耗UART)的唤醒功能通常与低功耗模式结合使用,以便在接收到特定条件下的数据或事件时唤醒微控制器。

使用特权

评论回复
地板
为你转身| | 2024-6-30 15:15 | 只看该作者
以下是一个简单的示例代码,展示了如何在STM32上配置LPUART以及如何使用唤醒功能。

使用特权

评论回复
5
理想阳| | 2024-6-30 15:22 | 只看该作者
内部闪存的容量还是很重要的。

使用特权

评论回复
6
suncat0504| | 2024-6-30 21:56 | 只看该作者
实际应用的时候,会不会发生异常,破坏存储的程序代码?

使用特权

评论回复
7
suncat0504| | 2024-6-30 21:56 | 只看该作者
总觉着这种处理不安全。现在芯片成本低,使用外部存储器不是更安全,容量也可以更大的吗?

使用特权

评论回复
8
而服务器人| | 2024-7-26 15:39 | 只看该作者
复位后,FPEC被保护,不能写入FLASH_CR

使用特权

评论回复
9
药无尘| | 2024-7-26 16:51 | 只看该作者
使用FLASH要注意该操作会占用总线

使用特权

评论回复
10
梵蒂冈是神uy| | 2024-8-23 14:42 | 只看该作者
主要是可以用于存放应用程序代码,且容量最大。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2028

主题

15903

帖子

13

粉丝