藍芽實現OTA韌體升級的原理

藍芽在現實生活中的應用非常廣泛,各種嵌入式、物聯網裝置隨處可見。

下面就來講講如何實現BLE OTA?如何透過UART實現韌體升級?又如何透過USB實現韌體升級?怎麼保證升級的安全性?等等內容。

1、概述

所謂DFU(Device Firmware Update),就是裝置韌體升級的意思,而OTA(Over The Air)是實現DFU的一種方式而已,準確說,OTA的全稱應該是OTA DFU,即透過空中無線方式實現裝置韌體升級。只不過大家為了方便起見,直接用OTA來指代韌體空中升級(有時候大家也將OTA稱為FOTA,即Firmware OTA,這種稱呼意思更明瞭一些)。只要是透過無線通訊方式實現DFU的,都可以叫OTA,比如2G/3G/4G/WiFi/藍芽/NFC/Zigbee,他們都支援OTA。DFU除了可以透過無線方式(OTA)進行升級,也可以透過有線方式進行升級,比如透過UART,USB或者SPI通訊介面來升級裝置韌體。

不管採用OTA方式還是有線通訊方式,DFU包括後臺式(background)和非後臺式兩種模式。

後臺式DFU

,又稱靜默式DFU(Silent DFU),在升級的時候,新韌體在後臺悄悄下載,即新韌體下載屬於應用程式功能的一部分,在新韌體下載過程中,應用可以正常使用,也就是說整個下載過程對使用者來說是無感的,下載完成後,系統再跳到BootLoader模式,由BootLoader完成新韌體覆蓋老韌體的操作,至此整個升級過程結束。比如智慧手機升級Android或者iOS系統都是採用後臺式DFU方式,新系統下載過程中,手機可以正常使用哦。

非後臺式DFU

,在升級的時候,系統需要先從應用模式跳入到BootLoader模式,由BootLoader進行新韌體下載工作,下載完成後BootLoader繼續完成新韌體覆蓋老韌體的操作,至此升級結束。早先的功能機就是採用非後臺式 DFU來升級作業系統的,即使用者需要先長按某些按鍵進入bootloader模式,然後再進行升級,整個升級過程中手機正常功能都無法使用。

下面再講雙區DFU(dual bank)和單區DFU(single bank),雙區或者單區DFU是新韌體和老韌體覆蓋的兩種方式。後臺式DFU必須採用雙區模式進行升級,即老系統(老韌體)和新系統(新韌體)各佔一塊bank(儲存區),假設老韌體放在bank0中,新韌體放在bank1中,升級的時候,應用程式先把新韌體下載到bank1中,只有當新韌體下載完成並校驗成功後,系統才會跳入BootLoader模式,然後擦除老韌體所在的bank0區,並把新韌體複製到bank0中。非後臺式DFU可以採用雙區也可以採用單區模式,與後臺式DFU相似,雙區模式下新老韌體各佔一塊bank(老韌體為bank0,新韌體為bank1),升級時,系統先跳入BootLoader模式,然後BootLoader程式把新韌體下載到bank1中,只有新韌體下載完成並校驗成功後,才會去擦除老韌體所在的bank0區,並把新韌體複製到bank0區。

單區模式的非後臺式DFU只有一個bank0,老韌體和新韌體分享這一個bank0,升級的時候,進入bootloader模式後

立馬擦除老韌體

,然後直接把新韌體下載到同一個bank中,下載完成後校驗新韌體的有效性,新韌體有效升級完成,否則要求重來。跟非後臺式DFU雙區模式相比,單區模式節省了一個bank的Flash空間,在系統資源比較緊張的時候,單區模式是一個不錯的選擇。不管是雙區模式還是單區模式,升級過程出現問題後,都可以進行二次升級,都不會出現“變磚”情況。

不過雙區模式有一個好處,如果升級過程中出現問題或者新韌體有問題,它還可以選擇之前的老韌體老系統繼續執行而不受其影響。而單區模式碰到這種情況就只能一直待在bootloader中,然後等待二次或者多次升級嘗試,此時裝置的正常功能已無法使用,從使用者使用這個角度來說,你的確可以說此時裝置已經“變磚”了。所以說,雖然雙區模式犧牲了很多儲存空間,但是換來了更好的升級體驗。

可參考下面三個圖來理解上述過程。

藍芽實現OTA韌體升級的原理

如果你是第一次接觸Nordic nRF5 SDK,那麼建議你先看一下這篇文章:開發你的第一個BLE應用程式—Blinky,或者看一下這一篇文章:手把手教你開發BLE資料透傳應用程式,以建立Nordic nRF5 SDK的一些基本知識,然後再往下看以下章節。

2、Nordic nRF5 SDK DFU工作原理

如文章“nRF5 SDK軟體架構及softdevice工作原理”所述,Nordic nRF5 SDK軟體架構跟其他家有點不一樣,程式儲存區最開始部分放得不是Bootloader,而是藍芽協議棧Softdevice,應用程式則緊挨著Softdevice,Bootloader則被nRF5 SDK放在程式儲存區的最上面,整個儲存區結構圖如下所示。如果使用者還有Flash資料需要存放,那麼這些資料緊挨著BootLoader下面。

藍芽實現OTA韌體升級的原理

目前Nordic SDK預設只提供非後臺式DFU開箱即用的例子(SDK16。0開始也支援後臺式DFU框架),即系統必須先跳到BootLoader中,然後才能透過BLE/UART/USB去接收新的韌體。如上所示,

如果採用雙區模式DFU

,那麼Bank0放的是應用程式,即老韌體,Bank1放的是新韌體。平時,Bank1為空或者忽略,系統只跑Bank0裡面的應用程式;升級的時候,先跳到BootLoader,然後接收新韌體並把它放在Bank1中,最後把Bank1裡面的韌體複製到Bank0中。

如果採用單區模式

,則沒有Bank1這個區。平時,系統只跑Bank0裡面的程式碼;升級的時候,跳到BootLoader,先擦除Bank0裡面的老程式,並把新韌體直接放在Bank0中。

