当前位置:   article > 正文

STM32的在线升级(IAP)实现方法:BOOT+APP原理详解_stm32的在线升级(iap)实现方法:boot+app原理详解

stm32的在线升级(iap)实现方法:boot+app原理详解

0 工具准备

Keil uVision5
Cortex M3权威指南(中文)
STM32参考手册

1 在线升级(IAP)设计思路

为了实现STM32的在线升级(IAP)功能,通常会将STM32的FLASH划分为BOOT和APP两个部分,BOOT就是引导APP的引导程序,当我们需要在线升级时就可以通过BOOT来实现。BOOT和APP在FLASH中的分布如下:
在这里插入图片描述

原理分析:
(1)当STM32复位后会跳转到FLASH首地址,也就是0x08000000的位置,读取1-4Byte获取主堆栈指针初始值(栈顶值)并设置,然后读取5-8Byte获取复位中断服务函数入口地址并执行,进入BOOT程序
(2)BOOT程序根据用户选择升级APP或者跳转到APP
(2.1)如果用户选择升级APP则擦除APP所在扇区,按照一定协议将APP程序复制到FLASH的APP扇区
(2.2)如果用户选择跳转到APP,首先失能全局中断,然后复位所有外设及RCC时钟,清除所有中断使能位及中断挂起标志,然后执行一些用户自定义的操作。最后设置主堆栈指针,跳转到APP的复位中断服务函数(相当于做了(1)中内核干的事情)。

2 BOOT设计

这里介绍一下BOOT跳转到APP函数的设计思路:

// 复位所有外设
void Per_DeInit(void)
{
    /* 复位APB1上的外设 */
    RCC->APB1RSTR = 0xFFFFFFFFU;
    RCC->APB1RSTR = 0x00U;

    /* 复位APB2上的外设 */
    RCC->APB2RSTR = 0xFFFFFFFFU;
    RCC->APB2RSTR = 0x00U;

    /* 复位AHB1上的外设 */
    RCC->AHB1RSTR = 0xFFFFFFFFU;
    RCC->AHB1RSTR = 0x00U;

    /* 复位AHB2上的外设 */
    RCC->AHB2RSTR = 0xFFFFFFFFU;
    RCC->AHB2RSTR = 0x00U;

    /* 复位AHB3上的外设 */
    RCC->AHB3RSTR = 0xFFFFFFFFU;
    RCC->AHB3RSTR = 0x00U;
}


