IAP远程更新系列文章目录如下:
1. IAP远程更新机制说明
文章目录
1. IAP更新机制
IAP即为In APPlication Programming(在应用中编程),一般情况下,设备在出厂时就已经使用仿真器烧录了应用代码,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再重新烧录代码,这样增加了很多不必要的麻烦。
为了解决上述问题,IAP方案将代码区划分为两部分,两部分区域各存放一个程序,一个叫Bootloader(引导加载程序),另一个叫APP(用户应用程序)。Bootloader在出厂时就固定下来,在需要变更APP时只需要通过触发Bootloader对APP的擦除和重新写入即可完成用户应用的更换。
STM32的内部闪存(FLASH)地址起始于0x08000000,一般情况下,程序文件就从此地址开始写入。此外STM32F1xx是基于Cortex-M3内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
2. 片内Flash的划分
通常将 Bootload 划分为两个或三个区域,如果有片外的存储器(eeprom或者Flash),可以将STM32内部的Falsh划分为两个区域Bootload区和App区,此时可以将启动标志位等参数存储在片外的存储器中。如果没有片外的存储器,需要将STM32的内部Flash划分为3个区域,多了一个存储启动参数的区域。
除此之外,有时候工程需要压缩App区域的大小,因为STM32的标准库再加上一些其他外设的库文件,将增大App区域所占的空间,对于lash较小的芯片或者采用LoRa等速度较慢的无线传输方式来更新的情况不太友好,所以有时会将一些库函数都定义在Bootload区,App中只需调用Bootload提供的接口函数即可完成功能,所以需要多一块区域来存储共有函数。
3. 函数和变量的绝对地址定位在IAR中的实现
为了实现共有函数的功能,需要补充一下函数和变量定位在绝对地址的实现方法。
3.1 IAR的扩展关键字
@ /* 用于变量的绝对地址定位 */
__no_init /* 禁止系统启动时初始化变量 */
__root /* 保证没有使用的函数或者变量也能够包含在目标代码中 */
3.2 函数的绝对定位
示例:函数定义时后面加上 @" … …"
void sendstr(unsigned *buf, unsigned int len) @".sendstr"
{
return;
}
在链接文件 .icf 中添加如下内容。其中0x08017000表示在Flash中的地址,.sendstr必须与函数@后面双引号中内容一致
place at address mem:0x08017000 { readonly section .sendstr};
3.3 变量的绝对定位
示例如下,变量绝对定位,无须修改.icf链接文件,直接指定。
__no_init char array1[100]@0x2000B000; /* 定义一个数组变量 */
3.4 常量的绝对定位
__root const char str1[4]@".MYSEG"="test"; /* 定义一个数组常量 */
常量绝对定位,需要改.icf文件:
place at address mem:0x08018500 { readonly section .MYSEG}; /* 常量放在 0x08018500位置 */
3.5 c文件的绝对定位
例如要将 test.c 文件定位到Flash的绝对地址,那么在 .icf文件中应该添加:
place at address mem:0x08018000 { section .text object test.o }; /* 将 test.c 编译后的 .o 文件放在0x08018000 位置 */
编译完成后整个test.c文件的所有函数,都在0x08018000 之后。
3.6 跨工程固件更新注意事项
固件更新区的绝对定位的函数,不能随意调用其他库函数,那些被调用的函数也必须是绝对定位的。否则跨工程更新固件,会导致失败,因为被调用的函数在不同工程里,动态链接到的位置不同。
绝对定位的函数,如果要使用常量,那么被使用的常量也必须是绝对定位的。否则跨工程更新固件,会导致失败。
绝对定位的函数,如果要使用全局变量,那么被使用的常量也必须是绝对定位的。否则跨工程更新固件,会导致失败。而局部变量则不受此限制。
绝对定位的函数,如果要使用全局变量,那么被使用的常量也必须是绝对定位的。否则跨工程更新固件,会导致失败。而局部变量则不受此限制。
特别注意:如果要将STM32的标准库作为共有函数只在 Bootload 中定义,在App只调用,需要注意 stm32f10x_rcc.c 中定义了两个全局的数据,需要将其定义在绝对位置
/* stm32f10x_rcc.c */
/* 修改前 */
//static __I uint8_t APBAHBPrescTable[16] = {0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};
//static __I uint8_t ADCPrescTable[4] = {2, 4, 6, 8};
/* 修改后 */
__root const uint8_t APBAHBPrescTable[16]@".ARRAY_1"={0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 3, 4, 6, 7, 8, 9};
__root const uint8_t ADCPrescTable[4]@".ARRAY_2"={2, 4, 6, 8};
/* stm32f10x_flash.icf 将数组定义在绝对位置 */
place at address mem:0x0800FC00 { readonly section .ARRAY_1}; /* 63K开始 */
place at address mem:0x0800FC10 { readonly section .ARRAY_2}; /* 63K+16B开始 */
3.7 共有函数的调用
在共有函数区域某个固定的位置存储共有函数地址表,APP定义指向函数地址表的函数指针,用函数指针调用共有函数。
3.7.1 Bootload中的定义
例如:Bootload中定义一个test.c文件里面定义两个函数
void fun1(uint32_t num) { }
void fun2(void) { }
定义绝对地址常量来表示共有函数地址表
__root const uint32_t func_table[]@".COMMON_FUNC_SEG" = {
(uint32_t)&fun1,
(uint32_t)&fun2
}; /* bootload中声明一个共有函数表,uint32_t 类型是因为函数函数的地址)占用四个字节 */
在链接文件中定义如下:将共有函数地址表定义在绝对地址。
place at address mem:0x0800F800 { readonly section .COMMON_FUNC_SEG}; /* 62K开始 */
3.7.2 App中的调用
在APP中的调用,首先声明函数指针,然后定义一个函数指针类型的变量,让这个变量指向共有函数表的相应位置即可。接着可以利用声明定义的函数指针对共有函数进行调用。
typedef void (*app_fun1)(uint32_t num); /* 声明函数指针 */
typedef void (*app_fun2)(void);
#define FUNC_TABLE_ADDR (0x0800F800) /* 定义函数表的位置 */
app_fun1 fun1; /* 定义函数指针类型的变量 */
app_fun2 fun2; /* 定义函数指针类型的变量 */
uint32_t *funcTableAddr = (uint32_t *)FUNC_TABLE_ADDR;
fun1 = (fun1)funcTableAddr[0]; /* 必须按照Bootload中的定义顺序来找 */
fun2 = (fun2)funcTableAddr[1]; /* 给函数指针变量赋值 */