一、前言

①在ECU OTA 程序升级过程中,需要执行一段比较特殊的代码,这段代码实现对自身flash的擦除与写入,又称flash driver;
②为了安全的考虑,会尽可能的避免在代码中固化有对flash空间进行擦除或写入的操作,主要为了避免在程序跑飞时误调用该部分代码,使软件代码部分受到破坏;
③在OTA开始后,会先传入flash driver到RAM中,再开始刷写APP
④在OTA完成之后,会执行11 01 MCU复位的操作,复位前会清除这部分RAM空间,或者重新给ECU上电后,flash driver就失能了。
⑤本文参考自github的SummerFalls:UDS_S32K144_FlashDriver
工程链接:https://github.com/SummerFalls/UDS_S32K144_FlashDriver

二、FlashDriver的生成

1、修改加载文件

1.1、定义FLASH空间的地址和大小:

这部分可以参考芯片手册(建议选一块单独的flash空间)

#define DAFLASH_START 0x01010000
#define DAFLASH_SIZE 0x00010000 /* 64KB */

1.2、定义函数存储的地址段同理,

这部分数据也可以自定义①RW_IROM_FlashDrvOffset :这部分用于存储偏移量地址,比如有擦除和写入两个函数,那么就是0x1010000 0x00000008,我这边有三个,所以是0x0000000C②RW_IROM_FlashDrv :这部分用于存储flash driver函数

LR_IROM3 DAFLASH_START DAFLASH_SIZE ; 定义加载空间地址和大小,对应工程设置的IROM3。LR_IROM3是加载空间的名称
{
RW_IROM_FlashDrvOffset 0x01010000 0x0000000C
{
  *(.Flash_Driver_Section_Offset)
} RW_IROM_FlashDrv 0x0101000C 0x000005C4
{
  *(.Flash_Driver_Section)
}
}

2、定义相关结构体、数据类型和宏定义

2.1、数据宏定义说明

#define FLASH_DRV_OFFSET (0x0101000C) //用于定位驱动函数起始地址段
#define CAL_OFFSET(funcPtr) ((uint32_t)(funcPtr) - FLASH_DRV_OFFSET) //用于定位驱动函数的起始地址 #define FLASH_DRIVER_SECTION __attribute__((section (".Flash_Driver_Section"))) //用于指定驱动函数存放的地址段
#define FLASH_DRIVER_SECTION_OFFSET __attribute__((section (".Flash_Driver_Section_Offset"))) //用于指定驱动函数偏移量存放的地址段

2.2、数据类型定义说明

这部分的三个函数,可以参考芯片的SDK库进行定义
typedef ResultStatus_t (*tpfFLASH_WaitCmdComplete) (flash_cb_t callBack);
typedef ResultStatus_t (*tpfFLASH_WaitEraseAllComplete) (flash_cb_t callBack);
typedef ResultStatus_t (*tpfFLASH_ExecuteCommandInt) (FLASH_CMD_t cmd);

2.3、结构体定义说明

此部分三个函数指针,用于之后映射flash驱动函数

typedef struct
{
  tpfFLASH_WaitCmdComplete pfFLASH_WaitCmdComplete;
  tpfFLASH_WaitEraseAllComplete pfFLASH_WaitEraseAllComplete;
  tpfFLASH_ExecuteCommandInt pfFLASH_ExecuteCommandInt;
} tFlashDriverAPIInfo;

3、将驱动函数存放到指定区域

3.1、函数声明

将操作Flash的几个驱动函数存放到指定地址,注意这几个函数里边不能再包含其他函数或者全局变量,如果有调用其他函数的话,需要使用do...while语句进行展开

__attribute__((used)) FLASH_DRIVER_SECTION static ResultStatus_t FLASH_WaitCmdComplete(flash_cb_t callBack);
__attribute__((used)) FLASH_DRIVER_SECTION static ResultStatus_t FLASH_WaitEraseAllComplete(flash_cb_t callBack);
__attribute__((used)) FLASH_DRIVER_SECTION static ResultStatus_t FLASH_ExecuteCommandInt(FLASH_CMD_t cmd); __attribute__((used)) NVM_DRIVER_SECTION_OFFSET static const tFlashDriverAPIInfo gs_FlashDriverAPI = {
  (tpfFLASH_WaitCmdComplete) CAL_OFFSET(FLASH_WaitCmdComplete),
  (tpfFLASH_WaitEraseAllComplete) CAL_OFFSET(FLASH_WaitEraseAllComplete),
  (tpfFLASH_ExecuteCommandInt) CAL_OFFSET(FLASH_ExecuteCommandInt),
};

3.2、编译后查看map文件,获取地址信息

