用KeilMDK实现FlashDriver的制作`

一、前言

①在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)) FLASH_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文件,获取地址信息

![map文件查看地址信息](https://img-blog.csdnimg.cn/b940a5b634d74415951ccf4ad7a612db.pngMAP文件

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

3.3、提取hex文件

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

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

FuncionNameAddress
FLASH_WaitCmdComplete0x0101000C-0x0101004B
FLASH_WaitEraseAllComplete0x0101004C-0x0101008B
FLASH_ExecuteCommandInt0x0101008C-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

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

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值