根據升級時如何跳轉到Bootloader,Nordic SDK又將DFU分為按鍵式DFU和非按鍵式(Buttonless)DFU,所謂按鍵式DFU,就是上電時長按某個按鍵以進入bootloader模式,而非按鍵式DFU,就是整個DFU過程中裝置端無任何人工干預,透過BLE/UART/USB介面給應用程式傳送一條指令,應用程式收到指令後再自動跳入bootloader模式。不管是按鍵式DFU還是非按鍵式DFU,兩者只是進入BootLoader的方式不一樣,其餘基本一樣,尤其是BootLoader工作過程基本上是一模一樣的。後面只會闡述非按鍵式DFU的過程,按鍵式DFU以此類似,就不再贅述。

程式跳到BootLoader後,根據BootLoader需不需要對新韌體進行驗籤,Nordic SDK又把DFU分為開放式DFU和安全式DFU(又稱簽名DFU)。開放式DFU,BootLoader不做任何驗證,直接把新韌體接收下來。安全式DFU,BootLoader存有一把公鑰,BootLoader會先用這把公鑰驗證新韌體的簽名,只有驗籤透過,才允許後續的工作:比如把新韌體接收下來;如果驗籤失敗,BootLoader將拒絕升級,重新跳回應用程式。

BootLoader可以透過不同的通訊介面來接收新的韌體,目前Nordic SDK支援BLE,UART和USB三種介面,所以大家可以在Nordic SDK中看到如下三種工程目錄:

藍芽實現OTA韌體升級的原理

其中pca0056表示nRF52840對應的開發板編號,S140對應Softdevice的型號,然後ble有兩個目錄:無debug和有debug,uart和usb也包含同樣的兩個目錄。有debug和無debug兩者功能是一樣的,兩者的區別是:debug版本BootLoader支援日誌列印(大家可以透過打印出的日誌去理解BootLoader的工作過程),並可以忽略各種校驗,debug版本佔據的程式碼空間要大很多;無debug版本 BootLoader不支援日誌列印功能並且版本和有效性校驗是強制的。正式量產的時候推薦使用無debug版本以節省程式碼空間。這裡要強調一下,

不管是debug版本還是無debug版本,兩者都可以用Keil進行單步和斷點除錯

BLE,UART和USB只是通訊方式不一樣,他們遵守的DFU流程是一模一樣的,這裡會以BLE通訊介面為例,詳細闡述DFU過程,UART和USB與之類似,就不再贅述。

講述DFU升級之前,先講一下nRF52的啟動流程,上電後,系統先執行softdevice,softdevice透過讀取UICR一個暫存器的值,來判斷目前系統是否有BootLoader,如果沒有BootLoader,系統直接跳到application;如果有BootLoader,系統先跳到BootLoader,BootLoader再根據目前的情況來決定是進入升級模式還是跳往application,BootLoader主要判斷如下幾種情況:

按鍵是否按下

保持暫存器GPREGRET1是否為0xB1

上次DFU過程是否還在進行中

應用程式校驗是否透過

如果按鍵沒有按下,GPREGRET1不為0xB1,本次復位不是上次DFU的繼續,並且應用程式校驗透過,那麼BootLoader就會直接跳到application,去執行application應用程式。那怎麼去校驗應用程式的有效性呢?為此BootLoader引入了一個放在Flash的結構體引數:m_dfu_settings_buffer(資料型別:nrf_dfu_settings_t),這個結構體引數雖然只有896位元組,但由於Flash只能按頁擦除,所以這個引數實際佔用了一個Flash page,

這個page稱為settings page

,settings page放在Flash的最後一個頁面,settings page目前有2個版本:版本1(SDK15。2及以前版本)和版本2(SDK15。3及以後版本),版本2可以相容版本1,前面所述的896位元組是指settings page版本2的大小。Settings page包含的資訊比較多,大家用得比較多的是:

各種版本資訊

DFU升級過程資訊

Application image的CRC值和大小

應用程式的bonding資訊

Init command內容

application/softdevice的啟動校驗資訊(版本2才有)

版本1的settings page只校驗application image的CRC值,如果CRC匹配,則認為application有效。版本2的settings page不僅可以校驗application image的CRC值,還可以校驗application/softdevice的CRC值或者hash值或者簽名,你可以選擇你自己想要的校驗方式,只有CRC值或者hash值或者簽名校驗透過(三選其一),應用程式才算有效,這時BootLoader才會跳到application去執行。為了保證settings page在發生意外時,比如寫settings page過程中發生了復位或者掉電,系統也能正確恢復,SDK15及以後版本引入了一個backup page,backup page也佔用一個Flash page,內容和settings page一模一樣。

上面是沒有觸發升級的情況下nRF52的正常啟動流程,那如果要執行DFU升級,流程又是怎麼樣的呢?下面詳細講一下無按鍵式BLE OTA的工作流程。

1) 正常啟動後,系統執行在應用程式中,此時手機透過app傳送一條開始DFU的指令給裝置,裝置收到指令後,將GPREGRET1賦值0xB1,並觸發軟復位

2) 復位後,系統再次進入BootLoader,因為GPREGRET1等於0xB1,BootLoader進入DFU模式,等待新韌體接收

3) 手機先將init packet傳送給裝置,裝置先做前期檢驗prevalidation,主要是各種版本校驗以及簽名驗籤,校驗通過後,更新settings page並準備開始資料接收

4) 接收新韌體。每接收4kB資料,回覆一次CRC校驗值,直至整個新韌體image接收完畢,如果新韌體校驗透過(版本1校驗CRC值,版本2校驗hash值),就會去invalidate(無效化) bank0裡面的老韌體,更新settings page,並再次觸發軟復位

5) BootLoader啟動後發現有新韌體需要activate(啟用),此時會去擦掉bank0裡面的韌體,並把bank1裡面的韌體複製到bank0,然後更新settings page,並再次觸發軟復位。注:上面講的是dual bank的流程,single bank與之相似,只不過在第3)步的時候就會去擦除老韌體

6) BootLoader再次啟動後,檢查新image的有效性,校驗通過後,跳到新的application去執行程式碼

