找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 5218|回復(fù): 2
打印 上一主題 下一主題
收起左側(cè)

STM32IAP升級(jí)----IAP升級(jí)功能編寫(xiě)初期的一些困惑與疑問(wèn)---完成功能后的總結(jié)

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:104287 發(fā)表于 2016-1-31 03:45 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
IAP的源碼等資料我上傳了,壓縮包內(nèi)有12個(gè)文件,,http://download.csdn.net/detail/f907279313/7524849(要積分的辛苦收集的你們就給點(diǎn)積分吧)
還有另一篇博客總結(jié)的IAP:http://blog.csdn.net/super_demo/article/details/32086541

一,網(wǎng)上下載的例程,跳轉(zhuǎn)部分的代碼有差異,尤其是用的匯編那句
eg:
①Jump_To_Application  = (pFunction)(*(vu32*) (IAPSTART + 4));
__MSR_MSP(*(vu32*) IAPSTART);
Jump_To_Application();
跟蹤__MSR_MSP(一般這個(gè)函數(shù)都在庫(kù)文件里有,跟蹤不到就用搜索找)找到匯編函數(shù)為
__MSR_MSP

    MSR MSP, r0 ; set Main Stack value
    BX r14

②//跳轉(zhuǎn)到應(yīng)用程序段
//appxaddr:用戶代碼起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//檢查棧頂?shù)刂肥欠窈戏?
{
jump2app=(iapfun)*(vu32*)(appxaddr+4);//用戶代碼區(qū)第二個(gè)字為程序開(kāi)始地址(復(fù)位地址)
MSR_MSP(*(vu32*)appxaddr);//初始化APP堆棧指針(用戶代碼區(qū)的第一個(gè)字用于存放棧頂?shù)刂?
jump2app();  //跳轉(zhuǎn)到APP.
}
}
跟蹤MSR_MSP找到函數(shù)為
//設(shè)置棧頂?shù)刂?br /> //addr:棧頂?shù)刂?br /> __asm void MSR_MSP(u32 addr)
{
    MSR MSP, r0 //set Main Stack value
    BX r14
}

③  //判斷用戶是否已經(jīng)下載程序,因?yàn)檎G闆r下此地址是棧地址。
        //若沒(méi)有這一句的話,即使沒(méi)有下載程序也會(huì)進(jìn)入而導(dǎo)致跑飛。
        if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
        {
            SerialPutString("Execute user Program\r\n\n");
            //跳轉(zhuǎn)至用戶代碼
            JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
            Jump_To_Application = (pFunction) JumpAddress;


            //初始化用戶程序的堆棧指針
            __set_MSP(*(__IO uint32_t*) ApplicationAddress);
            Jump_To_Application();
        }
跟蹤__set_MSP找到函數(shù)為
__ASM void __set_MSP(uint32_t mainStackPointer)
{
  msr msp, r0
  bx lr
}
總結(jié)以上發(fā)現(xiàn)都是操作ARM的R0跟R14(LR)寄存器。
還有一種不太一樣的,就是stm32F4的庫(kù)函數(shù)中的跳轉(zhuǎn),如下所示
④ //測(cè)試用戶app地址是不是在APPLICATION_ADDRESS位置。檢測(cè)棧頂?shù)牡刂,?lái)檢驗(yàn)app是否下載成功
    if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
    {
    //APPLICATION_ADDRESS + 4對(duì)應(yīng)的是app中斷向量表的第二項(xiàng),復(fù)位地址
    JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
//把地址強(qiáng)轉(zhuǎn)為函數(shù)指針
    Jump_To_Application = (pFunction) JumpAddress;
    //設(shè)置主函數(shù)棧指針
    __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
//調(diào)用函數(shù),實(shí)際失去app復(fù)位地址去執(zhí)行復(fù)位操作
    Jump_To_Application();
    }
跟蹤__set_MSP找到函數(shù)為
static __INLINE void __set_MSP(uint32_t topOfMainStack)
{
  register uint32_t __regMainStackPointer     __ASM("msp");
  __regMainStackPointer = topOfMainStack;
}
對(duì)于M4的這個(gè)庫(kù)函數(shù)我也不太懂,感覺(jué)最終的操作應(yīng)該跟其他的一樣吧
二,關(guān)于跳轉(zhuǎn)部分的代碼的理解(轉(zhuǎn))

