SeanLib系列函数库-MyFlash
查看其它库函数说明,请点击此处跳转到SeanLib主页
1. 本篇内容
本篇提供了一个适用于STM32F429的片内Flash操作库,支持读(一般不使用)、扇区擦除、写(按字节形式)三个方法,相比于HAL库中的函数,本函数库操作更简单方便。
本库不依赖HAL库等其它库文件。
其它的单片机型号暂不支持,但F4系列可能都支持,如果有更新,会更新此篇文章。
2. 使用说明
本库包含 MyFlash.lib 和 MyFlash.h 两个文件,头文件内容如下:
#include"SeanLib.h"/******************************************************************************* *本库适用于 STM32F429 ,其它型号未经测试 扇区划分如下: | Secotor Size | BANK1 | ADDRESS | BANK2 | ADDRESS | | 16K | 0 | 0x08000000 | 12 | 0x08100000 | | 16K | 1 | 0x08004000 | 13 | 0x08104000 | | 16K | 2 | 0x08008000 | 14 | 0x08108000 | | 16K | 3 | 0x0800C000 | 15 | 0x0810C000 | | 64K | 4 | 0x08010000 | 16 | 0x08110000 | | 128K | 5 | 0x08020000 | 17 | 0x08120000 | | 128K | 6 | 0x08040000 | 18 | 0x08140000 | | 128K | 7 | 0x08060000 | 19 | 0x08160000 | | 128K | 8 | 0x08080000 | 20 | 0x08180000 | | 128K | 9 | 0x080A0000 | 21 | 0x081A0000 | | 128K | 10 | 0x080C0000 | 22 | 0x081C0000 | | 128K | 11 | 0x080E0000 | 23 | 0x081E0000 | *******************************************************************************/typedefstruct{/*************************************************************************** 功能:读字节函数(一般用不上读Flash的函数) ***************************************************************************/void(*Read)(unsignedintAddr,unsignedchar*Data,unsignedintLength);/*************************************************************************** 功能:擦除指定编号的扇区,如0、1、2...23 ***************************************************************************/Sean_Result_t(*Erase)(unsignedcharSector);/*************************************************************************** 功能:写入一组字节数据 参数: Addr:Flash地址,必须是4字节对齐地址 Data:要写入的数据指针 Length:要写入的数据长度,允许不是4的倍数 返回值:操作完成状态或错误代码 ***************************************************************************/Sean_Result_t(*Write)(unsignedintAddr,unsignedchar*Data,unsignedintLength);}MyFlash_t;//系统已经创建一个Flash对象,应用程序中不需要再定义,可以直接使用MyFlash,其中封装了一些支持的方法externMyFlash_t MyFlash;使用Write方法写入可以确保写入成功,因为该方法在写入后会回读校验。
2.1 扇区擦除
Flash在写之前需要先擦除,因为Flash的存储单元仅允许由1写为0,若要由0改写为1,仅能通过擦除操作完成。而Flash的寿命与擦除次数有关,一般为10000次,因此项目中应谨慎擦除,采用合理的Flash存储策略,比如存储log,应先擦除1个扇区,之后开始逐条存储,在此扇区存满之前都不需要再擦除。
擦除是以扇区为单位的,单片机的扇区大小并不都是相同的,具体请看头文件开头部分的表格,列出了每个扇区的大小。
2.2 实例代码
以下是从SD卡读文件写入到Flash的代码:
//根据固件大小,计算需要擦除的扇区数量unsignedcharCalc_Erase_Count(unsignedintSize){constunsignedintSector_Size[16]={131072,// 0: 128KB262144,// 1: 256KB393216,// 2: 384KB524288,// 3: 512KB540672,// 4: 528KB557056,// 5: 544KB573440,// 6: 560KB589824,// 7: 576KB655360,// 8: 640KB786432,// 9: 768KB917504,// 10: 896KB1048576,// 11: 1MB1179648,// 12: 1.125MB1310720,// 13: 1.25MB1441792,// 14: 1.375MB1572864};// 15: 1.5MBunsignedcharcount;for(count=0;count<16;count++){if(Size<=Sector_Size[count]){returncount+1;}}return0;}//从指定的起始扇区号开始擦除指定数量的扇区Sean_Result_tFlash_Erase_Sectors(unsignedcharSector_Start,unsignedcharCount){unsignedchari;Sean_Result_t ret;for(i=0;i<Count;i++){ret=MyFlash.Erase(Sector_Start+i);if(ret!=Sean_PASS){returnSean_FAIL;}}returnSean_PASS;}unsignedcharbuf[1024]__attribute__((aligned(4)));//打开指定的固件文件,返回文件大小,若文件存在,则保持文件开启unsignedintOpen_File(constchar*Path){FRESULT ret;unsignedintFW_Size;ret=f_open(&File,Path,FA_OPEN_EXISTING|FA_READ);//打开文件if(ret==FR_OK)//打开成功时,返回文件大小{FW_Size=f_size(&File);//读取固件大小if(FW_Size==0)//若固件文件异常,则删除该文件{printf("Size of file is 0, delete file\r\n");f_close(&File);f_unlink(Path);return0;}returnFW_Size;}printf("Failed to open file %s:%s\r\n",Path,File_Err[ret]);return0;}//更新MCU的固件Sean_Result_tUpdate_MCU(constchar*FW_Path){FRESULT ret;unsignedintFW_Size;unsignedintRem_Count,Finish_Count;floatProcess;FW_Size=Open_File(FW_Path);//打开文件并读取固件大小if(FW_Size==0){returnSean_FAIL;}unsignedcharErase_Count;Erase_Count=Calc_Erase_Count(FW_Size);//计算要擦除的Flash扇区数量if(Flash_Erase_Sectors(8,Erase_Count)!=Sean_PASS)//从第8个扇区开始擦除需要擦除的所有扇区{printf("Failed to erase sector:%hhu\r\n",8+Erase_Count);f_close(&File);returnSean_FAIL;}unsignedintWrite_Addr=0x08080000;//固件起始地址unsignedintReadLen;unsignedshortPackage;Rem_Count=FW_Size;Finish_Count=0;while(Rem_Count>0)//读取固件写入Flash{Package=Rem_Count>1024?1024:Rem_Count;//每包大小1024字节ret=f_read(&File,buf,Package,&ReadLen);//从文件中读一包数据if(ret!=FR_OK||Package!=ReadLen)//读取失败,关闭文件{printf("Read file error: %s\r\n",File_Err[ret]);f_close(&File);returnSean_FAIL;}if(MyFlash.Write(Write_Addr,buf,Package)!=Sean_PASS){printf("Failed to write flash\r\n");f_close(&File);returnSean_FAIL;}Rem_Count-=Package;Finish_Count+=Package;Process=(float)Finish_Count;Process*=100.0f;Process/=(float)FW_Size;printf("Download process: %.1f%%\r\n",Process);if(Rem_Count==0)//更新完成,关闭文件{f_close(&File);returnSean_PASS;}Write_Addr+=Package;}returnSean_FAIL;}这里演示了擦除、写入的完整过程,Calc_Erase_Count函数用于计算要擦除的扇区数量,因为擦除操作耗时较长,扇区越大耗时越长,所以要采用最少化擦除原则。