從上面流程可以看出,DFU過程中,系統需要跑兩段完全獨立的程式碼:Application和BootLoader,Application和BootLoader都支援藍芽功能,也就是說,兩者都有自己的藍芽廣播和藍芽連線。

這裡面有一個問題:當系統從Application跳到BootLoader後,手機怎麼辨別兩者為同一個裝置?很多人會說,可以讓BootLoader和Application兩者的廣播名字一樣,根據廣播名字的一致性,來判斷二者來自同一個裝置。

這種方法存在兩個問題:一大部分手機都支援GATT cache(快取)功能,當application跟手機相連後,手機會把application的GATT資料快取下來以加快下次連線的速度(這個現象在蘋果手機最明顯),之後如果系統跳到BootLoader,然後再跟手機相連,如果兩者的藍芽裝置地址一樣,手機會認為是同一個裝置,從而跳過服務發現的過程而直接使用之前快取下來的GATT資料,這樣會導致BootLoader的服務無法被手機發現,從而出現升級失敗。二如果多個裝置同時在升級,而我們僅僅依靠廣播名字來決定兩者屬不屬於同一個裝置,這會導致裝置A application有可能跟裝置B的BootLoader進行錯配。為了解決這個問題,Nordic提出了兩套方案。

方案一,假設application的藍芽裝置地址為x,跳到BootLoader後藍芽裝置地址會變成x+1,這樣手機就可以透過這種地址+1的方式來辨別兩者屬不屬於同一個裝置,由於application和BootLoader使用不同的藍芽裝置地址,前面的GATT快取問題也就不存在。關於方案一,有一個問題需要特別注意:如果你想修改例子預設的藍芽裝置地址(比如使用IEEE的public藍芽MAC地址),此時一定要記得同時更改application和BootLoader的藍芽裝置地址,使他們滿足+1的條件,否則Nordic手機DFU庫無法辨別兩者是否屬於同一個裝置,以致於無法完成OTA過程。

方案二,application和BootLoader的藍芽裝置地址一模一樣,但裝置跟手機執行配對和bonding操作,裝置跟手機bonding後,就可以支援service changed indicate操作,這樣跳到BootLoader後可以讓手機主動再執行一次服務發現過程,從而解決GATT快取問題。

很多人對簽名驗籤不是很理解,這裡詳細說一下它的工作原理。首先,你需要一對公私鑰,其中私鑰用來生成新韌體的簽名,公鑰用來驗證簽名的有效性,大家可以用nrfutil來生成自己需要的公私鑰對,公私鑰製作成功後,私鑰一定要妥善保管(一般放在雲端),千萬不能丟,否則你自己也無法升級自己的裝置;也不能被第三方知道,否則升級的安全性就不能保證了。公鑰可以變成一個。c檔案,並覆蓋DFU工程下的同名檔案:dfu_public_key。c 。

其次,BootLoader要支援簽名驗籤密碼演算法,這個DFU程式碼已經有了,並且有四種後端可選:micro-ecc,cc310_bl,Oberon和mbedtls,四選其一即可(這4種後端,只有cc310是硬體實現,其餘都是軟體實現),nRF52840推薦選擇cc310作為演算法後端,其他nRF52晶片推薦選擇micro-ecc作為演算法後端。micro-ecc效率高,佔用的程式碼空間最小,但它的版權是CPOL,只要你能接受CPOL,那麼推薦使用micro-ecc;反之,如果接受不了CPOL版權,而且硬體又不支援cc310,那麼推薦使用Oberon,不過Oberon佔用的程式碼空間比micro-ecc要大一些,這個大家注意一下。再次,手機端要生成新韌體的簽名,並把新韌體的簽名傳給裝置端。

大家還是可以用nrfutil去生成新韌體的簽名。最後,BootLoader接收到新韌體hash值和簽名,並使用自己的公鑰對該簽名進行驗籤。這裡說一下,由於nrfutil是PC端應用程式,所以它可以整合各種加密演算法庫,並完成上面提及的公私鑰對,hash和簽名的生成工作。

嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和內容,導致工資要不上去!

無償分享大家一個資料包,差不多150多G。裡面學習內容、面經、專案都比較新也比較全!某魚上買估計至少要好幾十。

長按連結搜尋 https://s。pdb2。com/l/CMIsoKcnATFIF4M

3、DFU升級步驟詳解

3。1 安全式藍芽空中升級步驟

如前所述,Nordic SDK已經提供了DFU例子,下面我們一步一步給大家講解如何透過Nordic SDK來實現無按鍵式藍芽空中升級。欲實現空中升級,裝置需要同時下載softdevice,應用程式,BootLoader程式,以及BootLoader settings page。其中BootLoader程式碼位於目錄:SDK根目錄\examples\dfu\secure_bootloader,然後在該目錄下選擇你對應的板子和工程。Application對應的目錄:SDK根目錄\examples\ble_peripheral\ble_app_buttonless_dfu,而softdevice所在目錄:SDK根目錄\components\softdevice。

下面我們以nRF52832/PCA10040和S132/SDK16為例闡述無按鍵式藍芽空中升級實現步驟,

其他晶片/softdevice/SDK原理與之類似,這裡就不再贅述。當然,不同晶片不同softdevice不同SDK,他們的

實現指令碼還是會有一些細微差別,所以強烈建議大家去百度網盤下載跟大家相匹配的指令碼

,百度網盤裡面各個指令碼的命名規則請參考

3.2節

1) 安裝PC版nrfutil。nrfutil安裝有兩種方式,一種是直接下載exe檔案,一種是以Python的方式進行安裝。nrfutil。exe直接下載連結為:https://github。com/NordicSemiconductor/pc-nrfutil/releases,

記得把nrfutil.exe所在目錄放在Windows環境變數中

。Python方式安裝nrfutil步驟如下所示:

安裝Python2。7或者Python3。7,下載地址:https://www。python。org/downloads/,安裝成功後請確保Windows環境變數包含Python目錄

透過pip安裝最新版的nrfutil,即開啟Windows命令列工具CMD(管理員許可權),輸入如下命令:pip install nrfutil,即可以完成nrfutil的安裝。

