STM32 IAP远程更新

  IAP远程更新系列文章目录如下:

  1. IAP远程更新机制说明

  2. Bootload 和 App 间跳转的代码实现

  3. STM32内部Flash的操作函数
 

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];     /* 给函数指针变量赋值 */

猜你喜欢

转载自blog.csdn.net/qq_36310253/article/details/109673811