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

news/2025/2/10 7:42:07/

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)涉及到的复位寄存器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
复位寄存器写入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

相关寄存器如下:
在这里插入图片描述
当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!!!


http://www.ppmy.cn/news/1295589.html

相关文章

【通义千问】大模型Qwen GitHub开源工程学习笔记(5)-- 模型的微调【全参数微调】【LoRA方法】【Q-LoRA方法】

摘要: 训练数据的准备 你需要将所有样本放到一个列表中并存入json文件中。每个样本对应一个字典,包含id和conversation,其中后者为一个列表。示例如下所示: [{"id": "identity_0","conversations": [{"from": "user",…

用通俗易懂的方式讲解:使用 Mistral-7B 和 Langchain 搭建基于PDF文件的聊天机器人

在本文中&#xff0c;使用LangChain、HuggingFaceEmbeddings和HuggingFace的Mistral-7B LLM创建一个简单的Python程序&#xff0c;可以从任何pdf文件中回答问题。 一、LangChain简介 LangChain是一个在语言模型之上开发上下文感知应用程序的框架。LangChain使用带prompt和few…

华为端口隔离高级用法经典案例

最终效果&#xff1a; pc4不能ping通pc5&#xff0c;pc5能ping通pc4 pc1不能和pc2、pc3通&#xff0c;但pc2和pc3能互通 vlan batch 2 interface Vlanif1 ip address 10.0.0.254 255.255.255.0 interface Vlanif2 ip address 192.168.2.1 255.255.255.0 interface MEth0/0/1 i…

Vue中Vuex的环境搭建和原理分析及使用

Vuex的环境搭建 Vuex是Vue实现集中式数据管理的Vue的一个插件&#xff0c;集中式可以理解为一个老师给多个学生讲课。 Vue2.0版本的安装&#xff1a; npm i vuex3 使用Vuex需要在store中的index.js引入Vuex和main.js中引入store,目的是让vm和vc都能看到$store。实现多个组件…

Vue知识总结-中

VUE-生命周期 生命周期概述 生命周期也常常被称为生命周期回调函数/生命周期函数/生命周期钩子生命周期是Vue在关键时刻帮我们调用的一些特殊名称的函数生命周期函数的名字不能更改,但函数的具体内容是由我们程序员自己编写的生命周期函数中的this指向是vm或组件实例对象 生命周…

GO语言笔记1-安装与hello world

SDK开发工具包下载 Go语言官网地址&#xff1a;golang.org&#xff0c;无法访问Golang中文社区&#xff1a;首页 - Go语言中文网 - Golang中文社区下载地址&#xff1a;Go下载 - Go语言中文网 - Golang中文社区 尽量去下载稳定版本&#xff0c;根据使用系统下载压缩包格式的安装…

React组件中如何通讯

在React组件中&#xff0c;可以通过props和state来实现组件之间的通信。 父组件向子组件传递数据&#xff1a;父组件可以通过props将数据传递给子组件&#xff0c;在子组件中通过this.props来获取传递的数据。 子组件向父组件传递数据&#xff1a;子组件可以通过props中的一个…

【嵌入式移植】1、Ubuntu系统准备

【嵌入式移植】1、Ubuntu系统准备 虚拟机与Ubuntu安装下载Ubuntu创建虚拟机系统配置 虚拟机与Ubuntu安装 嵌入式移植通常使用Linux操作系统的环境&#xff0c;使用Linux下的交叉编译工具链对BootLoader、kernel以及应用程序进行编译&#xff0c;然后下载运行。当然也可以通过各…