安裝完成後,在Windows命令列工具輸入:nrfutil version,如果可以正確顯示版本資訊,說明安裝已經成功

對於Windows使用者,nrfutil執行需要幾個特殊的DLL庫,而這幾個庫有些Windows機器是沒有的,如此,可往:https://www。microsoft。com/en-us/download/details。aspx?id=40784下載

2) 透過nrfutil生成公私鑰對。

私鑰生成命令:nrfutil keys generate priv。pem (priv。pem就是私鑰)

公鑰生成命令:nrfutil keys display ——key pk ——format code priv。pem ——out_file dfu_public_key。c (dfu_public_key。c就是公鑰)

大家務必要儲存好私鑰priv.pem

,以後每次升級新韌體時,都會透過這個私鑰對它進行簽名,一旦priv。pem丟失或者被暴露,DFU將無法進行或者變得不安全

3) 請確保已按照“Nordic nRF51/nRF52開發環境搭建”把Nordic nRF5 SDK開發環境搭建成功

4) 生成micro-ecc演算法庫。由於micro-ecc是第三方演算法庫,需要使用者自己去安裝(這個是版權的要求,沒辦法直接編譯放在SDK中)。請先確保電腦已安裝了git和GCC編譯器,然後直接點選SDK如下目錄的build_all指令碼,就可以自動完成micro-ecc演算法庫的安裝。

為了方便一些開發者評估,我這裡在自己電腦上生成了micro-ecc演算法庫,micro-ecc目錄編排結構有兩種:SDK14及以後版本是一種目錄結構(百度雲盤壓縮包名稱:micro_ecc_new。rar),SDK13和SDK12又是一種目錄結構(百度雲盤壓縮包名稱:micro_ecc_old。rar),這兩個壓縮包只是目錄不一樣,裡面的演算法庫內容其實是一樣的,這兩個壓縮包大家都可以在前面的百度雲盤中找到,以供大家評估使用。大家下載下來後,直接覆蓋同名目錄即可。

注意:百度雲盤裡面的micro-ecc庫僅供大家評估使用,如要商用,請大家按照上面步驟去生成

藍芽實現OTA韌體升級的原理

5) 編譯bootloader程式碼。將剛才的dfu_public_key。c取代

SDK根目錄\examples\dfu

下的同名檔案,然後使用

Keil

編譯如下目錄中的工程:

SDK根目錄\examples\dfu\secure_bootloader\pca10040_ble\arm5_no_packs,

或者

nRF5SDK160098a08e2\examples\dfu\secure_bootloader\pca10040_s132_ble\arm5_no_packs

,將生成的hex檔案改名為:bootloader。hex(

注:本文所有專案都會採用Keil工程來講解,如果你使用其他IDE,請選擇其對應的工程檔案進行編譯,不管是Keil還是其他IDE,除了編譯時候選擇的工程檔案不一樣,其餘都大同小異,大家可以舉一反三完成其他IDE的相應工作

6) 編譯application程式碼。請編譯工程:

SDK根目錄 \examples\ble_peripheral\ble_app_buttonless_dfu\pca10040\s132\arm5_no_packs,將生成的hex檔案改名為:app。hex

7) 生成BootLoader settings page。Bootloader settings page儲存在Flash最後一個page,如前所述,BootLoader settings page有2個版本,他們的生成指令碼命令如下所示:

版本2生成命令:

nrfutil settings generate ——family NRF52 ——application app。hex ——application-version 1 ——bootloader-version 1 ——bl-settings-version 2 settings。hex

版本1生成命令:

nrfutil settings generate ——family NRF52 ——application app。hex ——application-version 1 ——bootloader-version 1 ——bl-settings-version 1 settings。hex

8) 燒寫韌體。將上文生成的3個hex檔案和softdevice hex檔案merge成一個檔案,然後透過nrfjprog或者nRF Connect桌面版進行燒寫,相關命令如下所示:

合併hex檔案命令:

mergehex ——merge bootloader。hex settings。hex ——output bl_temp。hex

mergehex ——merge bl_temp。hex app。hex s132_nrf52_7。0。1_softdevice。hex ——output whole。hex

燒寫hex檔案命令(以nrfjprog為例):

nrfjprog ——eraseall -f NRF52

nrfjprog ——program whole。hex ——

verify

-f NRF52

nrfjprog ——reset -f NRF52

9) 透過nrfutil生成新韌體對應的zip包:new_app。zip。zip包包含新韌體(

新韌體廣播名改為:Nordic_New

,其餘跟老韌體一模一樣)和init包,zip包一般透過雲端下發到手機app,手機app再透過藍芽下載到裝置中。生成zip包的命令如下所示:

nrfutil pkg generate ——application app_new。hex ——application-version 2 ——hw-version 52 ——sd-req 0xCB ——key-file priv。pem SDK160_app_s132。zip

其中,——application表示新韌體hex檔案。——hw-version表示板子版本,只要BootLoader裡面的hw version和這裡的hw version對應起來,大家可以改成任何自己想要的值。——key-file 表示簽名用的私鑰檔案。——sd-req表示老韌體執行在哪個版本softdevice上,這個值一定要跟自己的softdevice相匹配,否則無法升級,各個softdevice版本ID資訊可以透過命令“nrfutil pkg generate ——help”獲得,如下為當前所有softdevice ID列表:

藍芽實現OTA韌體升級的原理

10) 將“new_app。zip”複製到手機上。安卓和蘋果手機都可以透過微信的‘檔案傳輸助手’拷過去,非常方便。請注意,手機nRF Connect和nRF Toolbox都支援DFU功能,蘋果手機複製的時候可以隨便選擇其中一個app。

11) 透過手機版nRF Connect或者nRF Toolbox進行藍芽空中升級,這裡以nRF Connect為例闡述升級詳細步驟,nRF Toolbox與此類似,就不再贅述

第8)步完成後,開發板就可以正常跑起來,並廣播為Nordic_Buttonless

藍芽實現OTA韌體升級的原理

連線該裝置,使能CCCD(這一步可選),然後選擇“DFU”,如下所示:

藍芽實現OTA韌體升級的原理

