STM32 CM3中APP和Boot跳转问题分析

本文主要是分析一下CM3中程序跳转原理以及需要注意的坑,CM4中跳转原理也是一样的。

一 应用场景

有时我们希望CM3上电后先运行一段bootloader程序,用于做一些前序的处理和控制,然后再跳转到APP代码去运行。

例如产品交付客户后发现需要升级一些功能,这个时候又不能打开产品外壳去重新下载程序,那么就可以通过这段bootloader程序来进行更新,那么这个bootloader就需要具有和外部通信的功能(例如使用CAN或者串口),可以接收传入的代码,并可以读写flash。

前序操作结束后,就需要跳转到APP代码去。总共需要2个Keil工程,一个bootloader的,一个APP的。


二 跳转代码

本文使用STM32F103RCT6,Keil 5.25来做的测试,期望的Flash代码结构如下,
在这里插入图片描述
跳转代码网上很多,本文也是从别的博客上copy过来的,如下(经过验证是OK的),

#include <stdint.h>

const uint32_t APP_FLASH_ADDRESS = 0X08008000; // APP代码在Flash上的起始地址


typedef void (*pFunction)(void); // 定义一个函数指针类型pFunction
pFunction Jump_To_Application; // 定义一个函数指针变量 

void JumpToApp(uint32_t target_address)
{
		__disable_irq(); // 关闭总中断
		
		// 代码的flash起始地址加4,就是Reset Handler的函数地址
		uint32_t JumpAddress = *(__IO uint32_t*)(target_address+4); 


		Jump_To_Application = (pFunction)JumpAddress; // 把这个地址转为函数指针
		
		__set_MSP(*(__IO uint32_t*)target_address); // 设置栈顶地址

		Jump_To_Application(); // 开始运行APP的Reset Handler
}

int main(void)
{
	JumpToApp(APP_FLASH_ADDRESS); // 执行跳转命令
	return 0;
}

其对应的Keil配置如下,
在这里插入图片描述
编译好直接烧录就行了。

APP的Keil配置如下,
在这里插入图片描述
APP跳转回Bootloader,其操作和Bootloader跳转到APP是一样的,只要改变跳转的Flash地址就行了。


三 遇到的坑和解决办法

遇到最多的坑是跳转过去后不运行,而单独测试APP是没问题的(就是不要Bootloader,直接把APP烧到0x8000000地址上),这是为什么呢?90%的原因是中断的问题,这个问题又分为2种,

  1. 中断向量表的重定向
  2. 外设中断的关闭

下面来分析一下这2个问题

1. 中断向量表的重定向

由前面的描述知道,要实现跳转就需要2个Keil工程,而每个Keil工程都会有自己的中断向量表,所以运行指定的Keil工程代码就需要使用对应的中断向量表。

在STM32启动时会去执行SystemInit()函数,这个是在startup_stm32f10x_hd.s里定义的(不同的芯片或keil版本这个汇编文件的名字可能不一样,调用函数都是一样的),
在这里插入图片描述
可以看到这是属于Reset_Handler里的代码(上电后或按reset键都会执行这段代码),先执行SystemInit()后,再去执行main()函数。在SystemInit()里会对中断向量表进行定位,如下,
在这里插入图片描述
因为没有定义VECT_TAB_SRAM,所以会执行黄色标记部分,右边的2个宏定义如下,
在这里插入图片描述
在这里插入图片描述
可以看到SCB->VTOR会被设置为0x08000000,这样APP的中断向量表也是被设置成这个值,因为SystemInit()函数是Keil提供的,是固定的,这样就会导致跳转后运行失败。

解决办法:在APP的main()函数开始的第一行,把中断向量表重定向一下,即设置为APP代码的Flash起始地址,

SCB->VTOR = 0x08008000;

可能有的人没有去重定向中断向量表也没问题,那是因为APP代码比较简单,没有用到中断向量表,这样APP使用Bootloader的中断向量表就不会有问题。

2. 外设中断的关闭

这个要先说下原理,为什么跳转前要关闭已经开启的外设中断。如下图
在这里插入图片描述
现在芯片里有2套程序,大家都要使用外设中断寄存器来做中断相关的操作(配置,开启等),但是外设中断寄存器只有一套

如果Bootloader配置了一个定时器中断寄存器并开启,也写好了中断处理函数,然后没有disable它就跳转到APP去,而APP代码里没有使用定时器中断,也没有写中断处理函数,那么就会导致崩溃。因为配置好的定时器中断,到了定时时间要去执行中断处理函数,而中断向量表已经重定向了,现在的中断向量表里没有对应的中断处理函数。

看过前面的跳转代码的人可能会问:不是已经使用了__disable_irq()了吗,为什么还不行?
因为这个语句是关的总中断,当跳到APP后需要重新开启总中断,那么到时候它下面的那些外设中断又重新开始运行了(外设中断寄存器的值没有被清除掉)

就跟家里的水阀和水龙头一个道理,水阀是总阀,水阀关了,即使水龙头开着也不会出水,但是如果水阀开着,那么水龙头出不出水就取决于水龙头的开关情况了。
在这里插入图片描述
所以在__disable_irq()语句之后,我们要使用NVIC_DisableIRQ()来关闭外设中断,参数就是某个指定的中断,如下,
在这里插入图片描述
如果有多个中断开着,就多次调用这个函数,如下,

NVIC_DisableIRQ(WWDG_IRQn)NVIC_DisableIRQ(RTC_IRQn)NVIC_DisableIRQ(DMA1_Channel1_IRQn)

四 总结

本文简单分析了程序跳转的原理以及相关问题的解决办法,只要知道了原理,就可以很容易解决问题。写这篇文章时也参阅了很多其它网友的博客。另外,本文使用的是STM32单片机,对于其它公司的单片机也解决思路也都是类似的。

如果有写的不对的地方,请留言指正,谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值