万能的Flash烧写程序—基于FLM文件实现
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文件结构
那么FLM文件是怎么个构成呢?Keil为我们提供了新建FLM工程的步骤,有兴趣的朋友可以自行参考。
当然,正是因为Keil规定了FLM文件的构成,它是一成不变的,我们才可以无忧无虑的对文件本身进行解析。
Keil规定,一个FLM文件中一般要有以下函数:
1、int Init (unsigned long adr, unsigned long clk, unsigned long fnc);
负责flash器件的初始化工作,其中:
adr: 设备首地址
clk:时钟频率(Hz)
fnc:要执行的flash操作,包括,1:Erase,2:Program,3:Verify
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); 擦除整块flash
5、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:
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;
[color=rgb(51, 102, 153) !important]复制代码
同时定义一个宏,以对变量进行初始化:
- #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), \
- };
[color=rgb(51, 102, 153) !important]复制代码
效果展示
实际使用就很方便了,用户只需要调用一次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);
[color=rgb(51, 102, 153) !important]复制代码
小编这次为大家带来了一种基于Keil的FLM文件进行Flash烧写的新思路,这样一来,在进行Flash操作时,我们可以不必重复造轮子,依靠Keil这颗大树,真的好乘凉。
换句话说,实现了一种几乎万能的Flash下载器,Keil所能支持的Flash器件,我们都可以对其进行操作。本文代码请点击。
好文章!
源码能不能贴出来 学习一下?
"本文代码请点击。"