0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何實(shí)現(xiàn)自己的DFU

lilihe92 ? 來(lái)源:最后一個(gè)bug ? 作者:最后一個(gè)bug ? 2022-10-11 09:57 ? 次閱讀

正文

如何實(shí)現(xiàn)BLE OTA?什么叫DFU?如何通過(guò)UART實(shí)現(xiàn)固件升級(jí)?又如何通過(guò)USB實(shí)現(xiàn)固件升級(jí)?怎么保證升級(jí)的安全性?什么叫雙區(qū)(dual bank)DFU?什么叫單區(qū)(single bank)DFU?什么叫后臺(tái)式(background)DFU?本文將對(duì)上述問(wèn)題進(jìn)行探討。

腳本是按照SDK版本進(jìn)行分類(lèi)的,建議大家把自己SDK版本對(duì)應(yīng)的腳本下載下來(lái),然后跟著第3章的操作步驟一步一步去實(shí)現(xiàn)自己的DFU。

1.概述


所謂DFU(Device Firmware Update),就是設(shè)備固件升級(jí)的意思,而OTA(Over The Air)是實(shí)現(xiàn)DFU的一種方式而已,準(zhǔn)確說(shuō),OTA的全稱應(yīng)該是OTA DFU,即通過(guò)空中無(wú)線方式實(shí)現(xiàn)設(shè)備固件升級(jí)。只不過(guò)大家為了方便起見(jiàn),直接用OTA來(lái)指代固件空中升級(jí)(有時(shí)候大家也將OTA稱為FOTA,即Firmware OTA,這種稱呼意思更明了一些)。只要是通過(guò)無(wú)線通信方式實(shí)現(xiàn)DFU的,都可以叫OTA,比如2G/3G/4G/WiFi/藍(lán)牙/NFC/Zigbee,他們都支持OTA。DFU除了可以通過(guò)無(wú)線方式(OTA)進(jìn)行升級(jí),也可以通過(guò)有線方式進(jìn)行升級(jí),比如通過(guò)UART,USB或者SPI通信接口來(lái)升級(jí)設(shè)備固件。

不管采用OTA方式還是有線通信方式,DFU包括后臺(tái)式(background)和非后臺(tái)式兩種模式。后臺(tái)式DFU,又稱靜默式DFU(Silent DFU),在升級(jí)的時(shí)候,新固件在后臺(tái)悄悄下載,即新固件下載屬于應(yīng)用程序功能的一部分,在新固件下載過(guò)程中,應(yīng)用可以正常使用,也就是說(shuō)整個(gè)下載過(guò)程對(duì)用戶來(lái)說(shuō)是無(wú)感的,下載完成后,系統(tǒng)再跳到BootLoader模式,由BootLoader完成新固件覆蓋老固件的操作,至此整個(gè)升級(jí)過(guò)程結(jié)束。比如智能手機(jī)升級(jí)Android或者iOS系統(tǒng)都是采用后臺(tái)式DFU方式,新系統(tǒng)下載過(guò)程中,手機(jī)可以正常使用哦。非后臺(tái)式DFU,在升級(jí)的時(shí)候,系統(tǒng)需要先從應(yīng)用模式跳入到BootLoader模式,由BootLoader進(jìn)行新固件下載工作,下載完成后BootLoader繼續(xù)完成新固件覆蓋老固件的操作,至此升級(jí)結(jié)束。早先的功能機(jī)就是采用非后臺(tái)式 DFU來(lái)升級(jí)操作系統(tǒng)的,即用戶需要先長(zhǎng)按某些按鍵進(jìn)入bootloader模式,然后再進(jìn)行升級(jí),整個(gè)升級(jí)過(guò)程中手機(jī)正常功能都無(wú)法使用。

下面再講雙區(qū)DFU(dual bank)和單區(qū)DFU(single bank),雙區(qū)或者單區(qū)DFU是新固件和老固件覆蓋的兩種方式。后臺(tái)式DFU必須采用雙區(qū)模式進(jìn)行升級(jí),即老系統(tǒng)(老固件)和新系統(tǒng)(新固件)各占一塊bank(存儲(chǔ)區(qū)),假設(shè)老固件放在bank0中,新固件放在bank1中,升級(jí)的時(shí)候,應(yīng)用程序先把新固件下載到bank1中,只有當(dāng)新固件下載完成并校驗(yàn)成功后,系統(tǒng)才會(huì)跳入BootLoader模式,然后擦除老固件所在的bank0區(qū),并把新固件拷貝到bank0中。非后臺(tái)式DFU可以采用雙區(qū)也可以采用單區(qū)模式,與后臺(tái)式DFU相似,雙區(qū)模式下新老固件各占一塊bank(老固件為bank0,新固件為bank1),升級(jí)時(shí),系統(tǒng)先跳入BootLoader模式,然后BootLoader程序把新固件下載到bank1中,只有新固件下載完成并校驗(yàn)成功后,才會(huì)去擦除老固件所在的bank0區(qū),并把新固件拷貝到bank0區(qū)。單區(qū)模式的非后臺(tái)式DFU只有一個(gè)bank0,老固件和新固件分享這一個(gè)bank0,升級(jí)的時(shí)候,進(jìn)入bootloader模式后立馬擦除老固件,然后直接把新固件下載到同一個(gè)bank中,下載完成后校驗(yàn)新固件的有效性,新固件有效升級(jí)完成,否則要求重來(lái)。跟非后臺(tái)式DFU雙區(qū)模式相比,單區(qū)模式節(jié)省了一個(gè)bank的Flash空間,在系統(tǒng)資源比較緊張的時(shí)候,單區(qū)模式是一個(gè)不錯(cuò)的選擇。不管是雙區(qū)模式還是單區(qū)模式,升級(jí)過(guò)程出現(xiàn)問(wèn)題后,都可以進(jìn)行二次升級(jí),都不會(huì)出現(xiàn)“變磚”情況。不過(guò)雙區(qū)模式有一個(gè)好處,如果升級(jí)過(guò)程中出現(xiàn)問(wèn)題或者新固件有問(wèn)題,它還可以選擇之前的老固件老系統(tǒng)繼續(xù)執(zhí)行而不受其影響。而單區(qū)模式碰到這種情況就只能一直待在bootloader中,然后等待二次或者多次升級(jí)嘗試,此時(shí)設(shè)備的正常功能已無(wú)法使用,從用戶使用這個(gè)角度來(lái)說(shuō),你的確可以說(shuō)此時(shí)設(shè)備已經(jīng)“變磚”了。所以說(shuō),雖然雙區(qū)模式犧牲了很多存儲(chǔ)空間,但是換來(lái)了更好的升級(jí)體驗(yàn)。

可參考下面三個(gè)圖來(lái)理解上述過(guò)程。

531e7b7e-48a8-11ed-a3b6-dac502259ad0.png

如果你是第一次接觸Nordic nRF5 SDK,那么建議你先看一下這篇文章:開(kāi)發(fā)你的第一個(gè)BLE應(yīng)用程序—Blinky,或者看一下這一篇文章:手把手教你開(kāi)發(fā)BLE數(shù)據(jù)透?jìng)鲬?yīng)用程序,以建立Nordic nRF5 SDK的一些基本知識(shí),然后再往下看以下章節(jié)。

2. Nordic nRF5 SDK DFU工作原理


如文章“nRF5 SDK軟件架構(gòu)及softdevice工作原理”所述,Nordic nRF5 SDK軟件架構(gòu)跟其他家有點(diǎn)不一樣,程序存儲(chǔ)區(qū)最開(kāi)始部分放得不是Bootloader,而是藍(lán)牙協(xié)議棧Softdevice,應(yīng)用程序則緊挨著Softdevice,Bootloader則被nRF5 SDK放在程序存儲(chǔ)區(qū)的最上面,整個(gè)存儲(chǔ)區(qū)結(jié)構(gòu)圖如下所示。如果用戶還有Flash數(shù)據(jù)需要存放,那么這些數(shù)據(jù)緊挨著B(niǎo)ootLoader下面。

5331d174-48a8-11ed-a3b6-dac502259ad0.png

