文章目录
- 1. Bootload 跳转到 App
- 2. App 跳转到 Bootload
- 3. App中设置中断向量表的偏移
1. Bootload 跳转到 App
jump_to_app.c
#include "stm32f10x.h"
#include "jump_to_app.h"
/* 功 能: 关闭全局中断 */
void BoardDisableIrq(void)
{
__disable_interrupt();
}
/* 功 能: 使能全局中断 */
void BoardEnableIrq(void)
{
__enable_interrupt();
}
/* 初始化堆栈指针 */
void MSR_MSP(uint32_t addr)
{
__ASM("msr msp, r0"); // set Main Stack value 将主堆栈地址保存到MSP寄存器(R13)中
__ASM("bx lr"); // 跳转到lr中存放的地址处。bx是强制跳转指令 lr是连接寄存器,是STM32单片机的R14
}
typedef void (*IapFun)(void); // 声明一个函数指针,用于跳转到绝对地址执行程序
IapFun JumpToApp;
/*!
* 功 能: 跳转到应用程序
* param1: 用户代码起始地址
* retval: 无返回值
*/
void IapLoadApp(uint32_t AppAddr)
{
/*
应用程序APP中设置把 中断向量表 放置在0x08003000 开始的位置;而中断向量表里第一个放的就是栈顶地址的值
也就是说,这句话即通过判断栈顶地址值是否正确(是否在0x2000 0000 - 0x 2000 2000之间) 来判断是否应用程序
已经下载了,因为应用程序的启动文件刚开始就去初始化化栈空间,如果栈顶值对了,说应用程已经下载了启动文件,初始化也执行了;
*/
if( ((*(uint32_t*)AppAddr) & 0x2FFE0000) == 0x20000000 )// 检查栈顶地址是否合法,查看参考手册内存章节的SRAM小节
{
BoardDisableIrq(); // 禁止中断
JumpToApp = (IapFun)*(uint32_t*)(AppAddr+4); // 用户代码区第二个字为程序开始地址(新程序复位地址)
MSR_MSP(*(uint32_t*)AppAddr); // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
JumpToApp(); // 设置PC指针为bootload复位中断函数的地址,往下执行
}
}
2. App 跳转到 Bootload
jumo_to_bootload.c
#include "stm32f10x.h"
#include "jump_to_boot.h"
#define INFLASH_ADDR_BOOTLOAD ((uint32_t)0x08000000) // bootload的起始地址
/* 初始化堆栈指针 */
void MSR_MSP(uint32_t addr)
{
__ASM("msr msp, r0"); // set Main Stack value 将主堆栈地址保存到MSP寄存器(R13)中
__ASM("bx lr"); // 跳转到lr中存放的地址处。bx是强制跳转指令 lr是连接寄存器,是STM32单片机的R14
}
typedef void (*IapFun)(void); // 声明一个函数指针,用于跳转到绝对地址执行程序
IapFun JumpToBootload;
/*!
* 功 能: 跳转到应用程序
* param1: 用户代码起始地址
* retval: 无返回值
*/
void IapLoadBootload(void)
{
/*
应用程序APP中设置把 中断向量表 放置在0x08003000 开始的位置;而中断向量表里第一个放的就是栈顶地址的值
也就是说,这句话即通过判断栈顶地址值是否正确(是否在0x2000 0000 - 0x 2000 2000之间) 来判断是否应用程序
已经下载了,因为应用程序的启动文件刚开始就去初始化化栈空间,如果栈顶值对了,说应用程已经下载了启动文件,初始化也执行了;
*/
if( ((*(uint32_t*)INFLASH_ADDR_BOOTLOAD) & 0x2FFE0000) == 0x20000000 )// 检查栈顶地址是否合法,查看参考手册内存章节的SRAM小节
{
BoardDisableIrq(); // 禁止中断
JumpToBootload = (IapFun)*(uint32_t*)(INFLASH_ADDR_BOOTLOAD+4); // 用户代码区第二个字为程序开始地址(新程序复位地址)
MSR_MSP(*(uint32_t*)INFLASH_ADDR_BOOTLOAD); // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
JumpToBootload(); // 设置PC指针为bootload复位中断函数的地址,往下执行
}
}
3. App中设置中断向量表的偏移
在APP程序中,main函数应该首先修改中断向量表的起始地址,可以通过修改VTOR向量表偏移寄存器来重定位向量表。参考《Cortex-M3 权威指南》7.3小节“向量表的起始地址是有要求的:必须先求出系统中共有多少个向量,再把这个数字向上“圆整”到 2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有 32 个中断,则共有 32+16(系统异常)=48 个向量,向上圆整到2的整次幂后值为64,因此向量表重定位的地址必须能被 64*4=256 整除,从而合法的起始地址可以是:0x0, 0x100, 0x200 等”
STM32F103系列有59个中断+16个系统异常,一共65个向量,向上圆整到2的整次幂后值为128,计算得合法的地址是0x0, 0x400, 0x800 … 在main函数中可以设置寄存器的值来重定位向量表,具体实现如下,其中FLASH_VTOR_OFFSET需要根据自己的APP的起始地址进行相应的修改。
#define INFLASH_START_ADDR ((uint32_t)0x08000000) // STM32 内部FLASH的起始地址
#define INFLASH_VTOR_OFFSET ((uint32_t)0x00004000) // APP向量表的偏移地址,与APP的起始地址保持一致
int main()
{
SCB->VTOR = INFLASH_START_ADDR | INFLASH_VTOR_OFFSET; // 设置向量表的起始地址
/* 其他代码 */
}