首页 > 代码库 > IAP升级功能编写初期的一些困惑与疑问---完成功能后的总结

IAP升级功能编写初期的一些困惑与疑问---完成功能后的总结

IAP的源码等资料我上传了,压缩包内有12个文件,,http://download.csdn.net/detail/f907279313/7524849(要积分的辛苦收集的你们就给点积分吧)

还有另一篇博客总结的IAP:http://blog.csdn.net/super_demo/article/details/32086541


一,网上下载的例程,跳转部分的代码有差异,尤其是用的汇编那句

eg:

Jump_To_Application  = (pFunction)(*(vu32*) (IAPSTART + 4));
__MSR_MSP(*(vu32*) IAPSTART);
Jump_To_Application();

跟踪__MSR_MSP(一般这个函数都在库文件里有,跟踪不到就用搜索找)找到汇编函数为

__MSR_MSP 
 
    MSR MSP, r0 ; set Main Stack value
    BX r14


//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//检查栈顶地址是否合法.

jump2app=(iapfun)*(vu32*)(appxaddr+4);//用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}

跟踪MSR_MSP找到函数为

//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 //set Main Stack value
    BX r14
}


 //判断用户是否已经下载程序,因为正常情况下此地址是栈地址。
        //若没有这一句的话,即使没有下载程序也会进入而导致跑飞。
        if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
        {
            SerialPutString("Execute user Program\r\n\n");
            //跳转至用户代码
            JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
            Jump_To_Application = (pFunction) JumpAddress;


            //初始化用户程序的堆栈指针
            __set_MSP(*(__IO uint32_t*) ApplicationAddress);
            Jump_To_Application();
        }

跟踪__set_MSP找到函数为

__ASM void __set_MSP(uint32_t mainStackPointer)
{
  msr msp, r0
  bx lr
}

总结以上发现都是操作ARM的R0跟R14(LR)寄存器。

还有一种不太一样的,就是stm32F4的库函数中的跳转,如下所示

//测试用户app地址是不是在APPLICATION_ADDRESS位置。检测栈顶的地址,来检验app是否下载成功
    if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
   
    //APPLICATION_ADDRESS + 4对应的是app中断向量表的第二项,复位地址
    JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
//把地址强转为函数指针
    Jump_To_Application = (pFunction) JumpAddress;
    //设置主函数栈指针
    __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
//调用函数,实际失去app复位地址去执行复位操作
    Jump_To_Application();
    }

跟踪__set_MSP找到函数为

static __INLINE void __set_MSP(uint32_t topOfMainStack)
{
  register uint32_t __regMainStackPointer     __ASM("msp");
  __regMainStackPointer = topOfMainStack;
}

对于M4的这个库函数我也太懂,感觉最终的操作应该跟其他的一样吧

二,关于跳转部分的代码的理解(转)

这里重点说一下几句经典且非常重要的代码:

第一句: if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)   //判断栈定地址值是否在0x2000 0000 - 0x 2000 2000之间

怎么理解呢? (1),在程序里#define ApplicationAddress    0x8003000 ,*(__IO uint32_t*)ApplicationAddress)  即取0x8003000开始到0x8003003 的4个字节的值, 因为我们的应用程序APP中设置把 中断向量表 放置在0x08003000 开始的位置;而中断向量表里第一个放的就是栈顶地址的值

也就是说,这句话即通过判断栈顶地址值是否正确(是否在0x2000 0000 - 0x 2000 2000之间) 来判断是否应用程序已经下载了,因为应用程序的启动文件刚开始就去初始化化栈空间,如果栈顶值对了,说应用程已经下载了启动文件的初始化也执行了;


第二句:    JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);   [  common.c文件第18行定义了:  pFunction   Jump_To_Application;]
                      

ApplicationAddress + 4  即为0x0800 3004 ,里面放的是中断向量表的第二项“复位地址”  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 之后此时JumpAddress

第三句:    Jump_To_Application = (pFunction) JumpAddress;
 startup_stm32f10x_md_lv. 文件中别名  typedef  void (*pFunction)(void);     这个看上去有点奇怪;正常第一个整型变量   typedef  int  a;  就是给整型定义一个别名 a

 void (*pFunction)(void);   是声明一个函数指针,加上一个typedef 之后  pFunction只不过是类型 void (*)(void) 的一个别名;例如:

[cpp] view plaincopy
  1. pFunction   a1,a2,a3;  
  2.   
  3. void  fun(void)  
  4. {  
  5.     ......  
  6. }  
  7.   
  8. a1 = fun;  

所以,Jump_To_Application = (pFunction) JumpAddress;  此时Jump_To_Application指向了复位函数所在的地址;

第四 、五句: __set_MSP(*(__IO uint32_t*) ApplicationAddress);      \\设置主函数栈指针
               Jump_To_Application();                         \\执行复位函数

Jump_To_Application()是把用户代码的复位地址付给PC指针,我看到Jump_To_Application()这句代码debug的时候对应的汇编代码是

LDR r0,[pc,#12] ;相对PC的数据加载,去函数指针的地址
LDR r0,[r0,#00] ;R0做索引,无偏移,数据装载到R0,这个内容就是函数指针指向的内容,也就是函数的地址了,用户程序的起始地址;
BLX r0              ;这个不解释,说了是跳转

我们看一下启动文件startup_stm32f10x_md_vl.s 中的启动代码,更容易理解



三,关于跳转时能否不用按键,用软件标志位以及APP与IAP之间的互跳

完全可以不用按键,可以模拟一个按键信号,或者用软件的一个标志位来判断是否更新。。我设计的在flash中中存储一个值,当APP运行中需要更新时串口发来更新命令,然后在flash中存一个值之后跳到IAP部分,来读取flash中存储的那个值,如果是需要更新则更新,如果不是需要跟新标志位就直接跳转到APP部分。。。这样也不用重复上电,断电。


四,关于APP与IAP互跳之间的中断处理问题

网上很多人说跳转之后无法进入中断,然后百度了一下中断处理问题,反正没看明白,搞得自己也莫名的恐慌。。最后没搞明白,写完程序没处理中断问题,也照常运行自如(可能我的只是凑巧)我建议先不用管中断屏蔽的问题,先把逻辑写好,实际测试中再来处理中断问题,不要被中断问题吓到。处理的方式据我所知有①跳转之前关中断,避免跳转过程中受影响②跳转后在初始化时加入RCC_DeInit();,,NVIC_DeInit ();等让中断恢复默认值。。具体可参考下面这篇文章http://dzdesigned80.blog.163.com/blog/static/203259238201272425313152/