STM32的Bootloader实现和遇到的情况

0. 概述

实际中通常会用到升级功能。本文主要是记录下基本设置,使用MCU为STM32L051。

1. keil设置

  1. 设置flash起始地址和大小。IAP从起始地址运行,分配空间为8K,换算成16进制为0x2000,如下图:
    在这里插入图片描述
    APP在IAP后,即从0x8002000开始。使用的STM32L051C8Tx flash空间为64K,去除IAP部分还剩56K,APP分配空间为52K,换算成16进制为0xD000,如下图:
    在这里插入图片描述
  2. 设置为扇区擦除。IAP和APP程序设置相同,如下图:
    在这里插入图片描述

2. IAP跳转函数

跳转函数如下:

#define APPLICATION_ADDRESS 	(uint32_t)0x08002000

typedef void (*pfun) (void);
pfun Jump_To_Application;
uint32_t JumpAddress;


void go_to_app(void)
{
  if (((*(volatile uint32_t*)APPLICATION_ADDRESS) & 0x2FFFE000 ) == 0x20000000)
  {
    JumpAddress =  *(volatile uint32_t*)(APPLICATION_ADDRESS + 4);
    Jump_To_Application = (pfun)JumpAddress;
    __set_MSP(*(volatile uint32_t*)APPLICATION_ADDRESS);
    Jump_To_Application();
  }
}

函数解释如下:

  1. if (((*(volatile uint32_t*)ApplicationAddress) & 0x2FFFE000 ) == 0x20000000)
    ApplicationAddress存放的是用户程序Flash的首地址,((volatile uint32_t)ApplicationAddress)的意思是取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x20001FFF区间中,这个区间的大小为8K。
  2. test = (*(volatile u32*)ApplicationAddress),test保存的就是堆栈地址(并且是应用程序堆栈的栈顶地址)。查看STM32的向量表,可以知道:栈顶地址 + 4 存放的是复位地址,因此JumpAddress存放的是复位地址。
  3. 调用__set_MSP函数后,将把用户代码的栈顶地址设为栈顶指针。
  4. Jump_To_Application();的意思就是设置PC指针为复位地址。

CORTEX-M3上电后后检测BOOT引脚的电平来决定PC的位置。例:BOOT设置为FLASH启动,启动后CPU会先取两个地址:一个是栈顶地址,另一个是复位地址。因此才有了第3、第4点的写法。

摘自一篇非常详细的文章,可惜找不到链接了。 

3. APP重定向中断向量表

3.1 标准库

STM32标准库可通过下面函数实现重定向:

NVIC_SetVectorTable(0x08000000,0x2000);

3.2 HAL库

HAL库不再支持该函数,通过下面函数完成重定向:

void SystemInit (void)
{
  ......
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table 
  ......
}

我们修改VECT_TAB_OFFSET宏定义的值即可:

/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x2000U /*!< Vector Table base offset field.
                                   This value must be a multiple of 0x100. */

当需要调整IAP空间时,这里很容易漏掉。经常使用如下定义:

extern int Image$$ER_IROM1$$Base;
#define VECT_TAB_OFFSET  ((uint32_t)&Image$$ER_IROM1$$Base)

然后KEIL添加设置:
在这里插入图片描述
这样调整空间的同时,也调整了中断向量表:
在这里插入图片描述
源代码链接:STM32L051_Bootloader

4. 一些小问题

这一小节记录下boot遇到的问题,逐渐补充,不针对上一节贴的代码。

4.1 从IAP跳转到APP后运行异常

使用MCU为STM32G0B1
单独运行IAP和APP都正常,但是跳转后APP发现运行异常。仿真发现APP的HAL_Delay异常,原因是没有进入systick中断。最后发现是IAP中进行了关中断操作:
在这里插入图片描述
在STM32L051做升级功能时,IAP程序内没有关闭中断。这次使用了一个通用boot程序,处理的更全面些,我自己实现APP程序,需要在APP程序中手动开中断:
在这里插入图片描述

4.2 没有SCB->VTOR设置中断向量表

使用MCU为N32G031
这次现象和4.1相同,但是已经开启中断了。最后发现是,重映射中断向量表有问题。虽然我修改了VECT_TAB_OFFSET宏定义,但是system_n32g031.c的SystemInit函数并没有调用SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET。手动添加后反而报错了,没有SCB->VTOR变量。
前面的STM32L051和STM32G0B1是M0+核,而N32G031是M0核,不支持SCB->VTOR。搜索了下M0的中断向量表的重映射方法:

1.复制应用程序向量表到SRAM的起始地址。
2.设置为从SRAM启动。
3.在应用程序的Target菜单中,需要为向量表预留足够的SRAM空间。
(参考链接:添加链接描述添加链接描述

尝试做了一下,问题出现在第二步,设置为从SRAM启动。在STM32G0B1的工程中还能找到__HAL_REMAPMEMORY_SRAM这样的函数,而N32G031中完全找不到类似的接口,也没看到SYSCFG->CFGR1相关寄存器。咨询了FAE,直接给了一个demo。在IAP程序中有如下定义:

#define MMU_VTOR               ((__IO unsigned*)(0x40024C30))	
#define _VTOREN()              (*MMU_VTOR = (*MMU_VTOR) | 0x88000000);
#define _VTORVALUE()           (*MMU_VTOR = (*MMU_VTOR) | 0x0001800);//中断向量表重映射地址

只需要在跳转APP之前使用一下就行了:

    _VTORVALUE();         //先写中断映射地址,存在多次跳转时,每次写之前可以先进性寄存器清0操作,避免地址叠加
    _VTOREN();            //使能寄存器

这样看虽然M0核不支持SCB->VTOR,但n32g031提供了这个方法。并且本应该在APP中进行的设置(如STM32G0B1),改为在IAP中设置。APP只需要在魔术棒设置Flash地址+main函数中开启总中断就可以了。

  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值