前言
很多情况下,有些数据需要有断电保存的功能,比如一些按键值,或者报警阈值等。这些如果只是单纯使用变量或者宏定义,是没办法在断电的情况下保存的。这时就需要将数据保存到Flash或者ROM,而有很多单片机都没有ROM,Flash是每个单片机都有的。
简介
Flash(快闪存储器)是一种非易失性存储器,用于存储数据和程序代码。它是一种闪存技术,可以在断电情况下保持数据的完整性,因此非常适合用于嵌入式系统和其他需要长期存储数据和程序的应用。
Flash存储器的主要特点包括:
- 非易失性:Flash存储器可以在断电情况下保持数据的完整性,不需要外部电源维持数据。
- 可擦写和可编程:Flash存储器可以被擦除和编程,允许修改存储的数据和程序。
- 高密度:Flash存储器具有高存储密度,可以存储大量的数据和程序代码。
- 快速访问时间:Flash存储器具有较快的读取和写入速度,使得数据的读取和写入操作能够迅速完成。
在嵌入式系统中,Flash存储器常用于存储启动代码、操作系统、应用程序和配置数据等。它通常与处理器或微控制器紧密集成,可以直接访问和执行存储在其中的程序代码。
需要注意的是,Flash存储器具有有限的擦写次数(10w次),过多的擦写操作可能会导致存储器的衰减和损坏。因此,在使用Flash存储器时应注意合理规划和管理数据的擦写操作。
准备资料
1、闪存区范围
在写擦写程序之前,第一件要做的是确定Flash的地址范围,从规格书的这个地方(P32页点击蓝色字体)可以打开Flash详细手册。
从手册可以查到所用芯片属于哪个类型。
务必查清自己用的单片机是什么型号,属于哪一种类型的单片机,我是深有感触,买的明明是stm32f103c8t6,然后一直用的0x0800C000地址写了一天,一直擦写不了,最后看到芯片上丝印写着stm32f103c6t6,直接崩溃了。
2、页大小
知道闪存大小范围之后,就是要知道使用的单片机一页是多少,打开Keil,全局搜索“FLASH_PAGE_SIZE”,比较好记,flash_页_大小,全大写,如果怕自己记不住,那就记住库文件stm32f1xx_hal_flash_ex.h,跟flash相关的库函数有关的只有两个,flash...或者flash_ex...,然后宏定义一般都是放在.h文件的,最后找到文件,一直往下翻就好了。
黑色字体就是所用单片机在使用的,0x400,1024bit也就是1k。
3、主闪存区范围
根据闪存编程手册所示,主闪存区0x08000000~0x08007fff。
4、擦除步骤
如闪存编程手册所示,向flash写数据之前,必须先擦除所在地址的数据,不然会直接跳过写操作,然后发出警告,警告由FLASH->SR的第二位FLASH_SR_PGERR发出。
擦除就是将所在地址全部置1,0xff,但是不能直接写0xff,必须使用特别的方式。
程序
1、页擦除
知道这些之后,就可以直接 写程序了。
先擦除,而擦除又分全片擦除和页擦除,先来看一下页擦除:
刚查到了,主闪存区0x08000000~0x08007fff,一页0x400,为了防止写入的数据把程序覆盖掉,可以从最后面拿出一页来,专门用作掉电保存。0x08007fff往前推一页,就是0x08007c00.
※特别注意
1、擦除完成之后,必须解除擦除模式,不然FLASH_CR_PER一直为1,会一直处于擦除模式,写不进去数据;
2、所有操作完成之后,锁定闪存控制寄存器访问。
3、进行flash操作时,必须关闭所有中断
在进行flash操作时,都必须禁用中断,因为flash操作是一个比较耗时的操作,如果不禁用中断,很有可能就造成操作不完整或者操作错误。
void Flash_PageErase(uint32_t address)
{
__disable_irq(); //关闭所有中断
while(HAL_FLASH_Unlock() == HAL_ERROR); //等待解锁成功,
FLASH->CR |= FLASH_CR_PER; //选择页擦除模式
FLASH->AR = address; //设置擦除地址
FLASH->CR |= FLASH_CR_STRT; //开始擦除
while(FLASH->SR & FLASH_SR_BSY); //等待擦除完成
FLASH->CR &= ~FLASH_CR_PER; //解除页擦除模式
HAL_FLASH_Lock(); //锁定flash
__enable_irq(); //打开所有中断
}
2、FLASH写数据
void Flash_Wirte(uint32_t address, uint16_t data)
{
__disable_irq();
while(HAL_FLASH_Unlock() == HAL_ERROR);
FLASH->CR |= FLASH_CR_PG;
*(__IO uint16_t*)address = data;
while(FLASH->SR & FLASH_SR_BSY);
FLASH->CR &= ~FLASH_CR_PG;
HAL_FLASH_Lock();
__enable_irq();
}
3、 全片擦除
注意一点,全片擦除不要随便用,因为这个是可以把程序也擦除的,一般只会用在程序锁定,烧不进程序的时候,外部中断进行全片擦除,然后重新烧程序的。
void Flash_MassErase(uint32_t address)
{
__disable_irq();
while(HAL_FLASH_Unlock() == HAL_ERROR);
FLASH->CR |= FLASH_CR_MER;
FLASH->CR |= FLASH_CR_STRT;
while(FLASH->SR & FLASH_SR_BSY);
HAL_FLASH_Lock();
__enable_irq();
}
4、FLASH读
读是最简单的了,找到要读的地址,直接赋值就好了,如下:
uint32_t Flash_Read(uint32_t address)
{
return *(__IO uint32_t *)address;
}
5、验证
主闪存区末地址0x08007FFF。
擦除之前0x08007c00地址0x1234;
擦除成功,擦除后为0xffff;
写成功,0x1234;
读成功,0x1234。