打印
[STM32F4]

关于程序跳转

[复制链接]
1765|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lijiankun|  楼主 | 2016-7-6 18:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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函数。

  而如果在程序跳转之前,先关闭所有中断,这个时候在跳转的时候,中断向量表的重新定义根本不会产生任何的影响,因为这个时候根本就不会产生中断。


以上原因分析,只是个人的一些理解,如果有表述不对的地方,还望大家多多指正,谢谢。




沙发
mmuuss586| | 2016-7-6 19:35 | 只看该作者

谢谢分享;

使用特权

评论回复
板凳
aozima| | 2016-7-6 20:52 | 只看该作者
光 __disable_irq(); 不靠谱的,很容易出现APP里面一使能外设中断就进入了。

最好写个循环,把M系列的256个中断全清了,而且与具体芯片无关的。

使用特权

评论回复
地板
ofsummer| | 2016-7-6 21:41 | 只看该作者
就是要先关中断,然后出来的时候再开中断吧

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

94

帖子

3

粉丝