0. 概述
实际中通常会用到升级功能。本文主要是记录下基本设置,使用MCU为STM32L051。
1. keil设置
- 设置flash起始地址和大小。IAP从起始地址运行,分配空间为8K,换算成16进制为0x2000,如下图:
APP在IAP后,即从0x8002000开始。使用的STM32L051C8Tx flash空间为64K,去除IAP部分还剩56K,APP分配空间为52K,换算成16进制为0xD000,如下图:
- 设置为扇区擦除。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();
}
}
函数解释如下:
-
if (((*(volatile uint32_t*)ApplicationAddress) & 0x2FFFE000 ) == 0x20000000)
:
ApplicationAddress存放的是用户程序Flash的首地址,((volatile uint32_t)ApplicationAddress)的意思是取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x20001FFF区间中,这个区间的大小为8K。
-
test = (*(volatile u32*)ApplicationAddress)
,test保存的就是堆栈地址(并且是应用程序堆栈的栈顶地址)。查看STM32的向量表,可以知道:栈顶地址 + 4 存放的是复位地址,因此JumpAddress存放的是复位地址。
- 调用
__set_MSP
函数后,将把用户代码的栈顶地址设为栈顶指针。
- 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函数中开启总中断就可以了。