選擇“DFU”後,將跳出一個對話方塊,讓你選擇新韌體對應的zip包。由於zip包放在了微信下面的download目錄下,我們需要透過檔案瀏覽器找到這個zip包,大家可以先用系統自帶的檔案瀏覽器開啟這個zip包(如果開啟失敗,那麼大家就要去下載一些第三方的檔案瀏覽器了,比如es explorer),相關操作介面如下所示:

藍芽實現OTA韌體升級的原理

藍芽實現OTA韌體升級的原理

一旦zip包開啟成功,升級過程開始,介面如下所示:

藍芽實現OTA韌體升級的原理

升級成功後,裝置將執行新韌體,即廣播名字將變成:Nordic_New,如下所示:

藍芽實現OTA韌體升級的原理

如上以nRF52832/S132為例闡述了Nordic SDK實現無按鍵式簽名式藍芽空中升級的詳細步驟,Nordic SDK有多個版本,從SDK13。0。0到現在SDK16。0。0,他們的升級步驟基本上一模一樣,大家完全可以參考上述步驟來做。SDK12升級步驟也與上述步驟基本一樣,唯一如下地方需要注意一下:

編譯application程式碼的時候,把如下語句注掉,否則會造成BootLoader和application兩者的hex檔案相沖突

藍芽實現OTA韌體升級的原理

3。2節會按照上述步驟,對一些經典的安全式BLE OTA例子進行測試,並生成可直接執行的指令碼,以供大家參考。

3。2 各種安全式藍芽空中升級例子

3。1節是以升級nRF52832 application為例,詳細闡述了安全式BLE OTA步驟。除了升級application,有的人還需要升級softdevice和BootLoader;除了52832,有的人還會用52840/52833/52811/52810/51822等;除了SDK16。0。0,有的人還會用SDK15。3/15。2/14。2/12。3等。為此我選了一些經典組合,將他們DFU用得的所有指令碼都做好了,並進行了實際測試,有需要的可以去百度網盤下載。這些指令碼在百度網盤的命名規則為:安全模式_韌體傳輸介面_升級哪一部分韌體_SDK版本號_晶片型號。rar,比如

secure_ble_S132_app_SDK160_nRF52832.rar

表示採用安全簽名,韌體透過BLE傳輸,BLE使用S132協議棧,升級的時候只升級application而不升級BootLoader和SoftDevice,基於SDK16。0。0和nRF52832。

目前百度網盤上傳瞭如下安全式BLE OTA示例指令碼(注:這些指令碼都經過我的測試,

全都可以直接執行

):

secure_ble_S132_app_SDK160_nRF52832.rar

secure_ble_S140_app_sd_bl_SDK160_nRF52840.rar

secure_ble_S132_app_SDK153_nRF52832.rar

secure_ble_S132_app_SDK152_nRF52832.rar

secure_ble_S132_app_SDK150_nRF52832.rar

secure_ble_S140_app_SDK150_nRF52840.rar

secure_ble_S132_app_SDK142_nRF52832.rar

secure_ble_S132_app_SDK123_nRF52832.rar

secure_ble_S130_app_SDK123_nRF51.rar

3。3 透過UART口進行安全式韌體升級示例指令碼

我們以nRF52810為例來闡述如何透過UART進行安全式韌體升級步驟:

1) 請參考3。1節第1)到第4)步,完成nrfutil安裝,mico-ecc演算法庫生成,以及公私鑰生成

2) 編譯bootloader程式碼。將剛才的dfu_public_key。c取代

SDK根目錄\examples\dfu

下的同名檔案,確保sdk_config。h中的NRF_BL_DFU_ENTER_METHOD_BUTTON為1,然後使用Keil編譯如下目錄中的工程:

SDK根目錄\ examples\dfu\secure_bootloader\pca10040e_uart\arm5_no_packs

,將生成的hex檔案改名為:bootloader。hex

3) 編譯application程式碼。3。1節講述OTA的時候,我們選擇的例子是ble_app_buttonless_dfu,因為我們是透過藍芽給裝置傳送一條命令,從而讓裝置進入DFU模式。透過串列埠升級韌體,如何進入DFU模式,取決於你的應用設計,你可以採用透過傳送藍芽命令讓其進入DFU模式,也可以透過上電檢測按鍵是否按下以決定是否進入DFU模式。如果想採用ble_app_buttonless_dfu作為application,那麼你需要把該工程中的main函式如下語句刪掉(這些語句是為藍芽版BootLoader設計的,我們現在是UART版BootLoader,不支援這些語句):

err_code = ble_dfu_buttonless_async_svci_init();APP_ERROR_CHECK(err_code);

這裡我們選擇以上電檢測按鍵的方式來決定是否進入DFU模式,並以ble_app_blinky作為應用例子,請直接編譯如下工程:

SDK根目錄\examples\ble_peripheral\ble_app_blinky\pca10040e\s112\arm5_no_packs,將生成的hex檔案改名為:app。hex

4) 生成BootLoader settings page並同時燒寫老韌體,雙擊“program。bat”即可完成,這個指令碼是使用nrfjprog來完成韌體燒寫的。

5) 生成新韌體zip包並進行UART DFU,雙擊“dfu。bat”即可完成,這個指令碼是使用nrfutil作為UART主機,並將新韌體透過電腦COM口傳給裝置的。請記得一定要修改指令碼中的UART對應的電腦COM口,否則升級無法完成。

注:所有bat指令碼都可透過右鍵選擇Notepad++開啟,然後檢視裡面包含的具體命令,並按照自己的需求進行修改。如需進一步理解指令碼中的命令,請參考3。1節的說明。

上述所有操作步驟已打包並上傳到百度網盤,請去網盤下載檔案:

secure_uart_app_SDK160_nRF52810.rar

,這個檔案已經過我的測試,大家可以直接使用。

3。4 透過USB口進行安全式韌體升級示例指令碼

我們以nRF52840為例來講述如何透過USB進行安全式韌體升級,其實透過USB口升級韌體步驟與3。3節的操作幾乎一模一樣,唯一不同的是,選擇如下目錄的BootLoader工程進行編譯:

SDK根目錄\ examples\dfu\secure_bootloader\pca10056_usb\arm5_no_packs

透過USB口進行安全式韌體升級示例指令碼已打包並上傳到百度網盤,請去網盤下載檔案:

secure_usb_app_SDK160_nRF52840.rar

,這個檔案已經過我的測試,大家可以直接使用。

3。5 透過USB口進行開放式韌體升級示例指令碼

我們還是以nRF52840為例來講述如何透過USB進行開放式韌體升級,其升級步驟與3。3節的操作幾乎一模一樣,唯一不同的是,選擇如下目錄的BootLoader工程進行編譯:

SDK根目錄\ examples\dfu\open_bootloader\pca10056_usb\arm5_no_packs

相關指令碼已上傳百度網盤,請下載:

open_usb_app_SDK160_nRF52840.rar

,這個檔案已經過我的測試,大家可以直接使用。

3。6 開放式藍芽空中升級(Legacy DFU)步驟

所謂開放式OTA,是指OTA過程中,不需要檢驗新韌體的簽名,也就是說BootLoader程式碼裡面不包含公鑰及相關密碼演算法庫,升級的時候,只校驗版本資訊,版本校驗透過,就可以開始升級流程。Nordic SDK目前支援兩套開放式OTA方案,一套是SDK15和SDK16提供的,一套是SDK9/SDK10/SDK11提供的。SDK15/16提供的開放式OTA工作原理和流程,與安全式OTA基本上一樣,只不過刪掉了簽名驗籤部分。SDK9/SDK10/SDK11提供的開放式OTA也叫legacy OTA DFU,它的工作流程與SDK15/16略有不同,下面將以nRF52832/S132為例,闡述如何在SDK11中實現無按鍵式開放式藍芽空中升級,詳細步驟如下所示:

1) 編譯bootloader程式碼,請使用Keil編譯目錄“nRF5_SDK_11。0。0_89a8197\examples\dfu\bootloader\pca10040\

dual_bank_ble_s132

\arm5_no_packs”中的工程,將生成的hex檔案改名為bootloader。hex

2) 編譯application程式碼,請編譯目錄“nRF5_SDK_11。0。0_89a8197\examples\ble_peripheral\

ble_app_hrs

\pca10040\

s132_with_dfu

\arm5_no_packs”中的工程,將生成的hex檔案改名為app。hex

3) 將softdevice,bootloader和app三個hex檔案合成一個檔案,命令如下所示:

mergehex ——merge s132_nrf52_2。0。1_softdevice。hex app。hex bootloader。hex –output whole。hex

4) 燒寫韌體到裝置中,大家可以用nRF Connect桌面版燒寫,也可以透過nrfjprog燒寫,nrfjprog燒寫命令如下所示:

nrfjprog。exe ——eraseall -f NRF52nrfjprog ——program whole。hex ——verify -f NRF52

5) 另外我們還需要在Flash中寫一個application有效標誌位,從而上電後程序直接跑到application中去執行,而不是停留在bootloader中不出來,其對應的命令如下所示:

nrfjprog ——memwr 0x0007F000 ——val 0x01 ——verify -f NRF52

6) 用老版本的nrfutil生成新韌體對應的zip包。該zip包除了包含新韌體image,還包含一些配置資訊。升級時,zip包會透過雲端下發到手機端app,手機端app再把zip包傳給藍芽裝置以進行韌體升級。

請使用老版本nrfutil(版本號0.3.0)來生成該zip包

,老版本nrfutil跟隨nRFgo studio一起安裝的,只要你安裝了nRFgo studio,老版本nrfutil就會自動安裝好,並放在目錄“C:\Program Files (x86)\Nordic Semiconductor\nRFgo Studio”中。生成zip包對應的命令如下所示:

nrfutil dfu genpkg ——application app_new。hex ——application-version 1 SDK110_app_s132。zip

7) 把上述的‘SDK110_app_s132。zip’拷到手機中,安卓和蘋果手機都可以透過微信的‘檔案傳輸助手’拷過去,非常方便。注:手機nRF Connect和nRF Toolbox都支援DFU功能,蘋果手機複製的時候可以隨便選擇其中一個app。

8) 使用nRF Connect或者nRF Toolbox來完成DFU過程。這裡以nRF Connect為例來闡述整個升級過程。

成功執行完第5)步後,如果開發板執行正常,那麼它將進行廣播,廣播名字為:Nordic_HRM

藍芽實現OTA韌體升級的原理

連線該裝置,並使能CCCD,然後選擇“DFU”

藍芽實現OTA韌體升級的原理

選擇“DFU”後,將跳出一個對話方塊,讓你選擇新韌體對應的zip包。由於zip包放在了微信下面的download目錄下,我們需要透過檔案瀏覽器找到這個zip包,大家可以先用系統自帶的檔案瀏覽器開啟這個zip包(如果開啟失敗,那麼大家就要去下載一些第三方的檔案瀏覽器了,比如es explorer),相關操作介面如下所示:

藍芽實現OTA韌體升級的原理

藍芽實現OTA韌體升級的原理

一旦zip包開啟成功,升級過程開始,介面如下所示:

藍芽實現OTA韌體升級的原理

升級成功,裝置將自動啟動,此時你會看到新韌體已經在執行,廣播名字也變成了:Nordic_HRM_new,如下所示:

藍芽實現OTA韌體升級的原理

目前百度網盤上傳瞭如下開放式BLE OTA示例指令碼(注:這些指令碼都經過我的測試,全都可以直接執行):

open_ble_S132_app_SDK110_nRF52832.rar

open_ble_S130_app_SDK110_nRF51.rar

如果你的應用是基於SDK11開發的,並且需要整合DFU功能,請參考上述例子ble_app_hrs來移植DFU功能,主要工作包括兩部分:一把BLE_DFU_APP_SUPPORT這個宏包括的所有程式碼拷到你的工程中,二如果你的裝置支援bonding的話,還需把Device manager相關程式碼也拷到你的工程中,如此即可完成DFU功能的移植。

詳解如何移植DFU功能到ble_app_uart

