本文章是自己学习和编写代码过程中的学习总结,目的是写下笔记供以后自己查看,学习不算深入,并不是说指导别人怎么做,但是如果大家发现有什么问题欢迎指出。以下是在HC32F460PETB芯片上进行,可能与ST或者其他的芯片有一定的区别。
bootloader跳转APP
一般情况下,不带bootloader的话中断向量表的地址是从0x00000000开始,代码地址从0x00000004开始,如下图:
这种情况代码不支持升级,只能通过烧录器进行烧录擦写,无法后续通过上位机进行代码升级。此时则需要引入bootloader和APP(Application)的概念,代码正常运行以及所有的应用层逻辑都是在APP中,APP接收到升级指令并且握手成功之后则进行复位,进入bootloader中。bootloader中接收升级包的指令,并且将接收到的数据进行校验和保存到APP地址或者备份APP地址中,整包数据接收完之后在跳转回到APP中,此过程便完成代码升级。
分配空间大小及配置起始地址
在建立Bootloader和APP工程的时候我们需要提前想好bootloader和app各自需要多大的空间,以及起始地址是多少。
一般来说,bootloader的起始地址就是0x00000000,大小一般给到0x10000就已经足够了。APP地起始地址可以自己确认,APP的地址选择一下规则:
1、必须要大于bootloader的最大地址,也就是说bootloader的地址和APP的地址不允许有重叠。
2、APP的地址不一定需要和bootloader的地址连续,中间可以划分部分空间出来进行参数或者标志位的存储
3、bootloader里面的配置的APP跳转地址需要和工程上keil配置的ROM地址对应
下图为我的空间地址分配,其中bootloader的起始地址为0x000000,大小为0x00005FFF;APP起始地址为0x00010000,大小为0x0006FF00。中间分配了部分空间存储参数和标志位:
bootloader地址设置:
APP地址设置:
bootloader地址跳转
bootloader地址跳转有几个步骤:
1、判断跳转的栈地址是否合法
2、获取程序的起始地址
3、初始化堆栈地址
4、跳转到APP
以上步骤都是直接调用ARM内核的函数即可,基本都是通用
#define BOOTLOADER_STARTADDR 0x00000000
#define BOOTLOADER_ENDADDR 0x00005FFF
#define APP_STARTADDR 0x00010000
#define APP_ENDADDR 0x0007FF00
static uint32_t JumpAddr;
static func_ptr_t JumpToAppFunc;
void JumpToApp(void)
{
/*获取栈顶地址*/
uint32_t AppAddr = *((__IO uint32_t *)APP_STARTADDR);
/* 检查栈顶地址是否合法 */
if ((AppAddr > SRAM_BASE) && (AppAddr <= (SRAM_BASE + SRAM_SIZE)))
{
/* 获取程序起始地址 */
JumpAddr = *(__IO uint32_t *)(APP_STARTADDR + 4U);
JumpToAppFunc = (func_ptr_t)JumpAddr;
/* 初始化APP堆栈指针 */
__set_MSP(*(__IO uint32_t *)APP_STARTADDR);
/*跳转到APP*/
JumpToAppFunc();
}
}
int main(void)
{
__disable_irq(); // 关闭总中断,防止在跳转过程中中断干扰产生异常
JumpToApp();
__enable_irq(); //理论上不会到这里
}
APP处理
APP代码中需要进行两个步骤:
1、中断向量表重映射
2、bootloader中已经关闭了总中断,需要重新打开总中断
原理:
对于内嵌flash的MCU中,初始中断向量表一般会被要求固定到flash的起始位置,系统启动之后总是从flash的起始地址获取初始栈地址和初始PC来开始应用程序代码的执行。ARM内核的中断向量表地址默认是0x00000000,这个地址是对应bootloader里面的中断向量表,如果在APP中没有对中断向量表进行重定向的话,那么中断发生的时候进入的僵尸bootloader的中断函数。APP程序是从0x10000开始的,程序起始的最开始的4个地址便是中断向量表的地址,所以需要将中断向量表的地址重定向到0x10000。
SCB->VTOR = 0x00010000; //中断向量表重定向
bootloader中的接收升级数据还没有做,未完待续…