目前Nordic SDK默認(rèn)只提供非后臺(tái)式DFU開(kāi)箱即用的例子(SDK16.0開(kāi)始也支持后臺(tái)式DFU框架),即系統(tǒng)必須先跳到BootLoader中,然后才能通過(guò)BLE/UART/USB去接收新的固件。如上所示,如果采用雙區(qū)模式DFU,那么Bank0放的是應(yīng)用程序,即老固件,Bank1放的是新固件。平時(shí),Bank1為空或者忽略,系統(tǒng)只跑Bank0里面的應(yīng)用程序;升級(jí)的時(shí)候,先跳到BootLoader,然后接收新固件并把它放在Bank1中,最后把Bank1里面的固件拷貝到Bank0中。如果采用單區(qū)模式,則沒(méi)有Bank1這個(gè)區(qū)。平時(shí),系統(tǒng)只跑Bank0里面的代碼;升級(jí)的時(shí)候,跳到BootLoader,先擦除Bank0里面的老程序,并把新固件直接放在Bank0中。

根據(jù)升級(jí)時(shí)如何跳轉(zhuǎn)到Bootloader,Nordic SDK又將DFU分為按鍵式DFU和非按鍵式(Buttonless)DFU,所謂按鍵式DFU,就是上電時(shí)長(zhǎng)按某個(gè)按鍵以進(jìn)入bootloader模式,而非按鍵式DFU,就是整個(gè)DFU過(guò)程中設(shè)備端無(wú)任何人工干預(yù),通過(guò)BLE/UART/USB接口給應(yīng)用程序發(fā)送一條指令,應(yīng)用程序收到指令后再自動(dòng)跳入bootloader模式。不管是按鍵式DFU還是非按鍵式DFU,兩者只是進(jìn)入BootLoader的方式不一樣,其余基本一樣,尤其是BootLoader工作過(guò)程基本上是一模一樣的。后面只會(huì)闡述非按鍵式DFU的過(guò)程,按鍵式DFU以此類(lèi)似,就不再贅述。

程序跳到BootLoader后,根據(jù)BootLoader需不需要對(duì)新固件進(jìn)行驗(yàn)簽,Nordic SDK又把DFU分為開(kāi)放式DFU和安全式DFU(又稱簽名DFU)。開(kāi)放式DFU,BootLoader不做任何驗(yàn)證,直接把新固件接收下來(lái)。安全式DFU,BootLoader存有一把公鑰,BootLoader會(huì)先用這把公鑰驗(yàn)證新固件的簽名,只有驗(yàn)簽通過(guò),才允許后續(xù)的工作:比如把新固件接收下來(lái);如果驗(yàn)簽失敗,BootLoader將拒絕升級(jí),重新跳回應(yīng)用程序。

BootLoader可以通過(guò)不同的通信接口來(lái)接收新的固件,目前Nordic SDK支持BLE,UART和USB三種接口,所以大家可以在Nordic SDK中看到如下三種工程目錄:

534470b8-48a8-11ed-a3b6-dac502259ad0.png

其中pca0056表示nRF52840對(duì)應(yīng)的開(kāi)發(fā)板編號(hào),S140對(duì)應(yīng)Softdevice的型號(hào),然后ble有兩個(gè)目錄:無(wú)debug和有debug,uart和usb也包含同樣的兩個(gè)目錄。有debug和無(wú)debug兩者功能是一樣的,兩者的區(qū)別是:debug版本BootLoader支持日志打印(大家可以通過(guò)打印出的日志去理解BootLoader的工作過(guò)程),并可以忽略各種校驗(yàn),debug版本占據(jù)的代碼空間要大很多;無(wú)debug版本 BootLoader不支持日志打印功能并且版本和有效性校驗(yàn)是強(qiáng)制的。正式量產(chǎn)的時(shí)候推薦使用無(wú)debug版本以節(jié)省代碼空間。這里要強(qiáng)調(diào)一下,不管是debug版本還是無(wú)debug版本,兩者都可以用Keil進(jìn)行單步和斷點(diǎn)調(diào)試。

BLE,UART和USB只是通信方式不一樣,他們遵守的DFU流程是一模一樣的,這里會(huì)以BLE通信接口為例,詳細(xì)闡述DFU過(guò)程,UART和USB與之類(lèi)似,就不再贅述。

講述DFU升級(jí)之前,先講一下nRF52的啟動(dòng)流程,上電后,系統(tǒng)先執(zhí)行softdevice,softdevice通過(guò)讀取UICR一個(gè)寄存器的值,來(lái)判斷目前系統(tǒng)是否有BootLoader,如果沒(méi)有BootLoader,系統(tǒng)直接跳到application;如果有BootLoader,系統(tǒng)先跳到BootLoader,BootLoader再根據(jù)目前的情況來(lái)決定是進(jìn)入升級(jí)模式還是跳往application,BootLoader主要判斷如下幾種情況:

按鍵是否按下

保持寄存器GPREGRET1是否為0xB1

上次DFU過(guò)程是否還在進(jìn)行中

應(yīng)用程序校驗(yàn)是否通過(guò)

如果按鍵沒(méi)有按下,GPREGRET1不為0xB1,本次復(fù)位不是上次DFU的繼續(xù),并且應(yīng)用程序校驗(yàn)通過(guò),那么BootLoader就會(huì)直接跳到application,去執(zhí)行application應(yīng)用程序。那怎么去校驗(yàn)應(yīng)用程序的有效性呢?為此BootLoader引入了一個(gè)放在Flash的結(jié)構(gòu)體參數(shù):m_dfu_settings_buffer(數(shù)據(jù)類(lèi)型:nrf_dfu_settings_t),這個(gè)結(jié)構(gòu)體參數(shù)雖然只有896字節(jié),但由于Flash只能按頁(yè)擦除,所以這個(gè)參數(shù)實(shí)際占用了一個(gè)Flash page,這個(gè)page稱為settings page,settings page放在Flash的最后一個(gè)頁(yè)面,settings page目前有2個(gè)版本:版本1(SDK15.2及以前版本)和版本2(SDK15.3及以后版本),版本2可以兼容版本1,前面所述的896字節(jié)是指settings page版本2的大小。Settings page包含的信息比較多,大家用得比較多的是:

各種版本信息

DFU升級(jí)過(guò)程信息

Application image的CRC值和大小

應(yīng)用程序的bonding信息

Init command內(nèi)容

application/softdevice的啟動(dòng)校驗(yàn)信息(版本2才有)

版本1的settings page只校驗(yàn)application image的CRC值,如果CRC匹配,則認(rèn)為application有效。版本2的settings page不僅可以校驗(yàn)application image的CRC值,還可以校驗(yàn)application/softdevice的CRC值或者h(yuǎn)ash值或者簽名,你可以選擇你自己想要的校驗(yàn)方式,只有CRC值或者h(yuǎn)ash值或者簽名校驗(yàn)通過(guò)(三選其一),應(yīng)用程序才算有效,這時(shí)BootLoader才會(huì)跳到application去執(zhí)行。為了保證settings page在發(fā)生意外時(shí),比如寫(xiě)settings page過(guò)程中發(fā)生了復(fù)位或者掉電,系統(tǒng)也能正確恢復(fù),SDK15及以后版本引入了一個(gè)backup page,backup page也占用一個(gè)Flash page,內(nèi)容和settings page一模一樣。

上面是沒(méi)有觸發(fā)升級(jí)的情況下nRF52的正常啟動(dòng)流程,那如果要執(zhí)行DFU升級(jí),流程又是怎么樣的呢?下面詳細(xì)講一下無(wú)按鍵式BLE OTA的工作流程。

1) 正常啟動(dòng)后,系統(tǒng)運(yùn)行在應(yīng)用程序中,此時(shí)手機(jī)通過(guò)app發(fā)送一條開(kāi)始DFU的指令給設(shè)備,設(shè)備收到指令后,將GPREGRET1賦值0xB1,并觸發(fā)軟復(fù)位

2) 復(fù)位后,系統(tǒng)再次進(jìn)入BootLoader,因?yàn)镚PREGRET1等于0xB1,BootLoader進(jìn)入DFU模式,等待新固件接收

3) 手機(jī)先將init packet發(fā)送給設(shè)備,設(shè)備先做前期檢驗(yàn)prevalidation,主要是各種版本校驗(yàn)以及簽名驗(yàn)簽,校驗(yàn)通過(guò)后,更新settings page并準(zhǔn)備開(kāi)始數(shù)據(jù)接收

