本帖最后由 lijiankun 于 2016-7-13 14:29 编辑
最近在做关于STM32F429单片机的程序跳转的功能,其中遇到了一些问题,现在总结如下,欢迎讨论
首先对跳转函数做一个介绍:
函数原型:
void Jump_Address(void)
{
if (((*(volatile u32*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
{
test = (*(volatile u32*)ApplicationAddress);
JumpAddress = *(volatile u32*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
__set_MSP(*(volatile u32*) ApplicationAddress);
Jump_To_Application();
}
}
1、if (((*(volatile u32*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)分析:
ApplicationAddress存放的是用户程序Flash的首地址, (*(volatile u32*)ApplicationAddress)的意思是取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x2001ffff区间中,这个区间的大小为128K,笔者查阅STM32各型号的RAM大小,目前RAM最大的容量可以做到192K+4K,时钟频率为168MHZ。一般情况下,我们使用的芯片较多的落在<128K RAM的区间,因此上面的判断语句是没有太大问题的。
2、经过2的分析,test保存的就是堆栈地址(并且是应用程序堆栈的栈顶地址),查看STM32的向量表,可以知道:栈顶地址的地址 + 4 存放的是复位地址,因此JumpAddress存放的是复位地址。
3、调用__set_MSP函数后,将把用户代码的栈顶地址设为栈顶指针
4、Jump_To_Application();的意思就是设置PC指针为复位地址。
CORTEX-M3上电后后检测BOOT引脚的电平来决定PC的位置。例:BOOT设置为FLASH启动,启动后CPU会先取两个地址:一个是栈顶地址,另一个是复位地址。因此才有了第4、第5点的写法。
注:以上内容为借鉴别人的内容,感觉别人写的很详细,自己写不到这么细,这里对作者表示感谢,要查看原作者的内容,请访问后面的链接(http://blog.sina.com.cn/s/blog_5fd719d60101eznx.html)
前言就介绍这些,下来写一点自己遇到的问题:
使用上面的跳转函数,使用时发现,跳转不稳定,有时候能跳过去,有时候就会失败,有时甚至在跳转时就直接进入"hard_fault",程序直接卡死,很是让人头疼,之后查阅资料,发现原来是跳转之前的APP程序里有些中断没有关,跳转之后中断触发,但IAP程序里没有定义中断响应函数,找不到地址才导致死机。
原因找到了,那怎么处理呢?
在跳转之前把自己使用到的中断关掉就好了
例如:HAL_NVIC_DisableIRQ(NonMaskableInt_IRQn);
HAL_NVIC_DisableIRQ(MemoryManagement_IRQn);
HAL_NVIC_DisableIRQ(BusFault_IRQn);
HAL_NVIC_DisableIRQ(UsageFault_IRQn);
HAL_NVIC_DisableIRQ(SVCall_IRQn);
HAL_NVIC_DisableIRQ(DebugMonitor_IRQn);
HAL_NVIC_DisableIRQ(PendSV_IRQn);
HAL_NVIC_DisableIRQ(SysTick_IRQn);
也可以使用总中断处理函数,将所有的中断都关掉就好了,这样就不用一个个去看自己使用了那些中断,那些中断没有使用,跳转之后再把总中断打开。
__disable_irq(); // 可以使用这个函数 关闭总中断
__enable_irq(); //跳转后再用这个函数打开总中断
这样的话,跳转就很流畅了,不会再出现之前卡死的现象了。
卡死原因分析:
对于程序跳转,使用最频繁的可能就是在BOOT程序和APP程序之间了,那么我们来分析一下,造成这种现象的原因是什么呢?
对于单片机程序,在上电的时候首先都会对中断向量表进行定义,之后如果某个具体的中断被初始化,就会与中断向量表之间建立一个对应的关系,这个对应关系引导中断在触发的时候,通过一定的偏移量,在中断向量表中找到自己的位置。从而完成中断的具体功能。
按理说进入APP程序的时候会重新映射中断向量表和重新定义中断。程序在BOOT程序时,遵循BOOT中的中断向量表规则,跳转到APP程序后,重新映射中断向量表,之后遵循新的中断向量表就好了,不应该出现跳转卡死的现象呀!!!
那是忽略了一种情况:
1.中断在某种意义上来说就相当于现实中的电脑外设,电脑的每个外设都是可以独立工作的,比方说音响,显示器等,而这些外设一旦被开启是不会自己去关闭的。说到中断,我们在BOOT程序中开启了这些中断,这些中断就开始工作了,而他们对应的是之前的中断向量表。
2.会存在这样一种情况,在程序跳转前,如果不关闭所有的中断,这些中断一直处于运行状态,当程序跳转到APP程序,中断向量表被重新映射,这个时候还没有重新定义任何的某个具体的中断,这个时候,之前的中断触发了,因为它绑定的是之前的中断向量表的位置,它就跑去之前的位置找,因为中断向量表已经被重新映射,它在这个位置没有找到中断向量表。而且中断向量表被重新映射后,之前的位置被释放,成为非访问区。而中断又去访问了一个不能访问的地方,所以内存溢出了,程序进入到hard fault函数。
而如果在程序跳转之前,先关闭所有中断,这个时候在跳转的时候,中断向量表的重新定义根本不会产生任何的影响,因为这个时候根本就不会产生中断。
以上原因分析,只是个人的一些理解,如果有表述不对的地方,还望大家多多指正,谢谢。
|