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

程序在3個區間按下面示意圖來回跳轉執行,永不停息。

經過簡單的編程即可達到上面目的。下圖是串口助手顯示的輸出結果的部分截圖。

這里實際上有3個工程,每個工程做跳轉時跳轉地址不一樣。這里不妨以從BOOT區跳往AAP1區為例,看看跳轉前做的哪些準備工作。

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

我們在跳轉前的準備工作里加上針對上面外設復位的操作,見下面橙色方框內代碼,分別針對TIM1、USART2和相關GPIO外設做了強制復位。

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

這些復位操作將讓相應總線上的外設得以強制復位。這樣一來,我們就可以將前面跳轉前的那一堆逐個針對ST外設的操作代碼改成HAL_DeInit()這一句即可。經實際驗證也是可行的。參考下面代碼的寫法,代碼一下變得精簡、清爽很多。

最后補充兩點,上面實驗代碼中從APP2區跳回BOOT區,通過調用系統復位函數也是可行的。另外,上面內容不完全適用于STM32F0系列。
好,關于程序在不同代碼區跳轉執行的話題就聊到這里,下次再聊!
審核編輯:黃飛
?
電子發燒友App













評論