4) 接收新固件。每接收4kB數(shù)據(jù),回復(fù)一次CRC校驗(yàn)值,直至整個(gè)新固件image接收完畢,如果新固件校驗(yàn)通過(guò)(版本1校驗(yàn)CRC值,版本2校驗(yàn)hash值),就會(huì)去invalidate(無(wú)效化) bank0里面的老固件,更新settings page,并再次觸發(fā)軟復(fù)位

5) BootLoader啟動(dòng)后發(fā)現(xiàn)有新固件需要activate(激活),此時(shí)會(huì)去擦掉bank0里面的固件,并把bank1里面的固件拷貝到bank0,然后更新settings page,并再次觸發(fā)軟復(fù)位。注:上面講的是dual bank的流程,single bank與之相似,只不過(guò)在第3)步的時(shí)候就會(huì)去擦除老固件

6) BootLoader再次啟動(dòng)后,檢查新image的有效性,校驗(yàn)通過(guò)后,跳到新的application去執(zhí)行代碼

從上面流程可以看出,DFU過(guò)程中,系統(tǒng)需要跑兩段完全獨(dú)立的代碼:Application和BootLoader,Application和BootLoader都支持藍(lán)牙功能,也就是說(shuō),兩者都有自己的藍(lán)牙廣播和藍(lán)牙連接。這里面有一個(gè)問(wèn)題:當(dāng)系統(tǒng)從Application跳到BootLoader后,手機(jī)怎么辨別兩者為同一個(gè)設(shè)備?很多人會(huì)說(shuō),可以讓BootLoader和Application兩者的廣播名字一樣,根據(jù)廣播名字的一致性,來(lái)判斷二者來(lái)自同一個(gè)設(shè)備。這種方法存在兩個(gè)問(wèn)題:一大部分手機(jī)都支持GATT cache(緩存)功能,當(dāng)application跟手機(jī)相連后,手機(jī)會(huì)把a(bǔ)pplication的GATT數(shù)據(jù)緩存下來(lái)以加快下次連接的速度(這個(gè)現(xiàn)象在蘋(píng)果手機(jī)最明顯),之后如果系統(tǒng)跳到BootLoader,然后再跟手機(jī)相連,如果兩者的藍(lán)牙設(shè)備地址一樣,手機(jī)會(huì)認(rèn)為是同一個(gè)設(shè)備,從而跳過(guò)服務(wù)發(fā)現(xiàn)的過(guò)程而直接使用之前緩存下來(lái)的GATT數(shù)據(jù),這樣會(huì)導(dǎo)致BootLoader的服務(wù)無(wú)法被手機(jī)發(fā)現(xiàn),從而出現(xiàn)升級(jí)失敗。二如果多個(gè)設(shè)備同時(shí)在升級(jí),而我們僅僅依靠廣播名字來(lái)決定兩者屬不屬于同一個(gè)設(shè)備,這會(huì)導(dǎo)致設(shè)備A application有可能跟設(shè)備B的BootLoader進(jìn)行錯(cuò)配。為了解決這個(gè)問(wèn)題,Nordic提出了兩套方案。方案一,假設(shè)application的藍(lán)牙設(shè)備地址為x,跳到BootLoader后藍(lán)牙設(shè)備地址會(huì)變成x+1,這樣手機(jī)就可以通過(guò)這種地址+1的方式來(lái)辨別兩者屬不屬于同一個(gè)設(shè)備,由于application和BootLoader使用不同的藍(lán)牙設(shè)備地址,前面的GATT緩存問(wèn)題也就不存在。關(guān)于方案一,有一個(gè)問(wèn)題需要特別注意:如果你想修改例子默認(rèn)的藍(lán)牙設(shè)備地址(比如使用IEEE的public藍(lán)牙MAC地址),此時(shí)一定要記得同時(shí)更改application和BootLoader的藍(lán)牙設(shè)備地址,使他們滿足+1的條件,否則Nordic手機(jī)DFU庫(kù)無(wú)法辨別兩者是否屬于同一個(gè)設(shè)備,以致于無(wú)法完成OTA過(guò)程。方案二,application和BootLoader的藍(lán)牙設(shè)備地址一模一樣,但設(shè)備跟手機(jī)執(zhí)行配對(duì)和bonding操作,設(shè)備跟手機(jī)bonding后,就可以支持service changed indicate操作,這樣跳到BootLoader后可以讓手機(jī)主動(dòng)再執(zhí)行一次服務(wù)發(fā)現(xiàn)過(guò)程,從而解決GATT緩存問(wèn)題。

很多人對(duì)簽名驗(yàn)簽不是很理解,這里詳細(xì)說(shuō)一下它的工作原理。首先,你需要一對(duì)公私鑰,其中私鑰用來(lái)生成新固件的簽名,公鑰用來(lái)驗(yàn)證簽名的有效性,大家可以用nrfutil來(lái)生成自己需要的公私鑰對(duì),公私鑰制作成功后,私鑰一定要妥善保管(一般放在云端),千萬(wàn)不能丟,否則你自己也無(wú)法升級(jí)自己的設(shè)備;也不能被第三方知道,否則升級(jí)的安全性就不能保證了。公鑰可以變成一個(gè).c文件,并覆蓋DFU工程下的同名文件:dfu_public_key.c 。其次,BootLoader要支持簽名驗(yàn)簽密碼算法,這個(gè)DFU代碼已經(jīng)有了,并且有四種后端可選:micro-ecc,cc310_bl,Oberon和mbedtls,四選其一即可(這4種后端,只有cc310是硬件實(shí)現(xiàn),其余都是軟件實(shí)現(xiàn)),nRF52840推薦選擇cc310作為算法后端,其他nRF52芯片推薦選擇micro-ecc作為算法后端。micro-ecc效率高,占用的代碼空間最小,但它的版權(quán)是CPOL,只要你能接受CPOL,那么推薦使用micro-ecc;反之,如果接受不了CPOL版權(quán),而且硬件又不支持cc310,那么推薦使用Oberon,不過(guò)Oberon占用的代碼空間比micro-ecc要大一些,這個(gè)大家注意一下。再次,手機(jī)端要生成新固件的簽名,并把新固件的簽名傳給設(shè)備端。大家還是可以用nrfutil去生成新固件的簽名。最后,BootLoader接收到新固件hash值和簽名,并使用自己的公鑰對(duì)該簽名進(jìn)行驗(yàn)簽。這里說(shuō)一下,由于nrfutil是PC端應(yīng)用程序,所以它可以集成各種加密算法庫(kù),并完成上面提及的公私鑰對(duì),hash和簽名的生成工作。

3. DFU升級(jí)步驟詳解

3.1 安全式藍(lán)牙空中升級(jí)步驟


如前所述,Nordic SDK已經(jīng)提供了DFU例子,下面我們一步一步給大家講解如何通過(guò)Nordic SDK來(lái)實(shí)現(xiàn)無(wú)按鍵式藍(lán)牙空中升級(jí)。欲實(shí)現(xiàn)空中升級(jí),設(shè)備需要同時(shí)下載softdevice,應(yīng)用程序,BootLoader程序,以及BootLoader settings page。其中BootLoader代碼位于目錄:SDK根目錄examplesdfusecure_bootloader,然后在該目錄下選擇你對(duì)應(yīng)的板子和工程。Application對(duì)應(yīng)的目錄:SDK根目錄examplesle_peripheralle_app_buttonless_dfu,而softdevice所在目錄:SDK根目錄componentssoftdevice。

下面我們以nRF52832/PCA10040和S132/SDK16為例闡述無(wú)按鍵式藍(lán)牙空中升級(jí)實(shí)現(xiàn)步驟,其他芯片/softdevice/SDK原理與之類(lèi)似,這里就不再贅述。當(dāng)然,不同芯片不同softdevice不同SDK,他們的實(shí)現(xiàn)腳本還是會(huì)有一些細(xì)微差別,所以強(qiáng)烈建議大家去百度網(wǎng)盤(pán)下載跟大家相匹配的腳本,百度網(wǎng)盤(pán)里面各個(gè)腳本的命名規(guī)則請(qǐng)參考3.2節(jié)。