// 跳转到APP
void Jump_to_APP(void)
{
    uint32_t i = 0;
    void (*SysMemBootJump)(void); /* 声明一个函数指针 */

    /* 关闭全局中断 */
    __disable_irq();

    /* 复位所有外设 */
    Per_DeInit();

    /* 设置所有时钟到默认状态,使用HSI时钟 */
    RCC_DeInit();


    /* 关闭所有中断,清除所有中断挂起标志 */
    for (i = 0; i < 8; i++)
    {
        NVIC->ICER[i] = 0xFFFFFFFF;
        NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    /* 关闭滴答定时器,复位到默认值 */
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    /* 重设独立看门狗超时时间为5S */
    IWDG_Config(4, 3125);
    /* 喂狗,给APP预留一些时间 */
    Raise_Dog();

    /* 使能全局中断 */
    __enable_irq();

    /* APP首地址+4就是APP复位中断服务程序地址 */
    SysMemBootJump = (void (*)(void))(*((uint32_t *)(FLASH_APP_ADDR + 4)));

    /* 设置主堆栈指针 */
    __set_MSP(*(uint32_t *)FLASH_APP_ADDR);

	/* 设置为特权级模式,使用MSP指针 (BOOT是RTOS的一定要设置,否则后面的APP无法运行,最好都加上无伤大雅)*/
    __set_CONTROL(0);
    
    /* 跳转到系统BootLoader */
    SysMemBootJump();

    /* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
    while (1)
    {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

相关知识:
(1)涉及到的复位寄存器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复位寄存器写入1复位相应外设,写入不复位。
(2)涉及到的SysTick寄存器
在这里插入图片描述
SysTick->CTRL:SysTick控制及状态寄存器
SysTick->LOAD:SysTick重装载数值寄存器
SysTick->VAL:SysTick当前数值寄存器
(3)涉及到的NVIC寄存器
(3.1)NVIC->ICER,中断失能寄存器,写入1失能中断
在这里插入图片描述
(3.2)NVIC->ICPR,中断挂起清除寄存器,写入1清除中断挂起
在这里插入图片描述
(4)涉及到的内核控制寄存器
在这里插入图片描述
这里我们设置为0,也就是特权级的线程模式+选择主堆栈指针MSP,恢复到MCU复位状态(BOOT是RTOS的一定要设置,否则后面的APP无法运行,最好都加上无伤大雅)。
(5)APP二进制文件含义
bin文件:
在这里插入图片描述
Byte1-4:0x20014168
Byte5-8:0x080101A1
Byte9-12:0x08012D75
Byte13-16:0x08012851
map文件:
__initial_sp 0x20014168 Data 0 startup_stm32f40xx.o(STACK)
Reset_Handler 0x080101a1 Thumb Code 8 startup_stm32f40xx.o(.text)
NMI_Handler 0x08012d75 Thumb Code 2 stm32f4xx_it.o(i.NMI_Handler)
HardFault_Handler 0x08012851 Thumb Code 8 stm32f4xx_it.o(i.HardFault_Handler)
可以看到,APP工程的bin文件含义如下:
Byte1-4:0x20014168 主堆栈指针初始值(栈顶值)
Byte5-8:0x080101A1 复位中断服务函数地址
Byte9-12:0x08012D75 NMI中断服务函数地址
Byte13-16:0x08012851 HardFault中断服务函数地址
该部分的定义在STM32的参考手册上也可以看到:
在这里插入图片描述
其实,我们只需要关注主堆栈指针初始值(栈顶值)和复位中断服务函数地址即可。如果想要了解APP前几个byte的全部内容,可以参看STM32参考手册的“STM32F405xx/07xx 和 STM32F415xx/17xx 的向量表”。
弄清楚了上述的寄存器使用方法和APP的bin文件内容后,接下来BOOT中跳转到APP的操作原理就一目了然了:
(1)关闭全局中断,避免被打断
(2)复位所有外设,为APP营造一个纯净的环境
(3)设置所有时钟到默认状态,为APP营造一个纯净的环境
(4)关闭所有中断同时清除所有中断挂起标志,避免APP使能中断后异常触发等情况
(5)关闭滴答定时器,复位到默认值,为APP营造一个纯净的环境【自定义】
(6)重设独立看门狗超时时间为10s,喂狗给APP预留一些时间(独立看门狗一旦打开就无法关闭)【自定义】
(7)使能全局中断,避免APP部分没有打开全局中断(这时也清除了所有中断使能位,可以避免跳转到APP过程被中断打断)
(8)函数指针指向APP的复位中断服务函数(也就是APP的第5-8Byte)
(9)设置主堆栈指针(也就是APP的前4Byte)
(10)跳转到APP
以上有2个地方需要特别注意:
(1)APP的复位中断服务函数地址是APP的第5-8Byte
(2)APP的主堆栈指针初始值(栈顶值)是APP的前4Byte

3 APP设计

APP设计时只需要修改工程的flash起始地址以及中断向量偏移地址寄存器即可。
(1)修改FLASH起始地址
在这里插入图片描述
如果我们的APP存放在FLASH的0x8010000开始的位置,则将FLASHA的起始地址修改为0x8010000即可。
(2)修改中断向量偏移地址
BOOT下我们的中断向量偏移地址为0x08000000和默认值一样无须特别设置,APP下由于FLASH起始地址被修改到0x8010000,因此需要将中断向量偏移地址设置为0x1000:

#define VECT_TAB_OFFSET  0x10000
  • 1

相关寄存器如下:
在这里插入图片描述
当STM32发生了中断需要响应时,内核会根据向量表偏移量寄存器的值在相应的FLASH空间找到异常服务函数入口地址(中断服务函数入口地址保存工作由编译器完成)。上电后的向量表如下:
在这里插入图片描述
假设我们设置的VTOR的值为0x8010000,在发生了硬错误时,会跳转到0x8010000+0x0000000C的位置找到硬错误中断服务函数地址并执行。这也是我们为什么需要在APP中设置VTOR的原因(BOOT里已经默认设置为0x0x8000000),保证我们的中断能够正确执行。

4 总结

(1)APP程序需要修改FLASH起始地址和向量表偏移量寄存器,以便内核能够在中断发生时进入正确的中断服务函数
(2)BOOT程序跳转到APP的过程实际上就是模拟内核的操作
(3)BOOT跳转到APP之前一定要失能所有中断、清除所有中断挂起标志,营造一个纯净的环境。最好将所有外设和RCC全部复位到初始值,使APP的环境更加贴近硬件复位后的环境。
(4)如果在BOOT内使能了独立看门狗,最好调整超时时间并喂狗保证在APP喂狗操作执行前预留足够的时间
(5)如果BOOT是RTOS,则一定要设置堆栈指针为MSP及特权级的线程模式
注意:
在这里插入图片描述
最好在BOOT内使能独立看门狗,当APP错误的时候依然能够触发复位(假如APP错误,会发生不可预知的错误,进入while(1)或其它地方看门狗均会减到0产生复位信号)。另外:独立看门狗的重载值是12位的,最大为4095!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/589441
推荐阅读
相关标签
  

闽ICP备14008679号