為了讓SDK14及以後版本的ble_app_uart具有DFU功能,有2種做法,一是把NUS服務移植到ble_app_buttonless_dfu中,這種方法相對來說更簡單,大家可以自己去實踐一下;二是把DFU服務移植到ble_app_uart中,這種移植方式挑戰更大,但更有利於我們理解DFU的工作原理,我們現在就來闡述如何給ble_app_uart加上OTA功能。如前所述,OTA過程中,手機跟裝置可以進行配對和bonding,也可以用明文進行藍芽通訊。配對bonding的時候,我們可以讓BootLoader和application共享bonding資訊,也可以只讓application進行配對bonding,而BootLoader還是以明文方式進行藍芽通訊。

Nordic已經把DFU服務做成了一個模組,大家只要把這個模組加到自己的應用中,然後完成一些必須的配置,初始化以及回撥函式的撰寫,再加上把SVCI模組(SVCI模組主要用來修改BootLoader的一些配置引數)加入到應用中移植即可大功告成。在SDK中,DFU服務的名字是:BLE_DFU_SERVICE,這個服務放在檔案ble_dfu。c中,而ble_dfu。c又有兩個後端實現:ble_dfu_unbonded。c和ble_dfu_bonded。c,分別對應無bonding明文藍芽連線和有bonding的藍芽連線,下面也將分這兩種情況詳細闡述移植過程。

4。1 明文正常連線OTA(無bonding)

1) 用Keil開啟如下工程:SDK根目錄\examples\ble_peripheral\ble_app_uart\pca10040\s132\arm5_no_packs

2) 新增DFU服務有關的檔案,目錄和宏定義。首先新增如下DFU目錄及相關檔案:

藍芽實現OTA韌體升級的原理

在define中新增這些宏:DEBUG DFU_SUPPORT BL_SETTINGS_ACCESS_ONLY NRF_DFU_SVCI_ENABLED NRF_DFU_TRANSPORT_BLE=1,其中DEBUG宏只是為了除錯方便而設定的,跟DFU本身無關。DFU_SUPPORT是我用來控制我新增的DFU程式碼的,刪掉DFU_SUPPORT,將不編譯所有DFU有關程式碼。其餘的宏都是系統自帶的,如果要支援DFU,就必須要新增。

藍芽實現OTA韌體升級的原理

然後包含如下目錄:

藍芽實現OTA韌體升級的原理

3) 修改sdk_config。h檔案。首先我們需要使能BLE_DFU模組,及選擇OTA藍芽連線方式,如下為使用明文進行藍芽通訊的配置:

#define BLE_DFU_ENABLED 1#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0

同時我們還需要修改softdevice配置。現在整個應用包括2個供應商自定義UUID:NUS和DFU(其實這兩個UUID可以合成一個,但由於歷史原因,DFU和NUS分別使用了兩個不同的vs UUID),相應地ATT table size也要變大,然後應用程式RAM起始地址也需要跟著變,如下(注:這裡的NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 設定得稍稍偏大):

#define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1600#define NRF_SDH_BLE_VS_UUID_COUNT 2

修改應用程式RAM起始地址,如下:

藍芽實現OTA韌體升級的原理

4) 修改main。c檔案。首先新增如下標頭檔案:

#include “ble_dfu。h”#include “nrf_bootloader_info。h”#include “nrf_power。h”

然後在main函式的開始處,新增修改BootLoader廣播名字的程式碼,

由於iOS DFU的時候預設就會去改廣播名字,為了相容iOS,這一行程式碼是必須的:

err_code = ble_dfu_buttonless_async_svci_init();APP_ERROR_CHECK(err_code);

然後在services_init()中新增ble dfu服務

dfus_init。evt_handler = ble_dfu_evt_handler;err_code = ble_dfu_buttonless_init(&dfus_init);APP_ERROR_CHECK(err_code);

ble_dfu_evt_handler回撥函式的撰寫,大家只要按照要求來,就沒問題,如果應用只支援一個連線,那麼ble_dfu_evt_handler可以直接為空。如果應用支援多個連線,可以參考ble_app_buttonless_dfu做法,這裡就不貼程式碼了。

5) 在跳轉到bootloader之前,如果你想做一些專門的程式碼處理,比如完成pending的Flash操作,比如關閉某些模組,那麼你可以註冊一個app_shutdown_handler來做這些工作。(注:這一步不是必須的,是可選的!)

NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);

6) (這一步可選)之前的ble_app_uart是沒有BootLoader的,所以啟動起來非常快。現在加了BootLoader程式碼,為了加快system off喚醒的速度,可以定義如下語句,

nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);

然後先disable softdevice,然後再進入system off模式。這一步本身跟DFU沒有什麼關係,主要是為了加快程式啟動速度而另加的。

7) 編譯工程,並將生成的hex檔案改名為“app。hex”

8) 然後按照

3.1節

的步驟一步一步完成後續的DFU過程。

4。2 bonding連線OTA

4。1節的工程已經移植了DFU功能,現在我們再把bonding功能移植到4。1節工程上,就可以讓我們的應用同時支援DFU和bonding。Bonding功能是透過peer_manager模組來實現的,大家只要把peer_manager有關的檔案新增進來,就可以實現bonding的目標。

1) 開啟4。1節的工程

2) 新增如下檔案:

藍芽實現OTA韌體升級的原理

3) 修改sdk_config。h檔案,需要修改多個地方,如下:

#define PEER_MANAGER_ENABLED 1#define FDS_ENABLED 1#define NRF_SDH_BLE_SERVICE_CHANGED 1#define NRF_FSTORAGE_ENABLED 1#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 1

當NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS設為1時,表示application將與主機進行bonding,同時該bonding資訊將共享給BootLoader,也就是說,進入bootloader模式後,主機將使用以前的bonding資訊與裝置進行加密連線。

4) 在main。c檔案開頭,包含如下標頭檔案:

#include “peer_manager。h”

5) 在main函式中新增peer_manager_init(),其定義如下所示:

藍芽實現OTA韌體升級的原理

