查看: 2147|回复: 0

[转发]万能的Flash烧写程序—基于FLM文件实现

[复制链接]
  • TA的每日心情
    奋斗
    昨天 09:27
  • 签到天数: 595 天

    [LV.9]以坛为家II

    51

    主题

    2225

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7072
    最后登录
    2024-4-28
    发表于 2021-4-22 09:37:21 | 显示全部楼层 |阅读模式
    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文件:

    1.png

    所谓Flash下载算法,是负责擦除或是下载应用数据到flash的一个软件。而Keil往往会集成不少FLM文件以支持大多数的flash型号。
    当然,正如前文所述,这些算法也是根据不同型号的flash所编写的。只不过,前人们已经为我们种好了大树,我们可以直接在树下乘凉了。

    FLM文件结构
    那么FLM文件是怎么个构成呢?Keil为我们提供了新建FLM工程的步骤,有兴趣的朋友可以自行参考。
    当然,正是因为Keil规定了FLM文件的构成,它是一成不变的,我们才可以无忧无虑的对文件本身进行解析。
    Keil规定,一个FLM文件中一般要有以下函数:
    2.png
    其中最为重要的有5个,我们来逐一说明:
    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); 擦除整块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:
    3.png
    没错,这些正是我们需要的,也正是对于Flash进行操作所应该有的基本函数。

    FLM文件解析与代码实现
    我们下一步要做的就是老生常谈的事儿了,我们要将这些函数从FLM文件中提取出来,此处略过具体实现,可以参考小编上传的代码。如此一来,我们就将所有用到的函数都打包成了一个整体,并记录各个函数所在的位置:
    4.png
    接下来,为了方便后续使用,定义对应的结构体变量:
    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);
    6.png
    烧写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器件,我们都可以对其进行操作。本文代码请点击


    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-29 02:38 , Processed in 0.103163 second(s), 19 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表