在STM32應(yīng)用中經(jīng)常會(huì)需要基于用戶程序的做代碼更新、升級(jí),即In Applicaton Programming,簡(jiǎn)稱IAP。這個(gè)過程往往需要程序從不同執(zhí)行區(qū)做跳轉(zhuǎn),最常見的自然是從啟動(dòng)區(qū)跳往應(yīng)用程序區(qū),即從BOOT區(qū)跳往APP區(qū)。這個(gè)跳轉(zhuǎn)過程中,經(jīng)常有人遇到跳轉(zhuǎn)失敗的問題。雖是個(gè)老話題,但關(guān)于這方面的問題咨詢可謂經(jīng)久不息。我這里以STM32G4系列芯片及開發(fā)板做下相關(guān)實(shí)驗(yàn),分享些應(yīng)用提醒,以供參考。
為了保證在跳轉(zhuǎn)過程不出異常,主要注意兩點(diǎn):
第一點(diǎn),即將跳轉(zhuǎn)到程序區(qū)的內(nèi)存地址、中斷矢量表地址。在STM32庫(kù)例程里,中斷矢量表地址的修改一般采用基地址加偏移量的代碼寫法。
第二點(diǎn),也是非常重要的一點(diǎn)。在做跳轉(zhuǎn)前,做好準(zhǔn)備工作。執(zhí)行跳轉(zhuǎn)前,當(dāng)前程序區(qū)不能存在尚未處理的中斷請(qǐng)求,要對(duì)開啟過的中斷使能全面地逐個(gè)清零關(guān)閉,切不可簡(jiǎn)單地只是調(diào)用一個(gè)所謂關(guān)總中斷函數(shù)的做法,即調(diào)用? __disable_irq()函數(shù)。該函數(shù)只是臨時(shí)關(guān)閉中斷響應(yīng),不會(huì)阻止中斷事件的發(fā)生及相應(yīng)中斷標(biāo)志的生成。這個(gè)做法極不可取,隱患很多。
一般來(lái)講,自己開啟了哪些中斷大致是清楚的。對(duì)于STM32用戶來(lái)講,如果基于CubeMx創(chuàng)建工程,SYSTICK定時(shí)器中斷默認(rèn)開啟,擔(dān)當(dāng)Cube庫(kù)函數(shù)的滴答時(shí)基,很多延時(shí)相關(guān)都使用到它。我們?cè)谧鎏D(zhuǎn)前,記得將其計(jì)數(shù)器停掉或關(guān)閉它的中斷請(qǐng)求能力。使用下面三句的任意一句都可以令其喪失中斷請(qǐng)求能力【下面是基于STM32庫(kù)函數(shù)的代碼寫法】:
SysTick->CTRL&=~SysTick_CTRL_TICKINT_Msk;
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->LOAD = 0 ;?
我這里以STM32G474芯片為藍(lán)本,劃分三個(gè)區(qū),分別稱之為BOOT區(qū)、APP1區(qū)、APP2區(qū)。三個(gè)區(qū)代碼內(nèi)容基本一樣,用到外設(shè)完全一樣,主要是UART2、TIM1?;赥IM1周期性事件通過USART向串口終端提示當(dāng)前程序運(yùn)行區(qū)間。

程序在3個(gè)區(qū)間按下面示意圖來(lái)回跳轉(zhuǎn)執(zhí)行,永不停息。

經(jīng)過簡(jiǎn)單的編程即可達(dá)到上面目的。下圖是串口助手顯示的輸出結(jié)果的部分截圖。

這里實(shí)際上有3個(gè)工程,每個(gè)工程做跳轉(zhuǎn)時(shí)跳轉(zhuǎn)地址不一樣。這里不妨以從BOOT區(qū)跳往AAP1區(qū)為例,看看跳轉(zhuǎn)前做的哪些準(zhǔn)備工作。

跳轉(zhuǎn)前的準(zhǔn)備工作如果像上面那樣,多數(shù)情況下跳轉(zhuǎn)是沒有啥問題的。不過,這還不能保證跳轉(zhuǎn)總成功,是否成功跟具體應(yīng)用場(chǎng)景有關(guān)。因此,我們強(qiáng)烈建議針對(duì)使用過的外設(shè)做復(fù)位操作,這就比較保險(xiǎn)了。畢竟前面的準(zhǔn)備工作,側(cè)重跳轉(zhuǎn)過程中避免產(chǎn)生中斷事件或停止外設(shè)的運(yùn)行,但不能保證跳轉(zhuǎn)過程中不會(huì)出現(xiàn)一些不確定的狀態(tài)。所以,建議跳轉(zhuǎn)前對(duì)開啟過的外設(shè)做復(fù)位,讓他們徹底靜默下來(lái),待到新的程序執(zhí)行區(qū)根據(jù)實(shí)際情況再行初始化。
從main()函數(shù)開始的地方不難看到,目前開啟的外設(shè)主要是下面幾個(gè)。

我們?cè)谔D(zhuǎn)前的準(zhǔn)備工作里加上針對(duì)上面外設(shè)復(fù)位的操作,見下面橙色方框內(nèi)代碼,分別針對(duì)TIM1、USART2和相關(guān)GPIO外設(shè)做了強(qiáng)制復(fù)位。

顯然,如果之前開啟的外設(shè)較多的話,這樣一個(gè)個(gè)添加強(qiáng)制初始化代碼也挺啰嗦的。這里再推薦一個(gè)等效做法。在STM32 HAL庫(kù)有一個(gè)專門用來(lái)對(duì)所有外設(shè)進(jìn)行復(fù)位的函數(shù),它就是HAL_Deinit()。下面是其函數(shù)體內(nèi)的具體內(nèi)容。

這些復(fù)位操作將讓相應(yīng)總線上的外設(shè)得以強(qiáng)制復(fù)位。這樣一來(lái),我們就可以將前面跳轉(zhuǎn)前的那一堆逐個(gè)針對(duì)ST外設(shè)的操作代碼改成HAL_DeInit()這一句即可。經(jīng)實(shí)際驗(yàn)證也是可行的。參考下面代碼的寫法,代碼一下變得精簡(jiǎn)、清爽很多。

最后補(bǔ)充兩點(diǎn),上面實(shí)驗(yàn)代碼中從APP2區(qū)跳回BOOT區(qū),通過調(diào)用系統(tǒng)復(fù)位函數(shù)也是可行的。另外,上面內(nèi)容不完全適用于STM32F0系列。
好,關(guān)于程序在不同代碼區(qū)跳轉(zhuǎn)執(zhí)行的話題就聊到這里,下次再聊!
審核編輯:黃飛
?
電子發(fā)燒友App













評(píng)論