国产精品久久久久影院,成人午夜福利视频,国产精品久久久久高潮,国产精品 欧美 亚洲 制服,国产精品白浆无码流出

歷史上的今天

今天是:2024年12月11日(星期三)

正在發(fā)生

2019年12月11日 | 基于ARM Cortex-M0+內(nèi)核的bootloader程序升級原理及代碼解析

發(fā)布者:zonheny 來源: eefocus關(guān)鍵字:ARM  Cortex-M0+內(nèi)核  bootloader  程序升級 手機看文章 掃描二維碼
隨時隨地手機看文章

本文主要講述BootLoader程序升級原理及一些代碼的解析,力圖用通俗易懂的語言描述清楚BootLoader升級的主要關(guān)鍵點。


BootLoader 升級原理概述

首次接觸這一塊時,有一個概念叫IAP(在應(yīng)用編程),通俗一點講便是通過一段已有的程序(我們稱之為BootLoader程序)去升級另外的一段程序(用戶程序)。升級的方式多種多樣,可以通過串口、USB、SPI等等多種接口去升級。實際上,我們是把我們需要升級的芯片里面分為兩個區(qū)域,暫且稱之為A區(qū)域和B區(qū)域。 


A區(qū)域主要存放BootLoader程序,B區(qū)域主要存放用戶程序,也就是我們希望升級或修改的程序。 


一般情況下,為了升級流程的方便,我們會把A區(qū)域布置在芯片flash(有人喜歡稱ROM,就是存放代碼的區(qū)域)的起始位置,也就是0x0開始的位置,至于A區(qū)域在哪里結(jié)束,這需要看你的BootLoader程序有多大了,它能占用多少的代碼量了。比如你的BootLoader程序編譯完后有2.5KB左右的大小,那么你可以計算一下: 


2.5K = 2.5 X 1024 = 2560B = A00(H) 

也就是說,你的這段代碼如果從零地址開始存放的話,他會在0xA00的位置結(jié)束,0xA00之后的區(qū)域你便可以用來存放需要升級的用戶代碼了。但有時我們并不會緊接著0xA01的位置開始放置用戶代碼,而是會留出一定的空間,比如從0xB00處開始存放代碼,這主要是因為BootLoader程序在flash中存放時不一定會緊挨著存放,有時代碼段之間會有空閑區(qū)域,這一點可以參考之前的文章 IAR編譯器如何節(jié)省代碼占用的flash空間?然后,我們通過在BootLoader程序中設(shè)置相關(guān)參數(shù)(應(yīng)用程序起始位置等),使應(yīng)用程序升級時按照我們設(shè)置的位置存放在B區(qū)域,從而完成升級。這一點我會在后面的代碼詳解中介紹。


我們可以通過下面的圖解來理解bootloader程序升級時的區(qū)域占用情況。 

BootLoader升級程序占用分布

需要了解的關(guān)鍵點

進(jìn)行BootLoader程序編寫之前,我們需要了解并熟悉以下幾個關(guān)鍵點: 

  1. 文件傳輸協(xié)議。因為升級時需要上位機軟件配合下位機的BootLoader程序進(jìn)行應(yīng)用程序代碼的傳輸,因此文件傳輸協(xié)議至關(guān)重要,筆者推薦使用Xmodem 1K協(xié)議。 這個協(xié)議的好處便是上位機可以自動打包數(shù)據(jù),每一包數(shù)據(jù)含有1k字節(jié)的代碼,傳輸效率很高,傳輸時間很短。 


2. 芯片空間map。 做BootLoader升級相關(guān)的項目,肯定離不開對芯片空間的了解,需要對自己所用芯片的RAM、ROM以及向量表(如果有的話)等占用情況有比較深入的了解。 

3. 跳轉(zhuǎn)函數(shù)。這個是程序從BootLoader程序跳轉(zhuǎn)到應(yīng)用程序運行的關(guān)鍵。筆者在做項目時曾經(jīng)在這一塊浪費了不少的時間。文末筆者會提供實用的跳轉(zhuǎn)函數(shù)。


升級代碼解析

其實前面說的再多,脫離了代碼都是紙上談兵。下面通過一個實際的BootLoader升級例子,結(jié)合筆者自己編寫的BootLoader代碼,對這一過程進(jìn)行解析。 

