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 plain copy
  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 中的啟動代碼,更easy理解



三,關於跳轉時是否能不用按鍵,用軟件標志位以及APP與IAP之間的互跳

全然能夠不用按鍵,能夠模擬一個按鍵信號,或者用軟件的一個標志位來推斷是否更新。。我設計的在flash中中存儲一個值,當APP執行中須要更新時串口發來更新命令,然后在flash中存一個值之后跳到IAP部分,來讀取flash中存儲的那個值,假設是須要更新則更新,假設不是須要跟新標志位就直接跳轉到APP部分。。。這樣也不用反復上電,斷電。


四,關於APP與IAP互跳之間的中斷處理問題

跳轉時中斷問題還是一個比較棘手的問題。。常常跳轉之后無法進入中斷,然后百度了一下,自己理解大概是,跳轉時僅僅是強制改變了PC指正的位置,可是里面的中斷寄存器什么的都沒有變,這樣中斷存在,可是中斷函數什么的都沒有了,造成程序死掉。。我在寫的過程中也遇到了問題,第一次從iap跳到app正常,可是從app跳回iap的時候因為殘留的中斷太多,在iap中程序死了。我的處理方式是把app中的跳轉命令換成了系統復位NVIC_SystemReset();(不同的固件庫可能函數名不同)其它的處理理的方式據我所知還有有①跳轉之前復位或者關閉全部打開的中斷②跳轉后在初始化時添�RCC_DeInit();,,NVIC_DeInit ();等讓中斷恢復默認值。。詳細可參考以下這篇文章http://dzdesigned80.blog.163.com/blog/static/203259238201272425313152/

關於stm32的軟件復位:

STM32軟件復位(基於庫文件V3.5) ,對於STM32來說軟件復位有兩種方式:    

1)採用官方自帶的軟件庫  
    在官方軟件庫的 core_cm3.h 文件中 直接提供了 系統復位的函數    

static __INLINE void NVIC_SystemReset(void) 


SCB->AIRCR  = ( (0x5FA << SCB_AIRCR_VECTKEY_Pos)    |   

(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk)   |  

SCB_AIRCR_SYSRESETREQ_Msk);                   /* Keep priority group unchanged */ 
  __DSB();                                                                                       /* Ensure completion of memory access */               
  while(1);                                                                                        /* wait until reset */

 } 
  可是不是直接調用這個函數就OK了?    在Cortex-M3權威指南中有這么一句話:     
這里有一個要注意的問題:從SYSRESETREQ 被置為有效,到復位發生器運行復位命令,  往往會有一個延時。在此延時期間,處理器仍然能夠響應中斷請求。但我們的本意往往是要  讓此次運行到此為止,不要再做不論什么其他事情了。所以,最好在發出復位請求前,先把  FAULTMASK 置位。  

  所以最好在將FAULTMASK 置位才萬無一失。    相同官方 core_cm3.h 文件中也直接提供了該函數  
  static __INLINE void __set_FAULTMASK(uint32_t faultMask) 

register uint32_t __regFaultMask       __ASM("faultmask");   

__regFaultMask = (faultMask & 1);

 } 
  把上面這兩個函數寫在一起就能夠實現軟件復位了~~ 

void SoftReset(void) 


__set_FAULTMASK(1);      // 關閉全部中端 

NVIC_SystemReset();// 復位 


 /*------



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM