Flash Driver测试实验——LED Driver

1、什么是Flash Driver

考虑到汽车ECU升级过程中可能存在外部干扰导致跑飞情况,如果直接将flash擦除有关程序固化到bootloader中,有可能在汽车运行过程中导致单片机flash被擦除或篡改的可能性,严重影响汽车的功能安全,所以一般会将与flash相关的程序单独剥离出bootloader,打包作为flash driver通过上位机下发的方式给到单片机。同时,单片机在接收到完整的flash driver包并通过校验后,会将其放入ram段执行调用;当在IAP升级过程中出现异常掉电或完成升级跳转至app程序中时,在ram段中的flash driver就不复存在了。

2、实验过程

在仔细思考flash driver运行过程后,我发现想要实现功能主要有以下疑问点:①如何将driver部分代码从flash搬入ram段执行;②如何在代码中(这里指整个代码工程)没有driver代码的情况下给driver分配一块区域;③通过什么方式将driver代码写入到规定好的ram段中。

由于仅做实验用途,我选用了最简单的测试代码——LED闪烁,测试环境为AutoChips杰发的AC7840x系列单片机开发板(ARM核 编译器Keil5),将LED闪烁的代码从工程中剔除,再通过按键触发将LED闪烁代码写入ram段,实现这个测试,下面我将根据这三个疑问点分布展开:

2.1 如何将driver部分代码从flash搬入ram段执行

通过Keil5的Linker修改.sct文件实现,操作步骤如下:

2.1.1 修改c代码

在想要放入ram段执行的函数前后分别加入#pragma arm section code = "XXX"和#pragma arm section字段,其中XXX可以自命名。要注意,如果你包含的函数中存在函数的调用,建议将所有相关的函数均包含在section字段中间(初始化函数不用放入),方便我们后面进行实验。

#pragma arm section code = "LED_DRV"
/*!
* @brief  GPIO翻转点亮函数
*
* @param[in] none
* @return none
*/
void LED_DRV_TOGGLE(void)
{
    GPIOC->PIOR = (1<<6);
    GPIOC->PIOR = (1<<7);
}
#pragma arm section

2.1.2 修改.sct文件

1、在勾选Use Memory Layout from Target Dialog的情况下先进行一次编译,编译器会根据你Target页的配置生成一个.sct文件

2、点击options魔术棒,选择linker

3、取消勾选Use Memory Layout from Target Dialog

3、点击Edit,即可弹出.sct文件

4、在单片机ram段处加入代码 *.o (LED_DRV),括号内的名字为前面在c代码中定义的section名称,这里大家根据自己用的单片机调下添加的地址即可,博主用的单片机SRAM起始地址为0x1FFF0000

LR_IROM1 0x00000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x00000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x1FFF0000 0x00010000  {  ; RW data
   *.o (LED_DRV)
  }
  RW_IRAM2 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

5、编译后查看map文件即可发现,编译器已经将flash的地址搬入ram中了,此时仿真程序也发现pc指针也指向的ram段

Execution Region LED_DRV (Exec base: 0x1fff0000, Load base: 0x00003008, Size: 0x00000018, Max: 0x00000018, ABSOLUTE)

Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

0x1fff0000   0x00003008   0x00000018   Code   RO          104    LED_DRV             gpio.o

2.2 如何在代码中(这里指整个代码工程)没有driver代码的情况下给driver分配一块区域

我们回到2.1中说到的.sct文件,之前添加的代码将LED_DRV这个section放在了RW_IRAM1下,并且通过map文件我们可以知道,咱们的LED_DRV占用了0x18个大小的字节,知道了以上信息,下面更改一下.sct文件,将LED_DRV从RW_IRAM1里分离出来就可以了。

LR_IROM1 0x00000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x00000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x1FFF0000 0x00010000  {  ; RW data
   *.o (LED_DRV)
  }
  RW_IRAM2 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

改动完的.sct文件如下:可以看到我们单独定义了一块区域专门用来放0x18大小的LED_DRV section,这样我们就在SRAM开辟了一块区域。

LR_IROM1 0x00000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x00000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  LED_DRV 0x1FFF0000 0x00000018  {	 ; LED_DRV code
   *.o (LED_DRV)
  }
  RW_IRAM1 0x1FFF0018 0x0000FFE8  {  ; RW data
  }
  RW_IRAM2 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

有的朋友就要说了,不对啊,光改了sct文件你现在flash里还是存在原来咱们在LED_DRV里添加的函数代码啊。说的没问题,下面我们之间把函数里面的内容删除,就留下一个“空壳”函数不就完成了咱们需要的操作了吗?代码如下:至此咱们完成了两项疑问点的操作,就剩下一项了,把LED_DRV_TOGGLE这个函数里面的代码重新放回到ram里去执行。

#pragma arm section code = "LED_DRV"
/*!
* @brief  GPIO翻转点亮函数
*
* @param[in] none
* @return none
*/
void LED_DRV_TOGGLE(void)
{
	//空函数等待写入
//	GPIOC->PIOR = (1<<6);
//	GPIOC->PIOR = (1<<7);
}
#pragma arm section

2.3 通过什么方式将driver代码写入到规定好的ram段中

首先,既然要写到ram里,我们就需要有东西写进去,可以通过查看hex文件的方式了解到目标写入的内容。在2.1中还没有删除driver部分的代码,从map文件可以看到载入地址为0x00003008

打开hex文件找到地址所在代码数据,从0x00003008后共查找24个字节,即为咱们要的led driver代码了。

那么,重点到用什么方式写入呢,这里想了一些方法还是决定把led driver代码放到数组里,再传输到ram上即可,至此就完成了搬运的操作了,实验的目的也达成了。下面贴一下主函数给大家参考,在正常上电后LED为熄灭状态,当开关按下后在ram段写入led闪烁的驱动代码,然后就可以看到led以1s的周期闪烁了。

uint8_t function_code[0x18] = {0x40, 0x20, 0x04, 0x49, 0x08, 0x60, 0x80, 0x20, 0x02, 0x49, 0x8C, 0x39, 0xC1, 0xF8, 0x8C, 0x00, 0x70, 0x47, 0x00, 0x00, 0x8C, 0x50, 0x08, 0x40};

int main(void)
{
    uint8_t i;
    SystemClock_Config();	//初始化时钟
    GPIO_LedInit();		//初始化板载LED引脚
    GPIO_KeyInit();		//初始化板载按键引脚
    while (1)
    {
		if(1 == Get_Key4Value())	//按下开关后写入
		{
			for(i = 0; i<0x18; i++)
			{
				*(uint8_t *)(0x1fff0000 + i) = function_code[i];
			}
		}
		LED_DRV_TOGGLE();
		OSIF_TimeDelay(500);
    }
}

3、小结

通过此实验发现了一些问题,这里提出来:1、所有driver相关的函数需要全部放入ram段,不然编译器在编译之后会在map文件中添加一些链接,相当于还有一部分代码还是在flash中,没有达到driver不放在flash中的目的;2、文章只是使用了总工程下的hex文件作为写入ram的代码参考,如何单独制作一个driver工程并使用其hex文件,还存在一些问题。

  • 23
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

您猜我猜不猜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值