解决STM32单片机从用户程序App无法成功跳转到系统Bootloader实现USB固件升级的问题

一、实验目的

实验平台: 使用的是正点原子阿波罗F429IGT6开发板,芯片为STM32F429IGT6,使用的仿真器为CMSIS-DAP

开发环境: Keil软件,编译器使用Keil内置的VC6编译器,编译优化选择-O3

实验前言: STM32的单片机出厂的时候在内部Flash中烧录了一段程序,这个程序称为自举程序Bootloader,用户可以通过拉高Boot0引脚的方式设置单片机进入Bootloader,也可以通过软件跳转的方式进入Bootloader,进入Bootloader以后就可以基于串口或者USB等接口实现程序烧录达到更新固件的目的。

实验目的: 主要是想通过软件方式在用户程序中跳转到STM32内置的Bootloader程序,然后通过串口或者USB接口实现固件更新。

图1.1:使用的开发板示意图
图一:使用的开发板示意图


二、问题描述

遇到的问题: 按照安福莱的教程,编写了相应的代码,但是无法成功跳转到系统Bootloader中,主要现象是一跳转到系统Bootloader后,就会发生软件复位,导致单片机复位。

所使用的有问题的代码如下所示: 在main()函数中调用此函数跳转Bootloader后单片机会直接软复位。

static void JumpToApp(void)
{
	uint32_t i = 0;
	void (*SysMemBootJump)(void);        /* 声明一个函数指针 */
	__IO uint32_t BootAddr = 0x1FFF0000; /* STM32F429的系统BootLoader地址 */
	
	__set_PRIMASK(0);  /* 关闭全局中断 */
	
	SysTick->CTRL = 0;  /* 关闭滴答定时器,复位到默认值 */
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

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

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

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

	__set_MSP(*(uint32_t *)BootAddr);  /* 设置主堆栈指针 */
	
	__set_CONTROL(0);  	/* 设置为特权级模式,使用MSP指针 */

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

三、问题解决

首先给出能够成功跳转到Bootloader的代码: 如下所示,在main()函数中调用此函数即可成功跳转到Bootloader。
重点注意事项:

static void DirJumpToBootloader(void)
{
    uint32_t i=0;
    void (*SysMemBootJump)(void);
	volatile uint32_t BootAddr = 0x1FFF0000;
	
    /* 重点:下面的顺序不可以弄错,必须使用__disable_irq关闭中断,随后先复位RCC,再复位SysTick */
    
	__disable_irq();  /* Disable all interrupts */
    
	HAL_RCC_DeInit();  /* Set the clock to the default state */
    HAL_DeInit();
    
	SysTick->CTRL = 0;  /* Disable Systick timer */
    SysTick->VAL = 0;
    SysTick->LOAD = 0;
    
	for (i=0; i<sizeof(NVIC->ICER)/sizeof(NVIC->ICER[0]); i++)  /* Clear Interrupt Enable Register & Interrupt Pending Register */
	{
		NVIC->ICER[i]=0xFFFFFFFF;
		NVIC->ICPR[i]=0xFFFFFFFF;
	}
    
	__enable_irq();  /* Re-enable all interrupts */
	
	SysMemBootJump = (void (*)(void)) *((uint32_t *) ((BootAddr + 4)));  /* Set up the jump to boot loader address + 4 */
	
	__set_MSP(*(uint32_t *)BootAddr);  /* Set the main stack pointer to the boot loader stack */
    
    __HAL_REMAPMEMORY_SYSTEMFLASH();  /* Remapping */    
	
	SysMemBootJump();  /* Call the function to jump to boot loader location */
	
	while (1)
	{
		/* Code should never reach this loop */
	}
}

上述代码主要的改动有四处,分别为:
(1) 使用__disable_irq()关闭中断,而不是使用__set_PRIMASK(0);
(2) 先复位HAL_RCC_DeInit()然后再清空SysTick,否则无法关闭SysTick定时器
(3) 使用__enable_irq()开启中断,而不是使用__set_PRIMASK(1);
(4) Keil软件中一定要使用-O3优化,不要使用-O1
Keil软件优化
通过上述三处修改即可成功跳转到Bootloader,然后就可以使用CubeProgrammer软件通过UART或者USB接口实现程序的烧录。


四、其他方案

基于软件复位的方式跳转Bootloader: 根据安福莱的教程,还有一种跳转Bootloader的终极教程,该方法通过软件复位实现所有片内寄存器的复位,能够给Bootloader程序提供一个绝对干净的运行环境,具体可以参考链接基于软件复位跳转Bootloader。

本文给出该方式的代码: main()部分内容函数如下,完整代码参考博客最后的完整工程附件:

uint32_t g_JumpInit __attribute__( ( section( ".bss.NoInit")));

int main(void)
{    
    if(g_JumpInit == 0xAA553344)
    {
        SftJumpToBootloader();  /* 软件复位后跳转 */
    }
    
    g_JumpInit = 0xAA553344;
    HAL_NVIC_SystemReset();	 /* 软件自动复位后跳转 */
}

软件复位方式跳转Bootloader代码:

static void SftJumpToBootloader(void)
{
    void (*SysMemBootJump)(void);
	volatile uint32_t BootAddr = 0x1FFF0000;

    g_JumpInit = 0xAA553344;
    
	SysMemBootJump = (void (*)(void)) *((uint32_t *) ((BootAddr + 4)));  /* Set up the jump to boot loader address + 4 */

	__set_MSP(*(uint32_t *)BootAddr);  /* Set the main stack pointer to the boot loader stack */

    __HAL_REMAPMEMORY_SYSTEMFLASH();    /* Remapping */
    
	SysMemBootJump();  	/* Call the function to jump to boot loader location */
	
	while (1)
	{
		/* Jump is done successfully */
		/* Code should never reach this loop */
	}
}

五、完整工程代码

工程下载百度云链接
链接:https://pan.baidu.com/s/1BBTdN3zUhlxPc2hsiM3B8Q?pwd=1234
提取码:1234


六、参考博客

【1】一劳永逸的解决BOOT跳转APP失败问题
【2】解决思路来源于此博客“无关风月”网友回答

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值