代碼主要包括 

main.c 

BootLoader.c 

xmodem-1k.c 

跳轉(zhuǎn)函數(shù) 

其中, 

main.c主要是升級執(zhí)行初始化及升級完成后初始化升級環(huán)境及跳轉(zhuǎn)代碼實現(xiàn)的部分。 

BootLoader.c部分主要是升級流程的代碼控制。 

xmodem-1k.c主要是文件傳輸協(xié)議的代碼實現(xiàn)。


至于其他部分的代碼,比如串口相關(guān)以及時鐘相關(guān)的代碼,每種芯片的編程方式都不盡相同,因此筆者不詳細(xì)介紹這部分,該部分代碼大家可以從需要升級的應(yīng)用程序中直接移植即可。


main.c

int main (void)

{

    SystemCoreClockUpdate();  // 時鐘初始化 

    WatchDog_Initial();       // 看門狗初始化

    vBootLoader(&vScene_Init,&vScene_Renew);  // BootLoader主程序

}


在vBootloader()這個函數(shù)中用到了兩個函數(shù)指針,分別指向初始化函數(shù)vScene_Init()和環(huán)境重置函數(shù)vScene_Renew()。初始化函數(shù)很好理解,在運行程序之前,先對芯片時鐘、管腳等初始化,或者有些參數(shù)需要初始化,這個根據(jù)自己的代碼情況進(jìn)行選擇。


那么環(huán)境重置函數(shù)什么意思呢?這主要是為了和需要升級的應(yīng)用程序的運行想配合,因為我們的bootloader程序的相關(guān)配置有時候并不一定會和應(yīng)用程序的配置完全一致,如果運行完BootLoader之后,沒有把BootLoader程序的相關(guān)配置關(guān)閉掉或者恢復(fù)到默認(rèn)值,運行到應(yīng)用程序之后,還可能會執(zhí)行BootLoader程序的配置,這樣會出現(xiàn)問題。舉個栗子,在BootLoader程序中用中斷喂狗,跳轉(zhuǎn)到應(yīng)用程序之前,沒有關(guān)閉喂狗中斷,如果在應(yīng)用程序中沒有配置相關(guān)喂狗中斷的程序,那么應(yīng)用程序仍然會按照bootloader的配置執(zhí)行中斷喂狗,這樣會導(dǎo)致應(yīng)用程序中的喂狗失效,因為中斷喂狗是很準(zhǔn)時的,往往起不到喂狗的效果,有時會影響程序的復(fù)位操作。因此,環(huán)境重置函數(shù)說白了,就是把bootloader用到的配置關(guān)掉。筆者建議把用到的所有的東西全部關(guān)閉(包括但不限于串口、時鐘、看門狗、IO等),因為在應(yīng)用程序中會根據(jù)自己的應(yīng)用程序配置相關(guān)的代碼。


BootLoader.c

/**********************************************************

*  BootLoader流程控制函數(shù)

** 參 數(shù): pfunSenceInitCallBack   初始化芯片指針函數(shù)

**       pfunSenceRenewCallBack  重新初始化代碼環(huán)境指針函數(shù)

** 返回值: 無

**********************************************************/

void vBootLoader(void(* pfunSenceInitCallBack)(void), void (* pfunSenceRenewCallBack)(void))

{

    uint8_t ucMessage = 0;


    unsigned int sp;

    unsigned int pc;

    uint16_t bootflag_read;


    sp = APP_START_Flash;

    pc = sp + 4;


    pfunSenceInitCallBack();  //初始化函數(shù)指針,具體函數(shù)怎么寫這里不再贅述


    while(1)

    {

        wdt_feed();

        do{

            ucMessage = u8UpdateMode();  // 此函數(shù)為升級主函數(shù)

            if (UPDATE_OK == ucMessage)        /* 升級成功 */

            {

                memcpy(erase_pg_buf, bootflag_OK,    sizeof(bootflag_OK));

                update_bootflag();

                break;

            } 

            else if (UPDATE_NO == ucMessage)   /* 沒有升級 */

            {

                break;

            }

            else                              /* 升級錯誤 */

            {

                memcpy(erase_pg_buf, bootflag_ERROR, sizeof(bootflag_ERROR));

                update_bootflag();

                break;

            }       

        } while (1);


        bootflag_read = *( volatile uint16_t *)(BOOT_FLAG_ADDR);   /* 讀取存放在bootflag地址的值 */


        if (u8UserCodeEffect() == USERCODE_OK) // 代碼判斷

        {

            if (bootflag_read == 0xAA55)

            {

                pfunSenceRenewCallBack();  // 環(huán)境重置函數(shù)

                vControlSwitch(sp,pc); // 跳轉(zhuǎn)函數(shù)

            }

        }

    }

}


