如今,幾乎所有可聯(lián)網(wǎng)的電子設(shè)備都支持遠(yuǎn)程升級(jí)(OTA)功能,OTA 一是讓電子設(shè)備能夠支持更多的功能,二是能夠修復(fù)一些應(yīng)用程序中的漏洞。
前文中,我們?cè)?2nd Bootloader 中實(shí)現(xiàn)了類似 ISP 的下載的功能,上位機(jī)如果基于 Ymodem 協(xié)議,通過 2nd Bootloader 下載應(yīng)用程序到 Flash 中,其實(shí)就可以實(shí)現(xiàn)簡單的 OTA 功能,但是,這樣的 OTA 卻是不安全的:無法保證升級(jí)過程中,若出現(xiàn)意外情況,打斷升級(jí)后,微控制器還能夠執(zhí)行原有的應(yīng)用程序。健全的 OTA,需要保證微控制器即使在升級(jí)失敗之后,也依然能夠按照升級(jí)之前的應(yīng)用程序繼續(xù)工作。
?設(shè)計(jì)
升級(jí)應(yīng)用程序這個(gè)需求其實(shí)通過前文實(shí)現(xiàn)的 ISP 就能實(shí)現(xiàn),而 OTA 升級(jí)失敗后,依然能夠執(zhí)行升級(jí)前的應(yīng)用程序,才是 OTA 升級(jí)的難點(diǎn)。
OTA 為什么會(huì)升級(jí)失敗?有如下可能:
OTA 升級(jí)過程中,意外斷開網(wǎng)絡(luò),固件包接收不完整。
OTA 升級(jí)過程中,設(shè)備突然斷電,固件包接收不完整。
OTA 升級(jí)過程中,設(shè)備突然斷電,接收到的固件包未寫入到 Flash 中。
如果要保證 OTA 升級(jí)失敗后,原有的應(yīng)用程序還能夠正常運(yùn)行,就需要讓接收到的固件包,不能直接覆蓋到原有的固件中。因此,我們需要把 Flash 空間一分為二,分別是執(zhí)行區(qū)域和備份區(qū)域,執(zhí)行區(qū)域存儲(chǔ)的是當(dāng)前微控制器可執(zhí)行的固件,2nd Bootloader 會(huì)引導(dǎo)微控制器跳轉(zhuǎn)到這塊區(qū)域執(zhí)行應(yīng)用程序,而備份區(qū)域就是存放將要下載的新固件,如圖1所示:
圖1 Flash 空間劃分
還需要將 OTA 的過程分為兩個(gè)大的步驟:
固件下載過程
固件更新過程
固件下載過程中,微控制器 會(huì)把接收到的固件保存到備份區(qū)域,如圖2所示。
圖2 固件包存儲(chǔ)在備份區(qū)域
固件更新過程中,微控制器 會(huì)把備份區(qū)域的固件復(fù)制到執(zhí)行區(qū)域中如圖3所示。
圖3 固件包轉(zhuǎn)移到執(zhí)行區(qū)域
由于固件包存儲(chǔ)在備份區(qū)域,固件下載過程中再怎么失敗,也影響不到執(zhí)行區(qū)域的固件,所以應(yīng)用程序能夠正常運(yùn)行。
然后問題來了,當(dāng)備份區(qū)域的固件下載好后,該怎么轉(zhuǎn)移到執(zhí)行區(qū)域上呢?其流程如圖4所示:
圖4 備份區(qū)域內(nèi)容復(fù)制到執(zhí)行區(qū)域
是不是很簡單,但如果在固件更新的過程中斷電,固件復(fù)制到一半怎么辦?
因此,我們需要在 Flash 中保存一些信息,讓微控制器復(fù)位后,在執(zhí)行應(yīng)用程序前,都先檢查下固件是否已經(jīng)復(fù)制完畢。
我們?cè)趫?zhí)行區(qū)域和備份區(qū)域的末尾分別上寫一些內(nèi)容,來驗(yàn)證執(zhí)行區(qū)域內(nèi)容的完整性,存放這些內(nèi)容的區(qū)域可稱為信息塊,如圖5所示。
圖5 在各區(qū)域末尾添加信息塊存儲(chǔ)區(qū)
信息塊的大小至少是 Flash 的最小擦除單位,以保證擦除信息塊時(shí)不影響其它內(nèi)容。大多數(shù)常見的 QSPI Flash 的最小擦除單位大小是 4KB,片內(nèi) Flash 是 1KB,因此這個(gè)信息塊的大小可按照 4KB 大小設(shè)計(jì),執(zhí)行區(qū)域和備份區(qū)域各一個(gè),共計(jì) 8KB 大小。
信息塊中包含以下信息:
固件版本,用于判斷備份區(qū)域的固件是否為新的固件,是否執(zhí)行固件更新操作
固件大小,需要復(fù)制備份區(qū)域多少內(nèi)容到執(zhí)行區(qū)域
信息塊內(nèi)容的校驗(yàn)值,建議使用是 CRC 校驗(yàn),而不建議使用 BCC 校驗(yàn),目的是證明這個(gè)信息塊的內(nèi)容可信,保證信息塊的完整性
為什么說不建議使用 BCC 校驗(yàn)?zāi)??一般來說,F(xiàn)lash 擦除信息后,讀取的數(shù)據(jù)一般是 0xFF,如果使用 BCC 校驗(yàn),其校驗(yàn)值只能是 0x00 或 0xFF,如果是 0xFF,則又與擦除后的 Flash 中的數(shù)據(jù)相同,起不到校驗(yàn)的效果,而 0x00 又容易碰撞到別的數(shù)據(jù)。
一般來說,上位機(jī)發(fā)送過來的文件中不包含版本號(hào)信息,那如何進(jìn)行版本號(hào)管理呢?可在微控制器中自行管理:當(dāng)新的固件下載好后,其版本號(hào)按照如圖6方式管理:
圖6 版本號(hào)管理
當(dāng) 2nd Bootloader 進(jìn)入到 ISP 模式后,會(huì)執(zhí)行固件下載的過程,從外界接收新的固件,隨后復(fù)位微控制器,進(jìn)入執(zhí)行應(yīng)用程序的模式。
固件下載過程的流程圖如圖7 所示:
圖7 固件下載過程
當(dāng) 2nd Bootloader 進(jìn)入到執(zhí)行應(yīng)用程序的模式后,并不會(huì)直接運(yùn)行應(yīng)用程序,而是執(zhí)行固件更新過程,將備份區(qū)域的新固件復(fù)制到執(zhí)行區(qū)域,待固件更新過程完畢后,才會(huì)執(zhí)行應(yīng)用程序。
事實(shí)上,每次 2nd Bootloader 進(jìn)入到執(zhí)行應(yīng)用程序的模式后,都會(huì)執(zhí)行固件更新過程,固件更新包括了檢查是否下載了新固件和是否已經(jīng)完成復(fù)制新固件到執(zhí)行區(qū)域兩部分內(nèi)容,如果沒有新的固件包在備份區(qū)域的話,固件更新程序就會(huì)提前結(jié)束,直接執(zhí)行應(yīng)用程序。
固件更新過程如圖8所示:
圖8 固件更新過程
?過程分析
為了保證 OTA 過程的安全,我們需要保證備份區(qū)域和執(zhí)行區(qū)域至少有一個(gè)固件是完整的,這樣,當(dāng)備份區(qū)域的固件破壞時(shí),我們能夠保證執(zhí)行區(qū)域的固件還能用,而執(zhí)行區(qū)域的固件損壞時(shí),還能從備份區(qū)域恢復(fù)過來。
那怎么確定某區(qū)域的固件是完整的呢?
通過圖7和圖8可知,不管是下載固件到備份區(qū)域,還是復(fù)制備份區(qū)域固件到執(zhí)行區(qū)域,只要有寫固件的操作,都會(huì)先將對(duì)應(yīng)區(qū)域的信息塊進(jìn)行擦除,直到寫固件完成,才會(huì)把新的信息塊寫入到指定區(qū)域。因此,我們可以通過信息塊是否完整來判斷該區(qū)域的固件是否完整,信息塊的校驗(yàn)值是驗(yàn)證信息塊完整性的保證。
不管是人事檔案,學(xué)生檔案,還是黨員檔案,在檔案袋上都會(huì)貼有密封條,并且蓋有部門的紅章,這個(gè)密封條就是證明檔案內(nèi)容完整性的保證。Flash 中的固件,就相當(dāng)于檔案袋里面的內(nèi)容,信息塊就相當(dāng)于檔案袋上的封條,版本號(hào)相當(dāng)于封條上的日期,校驗(yàn)值相當(dāng)于封條上的紅章,擦除信息塊的過程,相當(dāng)于給 Flash 進(jìn)行撕封條的操作,而寫入信息塊的過程,就相當(dāng)于給 Flash進(jìn)行貼封條的過程,通過這個(gè)封條,就能夠證明 Flash 中的內(nèi)容是完整的。
有了“封條”證明固件的完整性,還需要確定什么時(shí)候才能拆“封條”:
拆封條的原則就是:保證兩個(gè)區(qū)域至少有一個(gè)固件是完整的。因此,在擦除其中一個(gè)區(qū)域的信息塊時(shí),需要查看另一個(gè)區(qū)域的信息塊是否完整,只有另一個(gè)區(qū)域的信息塊是完整的,才能夠擦除本區(qū)域信息塊的內(nèi)容。
在固件下載的過程中,如果發(fā)現(xiàn)執(zhí)行區(qū)域的信息塊不完整,那就暫時(shí)還不能擦除本區(qū)域的信息塊,需先執(zhí)行固件更新過程,將備份區(qū)域的固件寫入到執(zhí)行區(qū)域,在寫入執(zhí)行區(qū)域的信息塊后,才能繼續(xù)擦除備份區(qū)域的信息塊。一般來說,執(zhí)行區(qū)域的信息塊如果不完整,則說明該設(shè)備在上次固件更新過程中,意外掉電,固件更新失敗。
在固件更新的過程中,如果備份區(qū)域的信息塊不完整,則說明固件還沒有下載成功,自然是不能繼續(xù)進(jìn)行固件更新的,就得跳過固件更新的過程。如果備份區(qū)域的固件版本小于(一般不會(huì)小于)或等于執(zhí)行區(qū)域的固件版本,則說明備份區(qū)域的固件和執(zhí)行區(qū)域的固件一樣,沒有必要固件更新,也要跳過固件更新的過程。只有備份區(qū)域的信息塊完整,且固件版本大于執(zhí)行區(qū)域時(shí),或者執(zhí)行區(qū)域沒有完整的信息塊時(shí),才可以擦除執(zhí)行區(qū)域的信息塊,對(duì)執(zhí)行區(qū)域的內(nèi)容進(jìn)行寫操作。
有一種情況,執(zhí)行區(qū)域和備份區(qū)域是都沒有完整的固件的,那就是產(chǎn)品硬件生產(chǎn)完成后,還沒有燒錄程序的時(shí)候。
當(dāng)產(chǎn)品硬件生產(chǎn)完成后,微控制器內(nèi)部只有一個(gè) 2nd Bootloader,進(jìn)入到固件下載的過程時(shí)發(fā)現(xiàn),執(zhí)行區(qū)域沒有完整的信息塊,再跳轉(zhuǎn)到執(zhí)行固件更新過程,但固件更新過程發(fā)現(xiàn)備份區(qū)域也沒有完整的信息塊,2nd Bootloader 傻眼了,咋哪都沒有完整固件呢?
因此,在固件下載過程中,若發(fā)現(xiàn)執(zhí)行區(qū)域和備份區(qū)域都沒有完整信息塊時(shí),還是得允許向備份區(qū)域?qū)懝碳?/p>
當(dāng)意外出現(xiàn)在任何沒有操作 Flash 的時(shí)候,再次上電后,不影響應(yīng)用程序的正常執(zhí)行,不影響固件更新,也不影響固件下載的過程;當(dāng)意外出現(xiàn)在對(duì)備份區(qū)域的 Flash 操作時(shí),執(zhí)行區(qū)域的固件還能夠正常執(zhí)行,也不影響重新執(zhí)行固件下載的過程。當(dāng)意外出現(xiàn)在對(duì)執(zhí)行區(qū)域的 Flash 操作時(shí),再次上電后,重新進(jìn)行固件更新,應(yīng)用程序還是能夠正常運(yùn)行。所以,這個(gè)完整的 OTA 過程,處處安全。
?測(cè)試
根據(jù)本文講述的過程,實(shí)現(xiàn)帶 OTA 功能的 2nd Bootloader,進(jìn)行驗(yàn)證。
下載新的固件前效果,如圖9所示:
圖9 下載新固件前的應(yīng)用程序
下載固件過程中中斷下載,通過觀察應(yīng)用程序打印的時(shí)間可知,微控制器會(huì)繼續(xù)執(zhí)行更新固件前的應(yīng)用程序,如圖10所示:
圖10 Ymodem 升級(jí)失敗后的應(yīng)用程序
當(dāng)固件下載成功后,在進(jìn)入應(yīng)用程序前,立刻按下復(fù)位按鍵,產(chǎn)生一次固件更新失敗的事件,隨后釋放復(fù)位按鍵,觀察通過應(yīng)用程序打印的時(shí)間可以發(fā)現(xiàn),新的固件在再次復(fù)位之后,依然進(jìn)行了更新,如圖11所示:
圖11 固件更新失敗后,再次復(fù)位程序
?結(jié)語
本文主要針對(duì) OTA 升級(jí)的流程進(jìn)行了講解,把 OTA 升級(jí)的過程拆分為固件下載和固件更新兩個(gè)過程,每個(gè)過程都想辦法保證設(shè)備復(fù)位后,執(zhí)行區(qū)域總有可用固件,保障 OTA 過程中如果發(fā)生意外,不至于讓設(shè)備變 “磚”。
OTA 升級(jí)的方法不僅僅只有串口一種,我們還可以使用 I2C 接口,SPI 接口升級(jí)固件,如果支持 USB Device,那么 微控制器微控制器可以模擬成為一個(gè) U 盤,插在電腦上,將二進(jìn)制文件放到這個(gè) U 盤中更新固件,如果支持 USB Host,則可以將固件放到 U 盤中,微控制器讀取 U 盤中的數(shù)據(jù)更新固件,或者將 U 盤換成 SD 卡…… 實(shí)現(xiàn) OTA 功能的方法極多。
當(dāng) OTA 的方法越來越多時(shí),2nd Bootloader 也隨之變得臃腫起來。其實(shí),我們也可以把 OTA 的大部分內(nèi)容做到應(yīng)用程序中,僅將備份區(qū)域的應(yīng)用程序轉(zhuǎn)移到執(zhí)行區(qū)域這個(gè)核心功能保留到 2nd Bootloader 中,并且提供對(duì)備份區(qū)域 Flash 擦除和寫操作的 API 即可,應(yīng)用程序中的 OTA 功能,將會(huì)把下載到的新固件通過 2nd Bootloader 提供的 API 寫入到備份區(qū)域,隨后復(fù)位 MCU,讓 2nd Bootloader 將新固件再轉(zhuǎn)移到執(zhí)行區(qū)域即可。
為了減少下載新固件所需的流量,其固件包可能進(jìn)行了壓縮處理,而解壓過程可以放在上位機(jī)中,讓上位機(jī)發(fā)送給微控制器的固件就是經(jīng)過解壓后的固件包;或者可以放在應(yīng)用程序中,在應(yīng)用程序中進(jìn)行解壓后寫入到 Flash 中,或者放在 2nd Bootloader 中,從備份區(qū)域放到執(zhí)行區(qū)域時(shí)再進(jìn)行解壓;第三種方法雖然可以減少備份區(qū)域占用的空間,但需要將壓縮算法確定好,永久不會(huì)再進(jìn)行改動(dòng)——2nd Bootloader 雖然靈活,但就像微控制器廠商出廠微控制器前,將 1st Bootloader 固化到微控制器中那樣,2nd Bootloader 只能在產(chǎn)品廠商將產(chǎn)品出廠前寫入到微控制器中,產(chǎn)品一旦售出,就不像應(yīng)用程序可以通過 OTA 升級(jí)了,除非進(jìn)行產(chǎn)品召回,否則就沒有修改的可能,因此,放在 2nd Bootloader 中的代碼,一定要謹(jǐn)慎對(duì)待。
審核編輯:劉清
評(píng)論