本帖最后由 wangjj19950516 于 2021-9-1 17:18 编辑
最近很多客户问到升级的问题,各种协议的,有CAN,UART,LIN等。借着这个机会,学习了一下程序跳转的原理,在这里记录一下。本人在这方面是菜鸟,如有错误,还望指出。环境:keil 5.23
硬件:AC780X开发板
IAP升级的话就需要两部分代码,一个是Bootloader(用于更新APP,并跳转至APP运行),另一个是APP(即真正的应用代码)。
一、Bootloader和APP的flash分配
根据所用芯片的flash大小,划分boot loader和app的大小。AC7801的flash为128K,从0x08000000~0x08020000。
bootloader实现功能简单,一般代码量比较小,我这里分配20K(按实际代码量分配),即范围为0x08000000~0x08004FFF。
APP则用剩下的108K,范围为0x08005000~0x08020000。
所以,APP的起始地址是0X08005000,这个地址很重要。
在keil下的option for target配置工程flash起始地址和大小。
图1.bootloader flash配置
图2.APP flash配置
二、Bootloader跳转到APP的代码解析
#define APP_ADDRESS 0X08005000 //为APP的起始地址,需要与工程配置中的起始地址一致
typedef void (*pFunction)(void);
static pFunction s_jumpToApp; //跳转指针
void JumpToApp(void)
{
uint32_t JumpAddress;
if(((*(__IO uint32_t *)APP_ADDRESS) & 0x2FFE0000) == 0x20000000) //判断栈顶指针内容是否有效(指向RAM)
{
__ASM("CPSID I");//关全局中断
JumpAddress = *(__IO uint32_t *)(APP_ADDRESS + 4); //取复位中断向量的值
s_jumpToApp = (pFunction)JumpAddress;
__set_MSP(*(__IO uint32_t*)APP_ADDRESS);//设置主函数栈指针
s_jumpToApp(); //执行复位函数
}
}
第一句: if(((*(__IO uint32_t *)APP_ADDRESS) & 0x2FFE0000) == 0x20000000)
1.这句是判断栈顶地址值是否在0X20000000~0X20020000之间。APP_ADDRESS通过宏定义为0X08005000,即APP的起始地址。
2.(*(__IO uint32_t *)APP_ADDRESS),即取 0X08005000~0X08005003的4个字节的值。因为应用程序APP中设置把中断向量表放在0X08005000开始的位置,而中断向量表里第一个放的就是栈顶地址的值。即这里取出了栈顶地址的值。
3.堆栈指针(SP)必须在SRAM中,SRAM的地址范围都是从0X20000000处开始的,128K的SRAM的地址范围是0X20000000~0X2001FFFF。当APP运行的程序栈顶指针落在SRAM区域时,这个时候就是有效的栈顶指针。
4.也就是说,这句代码通过判断栈顶地址值是否正确(是否在0x20000000~0x2001FFFF之间)来判断是否应用程序已经下载了,因为应用程序的启动文件刚开始就去初始化栈空间。如果栈顶指针对了,说明应用程序已经下载了,启动文件的初始化也执行了,就可以进行跳转。
5.128K的SRAM是比较大的了,用&0x2FFE0000就能覆盖大部分SRAM了,也可以根据实际的SRAM大小修改。
第二句:__ASM("CPSID I")
这句是关掉全局中断。跳转前最好将中断关掉,以防中断向量表还没映射导致程序跑飞。
第三句:JumpAddress = *(__IO uint32_t *)(APP_ADDRESS + 4)
1.这句是取复位中断向量的值
2. (APP_ADDRESS+4)即为0x08005004,里面放的是中断向量表的第二项“复位地址”,执行完完该句后,将复位中断的中断函数地址赋值给JumpAddress。
第四句:s_jumpToApp = (pFunction)JumpAddress
1.这句的意思是将上一句取得的中断函数地址转为函数指针。void (*pFunction)(void);是声明了一个函数指针。
2.此时,s_jumpToApp指向了复位中断函数所在的地址。
第五句:__set_MSP(*(__IO uint32_t*)APP_ADDRESS)
1.这句是设置主函数栈指针
2.__set_MSP,该函数的功能就是设置主栈指针。
第六句:s_jumpToApp()
执行复位函数,跳转到APP运行。
三、APP中配置处理
在APP中,进入main函数,首先应该重映射向量表,再使能全局中断。
int main(void)
{
SCB->VTOR = APP_ADDRESS; //重映射向量表
__ASM("CPSIE I"); //使能全局中断
/*用户代码 */
GPIO_Config();
TIMER_Config(1000);
while(1)
{
}
}
VTOR寄存器存放的是中断向量表的起始地址。对于APP,设置为flash基址+偏移量,即0X08005000,所以需要在APP的main函数最开头处添加SCB->VTOR = APP_ADDRESS,实现中断向量表的起始地址的重设。
四、跳转时对中断的处理
ARM芯片可以实现多个APP,通过boot来控制APP,通过不同的地址选择跳转到不同的APP。
常用的为Bootloader跳转到APP,若要实现逆向跳转,通过复位即可实现,因为复位后程序会从flash最开始处执行,即bootloader.
每个APP包括boot loader都有各自的中断向量表,若在相应代码中有使用中断,跳转前需先关闭中断,以免刚跳转过去还没来得及重新设置中断向量表就产生中断,找不到中断入口和执行函数,程序跑飞。
跳转到APP后,第一时间先将中断向量表的地址重新映射,再使能全局中断,就可以正常运行了。
需要注意的是:APP的工程配置中起始地址是0X08005000,所以程序中设置的APP_ADDRESS必须对应为0x08005000,否则无法正确跳转。
|