HAL库执行bootloader跳转操作:

用的芯片是f103RCT6,编译环境是keil5

从boot跳转到APP需要做的事:

我们首先应该把boot和app看作两个独立的程序,而由于执行一个程序第一件事就是初始化堆栈指针,这样才可以执行C语言程序;其次就是更新中断向量表,这样后续发生中断时才能进入相应的中断服务函数。所以跳转步骤为:

1、 屏蔽所有中断,防止在跳转过程中,中断干扰出现异常

2、初始化APP堆栈指针(用户代码区的第一个字起始位置用于存放栈顶地址)

3、清除B区使用的外设和所有使用的中断标志位

4、更新中断向量完成跳转

注:想要跳转后成功执行app程序,app程序的起始位置和中断向量表位置应该和BOOT跳转后的位置对应上;并且应该在程序开始时开启总中断。

具体实现:

main.c
 #include "main.h"
 #include "dma.h"
 #include "usart.h"
 #include "gpio.h"
 #include "boot.h"

int main(void)
{
  

  HAL_Init();

  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
  BootLoader_Brance();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
/*************************************************************/
main.h
#define  FLASH_SADDR   0x08000000                                             //FLASH起始地址
#define  PAGE_SIZE     2048                                                   //FLASH扇区大小
#define  PAGE_NUM      128                                                     //FLASH总扇区个数
#define  B_PAGE_NUM    32                                                     //B区扇区个数
#define  A_PAGE_NUM    PAGE_NUM - B_PAGE_NUM                        //A区扇区个数
#define  A_START_PAGE  B_PAGE_NUM                                        //A区起始扇区编号
#define  A_SADDR       FLASH_SADDR + A_START_PAGE * PAGE_SIZE  //FA区起始地址

/*************************************************************/
boot.c


load_a load_A;

void BootLoader_Brance(void){
	if(OTA_Info.OTA_flag == OTA_SET_FLAG){
		myprintf("OTA更新\r\n");
	}else{
		myprintf("跳转A分区\r\n");
		//Delay_Ms(1000);
		LOAD_A(A_SADDR);
	}
}



__asm void MSR_SP(uint32_t addr){
	MSR MSP, r0                        //更改栈顶指针初始值 
	BX r14
}


void LOAD_A(uint32_t addr){
	 
	
	  if((*(uint32_t *)addr>=0x20000000)&&(*(uint32_t *)addr<=0x2000C000)){     //由于我们已经下载了app程序,所以此时判断app程序的起始地址中保存的栈顶是否位于内存区域
		MSR_SP(*(uint32_t *)addr);          //将栈顶指针指向app程序设置的栈顶而不是bootloader的栈顶,当然这两个程序的栈顶一般都为0x20000000
		load_A = (load_a)*(uint32_t *)(addr+4);   //执行load_a函数时会让单片机pc指针指向app程序的复位向量,然后从这里开始往下执行,符合一个程序的正常开始流程
		BootLoader_Clear();     //清除所有外设
		CLOSE_ALL_INT();        //屏蔽所有中断并清除中断标志位
		load_A();               //跳转
	}
}

void BootLoader_Clear(void){
	HAL_UART_MspDeInit(&huart1);     //UART+对于DMA
	HAL_SPI_MspDeInit(&hspi1);      //SPI+对于DMA
	HAL_GPIO_DeInit(GPIOC, GPIO_PIN_11|GPIO_PIN_12);  //软件IIC
	HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);               //LED1
	HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8|GPIO_PIN_2);    //LED0和F_CS
	//HAL_DeInit(); 
	
}

void CLOSE_ALL_INT(void)
{
   __set_PRIMASK(1);  //  失能全局中断  
	
  // 关闭滴答定时器,复位到默认值
  SysTick->CTRL = 0;
  SysTick->LOAD = 0;
  SysTick->VAL = 0;

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

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

	
}

/*************************************************************/
boot.h

#ifndef BOOT_H
#define BOOT_H

#include "stdint.h"

typedef void (*load_a)(void);     //设置一个无参数无返回值的函数指针

void BootLoader_Brance(void);
__asm void MSR_SP(uint32_t addr);
//void MSR_SP(uint32_t addr);
void LOAD_A(uint32_t addr);
void BootLoader_Clear(void);
void CLOSE_ALL_INT(void);

#endif

这里我们设置bootloader的起始地址为:0x8000000,大小为0x10000,那么下载程序时擦除应该设置为按扇区擦除而不是整片擦除,app程序起始地址为0x8010000大小为0x30000,同理。

部分擦除设置擦除如下:

同时app程序的起始位置和中断向量表位置应该和BOOT跳转后的位置对应上;并且应该在程序开始时开启总中断。

中断向量表偏移0x10000,和boot跳转后0x8010000对应上了,同时app起始地址和大小也要设置。

开启总中断:

从APP跳转到boot:硬件复位或者软件复位。软件复位具体实现为:

参考网上一些博主说的,在调用复位函数和真正复位之间还有一段延迟,在这段时间单片机还是可以正常处理中断等程序的,为了避免这种情况,应该把相应的中断都屏蔽掉。

/*函数功能:STM32软复位函数 */

Void Stm32_SoftReset(void) {

__set_FAULTMASK(1);//禁止所有的可屏蔽中断

NVIC_SystemReset();//软件复位

}

//复位后就不需要再使能开启中断了,因为系统已经重置了。

参考链接:STM32笔记——软件复位相关知识小记_nvic_systemreset会让 rst引脚拉地么-优快云博客

【手把手写【STM32/GD32】OTA升级Boot程序,利用函数指针,编写跳转A区程序】https://www.bilibili.com/video/BV1BY4y1y7Rb?vd_source=d1e91e9e17a031e2d6e11ed04c3ab205

/*****************                补充             ****************/

        对于APP程序,APP的ADDRESS RANGE起始地址必须为08000000,即保持默认不要动类似下图这样,不然APP程序比较大时烧录可能会失败,比如我用F407VGT6,Start地址设置为程序的起始地址0x8010000,然后烧录时verify就报错Contents mismatch at: 08080094H  (Flash=05H  Required=A5H) !;改回成下图这样就能正常烧录;

    当然APP程序只有下面这处地方是保持默认,其他地方按照前面的该怎么改怎么改;

 

参考如下:

下载校验出错 Contents mismatch at: 08040000H (Flash=69H Required=6DH) - STM32/STM8单片机论坛 - ST MCU意法半导体官方技术支持论坛

GD32/KEIL Contents mismatch at: 08040000H (Flash=F3H Required=FFH) !-优快云博客 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值