這里重點(diǎn)說(shuō)一下幾句經(jīng)典且非常重要的代碼:
第一句: if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)   //判斷棧定地址值是否在0x2000 0000 - 0x 2000 2000之間
怎么理解呢? (1),在程序里#define ApplicationAddress    0x8003000 ,*(__IO uint32_t*)ApplicationAddress)  即取0x8003000開(kāi)始到0x8003003 的4個(gè)字節(jié)的值, 因?yàn)槲覀兊膽?yīng)用程序APP中設(shè)置把 中斷向量表 放置在0x08003000 開(kāi)始的位置;而中斷向量表里第一個(gè)放的就是棧頂?shù)刂返闹?/font>
也就是說(shuō),這句話即通過(guò)判斷棧頂?shù)刂分凳欠裾_(是否在0x2000 0000 - 0x 2000 2000之間) 來(lái)判斷是否應(yīng)用程序已經(jīng)下載了,因?yàn)閼?yīng)用程序的啟動(dòng)文件剛開(kāi)始就去初始化化?臻g,如果棧頂值對(duì)了,說(shuō)應(yīng)用程已經(jīng)下載了啟動(dòng)文件的初始化也執(zhí)行了;

第二句:    JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);   [  common.c文件第18行定義了:  pFunction   Jump_To_Application;]
                     
ApplicationAddress + 4  即為0x0800 3004 ,里面放的是中斷向量表的第二項(xiàng)“復(fù)位地址”  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4); 之后此時(shí)JumpAddress
第三句:    Jump_To_Application = (pFunction) JumpAddress;
startup_stm32f10x_md_lv. 文件中別名  typedef  void (*pFunction)(void);     這個(gè)看上去有點(diǎn)奇怪;正常第一個(gè)整型變量   typedef  int  a;  就是給整型定義一個(gè)別名 a
void (*pFunction)(void);   是聲明一個(gè)函數(shù)指針,加上一個(gè)typedef 之后  pFunction只不過(guò)是類型 void (*)(void) 的一個(gè)別名;例如:

[cpp] view plaincopy



  • pFunction   a1,a2,a3;  
  •   
  • void  fun(void)  
  • {  
  •     ......  
  • }  
  •   
  • a1 = fun;  


所以,Jump_To_Application = (pFunction) JumpAddress;  此時(shí)Jump_To_Application指向了復(fù)位函數(shù)所在的地址;
第四 、五句: __set_MSP(*(__IO uint32_t*) ApplicationAddress);      \\設(shè)置主函數(shù)棧指針
               Jump_To_Application();                         \\執(zhí)行復(fù)位函數(shù)
