文章目录

  • 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;     // 设置向量表的起始地址

	  /* 其他代码 */
}