Flash作为一种非易失性(Non-Volatile)存储器,其最显著的特点在于掉电之后,其存储的内容不会丢失。还有一点需要注意的是,对于flash存储器件的数据访问往往都是以块为单位进行操作的,在这里,要对数据读取进行特殊说明,对于Nor Flash器件来说,是支持随机读取的,但Nand Flash器件是不支持的。 由于flash访问的特殊性,一般我们需要设计一套Flash烧写算法,至少要包括flash初始化,read、write、erase这些操作。说到这儿,仿佛一切都还算美好,只需要有一套适配的烧写算法即可。
但是,小编还是不得不泼一盆冷水。算法本身一般情况是无法复用的,每更换一个flash器件就要额外去编写一套新的算法,尽管有会者不难的属性加持,但适配所有的flash器件,工作量也可想而知。 小编今天就为大家提供一个适配几乎任意flash型号的flash程序,之所以说是几乎,不是小编谦虚啊,是因为我们这次要借东风,而这个东风就是Keil的FLM文件。也就是说,只有FLM所支持的,我们才可以使用。
FLM文件是什么?
熟悉Keil的朋友们都知道,当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件:
所谓Flash下载算法,是负责擦除或是下载应用数据到flash的一个软件。而Keil往往会集成不少FLM文件以支持大多数的flash型号。 当然,正如前文所述,这些算法也是根据不同型号的flash所编写的。只不过,前人们已经为我们种好了大树,我们可以直接在树下乘凉了。
FLM文件结构
当然,正是因为Keil规定了FLM文件的构成,它是一成不变的,我们才可以无忧无虑的对文件本身进行解析。 Keil规定,一个FLM文件中一般要有以下函数: 其中最为重要的有5个,我们来逐一说明: 1、int Init (unsigned long adr, unsigned long clk, unsigned long fnc); 负责flash器件的初始化工作,其中: 2、int EraseSector (unsigned long adr);擦除addr所指定地址处的整个sector 3、int ProgramPage (unsigned long adr, unsigned long sz, unsigned char*buf);对flash进行烧写操作,其中: adr:待烧写地址 sz:待烧写数据长度 bug:待烧写数据
4、int EraseChip (void); 擦除整块flash5、int UnInit (unsigned long fnc); Uninit flash, 并根据传入的fnc执行不同的flash后操作,fnc的定义同Init。下面让我们解析一下现有的FLM文件,以MIMXRT106x_QSPI_4KB_SEC.FLM为例:打开windows的命令行工具,输入readelf.py MIMXRT106x_QSPI_4KB_SEC.FLM-S:没错,这些正是我们需要的,也正是对于Flash进行操作所应该有的基本函数。
FLM文件解析与代码实现
我们下一步要做的就是老生常谈的事儿了,我们要将这些函数从FLM文件中提取出来,此处略过具体实现,可以参考小编 上传的代码。如此一来,我们就将所有用到的函数都打包成了一个整体,并记录各个函数所在的位置: 接下来,为了方便后续使用,定义对应的结构体变量: typedef struct {
int (*Init)(ulong adr, ulong clk, ulong fnc);
int (*UnInit)(ulong fnc);
int (*EraseSector)(ulong adr);
int (*ProgramPage)(ulong adr, ulong sz, uchar* buf);
int (*EraseChip)(void);
} flash_ops_t;同时定义一个宏,以对变量进行初始化: #define INIT_FLASH_OPS(ops) \
flash_ops_t ops = { \
.Init = CAST_VALUE_TO_FUNC(OPS_OFFSET + INIT_OFFSET, ops.Init), \
.UnInit = CAST_VALUE_TO_FUNC(OPS_OFFSET + UNINIT_OFFSET, ops.UnInit), \
.EraseSector = CAST_VALUE_TO_FUNC(OPS_OFFSET + ERASESECTOR_OFFSET, ops.EraseSector), \
.ProgramPage = CAST_VALUE_TO_FUNC(OPS_OFFSET + PROGRAMPAGE_OFFSET, ops.ProgramPage), \
.EraseChip = CAST_VALUE_TO_FUNC(OPS_OFFSET + ERASECHIP_OFFSET, ops.EraseChip), \
};
效果展示
实际使用就很方便了,用户只需要调用一次INIT_FLASH_OPS(ops); 即可实现变量声明已经变量赋值,我们以ProgramPage函数进行展示,代码如下: #define FLASH_BASE_ADDR(0x60000000U) #define SECTOR_SIZE (4096) #define PAGE_SIZE (256) #define OP_NUM (0U) 首先对sector进行擦除: ops.EraseSector(FLASH_BASE_ADDR +OP_NUM * SECTOR_SIZE); 烧写flash为0x56 char *pool = malloc(sizeof(char) * PAGE_SIZE);
memcpy(pool, (void*)FLASH_BASE_ADDR, PAGE_SIZE);
memset(pool, 0x56, PAGE_SIZE);
ops.ProgramPage(FLASH_BASE_ADDR + OP_NUM * PAGE_SIZE, PAGE_SIZE, (uchar*)pool);
free(pool);
烧写成功!
小编这次为大家带来了一种基于Keil的FLM文件进行Flash烧写的新思路,这样一来,在进行Flash操作时,我们可以不必重复造轮子,依靠Keil这颗大树,真的好乘凉。 换句话说,实现了一种几乎万能的Flash下载器,Keil所能支持的Flash器件,我们都可以对其进行操作。本文代码请 点击。
|