Jump_To_Application()是把用戶代碼的復(fù)位地址付給PC指針,我看到Jump_To_Application()這句代碼debug的時(shí)候?qū)?yīng)的匯編代碼是
LDR r0,[pc,#12] ;相對(duì)PC的數(shù)據(jù)加載,去函數(shù)指針的地址
LDR r0,[r0,#00] ;R0做索引,無(wú)偏移,數(shù)據(jù)裝載到R0,這個(gè)內(nèi)容就是函數(shù)指針指向的內(nèi)容,也就是函數(shù)的地址了,用戶程序的起始地址;
BLX r0              ;這個(gè)不解釋,說(shuō)了是跳轉(zhuǎn)
我們看一下啟動(dòng)文件startup_stm32f10x_md_vl.s 中的啟動(dòng)代碼,更容易理解


三,關(guān)于跳轉(zhuǎn)時(shí)能否不用按鍵,用軟件標(biāo)志位以及APP與IAP之間的互跳
完全可以不用按鍵,可以模擬一個(gè)按鍵信號(hào),或者用軟件的一個(gè)標(biāo)志位來(lái)判斷是否更新。。我設(shè)計(jì)的在flash中中存儲(chǔ)一個(gè)值,當(dāng)APP運(yùn)行中需要更新時(shí)串口發(fā)來(lái)更新命令,然后在flash中存一個(gè)值之后跳到IAP部分,來(lái)讀取flash中存儲(chǔ)的那個(gè)值,如果是需要更新則更新,如果不是需要跟新標(biāo)志位就直接跳轉(zhuǎn)到APP部分。。。這樣也不用重復(fù)上電,斷電。

四,關(guān)于APP與IAP互跳之間的中斷處理問(wèn)題
跳轉(zhuǎn)時(shí)中斷問(wèn)題還是一個(gè)比較棘手的問(wèn)題。。經(jīng)常跳轉(zhuǎn)之后無(wú)法進(jìn)入中斷,然后百度了一下,自己理解大概是,跳轉(zhuǎn)時(shí)只是強(qiáng)制改變了PC指正的位置,但是里面的中斷寄存器什么的都沒(méi)有變,這樣中斷存在,但是中斷函數(shù)什么的都沒(méi)有了,造成程序死掉。。我在寫(xiě)的過(guò)程中也遇到了問(wèn)題,第一次從iap跳到app正常,但是從app跳回iap的時(shí)候由于殘留的中斷太多,在iap中程序死了。我的處理方式是把a(bǔ)pp中的跳轉(zhuǎn)命令換成了系統(tǒng)復(fù)位NVIC_SystemReset();(不同的固件庫(kù)可能函數(shù)名不同)其他的處理理的方式據(jù)我所知還有有①跳轉(zhuǎn)之前復(fù)位或者關(guān)閉所有打開(kāi)的中斷②跳轉(zhuǎn)后在初始化時(shí)加入RCC_DeInit();,,NVIC_DeInit ();等讓中斷恢復(fù)默認(rèn)值。。具體可參考下面這篇文章http://dzdesigned80.blog.163.com ... 238201272425313152/
關(guān)于stm32的軟件復(fù)位:
STM32軟件復(fù)位(基于庫(kù)文件V3.5) ,對(duì)于STM32來(lái)說(shuō)軟件復(fù)位有兩種方式:   
1)采用官方自帶的軟件庫(kù)  
    在官方軟件庫(kù)的 core_cm3.h 文件里 直接提供了 系統(tǒng)復(fù)位的函數(shù)   
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 */
}
  但是不是直接調(diào)用這個(gè)函數(shù)就OK了?    在Cortex-M3權(quán)威指南中有這么一句話:     
這里有一個(gè)要注意的問(wèn)題:從SYSRESETREQ 被置為有效,到復(fù)位發(fā)生器執(zhí)行復(fù)位命令,  往往會(huì)有一個(gè)延時(shí)。在此延時(shí)期間,處理器仍然可以響應(yīng)中斷請(qǐng)求。但我們的本意往往是要  讓此次執(zhí)行到此為止,不要再做任何其它事情了。所以,最好在發(fā)出復(fù)位請(qǐng)求前,先把  FAULTMASK 置位。  

  所以最好在將FAULTMASK 置位才萬(wàn)無(wú)一失。    同樣官方 core_cm3.h 文件里也直接提供了該函數(shù)  
  static __INLINE void __set_FAULTMASK(uint32_t faultMask)
{
register uint32_t __regFaultMask       __ASM("faultmask");   
__regFaultMask = (faultMask & 1);
}
  把上面這兩個(gè)函數(shù)寫(xiě)在一起就可以實(shí)現(xiàn)軟件復(fù)位了~~
void SoftReset(void)
{
__set_FAULTMASK(1);      // 關(guān)閉所有中端
NVIC_SystemReset();// 復(fù)位
}
/*------



評(píng)分

參與人數(shù) 1黑幣 +3 收起 理由
xn10086 + 3 很給力!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏8 分享淘帖 頂4 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:396960 發(fā)表于 2018-12-7 09:20 | 只看該作者
這好文怎么沒(méi)人贊一下。。。
回復(fù)

使用道具 舉報(bào)

板凳
ID:435221 發(fā)表于 2018-12-9 08:29 | 只看該作者
樓主辛苦了,給了我很多知識(shí)
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表