可以看到3.1的3个函数地址已经明确了

3.3、提取hex文件

编译后,找到hex文件,利用hexview提取相应信息:
(这里我已经删除了其他地址的数据)

这里需要注意的是,map文件中的起始地址需要向前一位开始复制:

 
FuncionName Address 
FLASH_WaitCmdComplete 0x0101000C-0x0101004B
FLASH_WaitEraseAllComplete  0x0101004C-0x0101008B 
FLASH_ExecuteCommandInt 0x0101008C-0x010100C3 

3.4、提取hex文件数据

将对应地址的hex数据提取出来,存放到一个数组(注意需要对齐,可以参考__):

3.5、将函数指针重定向

将之前定义的函数指针进行重定向:

tpfFLASH_WaitCmdComplete FLASH_WaitCmdComplete = (WaitCmdComplete_Handler)(u8WaitCmdComplete + 1);
tpfFLASH_WaitEraseAllComplete FLASH_WaitEraseAllComplete = (WaitEraseAllComplete_Handler)(u8WaitEraseAllComplete + 1);
tpfFLASH_ExecuteCommandInt FLASH_ExecuteCommandInt = (ExecuteCommandInt_Handler)(u8ExecuteCommandInt + 1);

这里+1的原因是,在ARM体系结构中,指令的最低有效位(LSB)被用来区分ARM指令和Thumb指令。当LSB为0时,表示该指令是一个32位的ARM指令;当LSB为1时,表示该指令是一个16位的Thumb指令。
在某些情况下,为了兼容不同指令集的代码,在进行指令访问时可能会对地址进行微调。当访问Thumb指令时,将地址的最低位设置为1,以表示要访问的是Thumb代码;当访问ARM指令时,将地址的最低位设置为0,以表示要访问的是ARM指令。

3.6、Debug测试

开始Debug调试......:
先定义两个测试函数:

先把0x01010000开始的这部分地址数据擦掉:

这里后边有数据是因为最小写入16个字节

到这里就验证完了。

4、生成FalshDriver文件

4.1、用hexview操作

把提取到的文件在hexview中打开,然后在左上角窗口cut这部分数据

然后粘贴到指定地址(因为这部分数据是要传到RAM里边的,所以起始地址需要在RAM部分),我这里是存在了0x200039F0开始的地址

然后在keil里边利用之前创建的函数指针重定向这部分数据
先定义好函数的起始地址:

#define FLS_WAIT_CMD_ADD 0x200039FC
#define FLS_WAIT_ERASE_ADD 0x20003A3C
#define FLS_EXEC_CMD_INT_ADD 0x20003A7C

然后将函数指针重定向:

void FlashApiInit(){
  FLASH_WaitCmdComplete = (tpfFLASH_WaitCmdComplete)(FLS_WAIT_CMD_ADD + 1);
  FLASH_WaitEraseAllComplete = (tpfFLASH_WaitEraseAllComplete)(FLS_WAIT_ERASE_ADD + 1);
  FLASH_ExecuteCommandInt = (tpfFLASH_ExecuteCommandInt )(FLS_EXEC_CMD_INT_ADD + 1);
}

只需要在传入FlashDriver后,调用这个函数即可,注意存放这部分的RAM空间在这过程中应该是没有被其他代码使用的,不然可能会出错。

三、End

被这部分折磨了挺久,好在终于搞定了,如果对大家有帮助的话,点个赞把。

