本帖最后由 多云转晴 于 2022-4-14 17:51 编辑
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执行,如何使用SRAM操作,可以查看我的上一篇帖子; (2)上面只说到FLASH的擦和写,读的方法是在debug调试状态时,通过memory来查看对应地址的信息,就可以知道FLASH是否操作成功; (3)上面已经提到过,在操作之前,要先知道自己使用的FLASH是多大容量,每页有多少字节,地址范围是多少以及使用的芯片的SRAM大小。
|