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,首先关闭全局中断及清除中断挂起标志,设置主堆栈指针,跳转到APP的复位中断服务函数**(相当于做了(1)中内核干的事情)**

2 BOOT设计

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

c 复制代码
void Jump_to_APP(void)
{
		uint32_t i=0;
		void (*SysMemBootJump)(void);

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

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

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

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

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

		/* 跳转到系统BootLoader,首地址是MSP,地址+4是复位中断服务程序地址 */
		SysMemBootJump = (void (*)(void)) (*((uint32_t *) (FLASH_APP_ADDR + 4)));

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

		
		/* 跳转到APP */
		SysMemBootJump();

		/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
		while (1)
		{
			
		}
}

相关知识:

(1)涉及到的NVIC寄存器

(1.1)NVIC->ICER,中断失能寄存器,写入1失能中断

(1.2)NVIC->ICPR,中断挂起清除寄存器,写入1清除中断挂起

(2)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)函数指针指向APP的复位中断服务函数(也就是APP的第5-8Byte)

(7)设置主堆栈指针(也就是APP的前4Byte)

(8)跳转到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:

c 复制代码
#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之前一定要失能所有中断、清除所有中断挂起标志,营造一个纯净的环境

相关推荐
好家伙VCC3 小时前
STM32与51单片机的区别:是否应该直接学习STM32?
stm32·学习·51单片机
szpc16213 小时前
28V_1MHZ电子烟,无线鼠标,医疗器械等专用恒频升压转换器超小体积封装
单片机·嵌入式硬件·计算机外设
luckyluckypolar5 小时前
STM32——SPI
stm32·单片机·嵌入式硬件·物联网
极客小张6 小时前
基于STM32MP157与OpenCV的嵌入式Linux人脸识别系统开发设计流程
linux·stm32·单片机·opencv·物联网
OH五星上将7 小时前
OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【扩展组件】上
linux·嵌入式硬件·harmonyos·openharmony·鸿蒙开发·liteos-a·鸿蒙内核
浅陌pa7 小时前
24:RTC实时时钟
c语言·stm32·单片机·嵌入式硬件
敲上瘾7 小时前
多态的使用和原理(c++详解)
开发语言·数据结构·c++·单片机·aigc·多态·模拟
小熊在忙fpga7 小时前
STM32如何修改外部晶振频率和主频
stm32·单片机·嵌入式硬件
我命由我123458 小时前
GPIO 理解(基本功能、模拟案例)
linux·运维·服务器·c语言·c++·嵌入式硬件·c#
学习日记hhh8 小时前
STM32G431RBT6(蓝桥杯)串口(发送)
stm32·单片机·嵌入式硬件