1) 安裝PC版nrfutil。nrfutil安裝有兩種方式,一種是直接下載exe文件,一種是以Python的方式進(jìn)行安裝。nrfutil.exe直接下載鏈接為:https://github.com/NordicSemiconductor/pc-nrfutil/releases,記得把nrfutil.exe所在目錄放在Windows環(huán)境變量中。Python方式安裝nrfutil步驟如下所示:

安裝Python2.7或者Python3.7,下載地址:https://www.python.org/downloads/,安裝成功后請(qǐng)確保Windows環(huán)境變量包含Python目錄

通過(guò)pip安裝最新版的nrfutil,即打開(kāi)Windows命令行工具CMD(管理員權(quán)限),輸入如下命令:pip install nrfutil,即可以完成nrfutil的安裝。

安裝完成后,在Windows命令行工具輸入:nrfutil version,如果可以正確顯示版本信息,說(shuō)明安裝已經(jīng)成功

對(duì)于Windows用戶,nrfutil運(yùn)行需要幾個(gè)特殊的DLL庫(kù),而這幾個(gè)庫(kù)有些Windows機(jī)器是沒(méi)有的,如此,可往:https://www.microsoft.com/en-us/download/details.aspx?id=40784下載

2) 通過(guò)nrfutil生成公私鑰對(duì)。

私鑰生成命令: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就是公鑰)

大家務(wù)必要保存好私鑰priv.pem,以后每次升級(jí)新固件時(shí),都會(huì)通過(guò)這個(gè)私鑰對(duì)它進(jìn)行簽名,一旦priv.pem丟失或者被暴露,DFU將無(wú)法進(jìn)行或者變得不安全

3) 請(qǐng)確保已按照“Nordic nRF51/nRF52開(kāi)發(fā)環(huán)境搭建”把Nordic nRF5 SDK開(kāi)發(fā)環(huán)境搭建成功

4) 生成micro-ecc算法庫(kù)。由于micro-ecc是第三方算法庫(kù),需要用戶自己去安裝(這個(gè)是版權(quán)的要求,沒(méi)辦法直接編譯放在SDK中)。請(qǐng)先確保電腦已安裝了git和GCC編譯器,然后直接點(diǎn)擊SDK如下目錄的build_all腳本,就可以自動(dòng)完成micro-ecc算法庫(kù)的安裝。為了方便一些開(kāi)發(fā)者評(píng)估,我這里在自己電腦上生成了micro-ecc算法庫(kù),micro-ecc目錄編排結(jié)構(gòu)有兩種:SDK14及以后版本是一種目錄結(jié)構(gòu)(百度云盤(pán)壓縮包名稱:micro_ecc_new.rar),SDK13和SDK12又是一種目錄結(jié)構(gòu)(百度云盤(pán)壓縮包名稱:micro_ecc_old.rar),這兩個(gè)壓縮包只是目錄不一樣,里面的算法庫(kù)內(nèi)容其實(shí)是一樣的,這兩個(gè)壓縮包大家都可以在前面的百度云盤(pán)中找到,以供大家評(píng)估使用。大家下載下來(lái)后,直接覆蓋同名目錄即可。注意:百度云盤(pán)里面的micro-ecc庫(kù)僅供大家評(píng)估使用,如要商用,請(qǐng)大家按照上面步驟去生成。

53a039ca-48a8-11ed-a3b6-dac502259ad0.png

5) 編譯bootloader代碼。將剛才的dfu_public_key.c取代SDK根目錄examplesdfu下的同名文件,然后使用Keil編譯如下目錄中的工程:

SDK根目錄examplesdfusecure_bootloaderpca10040_blearm5_no_packs,或者nRF5SDK160098a08e2examplesdfusecure_bootloaderpca10040_s132_blearm5_no_packs

,將生成的hex文件改名為:bootloader.hex(注:本文所有項(xiàng)目都會(huì)采用Keil工程來(lái)講解,如果你使用其他IDE,請(qǐng)選擇其對(duì)應(yīng)的工程文件進(jìn)行編譯,不管是Keil還是其他IDE,除了編譯時(shí)候選擇的工程文件不一樣,其余都大同小異,大家可以舉一反三完成其他IDE的相應(yīng)工作

6) 編譯application代碼。請(qǐng)編譯工程:

SDK根目錄 examplesle_peripheralle_app_buttonless_dfupca10040s132arm5_no_packs,將生成的hex文件改名為:app.hex

7) 生成BootLoader settings page。Bootloader settings page存儲(chǔ)在Flash最后一個(gè)page,如前所述,BootLoader settings page有2個(gè)版本,他們的生成腳本命令如下所示:

版本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) 燒寫(xiě)固件。將上文生成的3個(gè)hex文件和softdevice hex文件merge成一個(gè)文件,然后通過(guò)nrfjprog或者nRF Connect桌面版進(jìn)行燒寫(xiě),相關(guān)命令如下所示:

合并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

燒寫(xiě)hex文件命令(以nrfjprog為例):

nrfjprog --eraseall -f NRF52 
nrfjprog --program whole.hex --verify -f NRF52 
nrfjprog --reset -f NRF52

9) 通過(guò)nrfutil生成新固件對(duì)應(yīng)的zip包:new_app.zip。zip包包含新固件(新固件廣播名改為:Nordic_New,其余跟老固件一模一樣)和init包,zip包一般通過(guò)云端下發(fā)到手機(jī)app,手機(jī)app再通過(guò)藍(lán)牙下載到設(shè)備中。生成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對(duì)應(yīng)起來(lái),大家可以改成任何自己想要的值。--key-file 表示簽名用的私鑰文件。--sd-req表示老固件運(yùn)行在哪個(gè)版本softdevice上,這個(gè)值一定要跟自己的softdevice相匹配,否則無(wú)法升級(jí),各個(gè)softdevice版本ID信息可以通過(guò)命令“nrfutil pkg generate --help”獲得,如下為當(dāng)前所有softdevice ID列表:

53c60542-48a8-11ed-a3b6-dac502259ad0.png

10) 將“new_app.zip”拷貝到手機(jī)上。安卓和蘋(píng)果手機(jī)都可以通過(guò)微信的‘文件傳輸助手’拷過(guò)去,非常方便。請(qǐng)注意,手機(jī)nRF Connect和nRF Toolbox都支持DFU功能,蘋(píng)果手機(jī)拷貝的時(shí)候可以隨便選擇其中一個(gè)app。

11) 通過(guò)手機(jī)版nRF Connect或者nRF Toolbox進(jìn)行藍(lán)牙空中升級(jí),這里以nRF Connect為例闡述升級(jí)詳細(xì)步驟,nRF Toolbox與此類(lèi)似,就不再贅述

第8)步完成后,開(kāi)發(fā)板就可以正常跑起來(lái),并廣播為Nordic_Buttonless

53dc1cce-48a8-11ed-a3b6-dac502259ad0.png

連接該設(shè)備,使能CCCD(這一步可選),然后選擇“DFU”,如下所示:

5401525a-48a8-11ed-a3b6-dac502259ad0.png??

選擇“DFU”后,將跳出一個(gè)對(duì)話框,讓你選擇新固件對(duì)應(yīng)的zip包。由于zip包放在了微信下面的download目錄下,我們需要通過(guò)文件瀏覽器找到這個(gè)zip包,大家可以先用系統(tǒng)自帶的文件瀏覽器打開(kāi)這個(gè)zip包(如果打開(kāi)失敗,那么大家就要去下載一些第三方的文件瀏覽器了,比如es explorer),相關(guān)操作界面如下所示:

5431b6a2-48a8-11ed-a3b6-dac502259ad0.png

544f923a-48a8-11ed-a3b6-dac502259ad0.png

一旦zip包打開(kāi)成功,升級(jí)過(guò)程開(kāi)始,界面如下所示:

545e55e0-48a8-11ed-a3b6-dac502259ad0.png

升級(jí)成功后,設(shè)備將運(yùn)行新固件,即廣播名字將變成:Nordic_New,如下所示:

54704be2-48a8-11ed-a3b6-dac502259ad0.png