KeilMDK制作FlashDriver的更多相关文章

  1. 【AR实验室】ARToolKit之制作自己的Marker/NFT

    0x00 - 前言 看过example后,就会想自己动动手,这里改改那里修修.我们先试着添加自己喜欢的marker/nft进行识别. 比如我做了一个法拉利的marker: 还有网上找了一个法拉利log ...

  2. Unity3d学习 制作地形

    这周学习了如何在unity中制作地形,就是在一个Terrain的对象上盖几座小山,在山底种几棵树,那就讲一下如何完成上述内容. 1.在新键得项目的游戏的Hierarchy目录中新键一个Terrain对 ...

  3. 制作类似ThinkPHP框架中的PATHINFO模式功能

    一.PATHINFO功能简述 搞PHP的都知道ThinkPHP是一个免费开源的轻量级PHP框架,虽说轻量但它的功能却很强大.这也是我接触学习的第一个框架.TP框架中的URL默认模式即是PathInfo ...

  4. 使用Visual Studio SDK制作GLSL词法着色插件

    使用Visual Studio SDK制作GLSL词法着色插件 我们在Visual Studio上开发OpenGL ES项目时,避免不了写Shader.这时在vs里直接编辑shader就会显得很方便. ...

  5. 利用CSS中的:after、: before制作的边三角提示框

    小颖昨天分享了一篇参考bootstrap中的popover.js的css画消息弹框今天给大家再分享一篇使用:before和:after伪元素画消息弹框的CSS. 画出来是介个酱紫的: 有没有觉得画的萌 ...

  6. Photoshop将普通照片快速制作二次元漫画风格效果

    今天为大家分享Photoshop将普通照片快速制作二次元漫画风格效果,教程很不错,对于喜欢漫画的朋友可以参考本文,希望能对大家有所帮助! 一提到日本动画电影,大家第一印象肯定是宫崎骏,但是日本除了宫崎 ...

  7. 前端制作动画的几种方式(css3,js)

    制作动态的网页是是前端工程师必备的技能,很好的实现动画能够极大的提高用户体验,增强交互效果,那么动画有多少实现方式,一直对此有选择恐惧症的我就总结一下,以便在开发的时候选择最好的实现方式. 1.css ...

  8. JS图片上传预览插件制作(兼容到IE6)

    其实,图片预览功能非常地常见.很意外,之前遇到上传图片的时候都不需要预览,也一直没有去实现过.现在手上的项目又需要有图片预览功能,所以就动手做了一个小插件.在此分享一下思路. 一.实现图片预览的一些方 ...

  9. 用FSM一键制作逐帧动画雪碧图 Vue2 + webpack

    因为工作需要要将五六十张逐帧图拼成雪碧图,网上想找到一件制作工具半天没有找到,就自己用canvas写了一个. 写成之后就再没有什么机会使用了,因此希望有人使用的时候如果遇到bug了能及时反馈给我. 最 ...

  10. C#制作简易屏保

    前言:前段时间,有个网友问我C#制作屏保的问题,我瞬间懵逼了(C#还可以制作屏保!).于是我去查阅相关资料,下面把C#如何制作屏保的过程及我学习过程的心得也记录下来,希望对需要的人能有帮助. 基本思路 ...

随机推荐

  1. ByteHouse MaterializedMySQL 增强优化

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 前言 社区版 ClickHouse 推出了MaterializedMySQL数据库引擎,用于将 MySQL 中的表 ...

  2. 全网最详细中英文ChatGPT-GPT-4示例文档-快速创意生成从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  3. data.frame数据框操作——R语言

    统计分析中最常见的原始数据形式是类似于数据库表或Excel数据表的形式. 这样形式的数据在R中叫做数据框(data.frame). 数据框类似于一个矩阵,但各列允许有不同类型:数值型向量.因子.字符型 ...

  4. pysimplegui之窗口大小,位子,主题等属性修改

    重点 1finalize()或Window参数finalize=True 调用以强制窗口通过初始化的最后阶段.这将导致 tkinter 资源被分配,以便它们可以被修改.这也会导致您的窗口出现.如果您不 ...

  5. day39:MySQL:查询操作之单表查询&多表查询&子查询

    目录 part1:单表查询 1.where条件的使用 2.group 子句 分组分类 3.having 数据在分类分组之后,进行二次数据过滤 4.order by 排序, 按照什么字段进行排序 5.l ...

  6. 生成df的几种方法

    法一: pd.DataFrame( [ (第一行),(第二行),(第三行)] ) df = pd.DataFrame([('bird', 389.0), ('bird', 24.0), ('mamma ...

  7. Linux(五)用户管理与文件权限

    1 常用的基本命令 Shell可以看作一个命令解释器,为我们提供一个交互式的文本控制台界面,可以通过终端控制台来输入命令,由shell进行解释并最终交给linux内核运行.可以看作用户和硬件的桥梁. ...

  8. .NET Core反射获取带有自定义特性的类,通过依赖注入根据Attribute元数据信息调用对应的方法

    前言 前段时间有朋友问道一个这样的问题,.NET Core中如何通过Attribute的元数据信息来调用标记的对应方法.我第一时间想到的就是通过C#反射获取带有Custom Attribute标记的类 ...

  9. Semantic Kernel 入门系列:🍋Connector连接器

    当我们使用Native Function的时候,除了处理一些基本的逻辑操作之外,更多的还是需要进行外部数据源和服务的对接,要么是获取相关的数据,要么是保存输出结果.这一过程在Semantic Kern ...

  10. 网络框架重构之路plain2.0(c++23 without module) 环境

    接下来本来就直接打算分享框架重构的具体环节,但重构的代码其实并没有完成太多,许多的实现细节在我心中还没有形成一个定型.由于最近回归岗位后,新的开发环境需要自己搭建,搭建的时间来说花了我整整一天的时间才 ...