static void peer_manager_init(){ ble_gap_sec_params_t sec_param; ret_code_t err_code; err_code = pm_init(); APP_ERROR_CHECK(err_code); memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); // Security parameters to be used for all security procedures。 sec_param。bond = SEC_PARAM_BOND; sec_param。mitm = SEC_PARAM_MITM; sec_param。lesc = SEC_PARAM_LESC; sec_param。keypress = SEC_PARAM_KEYPRESS; sec_param。io_caps = SEC_PARAM_IO_CAPABILITIES; sec_param。oob = SEC_PARAM_OOB; sec_param。min_key_size = SEC_PARAM_MIN_KEY_SIZE; sec_param。max_key_size = SEC_PARAM_MAX_KEY_SIZE; sec_param。kdist_own。enc = 1; sec_param。kdist_own。id = 1; sec_param。kdist_peer。enc = 1; sec_param。kdist_peer。id = 1; err_code = pm_sec_params_set(&sec_param); APP_ERROR_CHECK(err_code); err_code = pm_register(pm_evt_handler); APP_ERROR_CHECK(err_code);}

藍芽實現OTA韌體升級的原理

新增pm_evt_handler定義,程式碼如下所示:

static void pm_evt_handler(pm_evt_t const * p_evt){ pm_handler_on_pm_evt(p_evt); pm_handler_flash_clean(p_evt);}

尤其要檢查如下程式碼有沒有新增,

由於iOS DFU的時候預設就會去改廣播名字,為了相容iOS,這一行程式碼是必須的

err_code = ble_dfu_buttonless_async_svci_init();APP_ERROR_CHECK(err_code);

6) 在ble_evt_handler中刪除BLE_GAP_EVT_SEC_PARAMS_REQUEST分支,因為這個分支在peer_manager模組中已經進行處理了,這裡再處理一次,會產生異常:

// case BLE_GAP_EVT_SEC_PARAMS_REQUEST:// // Pairing not supported// err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);// APP_ERROR_CHECK(err_code);// break;

7) 修改advertising_start定義,增加刪除bonding資訊功能(如果你不需要這個功能,也可以不改)

8) (此步可選)一般來說,如果使用者在手機端把配對資訊刪掉了,為了安全起見,裝置端也需要把相關配對資訊清掉,然後才可以允許手機和裝置再次進行配對和bonding。如何觸發裝置端bonding資訊的刪除操作?可以透過按鍵檢測的方式來做,比如目前我們這個例子的做法。但是有很多裝置沒有按鍵,而且很多人希望這種二次配對的操作對使用者來說無感,即哪怕使用者刪掉了手機端配對資訊,如果使用者想發起第二次配對請求,裝置也能接受,而且操作過程跟使用者第一次發起配對請求的過程一模一樣。Nordic SDK其實是相容這種操作的,使用者只需在pm_evt_handler()中新增如下程式碼即可:

藍芽實現OTA韌體升級的原理

if (p_evt->evt_id == PM_EVT_CONN_SEC_CONFIG_REQ) { pm_conn_sec_config_t cfg; cfg。allow_repairing = true; pm_conn_sec_config_reply(p_evt->conn_handle, &cfg); }

藍芽實現OTA韌體升級的原理

9) 上述所有程式碼都包括在“BONDING_SUPPORT”宏中。

10) 編譯工程,將生成的hex檔案改名為app。hex

11) 然後按照

3.1節

步驟來執行OTA過程,不過如下幾點需要注意:

如果你在應用中把NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS設為1,那麼bootloader程式碼就不能採用預設配置,請修改bootloader工程中的sdk_config。h檔案中的如下宏定義,然後重新編譯生成新的bootloader。hex。

#define NRF_DFU_BLE_REQUIRES_BONDS 1 #define NRF_SDH_BLE_SERVICE_CHANGED 1

在nRF Connect中勾選“keep bond information”選項,如下:

藍芽實現OTA韌體升級的原理

手機連線裝置成功後,請手動使能CCCD,以讓手機自動發起bonding請求

藍芽實現OTA韌體升級的原理

藍芽實現OTA韌體升級的原理

DFU升級成功後,裝置將會與手機自動重連,此時需點選“Refresh services”,以獲得裝置最新服務列表,如下:

藍芽實現OTA韌體升級的原理

上述程式碼工程我已打包成:

ble_app_uart_ota_SDK16_0_0.rar

,並上傳到百度網盤,大家下載下來解壓縮到:SDK根目錄\examples\ble_peripheral這個目錄下,就可以直接編譯和執行。DFU過程中用到的所有指令碼我也幫大家做好了,大家可以直接下載下來使用,其中

secure_ble_S132_uart_SDK160_nRF52832_Nobonding.rar

對應明文藍芽傳輸,

secure_ble_S132_uart_SDK160_nRF52832_bonding.rar

對應bonding藍芽傳輸。

5、 手機端DFU參考程式碼

Nordic不僅提供DFU裝置端的參考程式碼,同時提供手機端的參考程式碼。Nordic分別開發了Android版和iOS版的DFU庫,大家可以直接拿過來使用,整合到自己的移動端app中,這兩個庫都放在github上,連結如下所示:

Android版DFU庫:https://github。com/NordicSemiconductor/Android-DFU-Library

iOS版DFU庫:https://github。com/NordicSemiconductor/IOS-Pods-DFU-Library

Nordic還提供了一個移動端app:nRF Toolbox,nRF Toolbox是程式碼開源的,裡面也集成了上面提到的DFU庫,大家可以參考nRF Toolbox來開發自己的移動端app。nRF Toolbox原始碼也可以在github上找到:

Android版nRF Toolbox原始碼及開發說明請參考:https://github。com/NordicSemiconductor/Android-nRF-Toolbox

iOS版nRF Toolbox原始碼及開發說明請參考:https://github。com/NordicSemiconductor/IOS-nRF-Toolbox

nRF Toolbox軟體介面如下所示:

藍芽實現OTA韌體升級的原理

原文轉載於:https://www。cnblogs。com/iini/p/9314246。html

轉載自:嵌入式專欄

文章來源:部落格園

文章連結:https://mp。weixin。qq。com/s/7IbQbBi6Osludz6VnKd2Jw

版權申明:本文來源於網路,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯絡我進行刪除。