如上以nRF52832/S132為例闡述了Nordic SDK實(shí)現(xiàn)無(wú)按鍵式簽名式藍(lán)牙空中升級(jí)的詳細(xì)步驟,Nordic SDK有多個(gè)版本,從SDK13.0.0到現(xiàn)在SDK16.0.0,他們的升級(jí)步驟基本上一模一樣,大家完全可以參考上述步驟來(lái)做。SDK12升級(jí)步驟也與上述步驟基本一樣,唯一如下地方需要注意一下:

編譯application代碼的時(shí)候,把如下語(yǔ)句注掉,否則會(huì)造成BootLoader和application兩者的hex文件相沖突

54b9215a-48a8-11ed-a3b6-dac502259ad0.png

3.2節(jié)會(huì)按照上述步驟,對(duì)一些經(jīng)典的安全式BLE OTA例子進(jìn)行測(cè)試,并生成可直接運(yùn)行的腳本,以供大家參考。

3.2 各種安全式藍(lán)牙空中升級(jí)例子


3.1節(jié)是以升級(jí)nRF52832 application為例,詳細(xì)闡述了安全式BLE OTA步驟。除了升級(jí)application,有的人還需要升級(jí)softdevice和BootLoader;除了52832,有的人還會(huì)用52840/52833/52811/52810/51822等;除了SDK16.0.0,有的人還會(huì)用SDK15.3/15.2/14.2/12.3等。為此我選了一些經(jīng)典組合,將他們DFU用得的所有腳本都做好了,并進(jìn)行了實(shí)際測(cè)試,有需要的可以去百度網(wǎng)盤(pán)下載。這些腳本在百度網(wǎng)盤(pán)的命名規(guī)則為:安全模式_固件傳輸接口_升級(jí)哪一部分固件_SDK版本號(hào)_芯片型號(hào).rar,比如secure_ble_S132_app_SDK160_nRF52832.rar表示采用安全簽名,固件通過(guò)BLE傳輸,BLE使用S132協(xié)議棧,升級(jí)的時(shí)候只升級(jí)application而不升級(jí)BootLoader和SoftDevice,基于SDK16.0.0和nRF52832。

目前百度網(wǎng)盤(pán)上傳了如下安全式BLE OTA示例腳本(注:這些腳本都經(jīng)過(guò)我的測(cè)試,全都可以直接運(yùn)行):

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 通過(guò)UART口進(jìn)行安全式固件升級(jí)示例腳本


我們以nRF52810為例來(lái)闡述如何通過(guò)UART進(jìn)行安全式固件升級(jí)步驟:

1) 請(qǐng)參考3.1節(jié)第1)到第4)步,完成nrfutil安裝,mico-ecc算法庫(kù)生成,以及公私鑰生成

2) 編譯bootloader代碼。將剛才的dfu_public_key.c取代SDK根目錄examplesdfu下的同名文件,確保sdk_config.h中的NRF_BL_DFU_ENTER_METHOD_BUTTON為1,然后使用Keil編譯如下目錄中的工程:

SDK根目錄 examplesdfusecure_bootloaderpca10040e_uartarm5_no_packs

,將生成的hex文件改名為:bootloader.hex

3) 編譯application代碼。3.1節(jié)講述OTA的時(shí)候,我們選擇的例子是ble_app_buttonless_dfu,因?yàn)槲覀兪峭ㄟ^(guò)藍(lán)牙給設(shè)備發(fā)送一條命令,從而讓設(shè)備進(jìn)入DFU模式。通過(guò)串口升級(jí)固件,如何進(jìn)入DFU模式,取決于你的應(yīng)用設(shè)計(jì),你可以采用通過(guò)發(fā)送藍(lán)牙命令讓其進(jìn)入DFU模式,也可以通過(guò)上電檢測(cè)按鍵是否按下以決定是否進(jìn)入DFU模式。如果想采用ble_app_buttonless_dfu作為application,那么你需要把該工程中的main函數(shù)如下語(yǔ)句刪掉(這些語(yǔ)句是為藍(lán)牙版BootLoader設(shè)計(jì)的,我們現(xiàn)在是UART版BootLoader,不支持這些語(yǔ)句):

err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

這里我們選擇以上電檢測(cè)按鍵的方式來(lái)決定是否進(jìn)入DFU模式,并以ble_app_blinky作為應(yīng)用例子,請(qǐng)直接編譯如下工程:

SDK根目錄examplesle_peripheralle_app_blinkypca10040es112arm5_no_packs,將生成的hex文件改名為:app.hex

4) 生成BootLoader settings page并同時(shí)燒寫(xiě)老固件,雙擊“program.bat”即可完成,這個(gè)腳本是使用nrfjprog來(lái)完成固件燒寫(xiě)的。

5) 生成新固件zip包并進(jìn)行UART DFU,雙擊“dfu.bat”即可完成,這個(gè)腳本是使用nrfutil作為UART主機(jī),并將新固件通過(guò)電腦COM口傳給設(shè)備的。請(qǐng)記得一定要修改腳本中的UART對(duì)應(yīng)的電腦COM口,否則升級(jí)無(wú)法完成。

注:所有bat腳本都可通過(guò)右鍵選擇Notepad++打開(kāi),然后查看里面包含的具體命令,并按照自己的需求進(jìn)行修改。如需進(jìn)一步理解腳本中的命令,請(qǐng)參考3.1節(jié)的說(shuō)明。

上述所有操作步驟已打包并上傳到百度網(wǎng)盤(pán),請(qǐng)去網(wǎng)盤(pán)下載文件:secure_uart_app_SDK160_nRF52810.rar,這個(gè)文件已經(jīng)過(guò)我的測(cè)試,大家可以直接使用。

3.4 通過(guò)USB口進(jìn)行安全式固件升級(jí)示例腳本


我們以nRF52840為例來(lái)講述如何通過(guò)USB進(jìn)行安全式固件升級(jí),其實(shí)通過(guò)USB口升級(jí)固件步驟與3.3節(jié)的操作幾乎一模一樣,唯一不同的是,選擇如下目錄的BootLoader工程進(jìn)行編譯:

SDK根目錄 examplesdfusecure_bootloaderpca10056_usbarm5_no_packs

通過(guò)USB口進(jìn)行安全式固件升級(jí)示例腳本已打包并上傳到百度網(wǎng)盤(pán),請(qǐng)去網(wǎng)盤(pán)下載文件:

secure_usb_app_SDK160_nRF52840.rar,這個(gè)文件已經(jīng)過(guò)我的測(cè)試,大家可以直接使用。

3.5 通過(guò)USB口進(jìn)行開(kāi)放式固件升級(jí)示例腳本

我們還是以nRF52840為例來(lái)講述如何通過(guò)USB進(jìn)行開(kāi)放式固件升級(jí),其升級(jí)步驟與3.3節(jié)的操作幾乎一模一樣,唯一不同的是,選擇如下目錄的BootLoader工程進(jìn)行編譯:

SDK根目錄 examplesdfuopen_bootloaderpca10056_usbarm5_no_packs

相關(guān)腳本已上傳百度網(wǎng)盤(pán),請(qǐng)下載:

open_usb_app_SDK160_nRF52840.rar,這個(gè)文件已經(jīng)過(guò)我的測(cè)試,大家可以直接使用。

3.6 開(kāi)放式藍(lán)牙空中升級(jí)(Legacy DFU)步驟


所謂開(kāi)放式OTA,是指OTA過(guò)程中,不需要檢驗(yàn)新固件的簽名,也就是說(shuō)BootLoader代碼里面不包含公鑰及相關(guān)密碼算法庫(kù),升級(jí)的時(shí)候,只校驗(yàn)版本信息,版本校驗(yàn)通過(guò),就可以開(kāi)始升級(jí)流程。Nordic SDK目前支持兩套開(kāi)放式OTA方案,一套是SDK15和SDK16提供的,一套是SDK9/SDK10/SDK11提供的。SDK15/16提供的開(kāi)放式OTA工作原理和流程,與安全式OTA基本上一樣,只不過(guò)刪掉了簽名驗(yàn)簽部分。SDK9/SDK10/SDK11提供的開(kāi)放式OTA也叫l(wèi)egacy OTA DFU,它的工作流程與SDK15/16略有不同,下面將以nRF52832/S132為例,闡述如何在SDK11中實(shí)現(xiàn)無(wú)按鍵式開(kāi)放式藍(lán)牙空中升級(jí),詳細(xì)步驟如下所示:

1) 編譯bootloader代碼,請(qǐng)使用Keil編譯目錄“nRF5_SDK_11.0.0_89a8197examplesdfuootloaderpca10040dual_bank_ble_s132arm5_no_packs”中的工程,將生成的hex文件改名為bootloader.hex

2) 編譯application代碼,請(qǐng)編譯目錄“nRF5_SDK_11.0.0_89a8197examplesle_peripheralble_app_hrspca10040s132_with_dfuarm5_no_packs”中的工程,將生成的hex文件改名為app.hex

3) 將softdevice,bootloader和app三個(gè)hex文件合成一個(gè)文件,命令如下所示:

mergehex --merge s132_nrf52_2.0.1_softdevice.hex app.hex bootloader.hex –output whole.hex

4) 燒寫(xiě)固件到設(shè)備中,大家可以用nRF Connect桌面版燒寫(xiě),也可以通過(guò)nrfjprog燒寫(xiě),nrfjprog燒寫(xiě)命令如下所示:

nrfjprog.exe --eraseall -f NRF52
nrfjprog --program whole.hex --verify -f NRF52

5) 另外我們還需要在Flash中寫(xiě)一個(gè)application有效標(biāo)志位,從而上電后程序直接跑到application中去執(zhí)行,而不是停留在bootloader中不出來(lái),其對(duì)應(yīng)的命令如下所示:

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

6) 用老版本的nrfutil生成新固件對(duì)應(yīng)的zip包。該zip包除了包含新固件image,還包含一些配置信息。升級(jí)時(shí),zip包會(huì)通過(guò)云端下發(fā)到手機(jī)端app,手機(jī)端app再把zip包傳給藍(lán)牙設(shè)備以進(jìn)行固件升級(jí)。請(qǐng)使用老版本nrfutil(版本號(hào)0.3.0)來(lái)生成該zip,老版本nrfutil跟隨nRFgo studio一起安裝的,只要你安裝了nRFgo studio,老版本nrfutil就會(huì)自動(dòng)安裝好,并放在目錄“C:Program Files (x86)Nordic Semiconductor RFgo Studio”中。生成zip包對(duì)應(yīng)的命令如下所示:

nrfutil dfu genpkg --application app_new.hex --application-version 1 SDK110_app_s132.zip

7) 把上述的‘SDK110_app_s132.zip’拷到手機(jī)中,安卓和蘋(píng)果手機(jī)都可以通過(guò)微信的‘文件傳輸助手’拷過(guò)去,非常方便。注:手機(jī)nRF Connect和nRF Toolbox都支持DFU功能,蘋(píng)果手機(jī)拷貝的時(shí)候可以隨便選擇其中一個(gè)app。

8) 使用nRF Connect或者nRF Toolbox來(lái)完成DFU過(guò)程。這里以nRF Connect為例來(lái)闡述整個(gè)升級(jí)過(guò)程。

成功執(zhí)行完第5)步后,如果開(kāi)發(fā)板運(yùn)行正常,那么它將進(jìn)行廣播,廣播名字為:Nordic_HRM

54dd6330-48a8-11ed-a3b6-dac502259ad0.png

連接該設(shè)備,并使能CCCD,然后選擇“DFU”

54ed73ba-48a8-11ed-a3b6-dac502259ad0.png

選擇“DFU”后,將跳出一個(gè)對(duì)話框,讓你選擇新固件對(duì)應(yīng)的zip包。由于zip包放在了微信下面的download目錄下,我們需要通過(guò)文件瀏覽器找到這個(gè)zip包,大家可以先用系統(tǒng)自帶的文件瀏覽器打開(kāi)這個(gè)zip包(如果打開(kāi)失敗,那么大家就要去下載一些第三方的文件瀏覽器了,比如es explorer),相關(guān)操作界面如下所示:

55029da8-48a8-11ed-a3b6-dac502259ad0.png

5535db6e-48a8-11ed-a3b6-dac502259ad0.png

一旦zip包打開(kāi)成功,升級(jí)過(guò)程開(kāi)始,界面如下所示:

555d17d8-48a8-11ed-a3b6-dac502259ad0.png

升級(jí)成功,設(shè)備將自動(dòng)啟動(dòng),此時(shí)你會(huì)看到新固件已經(jīng)在運(yùn)行,廣播名字也變成了:Nordic_HRM_new,如下所示:

556aa3e4-48a8-11ed-a3b6-dac502259ad0.png

目前百度網(wǎng)盤(pán)上傳了如下開(kāi)放式BLE OTA示例腳本(注:這些腳本都經(jīng)過(guò)我的測(cè)試,全都可以直接運(yùn)行):

open_ble_S132_app_SDK110_nRF52832.rar

open_ble_S130_app_SDK110_nRF51.rar

如果你的應(yīng)用是基于SDK11開(kāi)發(fā)的,并且需要集成DFU功能,請(qǐng)參考上述例子ble_app_hrs來(lái)移植DFU功能,主要工作包括兩部分:一把BLE_DFU_APP_SUPPORT這個(gè)宏包括的所有代碼拷到你的工程中,二如果你的設(shè)備支持bonding的話,還需把Device manager相關(guān)代碼也拷到你的工程中,如此即可完成DFU功能的移植。

4. 詳解如何移植DFU功能到ble_app_uart


為了讓SDK14及以后版本的ble_app_uart具有DFU功能,有2種做法,一是把NUS服務(wù)移植到ble_app_buttonless_dfu中,這種方法相對(duì)來(lái)說(shuō)更簡(jiǎn)單,大家可以自己去實(shí)踐一下;二是把DFU服務(wù)移植到ble_app_uart中,這種移植方式挑戰(zhàn)更大,但更有利于我們理解DFU的工作原理,我們現(xiàn)在就來(lái)闡述如何給ble_app_uart加上OTA功能。如前所述,OTA過(guò)程中,手機(jī)跟設(shè)備可以進(jìn)行配對(duì)和bonding,也可以用明文進(jìn)行藍(lán)牙通信。配對(duì)bonding的時(shí)候,我們可以讓BootLoader和application共享bonding信息,也可以只讓application進(jìn)行配對(duì)bonding,而B(niǎo)ootLoader還是以明文方式進(jìn)行藍(lán)牙通信。

Nordic已經(jīng)把DFU服務(wù)做成了一個(gè)模塊,大家只要把這個(gè)模塊加到自己的應(yīng)用中,然后完成一些必須的配置,初始化以及回調(diào)函數(shù)的撰寫(xiě),再加上把SVCI模塊(SVCI模塊主要用來(lái)修改BootLoader的一些配置參數(shù))加入到應(yīng)用中移植即可大功告成。在SDK中,DFU服務(wù)的名字是:BLE_DFU_SERVICE,這個(gè)服務(wù)放在文件ble_dfu.c中,而ble_dfu.c又有兩個(gè)后端實(shí)現(xiàn):ble_dfu_unbonded.c和ble_dfu_bonded.c,分別對(duì)應(yīng)無(wú)bonding明文藍(lán)牙連接和有bonding的藍(lán)牙連接,下面也將分這兩種情況詳細(xì)闡述移植過(guò)程。

4.1 明文正常連接OTA(無(wú)bonding)


1) 用Keil打開(kāi)如下工程:SDK根目錄examplesle_peripheralle_app_uartpca10040s132arm5_no_packs

2) 添加DFU服務(wù)有關(guān)的文件,目錄和宏定義。首先添加如下DFU目錄及相關(guān)文件:

55849cf4-48a8-11ed-a3b6-dac502259ad0.png

在define中添加這些宏:DEBUG DFU_SUPPORT BL_SETTINGS_ACCESS_ONLY NRF_DFU_SVCI_ENABLED NRF_DFU_TRANSPORT_BLE=1,其中DEBUG宏只是為了調(diào)試方便而設(shè)置的,跟DFU本身無(wú)關(guān)。DFU_SUPPORT是我用來(lái)控制我添加的DFU代碼的,刪掉DFU_SUPPORT,將不編譯所有DFU有關(guān)代碼。其余的宏都是系統(tǒng)自帶的,如果要支持DFU,就必須要添加。

559e523e-48a8-11ed-a3b6-dac502259ad0.png

然后包含如下目錄:

55d74904-48a8-11ed-a3b6-dac502259ad0.png

3) 修改sdk_config.h文件。首先我們需要使能BLE_DFU模塊,及選擇OTA藍(lán)牙連接方式,如下為使用明文進(jìn)行藍(lán)牙通信的配置:

#define BLE_DFU_ENABLED 1
#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0

同時(shí)我們還需要修改softdevice配置?,F(xiàn)在整個(gè)應(yīng)用包括2個(gè)供應(yīng)商自定義UUID:NUS和DFU(其實(shí)這兩個(gè)UUID可以合成一個(gè),但由于歷史原因,DFU和NUS分別使用了兩個(gè)不同的vs UUID),相應(yīng)地ATT table size也要變大,然后應(yīng)用程序RAM起始地址也需要跟著變,如下(注:這里的NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 設(shè)置得稍稍偏大):

#define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1600
#define NRF_SDH_BLE_VS_UUID_COUNT 2

修改應(yīng)用程序RAM起始地址,如下:

55f4e9c8-48a8-11ed-a3b6-dac502259ad0.png

4) 修改main.c文件。首先添加如下頭文件:

#include "ble_dfu.h"
#include "nrf_bootloader_info.h"
#include "nrf_power.h"

然后在main函數(shù)的開(kāi)始處,添加修改BootLoader廣播名字的代碼,由于iOS DFU的時(shí)候默認(rèn)就會(huì)去改廣播名字,為了兼容iOS,這一行代碼是必須的:

err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

然后在services_init()中添加ble dfu服務(wù)

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回調(diào)函數(shù)的撰寫(xiě),大家只要按照要求來(lái),就沒(méi)問(wèn)題,如果應(yīng)用只支持一個(gè)連接,那么ble_dfu_evt_handler可以直接為空。如果應(yīng)用支持多個(gè)連接,可以參考ble_app_buttonless_dfu做法,這里就不貼代碼了。

5) 在跳轉(zhuǎn)到bootloader之前,如果你想做一些專門(mén)的代碼處理,比如完成pending的Flash操作,比如關(guān)閉某些模塊,那么你可以注冊(cè)一個(gè)app_shutdown_handler來(lái)做這些工作。(注:這一步不是必須的,是可選的!)

NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);

6) (這一步可選)之前的ble_app_uart是沒(méi)有BootLoader的,所以啟動(dòng)起來(lái)非常快?,F(xiàn)在加了BootLoader代碼,為了加快system off喚醒的速度,可以定義如下語(yǔ)句,

 nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);

然后先disable softdevice,然后再進(jìn)入system off模式。這一步本身跟DFU沒(méi)有什么關(guān)系,主要是為了加快程序啟動(dòng)速度而另加的。

7) 編譯工程,并將生成的hex文件改名為“app.hex”

8) 然后按照3.1節(jié)的步驟一步一步完成后續(xù)的DFU過(guò)程。

4.2 bonding連接OTA


4.1節(jié)的工程已經(jīng)移植了DFU功能,現(xiàn)在我們?cè)侔裝onding功能移植到4.1節(jié)工程上,就可以讓我們的應(yīng)用同時(shí)支持DFU和bonding。Bonding功能是通過(guò)peer_manager模塊來(lái)實(shí)現(xiàn)的,大家只要把peer_manager有關(guān)的文件添加進(jìn)來(lái),就可以實(shí)現(xiàn)bonding的目標(biāo)。

1) 打開(kāi)4.1節(jié)的工程

2) 添加如下文件:

560338de-48a8-11ed-a3b6-dac502259ad0.png

3) 修改sdk_config.h文件,需要修改多個(gè)地方,如下:

#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

當(dāng)NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS設(shè)為1時(shí),表示application將與主機(jī)進(jìn)行bonding,同時(shí)該bonding信息將共享給BootLoader,也就是說(shuō),進(jìn)入bootloader模式后,主機(jī)將使用以前的bonding信息與設(shè)備進(jìn)行加密連接。

4) 在main.c文件開(kāi)頭,包含如下頭文件:

#include "peer_manager.h"

5) 在main函數(shù)中添加peer_manager_init(),其定義如下所示:

56235312-48a8-11ed-a3b6-dac502259ad0.jpg

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);
}

56235312-48a8-11ed-a3b6-dac502259ad0.jpg

添加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);
}

尤其要檢查如下代碼有沒(méi)有添加,由于iOS DFU的時(shí)候默認(rèn)就會(huì)去改廣播名字,為了兼容iOS,這一行代碼是必須的

err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

6) 在ble_evt_handler中刪除BLE_GAP_EVT_SEC_PARAMS_REQUEST分支,因?yàn)檫@個(gè)分支在peer_manager模塊中已經(jīng)進(jìn)行處理了,這里再處理一次,會(huì)產(chǎn)生異常:

//        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信息功能(如果你不需要這個(gè)功能,也可以不改)

8) (此步可選)一般來(lái)說(shuō),如果用戶在手機(jī)端把配對(duì)信息刪掉了,為了安全起見(jiàn),設(shè)備端也需要把相關(guān)配對(duì)信息清掉,然后才可以允許手機(jī)和設(shè)備再次進(jìn)行配對(duì)和bonding。如何觸發(fā)設(shè)備端bonding信息的刪除操作?可以通過(guò)按鍵檢測(cè)的方式來(lái)做,比如目前我們這個(gè)例子的做法。但是有很多設(shè)備沒(méi)有按鍵,而且很多人希望這種二次配對(duì)的操作對(duì)用戶來(lái)說(shuō)無(wú)感,即哪怕用戶刪掉了手機(jī)端配對(duì)信息,如果用戶想發(fā)起第二次配對(duì)請(qǐng)求,設(shè)備也能接受,而且操作過(guò)程跟用戶第一次發(fā)起配對(duì)請(qǐng)求的過(guò)程一模一樣。Nordic SDK其實(shí)是兼容這種操作的,用戶只需在pm_evt_handler()中添加如下代碼即可:

56235312-48a8-11ed-a3b6-dac502259ad0.jpg

        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);
        }

56235312-48a8-11ed-a3b6-dac502259ad0.jpg

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

10) 編譯工程,將生成的hex文件改名為app.hex

11) 然后按照3.1節(jié)步驟來(lái)執(zhí)行OTA過(guò)程,不過(guò)如下幾點(diǎn)需要注意:

如果你在應(yīng)用中把NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS設(shè)為1,那么bootloader代碼就不能采用默認(rèn)配置,請(qǐng)修改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”選項(xiàng),如下:

566ddc16-48a8-11ed-a3b6-dac502259ad0.png

手機(jī)連接設(shè)備成功后,請(qǐng)手動(dòng)使能CCCD,以讓手機(jī)自動(dòng)發(fā)起bonding請(qǐng)求

568b857c-48a8-11ed-a3b6-dac502259ad0.png

572e3c04-48a8-11ed-a3b6-dac502259ad0.png

DFU升級(jí)成功后,設(shè)備將會(huì)與手機(jī)自動(dòng)重連,此時(shí)需點(diǎn)擊“Refresh services”,以獲得設(shè)備最新服務(wù)列表,如下:

57413bc4-48a8-11ed-a3b6-dac502259ad0.png?

上述代碼工程我已打包成:ble_app_uart_ota_SDK16_0_0.rar,并上傳到百度網(wǎng)盤(pán),大家下載下來(lái)解壓縮到:SDK根目錄examplesle_peripheral這個(gè)目錄下,就可以直接編譯和運(yùn)行。DFU過(guò)程中用到的所有腳本我也幫大家做好了,大家可以直接下載下來(lái)使用,其中secure_ble_S132_uart_SDK160_nRF52832_Nobonding.rar對(duì)應(yīng)明文藍(lán)牙傳輸,secure_ble_S132_uart_SDK160_nRF52832_bonding.rar對(duì)應(yīng)bonding藍(lán)牙傳輸。

5 手機(jī)端DFU參考代碼


Nordic不僅提供DFU設(shè)備端的參考代碼,同時(shí)提供手機(jī)端的參考代碼。Nordic分別開(kāi)發(fā)了Android版和iOS版的DFU庫(kù),大家可以直接拿過(guò)來(lái)使用,集成到自己的移動(dòng)端app中,這兩個(gè)庫(kù)都放在github上,鏈接如下所示:

Android版DFU庫(kù):https://github.com/NordicSemiconductor/Android-DFU-Library

iOS版DFU庫(kù):https://github.com/NordicSemiconductor/IOS-Pods-DFU-Library

Nordic還提供了一個(gè)移動(dòng)端app:nRF Toolbox,nRF Toolbox是代碼開(kāi)源的,里面也集成了上面提到的DFU庫(kù),大家可以參考nRF Toolbox來(lái)開(kāi)發(fā)自己的移動(dòng)端app。nRF Toolbox源碼也可以在github上找到:

Android版nRF Toolbox源代碼及開(kāi)發(fā)說(shuō)明請(qǐng)參考:https://github.com/NordicSemiconductor/Android-nRF-Toolbox

iOS版nRF Toolbox源代碼及開(kāi)發(fā)說(shuō)明請(qǐng)參考:https://github.com/NordicSemiconductor/IOS-nRF-Toolbox

nRF Toolbox軟件界面如下所示:

57627eec-48a8-11ed-a3b6-dac502259ad0.png

審核編輯:彭靜

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 無(wú)線通信
    +關(guān)注

    關(guān)注

    58

    文章

    4493

    瀏覽量

    143345
  • SDK
    SDK
    +關(guān)注

    關(guān)注

    3

    文章

    1020

    瀏覽量

    45694
  • 腳本
    +關(guān)注

    關(guān)注

    1

    文章

    387

    瀏覽量

    14811

原文標(biāo)題:詳解藍(lán)牙OTA升級(jí)原理與步驟

文章出處:【微信號(hào):最后一個(gè)bug,微信公眾號(hào):最后一個(gè)bug】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    基于demo例程,PSoC4000T怎么用UART做DFU?

    我現(xiàn)在基于MTB里面CY8CPROTO-040T的例程PSoC_4_Basic_device_firmware_upgrade,程序限制了只能用IIC通訊做DFU,現(xiàn)在我想基于自己的板,用UART來(lái)
    發(fā)表于 06-04 10:30

    【CANNON試用體驗(yàn)】STM32 DFU下載與 DFU生成工具

    STM32支持DFU模式,可以通過(guò)USB下載固件,一切搞定。DFU是通過(guò)STM32的USB下載的,所以STM32的USB接口得引出。1. 下載安裝ST的DFU下載 工具:http
    發(fā)表于 04-23 22:19

    轉(zhuǎn):利用 USB DFU實(shí)現(xiàn) IAP功能

    前言伴隨著固件升級(jí)需求的增加,STM32提供了靈活的升級(jí)方式。本文一步一步介紹如何利用USB DFU Class以及ST提供的DfuSe demo軟件工具實(shí)現(xiàn)IAP(In Application
    發(fā)表于 07-08 18:18

    轉(zhuǎn):利用 USB DFU實(shí)現(xiàn) IAP功能

    前言伴隨著固件升級(jí)需求的增加,STM32提供了靈活的升級(jí)方式。本文一步一步介紹如何利用USB DFU Class以及ST提供的DfuSe demo軟件工具實(shí)現(xiàn)IAP(In Application
    發(fā)表于 07-12 15:18

    STM32做固件升級(jí):DFU上位機(jī)用開(kāi)源dfu-util,測(cè)試時(shí)打不開(kāi)設(shè)備

    我使用STM32做固件升級(jí),USB接口,下位機(jī)是在USB固件中的DFU例子上改的,但是上位機(jī)希望可以不用你們ST的,我從網(wǎng)上下了開(kāi)源的dfu-util,編譯好了,測(cè)試的時(shí)候老是打不開(kāi)設(shè)備,不知道是怎么回事。事都有誰(shuí)自己做過(guò)這類(lèi)
    發(fā)表于 09-04 09:27

    如何使用串口/DFU進(jìn)行程序燒錄

    報(bào)錯(cuò),顯示定時(shí)器中斷有問(wèn)題。懷疑可能是由于Arduino IDE編譯環(huán)境的問(wèn)題,因此花了一點(diǎn)時(shí)間配置Arduino IDE,并成功實(shí)現(xiàn)使用串口/DFU進(jìn)行程序燒錄,實(shí)現(xiàn)了最簡(jiǎn)單的LED閃爍和串口顯示。下面對(duì)整個(gè)步驟進(jìn)行介紹:1.
    發(fā)表于 02-21 07:03

    如何利用USB DFU Class實(shí)現(xiàn)IAP功能

    前言:本文主要解決的問(wèn)題是實(shí)現(xiàn)IAP功能,包括升級(jí)應(yīng)用程序(APP)和升級(jí)數(shù)據(jù)包到外部Flash。方法是利用USB DFU Class以及ST提供的DfuSe demo軟件工具實(shí)現(xiàn),至于DFU
    發(fā)表于 02-22 07:27

    利用USB DFU實(shí)現(xiàn)IAP功能之演示講解

    Program)功能,但并不涉及到DFU class移植。 一、 實(shí)現(xiàn)環(huán)境 開(kāi)發(fā)板:STM32F746G-DISCO 開(kāi)發(fā)庫(kù):STM32CubeF7v1.3.0 集成開(kāi)發(fā)環(huán)境:IAR
    發(fā)表于 11-29 16:04 ?2334次閱讀
     利用USB <b class='flag-5'>DFU</b><b class='flag-5'>實(shí)現(xiàn)</b>IAP功能之演示講解

    iPhone5S DFU驅(qū)動(dòng)原理圖下載

    dfu的看看
    發(fā)表于 03-01 15:31 ?17次下載

    STM32 DFU升級(jí)APP程序移植筆記

    STM32 DFU升級(jí)APP程序移植筆記免費(fèi)下載。
    發(fā)表于 06-15 16:13 ?24次下載

    STM HAL庫(kù)USB DFU(實(shí)戰(zhàn)1)

    前言: 本文主要解決的問(wèn)題是實(shí)現(xiàn)IAP功能,包括升級(jí)應(yīng)用程序(APP)和升級(jí)數(shù)據(jù)包到外部Flash。方法是利用USB DFU Class以及ST提供的DfuSe demo軟件工具實(shí)現(xiàn),至于D
    發(fā)表于 12-28 19:46 ?3次下載
    STM HAL庫(kù)USB <b class='flag-5'>DFU</b>(實(shí)戰(zhàn)1)

    如何使用CubeMx生成一個(gè)DFU工程

    DFU用來(lái)做IAP是很方便的,可以直接通過(guò)USB來(lái)對(duì)APP進(jìn)行升級(jí),因此,掌握DFU的制作還是挺有好處,特別是使用CubeMx工具可以快速制作,本文將基于STM3240G-EVL評(píng)估板來(lái)一步一步實(shí)現(xiàn)一個(gè)
    的頭像 發(fā)表于 10-26 09:31 ?1338次閱讀

    DFU驅(qū)動(dòng)安裝

    DFU驅(qū)動(dòng)安裝
    發(fā)表于 11-09 21:03 ?6次下載
    <b class='flag-5'>DFU</b>驅(qū)動(dòng)安裝

    AN3156_STM32 引導(dǎo)加載程序中使用的 USB DFU 協(xié)議

    AN3156_STM32 引導(dǎo)加載程序中使用的 USB DFU 協(xié)議
    發(fā)表于 11-21 17:07 ?2次下載
    AN3156_STM32 引導(dǎo)加載程序中使用的 USB <b class='flag-5'>DFU</b> 協(xié)議

    使用DFU方案實(shí)現(xiàn)STM32單片機(jī)的高級(jí)開(kāi)發(fā)

      DFU全稱為Device Firmware update,是ST官方推出的一個(gè)通過(guò)USB接口進(jìn)行IAP升級(jí)的方案,同串口ISP一樣,他們都集成在了芯片內(nèi)部的Bootloader區(qū)段,可以通過(guò)配置
    的頭像 發(fā)表于 11-28 09:33 ?2585次閱讀