上面是bootloader程序擦寫完flash之后判斷是否升級成功以及執(zhí)行跳轉(zhuǎn)函數(shù)的代碼。流程主要是,升級主函數(shù)u8UpdateMode()(下面是詳細(xì)代碼)中進(jìn)行數(shù)據(jù)接收校驗以及flash擦寫工作,如果擦寫成功,該函數(shù)返回0(UPDATE-OK),擦寫失敗,該函數(shù)返回1(UPDATE-ERROR),沒有擦寫操作,該函數(shù)返回2(UPDATE-NO)。在這個函數(shù)中根據(jù)相關(guān)返回標(biāo)志進(jìn)行處理。處理完去讀取flash中存放BootLoader標(biāo)志的地方的數(shù)據(jù),如果使我們希望的數(shù)據(jù),我們就執(zhí)行跳轉(zhuǎn)函數(shù),讓程序從BootLoader跳轉(zhuǎn)到應(yīng)用程序中,如果標(biāo)志不正確,說明升級過程出了問題,我們就不跳轉(zhuǎn),一直運行在BootLoader程序中。當(dāng)然,在跳轉(zhuǎn)之前需要執(zhí)行我們之前提到的環(huán)境重置函數(shù)pfunSenceRenewCallBack()。


// 升級主函數(shù)

static uint8_t u8UpdateMode(void)

{

   uint8_t ret;


   if (iap_prepare_sector(APP_START_SECTOR, APP_END_SECTOR) == CMD_SUCCESS) // 準(zhǔn)備扇區(qū)

    {

      if (iap_erase_sector(APP_START_SECTOR, APP_END_SECTOR) == CMD_SUCCESS) // 擦除扇區(qū)

         {

            ret = u8Xmodem1kClient(ProgramFlash, (uint16_t)BOOT_DELAYTIME_C, (uint16_t)BOOT_WAITTIME_UPDATE);// 編程指針+X-Modem協(xié)議識別


            if (0 == ret) 

            {

                return UPDATE_OK;// 返回標(biāo)志

            }

            if (2 == ret)

            {

                return UPDATE_NO;

            }


        }

   }

   return UPDATE_ERROR;

}


主函數(shù)代碼不難理解,進(jìn)來之后先準(zhǔn)備相應(yīng)的扇區(qū),然后擦除(FLash內(nèi)部值置全FF),然后啟動X-Modem協(xié)議接收數(shù)據(jù),數(shù)據(jù)接收完成啟動寫flash函數(shù)ProgramFlash()進(jìn)行代碼燒寫,這一部分在下一節(jié)X-Modem-1K.c中講。不同的片子的燒寫流程不同,這個得看芯片手冊,有的需要準(zhǔn)備扇區(qū),有的不需要,但是大多數(shù)流程都保留了先擦除后燒寫的內(nèi)容。


注意:擦除和燒寫之前需要看技術(shù)手冊搞明白芯片是支持區(qū)塊擦除還是支持頁擦除。


X-modem-1k.c

關(guān)于X-Modem協(xié)議是什么,大家可以自行去百度,這里也不再贅述。 

先看代碼,比較長:


/**********************************************************

** Xmodem1k協(xié)議傳輸程序

** 參  數(shù):    pfunPktHandle,: Xmodem1k協(xié)議傳輸所需函數(shù)結(jié)構(gòu)體指針

**          u16ShortDly:    輪詢發(fā)送C字符的時間間隔

**          u8LongDly:      等待傳輸開始超時時限

** 返回值: 傳輸結(jié)果: 0--成功,1--升級失敗(錯誤或取消升級),2--沒有升級

**********************************************************/

uint8_t u8Xmodem1kClient(pFunPKTHAND pfunPktHandle, uint16_t  u16ShortDly, uint16_t u8LongDly)

