天天看點

線上更新:OTA更新的原理和實作方式

作者:嵌入式之入坑筆記

在平常的項目開發和調試中,下載下傳程式一般使用的是外部下載下傳器或者序列槽的方式實作對單片機的程式下載下傳和重新整理,這種方法在項目的開發階段是常用的方式。

但是當項目開發完成推向市場的時候,很多時候需要對産品進行更新,而這個時候産品又已經是加了外殼的或者被封裝起來了,一般也不會在外面預留出來下載下傳接口之類的。

如果這個時候我想要更新産品的程式的話,可能就得要重新打開産品的外殼,然後通過下載下傳器更新程式,更新完成之後再把外殼裝上,這種做法顯然是不太現實的。但是我們又必須要給産品進行更新,那該怎麼辦呢?這個時候就可以考慮使用産品本身預留的一些外部通信接口(如:USB、RS232、ES485、以太網口等)或者内部無線(如:wifi、藍牙、4/5G網絡)等對産品進行更新。

上面介紹的這種通過外部有線接口或者無線通信的方式進行的更新其實是一種線上更新的方式,即OTA更新技術。

那問題來了,到底什麼是OTA更新技術呢?待我慢慢道來!

1、 OTA 線上更新

OTA:Over-the-Air Technology,字面意思了解為:空中下載下傳技術。

OTA 線上更新:通過OTA的方式實作産品軟體更新的一種方式。

是以,簡單而言,通過外部的方式(有線 / 無線)對産品進行更新,而不是用傳統的程式設計器刷入固件的方式就可以稱之為 OTA 線上更新。

嚴格意義上來講,OTA 指的是空中下載下傳,即隻有通過無線的方式進行更新的才稱之為 OTA 更新;而那種通過外部的接口接線來實作的更新,應該稱之為本地更新。這兩者還是有點差別的,隻是一般我們都沒有那麼嚴格去區分罷了!

2、實作方式

那既然了解了更新的概念了,該怎麼去實作呢?

我們一般的做法是會将這個更新功能進行劃分,分為兩部分:

1) 接收新的更新固件并完成新舊固件的替換,這部分代碼為 BootLoader;

2) 産品功能的正常程式,用于執行各種應用功能,這部分程式稱為 App。

那就是說,要實作線上更新,就需要準備兩份程式,一份是BootLoader ,另一份是App。其中 bootloader 用于将外部傳入的新固件(應用程式App)接收到内部并存儲,接收完成以後,由 bootloader 用新接收到的固件去替換舊的固件,替換完成之後跳轉到新的應用程式中進行執行。這樣就完成了産品的固件更新。

注意:需要将 bootloader 和應用程式App的空間分開,兩者是不能發生重疊的。

3、操作方式

3.1、背景式更新

背景式更新的意思是:在進行更新的時候,接收新固件包的方式是在背景進行的,不會影響功能的正常執行。等到固件更新完成之後,再跳轉到Bootloader中去用新的固件替換舊的固件,替換完成之後呢再跳轉到App去執行。

比如,現在的智能手機的線上更新就是背景式更新的方式。在你更新系統的時候,接收更新包的過程中,你還是可以正常使用的手機的,打電話、看視訊、玩遊戲等都不耽誤,直到下載下傳完成,你點選了開始更新之後,手機才進入更新狀态,不讓你操作,等更新完畢之後重新開機就又可以繼續操作了。

3.2、非背景式式更新

非背景式更新的意思是:在進行更新的時候,接收固件時需要跳轉到Bootloader,這個時候你不能在使用這個産品的任何功能,隻能一直等着它接收并完成更新,完成之後你才能繼續操作其他的功能。

4、STM32 的線上更新

本文以STM32為例展開講解怎麼實作OTA更新和操作的方法。

4.1、劃分 Flash 區域

按照前面講述的有關線上更新的原理,我們知道是要準備兩份代碼的,一份是BootLoader,另外一份是App。由于這兩份代碼在STM32中都是要存放在Flash中的,而且它們的空間還不能重疊,要獨立區分開。

是以,按照這個原則,我們可以考慮在Flash中分三個區域出來,如下圖所示:

線上更新:OTA更新的原理和實作方式

上圖中的三塊Flash分别用于:

(1)用于存放Bootloader程式;

(2)用于存放應用程式;

(3)用于存放接收到的新固件。(注:這部分可要可不要,根據你的設計選擇)

注意:上圖中(3)這個Flash區域是考慮用于儲存線上更新的固件的,作為備份固件。友善用于以後系統出現異常時,可以從這個備份固件中重新加載到App中,防止固件丢失!

4.2、實操1 - Flash空間位址的劃分

首先,我們要知道,在stm32中,flash的位址空間是從0x08000000開始的,在keil中也是預設的從這個位置開始的。

一般而言,Bootloader 是在上電時預設開始執行,是以将Bootloader程式可以存放到STM32預設執行的位置(keil編譯器預設從0x08000000位址開始存放)。

應用程式從 Bootloader 後開始存放即可,隻要不和BootLoader發生沖突即可。

假設 Bootloader 的大小為10k,即0x2800位元組,那麼可以選取 0x08000000 ~ 0x08002800 位址範圍作為BootLoader的存放區域。

應用程式從0x08005000開始存放(至于選多大的範圍,要根據你的應用程式大小進行考慮,建議保留一些餘量)。

劃分如下圖所示:

線上更新:OTA更新的原理和實作方式

4.3、實操2 - 設定工程

(1)設定起始位址和大小

準備兩個工程,一個是bootloader的程式,另外一個是應用程式的工程,并對工程進行設定。bootloader選擇預設執行的位置,應用程式根據實際需要設定開始存放到flash指定的位置。

比如:App的起始位址設為0x08005000,大小設為0x1B000(RAM總大小0x20000-0x5000)。設定如下圖:

線上更新:OTA更新的原理和實作方式

BootLoader 也是一樣的設定方式,隻是位址不同而已。

(2)生成 bin 檔案

另外需要注意的,應用程式需要轉換為bin檔案才可以寫入Flash中,能發送到産品中的固件也是要先轉為bin格式的檔案的。

編譯器可以選擇生成hex檔案,再把hex檔案轉換為bin檔案。還可以使用簡單方法,MDK-Keil中點開User頁籤,設定如下圖:

線上更新:OTA更新的原理和實作方式

即在編譯後直接執行fromelf.exe指令将.axf檔案轉換為.bin檔案。生成的bin檔案在工程目錄下的out檔案夾下。

4.4、實操3 - 接收固件更新包

接收固件更新包的話,就要根據你的實際産品進行選擇了。

首先,如果你是背景式更新的話,那接收固件更新包的功能就要在App中實作;如果你是非背景式更新的話,那接收固件更新包的功能就要在BootLoader中實作。

其次,要考慮接收固件更新包的方式。無線的話用的是wifi、藍牙、4/5G網絡還是啥的;有線的話用的是序列槽UART、Spi、IIC、CAN、以太網還是啥的。

最後,最好的方式是能夠對接收到的固件更新包做一個校驗,防止資料接收過程中出現錯誤,導緻更新後出現嚴重問題。

注意:BootLoader 需要提前刷入,不然無法完成線上更新!

4.5、實操4 - 拷貝程式至Flash

接收成功固件包之後,需要将資料寫入到 Flash 的指定位置(比如 0x08005000)完成固件的更新程式寫入。

STM32對Flash的操作過程如下:

1) Flash解鎖。(FlashUnclock)
2) 擦除App所在的Flash頁。
3) 向App的Flash區域寫入資料。
4) 寫完後,Flash上鎖。(FlashClock)
           

4.6、實操5 - 跳轉至 App 應用程式

将接收到應用程式全部正确寫入Flash的App指定位置後,Bootloader 需要完成跳轉到應用程式App的開始位置的操作。

跳轉過程如下:

1)關閉中斷,防止在跳轉過程中有中斷發生。
2)擷取棧指針。
3)擷取複位向量,即為棧指針後的四位元組内容(32bits)。
4)重定向中斷向量表,以保證應用程式中中斷的正常工作。應用程式從0x08005000存儲,是以相對于Flash基位址0x08000000偏移了0x5000。
5)設定新的棧指針,上述中所擷取的棧指針。
6)跳轉至複位向量開始運作。
           

4.7、特别注意 - 設定向量中斷表偏移

這個跳轉到應用程式之後,有一個非常重要的事情要注意,就是要重新設定App的中斷向量表,否則不能正常運作的。

首先要了解STM32的正常運作是怎麼樣的,如下圖所示:

線上更新:OTA更新的原理和實作方式

STM32的内部閃存(FLASH)的位址預設是從0x8000000開始的,預設也是從這個位置開始執行程式的。并且在STM32的内部有一張 “中斷向量表” 用于響應中斷,程式在啟動以後首先會從中斷向量表取出複位中斷程式,執行完複位中斷程式以後才會跳轉到main( )函數開始執行。

中斷向量表的位置從0x8000004開始,如果采用的是bootloader和應用程式的方式的話,bootloader一般放在預設開始的位置,是以它的中斷向量表還是正确的。

而APP應用程式是放置在其他的位置,那麼APP應用程式部分的中斷向量表就要發生偏移,才能為應用程式找到并響應中斷。

在應用程式中,與程式跳轉需要注意的是,單片機從Bootloader程式跳轉至應用程式的複位向量處開始執行,單片機在執行main函數之前會執行一些系統初始化程式。在系統初始化函數void SystemInit (void);在該函數中有對中斷向量表的設定,如下:

線上更新:OTA更新的原理和實作方式

在上圖中VECT_TAB_OFFSET預設為0x0,而應用程式的中斷向量表需要定位到0x08005000,是以這裡需要将VECT_TAB_OFFSET值修改為0x5000。(這裡可能跟跳轉程式中的設定中斷向量表的過程重複,是以需要慎重考慮)。

如下:

/* Configure the Vector Table location add offset address -配置中斷向量表的起始位址-*/
#ifdef VECT_TAB_SRAM    //預設沒有定義
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif

           

修改這個偏移:

#define VECT_TAB_OFFSET  0x5000 /*向量表基本偏移量字段。 此值必須是0x200的倍數 */
           

注意:這個中斷向量表的偏移一定要在初始化階段去完成!

到此,線上更新的原理和STM32的操作已經講完,如果有講的不正确的地方,還請指正!

繼續閱讀