引言(Opening)
想象一下,你開發的一款智能水杯、一個環境監測設備或者一臺共享充電寶,已經部署到了成千上萬的用戶手中。突然,你發現了一個軟件bug,或者需要增加一個酷炫的新功能。你不可能派人跑到每個設備面前用USB線給它燒錄程序。這時,OTA(空中升級)技術給你一把 鑰匙。
第一部分:核心概念解析——Bootloader, IAP, OTA
在深入流程之前,我們必須先理清這三個容易混淆的概念。
Bootloader(引導加載程序):
是什么:這是一段存儲在微控制器(MCU)啟動地址(通常是Flash起始地址)的小程序。它是設備上電后運行的第一段代碼,就像是設備的“BIOS”。
職責:它的核心工作是決定程序的流向。通常是:初始化基本硬件(時鐘、內存) -> 檢查是否有升級請求?-> 如果有,則留在Bootloader等待升級;如果沒有,則跳轉到用戶應用程序(Application, APP)執行。
IAP(在應用編程):
是什么:一種允許應用程序在運行過程中對自身Flash存儲器進行重新編程的技術。
如何工作:MCU的內部Flash通常被劃分為多個區(Bootloader區、APP區、備份區、數據區等)。IAP利用MCU自帶Flash控制器(Flash Controller)的擦除/編程函數,將接收到的新固件數據寫入到指定的Flash區域。
關鍵點:IAP是實現OTA的技術手段。
OTA(空中升級):
是什么:一個更大的概念,指通過無線網絡(如Wi-Fi, 4G/5G, Bluetooth)進行遠程升級的整個系統和服務端架構。
組成部分:
設備端:包含支持IAP的Bootloader和應用程序。
服務器端:存儲新固件包(firmware.bin)、提供下載接口、管理設備版本。
通信協議:設備與服務器之間的交互規則(如HTTP, MQTT, CoAP)。
關系:OTA = 通信能力 + IAP能力。我們本文重點討論的是OTA中的設備端IAP部分。
第二部分:OTA升級流程詳細分解
下圖清晰地展示了一次完整的OTA升級流程:
現在,我們來逐一拆解圖中的每一個關鍵步驟。
步驟一:接收升級指令與跳轉Bootloader
觸發時機:設備正常運行在APP中,通過無線模塊(如ESP8266、NB-IoT模組)與服務器保持通信。
指令下達:服務器通過某種通信協議(如MQTT主題?
device/001/update
)向設備發送升級指令。指令中通常包含新固件的版本號、大小、哈希值以及下載地址。預處理:APP收到指令后,需要做兩件至關重要的事:
校驗自身狀態:是否在充電?電量是否充足?是否處于空閑狀態?防止升級中途斷電變“磚”。
設置“升級標志”:這是一個非常關鍵的動作。在MCU的Flash(通常是一個專有的“數據區”)或備份寄存器(Backup Register)中,寫入一個特定的值(如
0xA5A5A5A5
)。這個區域需要保證在芯片復位后數據依然保持。
執行跳轉:不是直接擦寫Flash,而是軟件復位(Soft Reset)?單片機。復位后,PC指針回到起始地址,Bootloader開始運行。
步驟二:Bootloader的判斷與升級啟動
Bootloader初始化:上電/復位后,Bootloader最先運行,進行最基本的硬件初始化(時鐘、中斷向量表)。
檢查“升級標志”:Bootloader會去預定義的地址讀取“升級標志”的值。
如果標志有效:說明本次復位是由升級請求觸發的,Bootloader將停留在原地,不會跳轉到APP。并開始執行接下來的升級任務。
如果標志無效或不存在:Bootloader會延時一小段時間(如100ms),等待是否有來自串口等調試接口的升級命令(用于本地調試)。如果沒有,則直接跳轉到APP的起始地址,正常啟動應用程序。
步驟三:Bootloader開始升級與接收數據
建立連接:Bootloader通過設備的網絡模塊,按照APP之前收到的下載地址(地址信息也需要存儲在Flash的某個地方)主動連接服務器(例如發起HTTP GET請求)。
接收數據:服務器將固件文件(bin文件)發送給設備。由于嵌入式設備內存(RAM)有限,無法一次性接收整個固件,必須采用分段接收、分段寫入的方式。
數據包格式:為了可靠傳輸,通常需要自定義簡單的協議幀。例如:
[包頭][包序號][數據長度][數據內容][校驗和]。
流程控制:每收到一包數據,Bootloader需要回復一個ACK確認包,服務器再發送下一包。如果超時未收到,則請求重發該包。
步驟四:分段寫入Flash與校驗
這是IAP的核心操作,非常考驗代碼的健壯性。
Flash布局:必須事先規劃好。例如:
0x0800 0000 - 0x0800 7FFF
: Bootloader區(32KB)0x0800 8000 - 0x0807 FFFF
: APP區(480KB)0x0808 0000 - 0x0808 1FFF
: 數據區(8KB,存放升級標志、版本號等)
擦除APP區:在開始寫入新固件之前,必須先完整擦除舊的APP區。Flash的特性是只能由1寫0,但需要擦除(全部變為1)才能再次寫入。擦除操作通常以扇區(Sector)或頁(Page)為單位進行。
分段寫入:
開辟一個RAM緩沖區(如1-4KB)。
當接收到一包或多包數據填滿緩沖區后,調用MCU的Flash編程函數,將緩沖區數據寫入APP區的指定地址。
更新寫入地址指針。
校驗:
局部校驗:對每一包數據進行校驗和(Checksum)或CRC校驗,確保數據在傳輸過程中沒有出錯。
全局校驗:當接收完所有數據包后,對整個APP區的固件進行一次完整的校驗,通常計算其SHA-256哈希值,并與服務器提供的哈希值進行比對。這是防止固件錯誤、確保升級完整性的最后一道保險。
步驟五:收尾工作與版本確認
清除升級標志:全局校驗通過后,立即將之前設置的“升級標志”清除(如寫入
0xFFFFFFFF
),表示升級成功完成。這一步很重要,防止下次復位后又陷入升級循環。存儲新版本號:將服務器下發的版本號信息存儲到Flash的特定位置。
復位設備:執行軟件復位,讓整個系統重新啟動。
步驟六:啟動新APP與OTA結束
Bootloader再次啟動:本次復位后,Bootloader檢查“升級標志”,發現已被清除。
跳轉新APP:Bootloader順利跳轉到新的APP起始地址,新程序開始運行。
上報新版本:APP啟動后,可以讀取Flash中存儲的版本號,并主動上報給服務器,告知升級成功。至此,一次完整的OTA升級結束。
第三部分:關鍵技術與注意事項
中斷向量表重映射:Bootloader和APP有各自的中斷向量表。跳轉后,必須將中斷向量表地址切換到APP的向量表起始地址,否則中斷無法正確響應。
通信協議穩定性:尤其是在Bootloader中實現網絡通信,需要考慮斷線重連、包序管理、超時重傳等機制,設計一個簡單的應用層協議非常必要。
變磚與恢復:
原因:升級過程中斷電、固件校驗失敗、跳轉地址錯誤等。
對策:
雙備份(A/B系統):保留兩個APP區,一個運行,一個備用升級。新固件寫入備用區,校驗成功后再切換引導地址。
Bootloader自救:在Bootloader中預留一個通過串口升級的“后門”,即使APP區損壞,也能通過有線方式重新燒錄。
安全性:
固件加密:防止傳輸過程中被竊取。
數字簽名:驗證固件來源的合法性,防止惡意固件被寫入設備。