{

    uint32_t u32ByteCnt   = 0;                                          /* 位計數(shù)器  計數(shù)一包的第幾個字節(jié)數(shù)據(jù)   */

    uint8_t  u8TimeoutCnt = 0;                                          /* 超時次數(shù)                             */

    uint8_t  u8DataerrCnt = 0;                                          /* 數(shù)據(jù)錯誤次數(shù)                       */

    uint8_t  u8PktIndex   = 1;                                          /* 包序號期望值                       */


    uint8_t  u8STATE = STAT_IDLE_C;                                     /* 狀態(tài)變量                             */

    uint8_t  u8Data;                                                    /* 存放接收數(shù)據(jù)及發(fā)送命令              */

    volatile uint16_t u16PktLen;                                        /* 包中有效數(shù)據(jù)的長度                */

    uint8_t  u8Message;


    sysTimerClr(1);


    while (1)

    {

        wdt_feed();


        switch (u8STATE)

        {

        case STAT_IDLE_C:                                               /* 輪詢發(fā)C狀態(tài)                  */

             if (sysTimerGet(1) >= u8LongDly )

             {

                u8STATE = STAT_TIMEOUT_C;                               /* 等待開始超時,跳到結(jié)束狀態(tài)   */

             } 

[1] [2] [3]
關(guān)鍵字:ARM  Cortex-M0+內(nèi)核  bootloader  程序升級 引用地址:基于ARM Cortex-M0+內(nèi)核的bootloader程序升級原理及代碼解析

上一篇:ARM USB 通信
下一篇:Cortex-M3 的SVC、PendSV異常,與操作系統(tǒng)(ucos實時系統(tǒng))

推薦閱讀

說起全面屏?xí)r代的解鎖方式,前有屏下指紋后有結(jié)構(gòu)光。屏下指紋識別想必大家都很熟悉,目前國內(nèi)主攻全面屏設(shè)計的智能機產(chǎn)品大多選擇了屏下指紋設(shè)計,很大程度上加速了屏下指紋技術(shù)的普及?! ∠喾矗捎媒Y(jié)構(gòu)光技術(shù)的產(chǎn)品并不多,但結(jié)構(gòu)光名聲之響較屏下指紋是有之過而無不及。究其原因,還在于iPhone,這個全球最成功的智能機品牌,最新的產(chǎn)品均選擇了結(jié)構(gòu)...
世界領(lǐng)先的軟件服務(wù)公司、ST合作伙伴計劃成員Tieto與橫跨多重電子應(yīng)用領(lǐng)域的全球領(lǐng)先的半導(dǎo)體供應(yīng)商意法半導(dǎo)體(STMicroelectronics,簡稱ST;紐約證券交易所代碼:STM)于10日宣布,雙方正在合作開發(fā)在意法半導(dǎo)體的人氣頗高的Telemaco3P平臺上運行的汽車中控單元(CCU)軟件。 加快汽車電動化和網(wǎng)絡(luò)化的要求正在推動汽車處理器具備更強的處理能力和毋庸置疑...
機器人作為人工智能的細(xì)分領(lǐng)域,也隨著人工智能的發(fā)展日益精進(jìn)。傳統(tǒng)機器人通常是一種重型機器,有很多和設(shè)備,這意味著其將無法與包括人類在內(nèi)的軟結(jié)構(gòu)安全互動。 現(xiàn)在,美國西北大學(xué)的一支研究團隊就設(shè)計了具有分子智能的軟材料,使其能夠像任何尺寸的機器人一樣,在水下或地下的微小空間中執(zhí)行特定功能。 研究人員表示,這種外表和行為都類似于微型...
12月9日消息,vivo于線上舉辦OriginOS Ocean發(fā)布會,正式發(fā)布全系操作系統(tǒng)OriginOS Ocean,帶來了全新鎖屏體系、原子隨身聽、原子閱讀、原子隱私系統(tǒng)、原子筆記等交互模式及應(yīng)用。 vivo副總裁楊健  vivo副總裁楊健表示,在原系統(tǒng)OriginOS 1.0基礎(chǔ)上,OriginOS Ocean帶來了革命性升級。OriginOS Ocean構(gòu)建了人與世界的全新交互體驗,推動...

史海拾趣

小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機文章

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved