前面那篇文章中解决了一个怪问题,其中一点就是软复位采用直接跳转导致出现该怪现象,因此将软复位中的复位代码改成看门狗复位。然而,产品中的另外一个功能“在线升级”在完成升级后也是采用的直接地址跳转实现的复位,最好也改成看门狗复位。说干就干,可是一看代码就傻眼啦,原来这一块的代码是直接用二进制表示的一块数据,然后通过函数指针来调用的。
为什么会这样实现呢,分析了半天得到结论:由于在线升级必须擦除flash,那么升级用的代码也会被擦除,就无法实现升级了。所以只能将这些代码拷贝到ram中,然后从ram中运行来升级flash中的代码,才合乎逻辑。但是拷贝升级代码就必须知道所有升级代码的起始地址和长度,这些信息如何得到呢?因此,先将升级代码编译好,然后作为二进制数据存在数组中,就能够知道代码段的起始地址和长度啦。这种实现方法倒是很巧妙,可是如果要修改升级代码可就傻眼啦,而且也让人感觉不怎么规范。那么有没有别的方法呢?
其实很多嵌入式系统的启动代码中经常有将flash中的代码拷贝到ram中执行的例子,其获取代码起始地址和长度的方法多是采用段的基地址和长度来计算。因此,可以考虑利用scatter文件中定义的段来获取升级代码的基地址和长度。修改scatter文件如下:
ROM_EXEC 0x00000000 0x40000 { Startup.o (vectors, +First) * (+RO) } ROM_IAP +0 { Iapcode.o (+RO) }
|
其中iapcode.o就是升级代码文件的目标文件。这样就可以在程序中通过|Image$$ROM_IAP$$Base|来获取iapcode.o的基地址,而通过|Image$$ROM_IAP$$Limit|得到iapcode.o的末端地址,两者相减则得到长度。可是在C语言里面无法访问这两个变量,没有办法,只好在Startup.S中实现下面两个函数,用于获取这两个参数:
__get_iap_base LDR R0,=|Image$$ROM_IAP$$Base| MOV PC,LR
__get_iap_limit LDR R0,=|Image$$ROM_IAP$$Limit| MOV PC,LR
|
这时候,在新的image被发送到终端,调用IAP代码烧写程序之前,则可以通过调用上面这两个函数得到IAP代码的信息,然后将IAP代码拷贝到指定的ram中,再跳转到IAP代码的入口函数处执行,就可以在ram中实现烧写程序了。
base = __get_iap_base(); limit = __get_iap_limit(); size = limit - base; rbase = ADDR_IAPCODE; pf = (pfvoid_t)((UINT)Save2Flash - base + rbase);
memcpy((UCHAR *)rbase, (UCHAR *)base, (ULONG)size); (*pf)();
|
其中,Save2Flash就是IAP代码的入口函数,通过它的地址可以求得在ram中的入口函数的地址。而其中rbase则是指定的IAP代码在ram中的基地址。通过这些修改,并自己实现啦IAP的代码,就完成了升级程序部分代码的修改,这样以后如果需要修改升级程序的功能的话,就不会那么麻烦啦。通过实验,以上修改完全可以达到要求。
阅读(1020) | 评论(0) | 转发(0) |