FLASH,全称为“FLASH Memory”,属于内存器件的一种,是一种不挥发性(Non-Volatile)内存。但由于它特殊的物理特性,导致它与其他常见内存有根本性的差异;比如像SDRAM、RDRAM都属于挥发性内存,只要掉电,内存里的数据就无法保持,但FLASH不同,它在无电流供应的条件下也可以长久地保存数据,相当于一个硬盘。
FLASH根据不同容量大小,闪存模块组织又有小容量、中容量、大容量等不同的分类,因此在使用之前需要先查看手册,了解当前所使用的FLASH为哪类产品,它的FLASH大小为多少,每一页共有多少个字节以及其他必要信息。
下面,将介绍如何进行FLASH的擦读写操作。
PS:将通过程序实例进行介绍,使用的芯片型号为极海半导体-APM32F103RBT6。
FLASH:128K 每页1k SRAM:20K
一、头文件包含和宏定义操作
#ifndef __FLASH__H_
#define __FLASH__H_
#include "apm32f10x.h"
#include "Board.h"
#include "apm32f10x_fmc.h"
/*FLASH 128K SRAM 20K*/
#define Start_Addr ((uint32_t)0x08000000) //FLASH的起始地址
#define End_Addr ((uint32_t)0x08020000) //FLASH的结束地址
#define Flash_Page_Size ((uint32_t)0x00000400) //每页FLASH的大小
#define FLASH_PASS 1 //FLASH擦写成功标志
#define FLASH_FAILED 0 //FLASH擦写失败标志
void Flash_Init(uint32_t start_addr,uint32_t end_addr);
#endif
二、定义一个函数(用于FLASH的初始化等操作)
void Flash_Init(uint32_t start_addr,uint32_t end_addr)
设置的两个形参用于向函数传入FLASH操作的起始地址和结束地址。
三、定义各类变量,以便后续使用
uint32_t Write_addr = 0,i = 0;//定义写入的起始地址
uint32_t Data_Input[2] = {0x5a5a5a5a,0xa5a5a5a5};//定义写入的数据
uint32_t Data_counter = 0,Data_flag = 0;//定义写入数据的标志位
uint32_t Memory_Flag = FLASH_PASS;//定义写入数据状态标志位
uint32_t Flash_Page = ((end_addr - start_addr)/Flash_Page_Size);//页数计算
PS:
Write_addr作为一个局部变量,在每次需要对地址进行偏移时,可以操作该变量,保证传入的起始地址一直为同一个。
Data_Input是一个数组,存放写入FLASH的值。
Data_counter用来计数,Data_flag用来做标志位判断,使FLASH能写入不同的值。
Memory_Flag用来判定FLASH的操作是否成功。
Flash_Page是根据传入的起始地址和结束地址计算FLASH一共分为多少页。
四、对FLASH进行解锁操作(FLASH在锁定状态下无法擦写)
FMC_Unlock();//FLASH解锁
FMC_ClearStatusFlag(FMC_FLAG_OC|FMC_FLAG_PE|FMC_FLAG_WPE);//清除标志位
五、对FLASH进行擦除操作
Write_addr = start_addr;//将传输进来的起始地址赋给局部变量操作
①全擦操作:
/*先执行擦除操作*/
FMC_EraseAllPage();//全片擦除
②页擦操作:
/*页擦*/
for(i = 0;i < Flash_Page;i++)
{
FMC_ErasePage(Write_addr);
Write_addr += start_addr + (Flash_Page_Size * i);
}
PS:全擦即为FLASH的整片擦除;页擦是根据自己的需要,向页擦函数传入首地址,擦除指定的区域,上面的页擦函数是通过页擦,擦除整片FLASH的操作(每擦除一页进行一次地址偏移)。
六、对FLASH进行擦除验证
Write_addr = start_addr;//将传输进来的起始地址赋给局部变量操作
/*擦除校验*/
while(Write_addr < end_addr)
{
if(*(uint32_t*)Write_addr != 0XFFFFFFFF)
{
Memory_Flag = FLASH_FAILED;
}
Write_addr += 4;
}
PS:在擦除操作结束后,通过判断每个位是否为FF,来判断程序是否擦除成功;若擦除失败或有部分擦除不成功,标志位会被置为FAILED状态
七、对FLASH进行写操作
Write_addr = start_addr;//将传输进来的起始地址赋给局部变量操作
/*进行写入数据操作*/
while(Write_addr < end_addr)
{
FMC_ProgramWord(Write_addr,Data_Input[Data_flag]);//数据写入对应地址
Data_counter++;//计数标志位开始累加
Write_addr += 4;//地址偏移
if(Data_counter == 256)
{
Data_counter = 0;//计数标志位清零
Data_flag = !Data_flag;//翻转标志位进行翻转
}
FMC_ClearStatusFlag(FMC_FLAG_OC|FMC_FLAG_PE|FMC_FLAG_WPE);//清除标志位
}
PS:从起始地址开始写入数据,每次写入一个字的大小,同时Data_counter开始计数,即每写入1k字节就换一个数据,直至写到结束地址。
八、对写入的数据进行校验
Write_addr = start_addr;//将传输进来的起始地址赋给局部变量操作
/*写入数据验证*/
Data_counter = 0;//计数标志位清零
Data_flag = 0;//初始化翻转标志位
while(Write_addr < end_addr)
{
if(*(uint32_t*)Write_addr != Data_Input[Data_flag])
{
Memory_Flag = FLASH_FAILED;
}
Data_counter++;//计数标志位开始累加
Write_addr += 4;//地址偏移
if(Data_counter == 256)
{
Data_counter = 0;//计数标志位清零
Data_flag = !Data_flag;//翻转标志位进行翻转
}
}
PS:校验方法与擦除校验的方法类似,通过地址偏移来判断每个位是否准确写入了预定的数据,若出现异常,则可以通过标志位判断出来。
九、重新上锁、并做最终判断
FMC_Lock();//给FLASH重新上锁
Data_counter = 0;
Data_flag = 0;
if(Memory_Flag == FLASH_PASS)
{
//FLASH擦写成功
}
else
{
//FLASH擦写失败
}
十、注意事项
(1)若进行FLASH操作,那么存储和运行程序的工作就得交给SRAM执行。
(2)FLASH的擦和写,读的方法是在debug调试状态时,通过memory来查看对应地址的信息,就可以知道FLASH是否操作成功;
(3)在操作之前,要先知道自己使用的FLASH是多大容量,每页有多少字节,地址范围是多少以及使用的芯片的SRAM大小。