吴忠躺衫网络科技有限公司

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

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

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

DFU協(xié)議簡(jiǎn)介 NCS DFU升級(jí)步驟說(shuō)明

Nordic半導(dǎo)體 ? 來(lái)源:Nordic半導(dǎo)體 ? 作者:艾敏華 ? 2022-05-11 12:51 ? 次閱讀

nRF Connect SDK (NCS) / Zephyr固件升級(jí),主要包括MCUboot藍(lán)牙空中升級(jí)。

隨著nRF Connect SDK(NCS)/Zephyr固件升級(jí),近期不少朋友在后臺(tái)給Nordic君發(fā)來(lái)不少疑問(wèn): 為此,Nordic君特地找到一篇來(lái)自 Nordic 中國(guó)區(qū) FAE 經(jīng)理 Kevin 艾的原創(chuàng)博客文章對(duì)以上問(wèn)題進(jìn)行解答。

目錄

CONTENT

1.概述2. NCS中的Bootloader 2.1 nRF5 SDK Bootloader 2.2 MCUboot 2.3 B0,亦稱nRF Secure Immutable Bootloader(NSIB)3. DFU協(xié)議 3.1 概述 3.2 SMP DFU協(xié)議 3.2.1 SMP包頭和命令 3.2.2 SMP包payload和CBOR編碼 3.2.3 SMP包詳細(xì)解析示例 3.2.4 SMP DFU流程 3.3 nrf dfu協(xié)議4. NCS DFU升級(jí)步驟說(shuō)明 4.1 SMP DFU升級(jí)步驟說(shuō)明 4.2 nrf_dfu升級(jí)步驟說(shuō)明 4.3 存儲(chǔ)器分區(qū)(多image情況)5. 移植SMP DFU功能到peripheral_uart(NUS)6. 手機(jī)端DFU參考代碼 1、概述

DFU概念

DFU(Device Firmware Update),就是設(shè)備固件升級(jí)的意思。

OTA概念

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,比如4G/WiFi/藍(lán)牙/NFC/Zigbee/NB-IoT,他們都支持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)式兩種模式。 1、后臺(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ī)可以正常使用哦。 2、非后臺(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ū)(2 Slot)DFU和單區(qū)(1 Slot)DFU,雙區(qū)或者單區(qū)DFU是新固件覆蓋老固件的兩種方式。 后臺(tái)式DFU必須采用雙區(qū)模式進(jìn)行升級(jí),即老系統(tǒng)(老固件)和新系統(tǒng)(新固件)各占一塊Slot(存儲(chǔ)區(qū)),假設(shè)老固件放在Slot0中,新固件放在Slot1中,升級(jí)的時(shí)候,應(yīng)用程序先把新固件下載到Slot1中,只有當(dāng)新固件下載完成并校驗(yàn)成功后,系統(tǒng)才會(huì)跳入BootLoader程序,然后擦除老固件所在的Slot0區(qū),并把新固件拷貝到Slot0中,或者把Slot0和Slot1兩者的image進(jìn)行交換。 非后臺(tái)式DFU可以采用雙區(qū)也可以采用單區(qū)模式,與后臺(tái)式DFU相似,雙區(qū)模式下新老固件各占一塊Slot(老固件為Slot0,新固件為Slot1),升級(jí)時(shí),系統(tǒng)先跳入BootLoader程序,然后BootLoader程序把新固件下載到Slot1中,只有新固件下載完成并校驗(yàn)成功后,才會(huì)去擦除老固件所在的Slot0區(qū),并把新固件拷貝到Slot0區(qū)。 單區(qū)模式的非后臺(tái)式DFU只有一個(gè)Slot0,老固件和新固件分享這一個(gè)Slot0,升級(jí)的時(shí)候,進(jìn)入bootloader程序DFU模式后立馬擦除老固件,然后直接把新固件下載到同一個(gè)Slot中,下載完成后校驗(yàn)新固件的有效性,新固件有效升級(jí)完成,否則要求重來(lái)。 跟非后臺(tái)式DFU雙區(qū)模式相比,單區(qū)模式節(jié)省了一個(gè)Slot的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ò)程afd70038-d0e0-11ec-bce3-dac502259ad0.png ?如果你是第一次接觸nRF Connect SDK(NCS),那么建議你先看一下這篇文章:開(kāi)發(fā)你的第一個(gè)NCS/Zephyr應(yīng)用程序,以建立NCS的一些基本知識(shí),然后再往下看以下章節(jié)。 2、NCS中的Bootloader 如果你的應(yīng)用不需要DFU功能,那么Bootloader就可以不要;反之,如果你的應(yīng)用需要DFU功能,Bootloader就一定需要。 Bootloader在其中起到的作用包括:一判斷正常啟動(dòng)還是DFU升級(jí)流程,二啟動(dòng)并校驗(yàn)應(yīng)用image,三升級(jí)的時(shí)候完成新image和老image的交換或者拷貝工作。

Bootloader首先需要判斷是進(jìn)入正常應(yīng)用程序啟動(dòng)流程還是DFU流程。

要啟動(dòng)應(yīng)用image,Bootloader必須知道啟動(dòng)image的啟動(dòng)向量表在哪里。 要校驗(yàn)一個(gè)image,Bootloader必須知道這個(gè)image正確的校驗(yàn)值存在哪里。 要完成升級(jí),Bootloader必須知道新image所在位置和老image所在位置,并執(zhí)行一定的拷貝算法 啟動(dòng)向量表可以放在image的最開(kāi)始處,也可以放在其他地方,這就涉及到image的格式。 Image正確的校驗(yàn)值可以跟image合在一塊存放,也可以單獨(dú)放在一個(gè)flash page里面。如果image的校驗(yàn)值是跟image本身合在一塊存放的,這里再次涉及到image的格式。 關(guān)于新image和老image存放位置,這就涉及到存儲(chǔ)器分區(qū)問(wèn)題。Bootloader的實(shí)現(xiàn)將直接決定image的格式,以及存儲(chǔ)器的結(jié)構(gòu)劃分。 NCS支持MCUboot,B0和nRF5 Bootloader三種Bootloader,三個(gè)Bootloader選其一即可,一般推薦大家使用MCUboot。 由于很多讀者對(duì)Nordic老的SDK,即nRF5 SDK比較熟悉,我們先以這個(gè)nRF5 Bootloader為例來(lái)講解他們的Flash分區(qū)以及image格式,然后再講MCUboot和B0,看看他們又是如何分區(qū)和定義image格式的。 注意:如果你只對(duì)其中某一個(gè)具體的Bootloader感興趣,可以跳過(guò)其他章節(jié),直接閱讀相關(guān)章節(jié),比如如果你只對(duì)MCUboot感興趣,可以只看2.2節(jié)。

2.1 nRF5 SDK Bootloader介紹

nRF5 Bootloader 是指:nRF5_SDK_17.1.0_ddde560examplesdfusecure_bootloader 這里面定義的Bootloader。 如果你的DFU想使用這個(gè)Bootloader,那么nRF5 SDK的存儲(chǔ)區(qū)劃分(雙bank)是下面這樣的: aff31854-d0e0-11ec-bce3-dac502259ad0.png在nRF Connect SDK(NCS)中,如果也使用nRF5 Bootloader,此時(shí)存儲(chǔ)器的分區(qū)跟上面大同小異,我們用NCS中的語(yǔ)言重新組織如下: b013240a-d0e0-11ec-bce3-dac502259ad0.png ?當(dāng)前固件(老固件)在Bank0里面執(zhí)行,新固件接收后直接存放在Bank1,而且程序永遠(yuǎn)只執(zhí)行Bank0里面的代碼,Bank1的起始地址是動(dòng)態(tài)的,其計(jì)算公式為:Bank0起始地址 + Bank0 image大小。 由于nRF5 Bootloader跳到Bank0的時(shí)候,直接跳到一個(gè)固定地址(0x1000),因此它不需要專門去找新image的啟動(dòng)向量,換句話說(shuō),如果使用nRF5 Bootloader的話,新image就是應(yīng)用代碼編譯后的樣子,不需要添加任何的頭或者尾信息。 如果這樣的話,image的SHA256或者簽名校驗(yàn)怎么做? 在nRF5 Bootloader中,把正確的SHA256或者簽名放在settings page里面,這樣image就真得不需要任何頭或者尾信息,當(dāng)需要校驗(yàn)image的時(shí)候,從settings page中取出標(biāo)準(zhǔn)值,然后進(jìn)行校驗(yàn)。 那這些標(biāo)準(zhǔn)的SHA256或者簽名怎么從遠(yuǎn)程傳過(guò)來(lái)呢? 答案是init包,所以nRF5 Bootloader升級(jí)的時(shí)候,需要把一個(gè)zip包傳給目標(biāo)設(shè)備,如下所示: b043fb8e-d0e0-11ec-bce3-dac502259ad0.png 這個(gè)zip包除了新image本身,還包含一個(gè)dat文件,這個(gè)dat文件包含新image的大小,SHA256,簽名等信息。至于升級(jí)拷貝,nRF5 Bootloader做法也很簡(jiǎn)單,先擦掉Bank0里面的內(nèi)容,然后把Bank1里面的內(nèi)容拷貝到Bank0,然后重新從Bank0啟動(dòng),完成整個(gè)升級(jí)。 在拷貝之前,Bootloader會(huì)校驗(yàn)Bank1里面的image完整性,只有校驗(yàn)通過(guò)才會(huì)做下一步的拷貝工作,否則退出升級(jí)模式。 從上可以看出,雖然nRF5 Bootloader會(huì)校驗(yàn)image的完整性,但是如果出現(xiàn)發(fā)版錯(cuò)誤(打個(gè)比方,Win11和Win7都是微軟驗(yàn)簽,因此完整性校驗(yàn)都可以通過(guò),但是如果微軟把Win11發(fā)到一臺(tái)只能跑Win7的設(shè)備上,那么這臺(tái)設(shè)備將無(wú)法運(yùn)行),由于它沒(méi)有新image確認(rèn)操作,也不支持回滾操作,那么升級(jí)后系統(tǒng)有可能掛死在一個(gè)錯(cuò)誤的版本里面。 說(shuō)完了啟動(dòng),校驗(yàn)和升級(jí)拷貝,最后說(shuō)一下如何進(jìn)入DFU模式。在nRF5 Bootloader里面,通過(guò)判斷某些Flag(標(biāo)志位)來(lái)決定要不要進(jìn)入DFU模式,這些標(biāo)志位有一個(gè)為真,進(jìn)入DFU模式,否則正常啟動(dòng)app:
  • 特定按鍵是否按下
  • 保持寄存器GPREGRET1是否為0xB1
  • Settings page里面當(dāng)前bank是否為Bank1
  • 上次DFU過(guò)程是否還在進(jìn)行中
  • 應(yīng)用程序校驗(yàn)是否通過(guò)
可以看出,整個(gè)判斷邏輯還是比較簡(jiǎn)單,大家很容易讀懂相關(guān)的源代碼。 nRF5 Bootloader既可以運(yùn)行在nRF5 SDK中,也可以運(yùn)行在NCS中。nRF5 Bootloader既支持非后臺(tái)式DFU,也支持后臺(tái)式DFU。 我們做了一個(gè)跑在NCS中的后臺(tái)式DFU例子:https://github.com/aiminhua/ncs_samples/tree/master/nrf_dfu/ble_intFlash_nrf5_bl 跟nRF5 SDK DFU相比,這個(gè)例子有兩個(gè)要注意的地方
  1. 我們是通過(guò)把Settings page里面的當(dāng)前bank設(shè)置為Bank1來(lái)觸發(fā)DFU模式的。
  2. 由于是后臺(tái)式DFU,我們只把DFU進(jìn)度信息保存在RAM里面,沒(méi)有將其保存在Settings page這個(gè)Flash頁(yè)面中。
從這個(gè)例子大家可以體會(huì)到,分區(qū)和新image格式只跟Bootloader有關(guān),跟SDK或者DFU協(xié)議無(wú)關(guān)。 下面是nRF5 Bootloader啟動(dòng)的一個(gè)示例,供大家參考: b05b1b34-d0e0-11ec-bce3-dac502259ad0.png ?

2.2 MCUboot

MCUboot位于如下目錄:bootloader/mcuboot/boot/zephyr 在NCS中做DFU的時(shí)候,一般都推薦使用MCUboot。 MCUboot功能強(qiáng)大,兼容的芯片平臺(tái)多,而且是一個(gè)久經(jīng)考驗(yàn)的第三方開(kāi)源Bootloader。 MCUboot把存儲(chǔ)區(qū)劃分為Primary slot和Secondary slot,而且primary slot跟secondary slot兩者大小是一樣的,程序默認(rèn)在Primary slot中執(zhí)行。 有一點(diǎn)需要大家注意NCS對(duì)MCUboot進(jìn)行了定制,在NCS中,程序只能在Primary slot中執(zhí)行,Secondary slot只是用來(lái)存儲(chǔ)新image,而且Secondary slot可以放在內(nèi)部Flash,也可以放在外部Flash,這樣在NCS中,存儲(chǔ)器分區(qū)有如下兩種典型情況 b08cceae-d0e0-11ec-bce3-dac502259ad0.pngb0b7bb1e-d0e0-11ec-bce3-dac502259ad0.png ? ? ?:MCUboot放在0x000000地址。 如前所述,Bootloader有四大功能:?jiǎn)?dòng)image,校驗(yàn)image,拷貝image以及DFU模式判斷。 那么MCUboot是如何完成這4項(xiàng)功能的
  1. 啟動(dòng)image
MCUboot通過(guò)讀image的頭信息(header),得到啟動(dòng)向量,然后跳到啟動(dòng)向量,完成啟動(dòng)。 Image header信息如下:(感興趣的讀者,仔細(xì)看一下各個(gè)結(jié)構(gòu)體字段定義,并對(duì)應(yīng)image hex進(jìn)行解讀) b1123030-d0e0-11ec-bce3-dac502259ad0.png 從上可以看出,image的最開(kāi)始是image header,而不是image啟動(dòng)向量。 Image header里面有一個(gè)字段image header size,啟動(dòng)向量就位于image header size的偏移處,image header一般為0x200大小,一般來(lái)說(shuō),app的基地址是0xC000,這樣image的啟動(dòng)向量就在0xC000+0x200=0xC200,MCUboot啟動(dòng)app的時(shí)候就跳轉(zhuǎn)到0xC200這個(gè)地址。 2. 校驗(yàn)image MCUboot通過(guò)讀image的尾信息(tail或者tlv),得到image的SHA256和簽名,從而完成校驗(yàn)。 Image tlv緊跟在image后面,其內(nèi)容示例如下所示:(感興趣的讀者,仔細(xì)看一下各個(gè)結(jié)構(gòu)體字段定義,并對(duì)應(yīng)image hex進(jìn)行解讀) b1602510-d0e0-11ec-bce3-dac502259ad0.png ?上述示例解讀結(jié)果為:沒(méi)有IMAGE_TLV_PROT_INFO_MAGIC,只有普通的IMAGE_TLV_INFO_MAGIC,IMAGE_TLV_INFO_MAGIC總共有3個(gè)tag:IMAGE_TLV_SHA256 (0x10), IMAGE_TLV_KEYHASH(0x01),以及IMAGE_TLV_ECDSA256(0x22)。 nRF5 Bootloader把a(bǔ)pp image的SHA256和簽名放在settings page里,這樣每次重新編譯一次app image,還需要重新生成一個(gè)settings page,然后把兩者一起合并燒到芯片里,這樣Bootloader才能通過(guò)image完整性校驗(yàn)而跳到app。 如果只把新編譯的app image燒到芯片里,此時(shí)image完整性校驗(yàn)將失敗而導(dǎo)致程序一直死在Bootloader里,可以看出這種方案是不太方便開(kāi)發(fā)和調(diào)試的。 而MCUboot把a(bǔ)pp image的SHA256和簽名放在image后面,這樣每次重新編譯一次app image,新的sha256和簽名會(huì)自動(dòng)跟著一起更新,你只需直接下載app而無(wú)需去更改Bootloader任何部分,大大方便了開(kāi)發(fā)和調(diào)試。 3. Image拷貝 MCUboot支持多種image拷貝動(dòng)作,確切說(shuō)是image swap(交換)操作,即把secondary slot里面的image交換到Primary slot,如何swap呢? 總體上分swapoverwrite兩種。Overwrite跟上面的nRF5 Bootloader一樣,即先擦除primary slot里面的老image,然后把secondary slot里面的新image拷貝到primary slot,完成整個(gè)升級(jí)過(guò)程。 Swap就是把primary slot和secondary slot里面的image進(jìn)行交換,即primary slot里面的image搬移到secondary slot,secondary slot里面的image搬移到primary slot。 欲swap A和B,我們需引入一個(gè)媒介:C,算法是C=A;A=B;B=C,這樣就實(shí)現(xiàn)了A和B的交換。從上可知,實(shí)現(xiàn)swap的關(guān)鍵是媒介C的引入,據(jù)此MCUboot支持兩種swap算法:swap_move和swap_scratch,默認(rèn)采用swap_move。 swap_scratch的做法是:在存儲(chǔ)區(qū)中專門劃分一塊scratch區(qū)作為swap媒介,swap的時(shí)候,primary slot里面的image先放在scratch區(qū),然后把secondary slot里面的image拷貝到primary slot,最后把scratch區(qū)里面的內(nèi)容拷貝到secondary slot,從而完成一次交換操作,Scratch區(qū)應(yīng)該比primary或者secondary slot小很多,因此要完成整個(gè)image交換,需要循環(huán)執(zhí)行多次上述操作直至整個(gè)image(以兩個(gè)slot中最大的為準(zhǔn))交換完成。 這種算法有兩個(gè)弊端:一浪費(fèi)了scratch區(qū),二由于一次image交換,scratch區(qū)需要執(zhí)行多次擦寫(xiě)操作,scratch區(qū)的Flash壽命有可能會(huì)不夠,為解決上述兩個(gè)問(wèn)題,引入了第二套算法:swap_move,具體做法是:先把primary slot里面整個(gè)image向上搬移一個(gè)扇區(qū),即先擦掉image size + 1的扇區(qū),然后把image size所在的扇區(qū)內(nèi)容拷貝到image size + 1扇區(qū),然后擦掉image size扇區(qū),并把image size -1所在的扇區(qū)內(nèi)容拷貝到image size扇區(qū),以此循環(huán)往復(fù),直至把整個(gè)image向上挪動(dòng)一個(gè)扇區(qū),這樣就為下面的primary slot和secondary slot image交換做好準(zhǔn)備。 Primary slot和secondary slot image交換的時(shí)候,先擦掉primary slot第一個(gè)扇區(qū),然后把secondary slot第一個(gè)扇區(qū)的內(nèi)容拷貝到primary slot第一個(gè)扇區(qū)并擦掉secondary slot第一個(gè)扇區(qū),然后把primary slot第二個(gè)扇區(qū)內(nèi)容拷貝到secondary slot第一個(gè)扇區(qū)并擦掉primary slot第二個(gè)扇區(qū),然后把secondary slot第二個(gè)扇區(qū)內(nèi)容拷貝到primary slot第二個(gè)扇區(qū)并擦掉secondary slot第二個(gè)扇區(qū),然后把primary slot第三個(gè)扇區(qū)內(nèi)容拷貝到secondary slot第二個(gè)扇區(qū)并擦掉primary slot第三個(gè)扇區(qū),以此往復(fù),直至primary slot或者secondary slot兩者中最大的那個(gè)image size拷貝完成,整個(gè)image swap流程宣告完成。 從上面算法描述大家可以感覺(jué)出,swap操作是比較耗時(shí)的,但是它安全,支持回滾操作。 如果大家不需要這個(gè)回滾操作的話(就像nRF5 SDK那樣),那么大家可以選擇overwrite模式(打開(kāi)#define MCUBOOT_OVERWRITE_ONLY)以加快MCUboot拷貝速度。 4. 是否進(jìn)入DFU模式 nRF5 Bootloader通過(guò)判斷某些標(biāo)志位以此決定是否進(jìn)入DFU模式,與此簡(jiǎn)單判斷不同,MCUboot是通過(guò)primary slot和secondary slot的狀態(tài)組合來(lái)決定是否進(jìn)入DFU模式。 在MCUboot中,有一個(gè)變量:swap_type,它的取值將決定是否進(jìn)入DFU模式,而swap_type的值又依賴如下真值表 b1c0d734-d0e0-11ec-bce3-dac502259ad0.png 上述的magic,image_ok和copy_done三個(gè)字段位于slot最后一個(gè)扇區(qū),即slot的最高扇區(qū),他們?cè)谏葏^(qū)中的排布如下所示(magic字段在扇區(qū)的最高地址): b20cbf28-d0e0-11ec-bce3-dac502259ad0.png 從上可知,根據(jù)magic,image_ok和copy_done三個(gè)變量的不同取值情況,可以得到不同的結(jié)果,即swap_type。我們以State1 表格為例來(lái)解讀其中的結(jié)果,State1表格如下: b2459686-d0e0-11ec-bce3-dac502259ad0.png 可以看出,當(dāng)secondary slot最后一個(gè)扇區(qū)的magic字段為Good,即設(shè)置成正確的值,而且image_ok字段不等于1,即為unset狀態(tài),則不管其他變量為什么值(正常情況下,此時(shí)其他變量的值都是0xFF),此時(shí)swap_type的結(jié)果為:BOOT_SWAP_TYPE_TEST,大家以此類推,就知道State2,State3和State4表格的swap_type結(jié)果是怎么來(lái)的。這里有一點(diǎn)需要大家注意的,magic字段在Flash中只有兩種正常取值:全FF和0x96f3b83d,而image_ok和copy_done在Flash中也只有兩種正常取值:全FF和0x01,而表格中所謂的“Good”,“Any”,“Unset”,“0x01”,是對(duì)上述兩種取值的泛化,比如magic字段等于0x96f3b83d,就叫“Good”;image_ok等于0xFF,就叫“Unset”或者“Any”(當(dāng)然“Any”意味著0x55等其他非法值也可以兼容)。swap_type總共有6種結(jié)果,每種結(jié)果的意義如下所示:
  1. BOOT_SWAP_TYPE_TEST。MCUboot將進(jìn)入DFU模式,而且為test目的的DFU。跟下面的BOOT_SWAP_TYPE_ PERM模式相比,BOOT_SWAP_TYPE_TEST的DFU過(guò)程與之一模一樣,也就是說(shuō)BOOT_SWAP_TYPE_TEST就是進(jìn)行正常的真正DFU,只不過(guò)DFU完成后,MCUboot跳到新app,這個(gè)時(shí)候新app必須把secondary slot里面的image_ok字段寫(xiě)為1,即調(diào)用boot_write_img_confirmed()這個(gè)API來(lái)完成,否則再次復(fù)位進(jìn)入MCUboot的時(shí)候,MCUboot會(huì)認(rèn)為新image有問(wèn)題(沒(méi)有確認(rèn)),從而執(zhí)行回滾操作,重新把老image換到primary slot,然后繼續(xù)跑老image(此時(shí)升級(jí)應(yīng)該算失?。?/span>
  2. BOOT_SWAP_TYPE_ PERM。如前所述,BOOT_SWAP_TYPE_ PERM跟BOOT_SWAP_TYPE_TEST DFU過(guò)程一模一樣,唯一區(qū)別的是,一旦設(shè)為PERM(永久)模式,哪怕新image沒(méi)有去寫(xiě)image_ok字段,再次復(fù)位進(jìn)入MCUboot,MCUboot也不會(huì)去執(zhí)行回滾操作,而強(qiáng)制認(rèn)為升級(jí)已成功。
  3. BOOT_SWAP_TYPE_ REVERT,回滾操作。前述的回滾操作,swap_type就是BOOT_SWAP_TYPE_ REVERT。一旦檢測(cè)到BOOT_SWAP_TYPE_ REVERT,MCUboot將進(jìn)行回滾操作。
  4. BOOT_SWAP_TYPE_ NONE。正常啟動(dòng)模式,MCUboot將直接跳到app,而不是進(jìn)入DFU模式。
  5. BOOT_SWAP_TYPE_ FAIL。當(dāng)MCUboot校驗(yàn)primary slot里面的image失敗時(shí),就會(huì)報(bào)BOOT_SWAP_TYPE_ FAIL,此時(shí)程序?qū)⑺涝贛CUboot里面。
  6. BOOT_SWAP_TYPE_ PANIC。當(dāng)MCUboot啟動(dòng)過(guò)程中出現(xiàn)了致命錯(cuò)誤,就會(huì)報(bào)BOOT_SWAP_TYPE_ PANIC,此時(shí)程序?qū)⑺涝贛CUboot里面。
從上我們可以總結(jié)出,為了讓MCUboot進(jìn)入DFU模式,swap_type結(jié)果必須為BOOT_SWAP_TYPE_TEST或者BOOT_SWAP_TYPE_ PERM,而讓swap_type取值為BOOT_SWAP_TYPE_TEST或者BOOT_SWAP_TYPE_ PERM的關(guān)鍵是讓secondary slot最后一個(gè)扇區(qū)的magic字段為0x96f3b83d,這是通過(guò)調(diào)用boot_request_upgrade()來(lái)實(shí)現(xiàn)的,當(dāng)調(diào)用boot_request_upgrade(false)進(jìn)入BOOT_SWAP_TYPE_TEST模式,當(dāng)調(diào)用boot_request_upgrade(true)進(jìn)入BOOT_SWAP_TYPE_ PERM模式。 State1,State2,State3和State4四個(gè)表格是有優(yōu)先級(jí)順序的,越往前優(yōu)先級(jí)越高,也就是說(shuō),如果State1表格匹配成功就不再匹配后面的表格,此時(shí)swap_type就是BOOT_SWAP_TYPE_TEST。下面是MCUboot正常啟動(dòng)的一個(gè)示例,可以看出,因?yàn)閙agic,image_ok和copy_done三個(gè)變量的取值沒(méi)有匹配成功真值表State1,State2和State3,但匹配成功State4表格,所以swap_type的最終結(jié)果是BOOT_SWAP_TYPE_ NONE,即正常啟動(dòng)app。注:0x3就代表“Unset”(實(shí)際取值為0xFF),“Unset”可以看成“Any”一種,因此下述啟動(dòng)日志表明此時(shí)swap_type不匹配State1,State2和State3表格,而匹配State4表格。 b2646656-d0e0-11ec-bce3-dac502259ad0.png ?很多人會(huì)好奇為什么MCUboot使用這么復(fù)雜的DFU模式判斷算法?究其根本,還是因?yàn)镕lash的限制導(dǎo)致的。Flash每次只能擦一個(gè)page(擦除時(shí)間還比較長(zhǎng)),而且壽命又有限,在盡可能少擦Flash的情況下,又要實(shí)現(xiàn)上述那么多swap操作,然后有人就想出了上面的算法。 一旦你使能MCUboot(CONFIG_BOOTLOADER_MCUBOOT=y),編譯系統(tǒng)會(huì)自動(dòng)幫你生成升級(jí)需要的升級(jí)文件:app_update.bin或者app_signed.hex(兩者內(nèi)容一模一樣)。當(dāng)然如果你選擇雙核MCU,那么除了上述應(yīng)用核的升級(jí)文件,編譯系統(tǒng)還會(huì)自動(dòng)生成網(wǎng)絡(luò)核的升級(jí)文件:net_core_app_update.bin或者net_core_app_signed.hex(兩者內(nèi)容一模一樣)。 升級(jí)文件示例如下所示: b2ea9438-d0e0-11ec-bce3-dac502259ad0.png 升級(jí)的時(shí)候,把相應(yīng)的升級(jí)文件傳給設(shè)備端,設(shè)備端把接收到的升級(jí)文件放在secondary slot,待整個(gè)image接收完畢,復(fù)位進(jìn)入MCUboot,MCUboot將完成后續(xù)工作直至升級(jí)成功。

2.3B0=nRF Secure Immutable Bootloader(NSIB)

NSIB(nRF Secure Immutable Bootloader),亦稱B0,位于nrf/samples/bootloader,這個(gè)是Nordic自己開(kāi)發(fā)的一個(gè)不可升級(jí)的Bootloader。 b0把存儲(chǔ)區(qū)劃分成slot0和slot1,并且slot0大小等于slot1大小,s0_image跑在slot0,s1_image跑在slot1,B0根據(jù)s0_image和s1_image的版本號(hào)來(lái)決定跑哪一個(gè)image,如果s0_image的版本號(hào)高于或等于s1_image的版本號(hào),那么B0啟動(dòng)的時(shí)候就會(huì)跳到s0_image;反之,如果s1_image的版本號(hào)高于s0_image的版本號(hào),那么B0啟動(dòng)的時(shí)候就會(huì)跳到s1_image。 由于s0_image和s1_image都有可能被執(zhí)行,所以s0_image和s1_image必須都放置在內(nèi)部Flash,也就是說(shuō)slot0和slot1必須都在nRF設(shè)備內(nèi)部Flash中。 B0將存儲(chǔ)區(qū)劃分成如下模樣: b30d7782-d0e0-11ec-bce3-dac502259ad0.png 如前所述,Bootloader有四大功能:?jiǎn)?dòng)image,校驗(yàn)image,拷貝image以及DFU模式判斷。 那么b0是如何完成這4項(xiàng)功能的: 1. 啟動(dòng)image B0通過(guò)讀provision區(qū)域信息,得到s0_image和s1_image信息,provision屬于B0的一部分,下面為provision的定義及一個(gè)示例:(感興趣的讀者,仔細(xì)看一下結(jié)構(gòu)體各個(gè)字段定義,并對(duì)應(yīng)image hex進(jìn)行解讀) b32fcc10-d0e0-11ec-bce3-dac502259ad0.png 從上面示例可以看出,s0_address為0x9000,0x9000即為s0_image的起始地址,s1_image起始地址可以用同樣道理獲得。得到S0_image或者S1_image的起始地址后,就可以得到兩個(gè)image的fw_info,fw_info定義及示例如下所示 b35a5944-d0e0-11ec-bce3-dac502259ad0.png 通過(guò)fw_info就可以找到boot_address,從而跳轉(zhuǎn)到相應(yīng)app。 2、校驗(yàn)image B0也支持SHA256或者簽名驗(yàn)簽,SHA256或者簽名放在image的最后,稱為fw_validation_info,其定義及示例如下所示: b3a708c0-d0e0-11ec-bce3-dac502259ad0.png B0通過(guò)magic字段找到hash和signature,然后進(jìn)行校驗(yàn)。 3、拷貝image B0沒(méi)有拷貝image的操作,所謂升級(jí),就是執(zhí)行高版本image,具體來(lái)說(shuō),如果s1_image版本比s0_image版本高,則執(zhí)行s1_image;否則執(zhí)行s0_image。 4、DFU模式進(jìn)入 B0不存在DFU模式,也就不存在所謂進(jìn)入DFU模式判斷。每次復(fù)位B0都去讀s0_image和s1_image的版本,那個(gè)image版本高就執(zhí)行那個(gè)image。 基于b0的DFU,有一點(diǎn)需要特別注意,由于S0_image和S1_image兩者的偏移或者啟動(dòng)向量不一樣,因此即使S0_image和S1_image兩者功能一模一樣,他們的image內(nèi)容也不一樣,這也意味著slot0和slot1對(duì)應(yīng)的升級(jí)image是不一樣的。一般來(lái)說(shuō),手機(jī)app或者其他主機(jī)并不知道設(shè)備當(dāng)前正在運(yùn)行哪個(gè)slot里面的image,因此DFU的時(shí)候,手機(jī)app或其他主機(jī)需要先跟設(shè)備溝通,獲知設(shè)備當(dāng)前正在執(zhí)行哪個(gè)image。如果S0_image在運(yùn)行,就給它傳S1_image(signed_by_b0_s1_image.bin)并放置在slot1中;如果S1_image在運(yùn)行,就給它傳S0_image(signed_by_b0_s0_image.bin)并放置在slot0中。 升級(jí)image接收完畢,系統(tǒng)復(fù)位,B0自動(dòng)選擇高版本image執(zhí)行,至此整個(gè)升級(jí)完成。從上可知,DFU的升級(jí)文件必須同時(shí)包含signed_by_b0_s0_image.bin 和signed_by_b0_s1_image.bin,實(shí)際中我們一般使用如下zip文件 b43a3050-d0e0-11ec-bce3-dac502259ad0.png 這里我們做了一個(gè)基于b0的DFU例子:https://github.com/aiminhua/ncs_samples/tree/master/nrf_dfu/ble_intFlash_b0 大家感興趣的話,可以自己去看一下(按照里面的readme來(lái)操作)。下面是B0正常啟動(dòng)的一個(gè)示例,可以看出B0選擇了slot0里面的s0_image進(jìn)行裝載,校驗(yàn)和跳轉(zhuǎn)。 b43a3050-d0e0-11ec-bce3-dac502259ad0.png 3、DFU協(xié)議 3.1 概述 前面說(shuō)過(guò),為了實(shí)現(xiàn)固件升級(jí),需要把新image放在secondary slot(以MCUboot為例),如何把新image傳輸?shù)絪econdary slot?這就是DFU協(xié)議要做的事情,一般來(lái)說(shuō),DFU協(xié)議需要把image文件分塊一塊一塊傳給設(shè)備端,然后設(shè)備端按照要求將image塊寫(xiě)入secondary slot,并回復(fù)寫(xiě)入結(jié)果給主機(jī)。 期間有可能還需要校驗(yàn)傳輸?shù)膇mage對(duì)不對(duì),或者告知每次image塊寫(xiě)入的偏移地址。最后DFU協(xié)議還有可能涉及一些管理操作,比如image塊寫(xiě)入的準(zhǔn)備工作,讀取設(shè)備狀態(tài),復(fù)位設(shè)備等。 這里需要特別強(qiáng)調(diào)一下,DFU協(xié)議是脫離于傳輸層的,也就是說(shuō),同樣的DFU協(xié)議可以跑到不同的傳輸層,比如藍(lán)牙,WiFi,UDP,USB CDC,UART等,千萬(wàn)不要把DFU協(xié)議跟特定的傳輸層混為一談。 nRF Connect SDK包含多種DFU協(xié)議,最著名的就是SMP DFU協(xié)議,除此之外,還有其他DFU協(xié)議,比如http_update,hid_configurator,USB DFU class,PCD DFU,以及從nRF5 SDK移植過(guò)來(lái)的nrf_dfu協(xié)議。 不同的應(yīng)用場(chǎng)景有不同的DFU協(xié)議需求,大家需要根據(jù)自己的情況選擇合適的DFU協(xié)議,就像前述的Bootloader一樣,這些DFU協(xié)議選擇一個(gè)適合自己的就可以,不需要全部都要會(huì)用。下面著重講一下smp dfu和nrf_dfu兩個(gè)dfu協(xié)議。 3、2 SMP DFU協(xié)議 smp 全稱simple management protocol(簡(jiǎn)單管理協(xié)議),它是設(shè)備管理協(xié)議的一種,在NCS中,mcumgr模塊實(shí)現(xiàn)了smp協(xié)議,或者說(shuō),smp協(xié)議按照mcumgr的要求對(duì)相應(yīng)的傳輸數(shù)據(jù)進(jìn)行編碼,這樣mcumgr里面注冊(cè)的命令組(command group)可以直接對(duì)傳輸數(shù)據(jù)進(jìn)行解析。 mcumgr實(shí)現(xiàn)的功能比較多,smp DFU只是其中一種,除此之外,它還有很多其他功能,比如shell管理,日志管理等。這里我們只對(duì)DFU相關(guān)命令組進(jìn)行介紹,其他命令組就不在這里講了。 3.2.1SMP包頭和命令 mcumgr里面有兩個(gè)命令組跟DFU有關(guān): 1、img_mgmt,即image管理命令組,該命令組又具體包括3個(gè)命令集4個(gè)具體命令,詳細(xì)定義如下: b4ace26c-d0e0-11ec-bce3-dac502259ad0.png 2、os_mgmt,即OS管理命令組,該命令組又具體包括3個(gè)命令集4個(gè)具體命令,詳細(xì)定義如下:(實(shí)際上,DFU只用到了os_mgmt_reset這個(gè)命令) b4d88b60-d0e0-11ec-bce3-dac502259ad0.png smp協(xié)議把數(shù)據(jù)包(packet)分成兩部分:包頭(header)和有效載荷(payload),包頭每一個(gè)字節(jié)正好對(duì)應(yīng)如下結(jié)構(gòu)體的每一個(gè)字段,即第一個(gè)字節(jié)代表nh_op(操作類型),第二個(gè)字節(jié)代表nh_flags,第三和四個(gè)字節(jié)代表nh_len,第五和六個(gè)字節(jié)代表nh_group(命令組編號(hào)),第7個(gè)字節(jié)代表nh_seq,第8個(gè)字節(jié)代表nh_id(命令在該命令組中的編號(hào))。 b509c28e-d0e0-11ec-bce3-dac502259ad0.png ?這樣我們就可以通過(guò)SMP的包頭找到相應(yīng)的handler,比如包頭00 00 00 02 00 01 00 00,即對(duì)應(yīng)命令組1的0號(hào)命令集的00操作(讀命令),最終找到img_mgmt_state_read這個(gè)handler。我們會(huì)在3.2.3節(jié)對(duì)此示例的解析做詳細(xì)說(shuō)明。 3.2.2SMP包payload和CBOR編碼 SMP payload采用CBOR編碼,CBOR將一連串二進(jìn)制數(shù)據(jù)分成多個(gè)data item,如下所示: b53888d0-d0e0-11ec-bce3-dac502259ad0.png ?從上可知,每個(gè)data item第一個(gè)字節(jié)包含2部分:數(shù)據(jù)類型和數(shù)據(jù)長(zhǎng)度,數(shù)據(jù)類型定義如下:
  • 0,正數(shù)
  • 1,負(fù)數(shù)
  • 2,字節(jié)串(byte string)
  • 3,UTF-8字符串(text string)
  • 4,數(shù)組
  • 5,map(又稱字典)
  • 6,tag(這個(gè)用得少)
  • 7,浮點(diǎn)數(shù)或者特殊類型,其中特殊類型將short count 20–23定義為 false, true, null和undefined
關(guān)于數(shù)據(jù)長(zhǎng)度(count)字段,這個(gè)有點(diǎn)特殊,它的定義如下:
  • 如果長(zhǎng)度為0–23,則直接用short count的5 bits來(lái)表示,從第2個(gè)字節(jié)開(kāi)始表示data payload

  • 如果short count為24(0x18),則表示第2個(gè)字節(jié)代表長(zhǎng)度,從第3個(gè)字節(jié)開(kāi)始表示data payload

  • 如果short count為25(0x19),則表示第2和第3個(gè)字節(jié)合起來(lái)表示長(zhǎng)度,從第4個(gè)字節(jié)開(kāi)始表示data payload

  • 如果short count為26(0x1A),則表示第2,第3,第4和第5個(gè)字節(jié)合起來(lái)表示長(zhǎng)度,從第6個(gè)字節(jié)開(kāi)始表示data payload

  • 如果short count為27(0x1B),則表示第2至第9個(gè)字節(jié)合起來(lái)表示長(zhǎng)度,從第10個(gè)字節(jié)開(kāi)始表示data payload

  • 如果short count為31(0x1F),則表示長(zhǎng)度為未定義,從第2個(gè)字節(jié)開(kāi)始表示data payload,直到遇到停止符:0xFF

count字段后面就緊跟著data payload了,count有多大,data payload就有多長(zhǎng),比如count為0x0032,則表示后面0x32個(gè)字節(jié)都屬于data payload,至此一個(gè)data item結(jié)束,同時(shí)意味著另一個(gè)data item的開(kāi)始,以此往復(fù),周而復(fù)始。需要大家注意的是,CBOR中的data item可以嵌套另一個(gè)data item,也就是說(shuō),data item之間是可以有結(jié)構(gòu)的。 比如數(shù)據(jù)payload:64 64 61 74 61,0x64(0b011 00100)表示此data item的數(shù)據(jù)類型為utf-8字符串,長(zhǎng)度為4字節(jié),即后面緊跟的64 61 74 61,這4個(gè)ASCII碼對(duì)應(yīng)的字符就是:”data”,這樣我們就成功解析出這個(gè)payload了。 3.2.3SMP包詳細(xì)解析示例 smp協(xié)議的核心就是通過(guò)包頭找到要處理該數(shù)據(jù)包的handler(命令),并把payload打包成一個(gè)特定參數(shù)傳給該handler,然后執(zhí)行該handler 我們現(xiàn)在結(jié)合上面的定義,再看一個(gè)實(shí)際的smp數(shù)據(jù)包(包含包頭和payload),看看我們最終解析的結(jié)果是什么。
  • 00 00 00 02 00 01 00 00 bf ff
可以看出,nh_op為00,而nh_op定義如下,所以此時(shí)為read操作。 b557d9a6-d0e0-11ec-bce3-dac502259ad0.png nh_group的值為0x0001,目前mcumgr支持的group ID見(jiàn)下圖,所以該數(shù)據(jù)包將觸發(fā)img_mgmt命令組。 b5744d0c-d0e0-11ec-bce3-dac502259ad0.png nh_id為00,由于nh_group指向 image management group,而img_mgmt命令組定義了如下命令,可以看出00為IMG_MGMT_ID_STATE。 b5e7bd82-d0e0-11ec-bce3-dac502259ad0.png 再次結(jié)合下面這個(gè)命令或者h(yuǎn)andler定義列表: b62d60a8-d0e0-11ec-bce3-dac502259ad0.png 我們現(xiàn)在可以解讀出最終的結(jié)果:00 00 00 02 00 01 00 00 bf ff這個(gè)數(shù)據(jù)包將觸發(fā)img_mgmt組里面的IMG_MGMT_ID_STATE集里面的mh_read函數(shù),即img_mgmt_state_read,這個(gè)函數(shù)的定義是:
  • int img_mgmt_state_read(struct mgmt_ctxt *ctxt)
而數(shù)據(jù)包的payload,即bf ff,將作為實(shí)參賦給上面的ctxt。我們用CBOR編碼來(lái)解析一下bf ff,看看它表示什么意思? bf,即0b101 11111,可以看出,data type為5(表示map類型),count為0x1F(表示未定義長(zhǎng)度,通過(guò)0xFF劃分data item);ff,根據(jù)前面的描述,此處應(yīng)該是分隔符,至此一個(gè)data item結(jié)束??梢钥闯?,bf ff本身并沒(méi)有實(shí)際的意義,實(shí)際上img_mgmt_state_read也沒(méi)有使用輸入?yún)?shù):ctxt,兩者是可以對(duì)起來(lái)的。 3.2.4SMP DFU流程 講完smp DFU工作原理,我們?cè)僦vsmp DFU整個(gè)工作流程,具體來(lái)說(shuō),包括如下幾步:
  1. 簽名升級(jí)image。注:app_update.bin已經(jīng)是簽過(guò)名的image了

  2. 上傳image,即把a(bǔ)pp_update.bin傳送到目標(biāo)設(shè)備

  3. 列出image以獲得image的hash值

  4. 測(cè)試image,即寫(xiě)magic字段,以讓MCUboot進(jìn)入DFU模式

  5. 復(fù)位設(shè)備,以重新進(jìn)入MCUboot,從而MCUboot進(jìn)入DFU模式,并執(zhí)行相應(yīng)的swap操作,并完成兩個(gè)slot image之間的交換或者拷貝動(dòng)作

  6. Confirm image,即新image啟動(dòng)成功后,對(duì)其image_ok字段進(jìn)行置1操作

上述有幾個(gè)步驟,可以通過(guò)發(fā)命令遠(yuǎn)程去完成,也可以通過(guò)調(diào)用本地API自己去完成,兩種選擇都可以。比如confirm image這一步,你可以等待新image啟動(dòng)成功,然后重連主機(jī),主機(jī)再發(fā)“confirm image”命令,這個(gè)時(shí)候升級(jí)才算真正完成;也可以在新image啟動(dòng)成功后,在不連主機(jī)的情況下,通過(guò)調(diào)用前述API:boot_write_img_confirmed()來(lái)完成這個(gè)確認(rèn)過(guò)程。 不管采用那種方法,本質(zhì)上都是調(diào)用boot_write_img_confirmed()來(lái)實(shí)現(xiàn),不同的是觸發(fā)方式或者時(shí)機(jī),發(fā)命令的方式由主機(jī)遠(yuǎn)程觸發(fā)(SMP DFU就是選擇這種主機(jī)遠(yuǎn)程發(fā)命令方式),而本地API方式則是設(shè)備自己選擇時(shí)機(jī)來(lái)觸發(fā)(nrf dfu就是選擇這種本地API調(diào)用方式)。 DFU命令說(shuō)明當(dāng)采用UART或者USB傳輸層的時(shí)候,上述DFU流程對(duì)應(yīng)的命令如下:
  1. mcumgr conn add myCOM type="serial" connstring="dev=COM13,baud=115200,mtu=256" (Note: change the COM if needed)

  2. mcumgr -c myCOM image upload app_update.bin

  3. mcumgr -c myCOM image list

  4. mcumgr -c myCOM image test

  5. mcumgr -c myCOM reset

  6. mcumgr -c myCOM image confirm

上面每一個(gè)命令就是一個(gè)request(請(qǐng)求),每一個(gè)request就有一個(gè)response(響應(yīng)),通過(guò)這種request/response方式,SMP DFU可以安全可靠地完成DFU數(shù)據(jù)傳輸。

藍(lán)牙DFU流程解讀

當(dāng)采用BLE作為傳輸層的時(shí)候,上面命令都被手機(jī)app打包成二進(jìn)制數(shù)據(jù)包直接下發(fā)給設(shè)備端,但解析出來(lái)之后,你會(huì)發(fā)現(xiàn)藍(lán)牙DFU流程跟上面說(shuō)明的流程基本上一模一樣。比如前面的00 00 00 02 00 01 00 00 bf ff,就是手機(jī)發(fā)給設(shè)備的第一條DFU命令或者說(shuō)請(qǐng)求(request)。 我們?cè)倥e一個(gè)例子:上傳image命令(request),它的第一個(gè)數(shù)據(jù)包示例如下所示: b698dc02-d0e0-11ec-bce3-dac502259ad0.png ?從包頭02 00 00 eb 00 01 00 01可以看出,這個(gè)數(shù)據(jù)包將觸發(fā)handler: img_mgmt_upload,我們?cè)賮?lái)看數(shù)據(jù)包payload的前面8個(gè)字節(jié):bf 64 64 61 74 61 58 cc,bf表示后面是map數(shù)據(jù),即key/value數(shù)據(jù)對(duì),0x64,表示后面是text string數(shù)據(jù),長(zhǎng)度為4,從而得到64這個(gè)data item對(duì)應(yīng)的payload為:64 61 74 61,即key=”data”; 從0x58開(kāi)始,就表示value這個(gè)data item了,0x58表示這個(gè)item為字節(jié)串并且長(zhǎng)度為下一個(gè)字節(jié):0xcc,也就是說(shuō)”data”這個(gè)key對(duì)應(yīng)的value包含了0xcc個(gè)數(shù)據(jù)的字節(jié)流,這樣第一個(gè)key/value對(duì)解析完畢。然后再解析63 6c 65 6e 1a 00 02 05 a8,0x63,表示此item為text string數(shù)據(jù),長(zhǎng)度為3,從而得到payload為6c 65 6e,即key = ”len”; 0x1a表示此item為正數(shù),count為后面4個(gè)字節(jié),也就是說(shuō)”len”這個(gè)key對(duì)應(yīng)的value為0x000205a8,至此第二個(gè)key/value對(duì)解析完畢。以此類推,我們后面又可以解析出”sha”和”off”兩個(gè)key以及他們各自的value,最后碰到停止符:0xFF,整個(gè)map item結(jié)束。 前面說(shuō)過(guò),整個(gè)數(shù)據(jù)包的payload會(huì)通過(guò)參數(shù)傳給img_mgmt_upload作為實(shí)參,img_mgmt_upload的函數(shù)聲明為
  • img_mgmt_upload(struct mgmt_ctxt *ctxt)
而struct mgmt_ctxt定義如下: struct mgmt_ctxt { struct CborEncoder encoder; struct CborParser parser; struct CborValue it;}; 實(shí)際上,SMP數(shù)據(jù)包payload所在的buffer地址將賦給成員變量it后面的指針(這個(gè)指針本身不屬于結(jié)構(gòu)體的一部分,但它緊挨著結(jié)構(gòu)體最后一個(gè)元素),這樣我們通過(guò)ctxt就可以間接操作SMP數(shù)據(jù)包的payload,請(qǐng)看如下代碼: b6d820f6-d0e0-11ec-bce3-dac502259ad0.png b724dac2-d0e0-11ec-bce3-dac502259ad0.png 這樣我們就把一個(gè)image chunk拷貝到變量:req.img_data,再通過(guò)如下代碼調(diào)用Flash訪問(wèn)API。 b7563df6-d0e0-11ec-bce3-dac502259ad0.png 如前所述,每一個(gè)request命令都會(huì)有一個(gè)response,比如上面request命令的response為
  • 03 00 00 0d 00 01 00 01 bf 62 72 63 00 63 6f 66 66 19 09 40 ff
這樣,一個(gè)image chunk數(shù)據(jù)就成功寫(xiě)入到Flash中,不斷循環(huán)這個(gè)request和response過(guò)程,直至整個(gè)image傳送完畢,最后主機(jī)還會(huì)發(fā)送如下兩條命令以正式結(jié)束整個(gè)DFU傳輸過(guò)程:
  • 02 00 00 32 00 01 00 00 BF 67 63 6F 6E 66 69 72 6D F4 64 68 61 73 68 58 20 47 7C C8 4B 52 27 23 03 DA 27 41 F1 1D 38 46 0F 11 AE DB 5E 75 A2 D3 25 0C 6E DE EF 15 84 24 49 FF,大家可以仿照上面的做法來(lái)解析一下這個(gè)數(shù)據(jù)包,它解析的結(jié)果是:調(diào)用img_mgmt_state_write,并寫(xiě)入magic字段,同時(shí)將swap類型設(shè)為BOOT_SWAP_TYPE_TEST
  • 02 00 00 02 00 00 00 05 BF FF,這個(gè)包解析的結(jié)果是:調(diào)用os_mgmt_reset,對(duì)設(shè)備進(jìn)行復(fù)位

3.3 nrf dfu協(xié)議

nrf dfu協(xié)議就是nRF5 SDK使用的DFU協(xié)議,相信很多讀者都很熟悉它。 nrf dfu協(xié)議定義了兩個(gè)角色controller和target,controller發(fā)request,target回response,一來(lái)一往,完成DFU傳輸過(guò)程。 nrf dfu定義了如下request命令以及他們的response。 b78298e2-d0e0-11ec-bce3-dac502259ad0.png Request命令的格式是:Opcode + parameters Response的格式是:60 + Opcode + parameters 比如編碼:01 02 00 10 00 00,通過(guò)上面解析可以知道它是一個(gè)創(chuàng)建數(shù)據(jù)對(duì)象命令NRF_DFU_OP_OBJECT_CREATE,而這條命令的響應(yīng)是:60 01 01,可以看出也符合上面的定義。 nrf dfu用到了對(duì)象概念,什么叫對(duì)象(object)? 對(duì)象分兩種:command object和data object,其中init包是command對(duì)象,而image chunk(image塊)是data對(duì)象。 我們可以進(jìn)一步提煉一下,nrf dfu協(xié)議主要涉及的命令是如下幾個(gè):
  • 選擇對(duì)象(NRF_DFU_OP_OBJECT_SELECT),用來(lái)選擇init包或者image包
  • 創(chuàng)建對(duì)象(NRF_DFU_OP_OBJECT_CREATE),用來(lái)創(chuàng)建init包或者一個(gè)image 4kB塊
  • 寫(xiě)對(duì)象(NRF_DFU_OP_OBJECT_WRITE),即傳輸實(shí)際數(shù)據(jù)。由于藍(lán)牙將命令和數(shù)據(jù)分成兩個(gè)不同characteristic,寫(xiě)對(duì)象其實(shí)就是寫(xiě)數(shù)據(jù),是一個(gè)專門的characteristic:packet characteristic,因此發(fā)送寫(xiě)對(duì)象命令時(shí),就沒(méi)有必要加上Opcode,而是直接把數(shù)據(jù)寫(xiě)到packet characteristic上。由于串口只有一個(gè)RX線,因此通過(guò)串口DFU的時(shí)候,寫(xiě)對(duì)象命令還是有Opcode的。
  • 獲取對(duì)象的CRC(NRF_DFU_OP_CRC_GET),用來(lái)獲取前面init包或者4kB image塊的CRC值
  • 執(zhí)行對(duì)象(NRF_DFU_OP_OBJECT_EXECUTE),即把數(shù)據(jù)真正寫(xiě)入Flash中
我們可以把nrf dfu流程大致歸納為如下幾步:
  1. 選擇init對(duì)象
  2. 創(chuàng)建init對(duì)象
  3. 執(zhí)行init對(duì)象
  4. 選擇image data對(duì)象
  5. 建第一個(gè)4kB data對(duì)象
  6. 寫(xiě)對(duì)象,即設(shè)備(target)循環(huán)接收主機(jī)發(fā)過(guò)來(lái)的image chunk,直至4kB
  7. 計(jì)算4kB image塊的CRC,并返回給主機(jī)(controller)以供其校驗(yàn)
  8. 執(zhí)行4kB image塊對(duì)象,即將其寫(xiě)入到Flash中
  9. 循環(huán)往復(fù),直至整個(gè)image寫(xiě)入完畢
  10. 寫(xiě)DFU標(biāo)志,并復(fù)位設(shè)備
  11. 復(fù)位后進(jìn)入Bootloader DFU模式,Bootloader完成后續(xù)的拷貝工作,至此整個(gè)DFU過(guò)程宣告結(jié)束
這里就不再對(duì)nrf dfu協(xié)議進(jìn)行詳細(xì)解讀了,有興趣的讀者可以自己查閱Nordic infocenter的相關(guān)章節(jié)介紹,具體鏈接為:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsdk_nrf5_v17.1.0%2Flib_dfu_transport.html 4、NCS DFU升級(jí)步驟說(shuō)明 4.1 SMP DFU升級(jí)步驟說(shuō)明 在nRF connect SDK中,有一個(gè)現(xiàn)成的smp DFU例子,它所在的目錄為:zephyrsamplessubsysmgmtmcumgrsmp_svr,這個(gè)例子支持多種傳輸層:藍(lán)牙,串口,USB CDC,UDP,Shell,F(xiàn)S等,如果使用藍(lán)牙作為傳輸層,其升級(jí)操作步驟如下所示:
  1. 進(jìn)入項(xiàng)目目錄:cd zephyrsamplessubsysmgmtmcumgrsmp_svr
  2. 編譯:west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p -- -DOVERLAY_CONFIG="overlay-bt.conf"(根據(jù)你自己手上的板子情況,把nrf52840dk_nrf52840換成其他DK,比如nrf5340dk_nrf5340_cpuapp)
  3. 燒寫(xiě):west flash -d build_nrf52840dk_nrf52840,此時(shí)設(shè)備將廣播“Zephyr”b7b2fc6c-d0e0-11ec-bce3-dac502259ad0.png
  4. 修改原始工程,比如廣播名字(CONFIG_BT_DEVICE_NAME="NEW_DFU"放在overlay-bt.conf中)再重新編譯,然后拷貝以下字段到手機(jī)版nRF Connect“build_nrf52840dk_nrf52840/zephyr/app_update.bin"b7f95090-d0e0-11ec-bce3-dac502259ad0.png
  5. 用手機(jī)nRF Connect連接設(shè)備,成功后,點(diǎn)擊右上角的“DFU”圖標(biāo),選擇前面的“app_update.bin”文件,然后選擇“Test and Confirm”,DFU開(kāi)始b81ff768-d0e0-11ec-bce3-dac502259ad0.pngb83903c0-d0e0-11ec-bce3-dac502259ad0.pngb89da3ac-d0e0-11ec-bce3-dac502259ad0.png
  6. 升級(jí)文件傳輸完畢,系統(tǒng)將重啟b8c0e754-d0e0-11ec-bce3-dac502259ad0.png
  7. MCUboot完成swap操作,并跳到新app,廣播將變成“NEW_DFU”b8d7f7be-d0e0-11ec-bce3-dac502259ad0.pngb917eb26-d0e0-11ec-bce3-dac502259ad0.png
  8. 手機(jī)nRF Connect連接新app,并發(fā)送confirm命令
  9. 至此整個(gè)升級(jí)結(jié)束
除了上述的smp_svr例子,我們還做了其他smp例子這些例子都放在GitHub這里https://github.com/aiminhua/ncs_samples/tree/master/smp_dfu 請(qǐng)大家仔細(xì)閱讀例子里面的readme,并按照readme去操作。 4.2 nrf_dfu升級(jí)步驟說(shuō)明 <詳解藍(lán)牙空中升級(jí)(BLE OTA)原理與步驟>這篇文章(文章鏈接:https://www.cnblogs.com/iini/p/9314246.html)詳細(xì)闡述了nrf dfu升級(jí)步驟說(shuō)明,雖然文章是以nRF5 SDK為例來(lái)敘述的,但其步驟也適用NCS nrf dfu過(guò)程。 我們在NCS中做了很多nrf dfu例子,他們都放在這里:https://github.com/aiminhua/ncs_samples/tree/master/nrf_dfu 我們以nrf_dfu/ble_intFlash為例來(lái)簡(jiǎn)要闡述nrf dfu升級(jí)步驟,以幫助大家理解整個(gè)DFU過(guò)程:
  1. 準(zhǔn)備。a. 安裝PC版nrfutil。nrfutil安裝有兩種方式,一種是直接下載exe文件,一種是以Python的方式進(jìn)行安裝。nrfutil.exe直接下載鏈接為:https://github.com/NordicSemiconductor/pc-nrfutil/releases,記得把nrfutil.exe所在目錄放在Windows環(huán)境變量中。Python方式安裝nrfutil步驟如下所示: i. 安裝Python,下載地址//www.python.org/downloads/,安裝成功后請(qǐng)確保Windows環(huán)境變量包含Python目錄 ii. 通過(guò)pip安裝最新版的nrfutil,即打開(kāi)Windows命令行工具CMD,輸入如下命令: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下載b. 進(jìn)入nrf_dfu/ble_intFlash/sdk_change目錄,選擇你的SDK版本。比如ncs_v1.8.0,把nrf_dfu/ble_intFlash/sdk_change/ncs_v1.8.x下面內(nèi)容直接覆蓋nrf倉(cāng)庫(kù)目錄c. 建議大家對(duì)照例子里面的readme看一下還有沒(méi)有其他準(zhǔn)備工作
  2. 進(jìn)入項(xiàng)目目錄:cd nrf_dfu/ble_intFlash
  3. 編譯:west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p(根據(jù)你自己手上的板子情況,把nrf52840dk_nrf52840換成其他DK,比如nrf5340dk_nrf5340_cpuapp)
  4. 燒寫(xiě):west flash -d build_nrf52840dk_nrf52840此時(shí)設(shè)備將廣播“Nordic_DFU”b94b022c-d0e0-11ec-bce3-dac502259ad0.png
  5. 修改原始工程:比如廣播名字(CONFIG_BT_DEVICE_NAME="NEW_DFU"),再重新編譯,然后拷貝“build_nrf52840dk_nrf52840/zephyr/ app_signed.hex”到update目錄b9910312-d0e0-11ec-bce3-dac502259ad0.png
  6. 雙擊update目錄中的zip_generate.bat,將生成ble_intFlash.zip,將ble_intFlash.zip拷貝到手機(jī)nRF Connect中b9f2a478-d0e0-11ec-bce3-dac502259ad0.png
  7. 用手機(jī)nRF Connect連接設(shè)備,成功后,點(diǎn)擊右上角的“DFU”圖標(biāo),選擇前面的“ble_intFlash.zip”文件ba200490-d0e0-11ec-bce3-dac502259ad0.pngba3ab466-d0e0-11ec-bce3-dac502259ad0.pngba9a187a-d0e0-11ec-bce3-dac502259ad0.png
  8. 升級(jí)文件傳輸完畢,系統(tǒng)將重啟bad02dca-d0e0-11ec-bce3-dac502259ad0.png
  9. MCUboot完成swap操作,并跳到新app,新app自動(dòng)完成image confirm操作bb12fca4-d0e0-11ec-bce3-dac502259ad0.png
  10. 此時(shí)廣播已經(jīng)變成“NEW_DFU”,至此整個(gè)升級(jí)結(jié)束bb5a0932-d0e0-11ec-bce3-dac502259ad0.png
https://github.com/aiminhua/ncs_samples/tree/master/nrf_dfu這個(gè)目錄下面還有很多其他nrf dfu例子,建議大家可以好好看一下,按照里面的readme文件實(shí)際操作一下,相信對(duì)MCUboot和nrf dfu理解就會(huì)更深入了。 4.3 存儲(chǔ)器分區(qū)(多image情況) 不管是smp dfu還是nrf dfu,都存在secondary slot在內(nèi)部flash還是在外部flash情況,即ble_extFlash和ble_intFlash這兩個(gè)例子,兩個(gè)例子功能基本上一模一樣,唯一區(qū)別就是secondary slot所在位置,ble_intFlash這個(gè)例子secondary slot在內(nèi)部flash,ble_extFlash這個(gè)例子secondary slot在外部flash,這兩個(gè)例子的main.c文件一模一樣,唯一不同的是conf文件,以及分區(qū)文件partitions.yml。 conf文件大家比較容易理解,但是分區(qū)文件大家經(jīng)常困惑,這里再給大家介紹一下,具體可以參考:開(kāi)發(fā)你的第一個(gè)NCS(Zephyr)應(yīng)用程序 鏈接:https://www.cnblogs.com/iini/p/14174427.html 所謂分區(qū)(Partition),就是對(duì)Flash(包括內(nèi)部Flash和外部flash)或者RAM物理區(qū)域進(jìn)行一個(gè)邏輯劃分,人為劃定哪塊區(qū)域干什么工作,比如把MCUboot這個(gè)image放在0x0000到0xC000這塊區(qū)域,這種分區(qū)是人為的,所以你可以隨意調(diào)整,比如你把MCUboot放在0x0000到0x10000,當(dāng)然也是可以的。 我們對(duì)Flash或者RAM進(jìn)行分區(qū),目的就是為了把空間利用好,給各個(gè)分區(qū)一個(gè)ID以便后續(xù)引用,如果代碼里不引用這個(gè)分區(qū),那么此分區(qū)只是一個(gè)占位符而已,比如app和mcuboot這兩個(gè)分區(qū)。 我們先看一下smp_dfu/ble_intFlash這個(gè)例子生成的partitions.yml bb834e64-d0e0-11ec-bce3-dac502259ad0.png 從上面可以看出,這個(gè)partitions.yml定義了很多分區(qū),比如app,mcuboot,mcuboot_pad,mcuboot_primary等(冒號(hào)前面的就是分區(qū)名),而且每一個(gè)分區(qū)規(guī)定了它的起始地址,結(jié)束地址,大小,相對(duì)位置以及放在什么物理存儲(chǔ)器上,比如app這個(gè)分區(qū) bba18e7e-d0e0-11ec-bce3-dac502259ad0.png 關(guān)于分區(qū)名,只有“app”這個(gè)名字是必須有,而且是固定的,代表著主應(yīng)用程序image;其他分區(qū)名,比如mcuboot,settings_storage,external_flash等,都是隨意定義的,可以修改。 比如0x0~0xc000這塊內(nèi)部Flash區(qū),上面取名叫mcuboot,你也可以改成“my_boot”之類的名字,這個(gè)也沒(méi)關(guān)系的,取名字主要考慮兩點(diǎn):一是能醒目標(biāo)識(shí)這塊區(qū)域的功能,二是跟代碼里面的引用對(duì)起來(lái),比如如下分區(qū)定義,經(jīng)常有人困惑: bbee52e0-d0e0-11ec-bce3-dac502259ad0.png 第一個(gè)“external_flash”是分區(qū)名,第二個(gè)“external_flash”是物理存儲(chǔ)器名。 作為分區(qū)名的“external_flash”,其實(shí)我們可以改成其他名字,以消除某些困惑,之所以使用這個(gè)名字,是因?yàn)槔系膌ittlefs例子里面對(duì)外部文件系統(tǒng)所在區(qū)域就稱為“external_flash”,代碼如下所示: bc3157fc-d0e0-11ec-bce3-dac502259ad0.png 實(shí)際上最新的littlefs例子已經(jīng)把這塊區(qū)域重新命名為:littlefs_storage或者storage,所以大家可以把這塊分區(qū)名改為littlefs_storage,如下: bc4d4f7a-d0e0-11ec-bce3-dac502259ad0.png partitions.yml 里面使用的 region 其實(shí)是在這個(gè)文件:nrfcmakepartition_manager.cmake定義的。 大家可以通過(guò) build 目錄下的 regions.yml 文件得知目前定義了幾個(gè)物理存儲(chǔ)器: bc725dd8-d0e0-11ec-bce3-dac502259ad0.png 至于partitions.yml里面使用的placement/span等,這個(gè)是用來(lái)指定各個(gè)分區(qū)的相對(duì)位置的,很多人會(huì)疑問(wèn),既然指定了分區(qū)的起始地址和結(jié)束地址,那還有必要去指定各個(gè)分區(qū)的相對(duì)位置嗎? 這種情況下的確沒(méi)必要再指定相對(duì)位置了,其實(shí)這里弄反了一件事情:partitions.yml里面的地址是placement相對(duì)位置定下來(lái)之后的結(jié)果。使用placement相對(duì)位置,為編譯系統(tǒng)動(dòng)態(tài)確定各個(gè)分區(qū)的位置提供了便利。如果是我們自己來(lái)劃分存儲(chǔ)器的分區(qū),我們就可以直接使用絕對(duì)地址的方式靜態(tài)指定各個(gè)分區(qū)的位置(當(dāng)然使用placement也是可以的)。 如何人為靜態(tài)指定? 答案就是把剛才動(dòng)態(tài)生成的partitions.yml文件拷貝到項(xiàng)目根目錄下,然后改名為:pm_static.yml,然后再按照自己的需求去修改,比如smp_dfu/ble_extFlash這個(gè)例子,如果由系統(tǒng)動(dòng)態(tài)生成partitions.yml文件,此時(shí)mcuboot_secondary分區(qū)所在地址為0x0~0xf0000,而文件系統(tǒng)external_flash或者littlefs_storage分區(qū)所在地址為0xf0000~0x800000,實(shí)際上很多客戶喜歡把文件系統(tǒng)放在外部Flash 0x00地址,而把secondary slot放在外部flash最后,據(jù)此可以做如下修改: bcebb48a-d0e0-11ec-bce3-dac502259ad0.png 這個(gè)pm_static.yml文件沒(méi)有定義的分區(qū),還是由系統(tǒng)動(dòng)態(tài)分配。有時(shí)為了后續(xù)升級(jí)方便,我們會(huì)在pm_static.yml文件里面把所有的分區(qū)都按照自己的規(guī)劃重新定義一遍,這樣就不擔(dān)心某個(gè)image突然變大而導(dǎo)致新的partitions.yml跟老的文件不兼容,從而無(wú)法升級(jí)。在定義pm_static.yml文件時(shí),有如下規(guī)則必須遵守:
  • mcuboot_primary大小必須等于mcuboot_secondary,而且CONFIG_BOOT_MAX_IMG_SECTORS最好也等于他們大小/4096
  • 如果使用了一個(gè)region(flash_primary這個(gè)region除外),那么這個(gè)region每一塊區(qū)域都要屬于一個(gè)分區(qū)名字,不能出現(xiàn)某塊區(qū)域沒(méi)有分區(qū)名字情況。比如上面重新定義了external_flash region,根據(jù)regions.yml文件定義,external_flash總共有8Mbytes,那么這8Mbytes都必須有一個(gè)分區(qū)名字,而我們定義的littlefs_storage和mcuboot_secondary兩個(gè)分區(qū)的確包含了全部8MB區(qū)域。如果我們定義littlefs_storage所在區(qū)域?yàn)?x0~0x700000,而mcuboot_secondary所在區(qū)域?yàn)?x710000~0x800000,那么系統(tǒng)就會(huì)報(bào)錯(cuò),因?yàn)檫@里還有一個(gè)空隙(gap):0x700000~0x710000是沒(méi)有取分區(qū)名字的。解決這個(gè)問(wèn)題有兩個(gè)辦法:一個(gè)就是上面的方法把0x700000~0x710000劃到littlefs_storage分區(qū),一個(gè)就是給這塊區(qū)域?qū)iT取一個(gè)名字,比如:my_unused_area(見(jiàn)下面示意),也是可以解決問(wèn)題的。bd3230ae-d0e0-11ec-bce3-dac502259ad0.png對(duì)于flash_primary這個(gè)region,由于系統(tǒng)默認(rèn)認(rèn)為必須要有一個(gè)“app”分區(qū),所以它可以存在而且只能存在一個(gè)空隙(gap),這樣系統(tǒng)默認(rèn)這個(gè)gap就是“app”分區(qū)。當(dāng)然你也可以把flash_primary所有區(qū)域都分好區(qū),包括“app”分區(qū)。
  • regions.yml文件里面各個(gè)存儲(chǔ)器的物理大小必須符合實(shí)際,這個(gè)通過(guò)修改dts文件來(lái)保證的。這里面最容易出錯(cuò)的就是external_flash,external_flash的大小在regions.yml文件里面是以字節(jié)為單位(在kconfig文件里面也是以字節(jié)為單位的),但是external_flash對(duì)應(yīng)的設(shè)備樹(shù),比如MX25R64,它在dts文件里面是以bit為單位的,所以當(dāng)大家使用其他外部Flash的時(shí)候,請(qǐng)仔細(xì)檢查這些size對(duì)不對(duì)
  • settings_storage,即settings使用的分區(qū),大家可以將分區(qū)名改成:storage,這是其一,其二settings系統(tǒng)最終使用的最大flash區(qū)域大小是由CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE決定,而不是settings_storage分區(qū)本身大小決定,所以建議大家把CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE的值設(shè)為settings_storage分區(qū)大小。
  • 至于RAM分區(qū),道理也是一樣的。這里需要注意的是,RAM各個(gè)分區(qū)的大小大家可以直接到dts文件里面去調(diào)整,而無(wú)需在pm_static.yml文件里面調(diào)整。當(dāng)然,大家在pm_static.yml里面調(diào)整也是可以的,殊途同歸,達(dá)到目的就好了。對(duì)于nRF52系列,只有一個(gè)sram_primary分區(qū),這個(gè)沒(méi)什么好講的;對(duì)于nRF53系列,除了sram_primary這個(gè)分區(qū),它還有rpmsg_nrf53_sram分區(qū)以及pcd_sram分區(qū),其中rpmsg_nrf53_sram是用來(lái)藍(lán)牙協(xié)議棧host和controller之間進(jìn)行雙核通訊的,而pcd_sram是用來(lái)升級(jí)網(wǎng)絡(luò)核image的。
5、移植SMP DFU功能到peripheral_uart 現(xiàn)在我們從零開(kāi)始,一步一步教大家如何把smp服務(wù)添加到peripheral_uart例子中。 peripheral_uart例子所在目錄為:nrfsamplesluetoothperipheral_uart 這個(gè)例子跟nRF5 SDK里面的功能一模一樣,都實(shí)現(xiàn)了著名的NUS服務(wù),即藍(lán)牙透?jìng)鞣?wù)。如前所述zephyrsamplessubsysmgmtmcumgrsmp_svr這個(gè)例子則實(shí)現(xiàn)了SMP DFU服務(wù),我們現(xiàn)在把smp藍(lán)牙服務(wù)移植到peripheral_uart上。 我們仔細(xì)查看zephyrsamplessubsysmgmtmcumgrsmp_svr這個(gè)例子,為了實(shí)現(xiàn)SMP DFU,主要修改兩個(gè)地方:一是修改prj.conf以包含相應(yīng)模塊,二是修改main.c的初始化函數(shù)以初始化SMP相關(guān)模塊,prj.conf主要修改點(diǎn)如下: CONFIG_BOOTLOADER_MCUBOOT=yCONFIG_MCUMGR=yCONFIG_MCUMGR_CMD_IMG_MGMT=yCONFIG_MCUMGR_CMD_OS_MGMT=yCONFIG_BT_L2CAP_TX_MTU=252CONFIG_BT_BUF_ACL_RX_SIZE=256CONFIG_MCUMGR_SMP_BT=yCONFIG_MCUMGR_SMP_BT_AUTHEN=nCONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2304CONFIG_MAIN_STACK_SIZE=2048 我們把上述config加在nrfsamplesluetoothperipheral_uartprj.conf文件最后,這樣prj.conf就改完了。 bd4eeb68-d0e0-11ec-bce3-dac502259ad0.png main.c的修改就更簡(jiǎn)單,在啟動(dòng)廣播之前,我們加入如下初始化函數(shù): smp_bt_register(); os_mgmt_register_group(); img_mgmt_register_group(); bdb37e70-d0e0-11ec-bce3-dac502259ad0.png 就這樣兩步工作,輕輕松松就把SMP DFU服務(wù)移植到peripheral_uart上,整個(gè)代碼已經(jīng)上傳到:https://github.com/aiminhua/ncs_samples/tree/master/smp_dfu/peripheral_uart

大家可以下載下來(lái)參考或者測(cè)試一下。
從上述例子我們可以看出,在NCS中移植一個(gè)例子非常方便,它不需要去添加c文件和頭文件,也不需要去修改編譯選項(xiàng),還不需要去修改傳統(tǒng)的頭文件進(jìn)行配置,僅僅修改conf文件和初始化函數(shù),就輕輕松松完成了整個(gè)移植,這也是NCS非常大的一個(gè)好處。 https://github.com/aiminhua/ncs_samples/tree/master/smp_dfu其實(shí)鏈接下面包含的例子都同時(shí)具備smp和nus兩個(gè)服務(wù),并且區(qū)分各種不同情形下的DFU情況,比如secondary slot在外部Flash,通過(guò)串口傳輸image等,同時(shí)其對(duì)peripheral_uart例子進(jìn)行了小小改動(dòng),以更符合某些實(shí)際應(yīng)用場(chǎng)景,建議大家好好看一下,相信對(duì)大家理解MCUboot和SMP會(huì)幫助不少。 6、手機(jī)端DFU參考代碼 Nordic不僅提供設(shè)備端的DFU參考代碼,同時(shí)提供手機(jī)端的參考代碼。Nordic分別開(kāi)發(fā)了Android版和iOS版的DFU庫(kù),大家可以直接拿過(guò)來(lái)使用,集成到自己的移動(dòng)端app中,這兩個(gè)庫(kù)都放在github上,其中smp dfu對(duì)應(yīng)的DFU庫(kù)鏈接如下所示:
  • Android版SMP DFU庫(kù):https://github.com/NordicSemiconductor/Android-nRF-Connect-Device-Manager
  • iOS版SMP DFU庫(kù):https://github.com/JuulLabs-OSS/mcumgr-ios
而nrf dfu對(duì)應(yīng)的DFU庫(kù)鏈接如下所示:
  • Android版nrf dfu庫(kù):https://github.com/NordicSemiconductor/Android-DFU-Library
  • iOS版nrf dfu庫(kù):https://github.com/NordicSemiconductor/IOS-DFU-Library
Nordic還提供了一個(gè)移動(dòng)端app:nRF Toolbox,nRF Toolbox是代碼開(kāi)源的,里面也集成了上面提到的兩種DFU庫(kù)(iOS版同時(shí)支持SMP DFU和nrf dfu,而Android版僅支持nrf dfu),大家可以參考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軟件界面如下所示: bdf1acb8-d0e0-11ec-bce3-dac502259ad0.png 感興趣的朋友可以按步驟實(shí)操,今天Nordic君就分享到這里啦~

原文標(biāo)題:【Nordic博文分享系列】nRF Connect SDK(NCS)/Zephyr固件升級(jí)詳解來(lái)啦!

文章出處:【微信公眾號(hào):Nordic半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅
聲明:本文內(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)投訴
  • 固件升級(jí)
    +關(guān)注

    關(guān)注

    0

    文章

    34

    瀏覽量

    12154
  • NCS
    NCS
    +關(guān)注

    關(guān)注

    1

    文章

    8

    瀏覽量

    9117
  • Nordic
    +關(guān)注

    關(guān)注

    9

    文章

    174

    瀏覽量

    47410

原文標(biāo)題:【Nordic博文分享系列】nRF Connect SDK(NCS)/Zephyr固件升級(jí)詳解來(lái)啦!

文章出處:【微信號(hào):nordicsemi,微信公眾號(hào):Nordic半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

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

    腳本是按照SDK版本進(jìn)行分類的,建議大家把自己SDK版本對(duì)應(yīng)的腳本下載下來(lái),然后跟著第3章的操作步驟一步一步去實(shí)現(xiàn)自己的DFU
    的頭像 發(fā)表于 10-11 09:57 ?2152次閱讀

    轉(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

    NRF51822 DFU升級(jí)功能操作說(shuō)明

    NRF51822 DFU升級(jí)功能操作說(shuō)明
    發(fā)表于 09-28 15:04

    求助 STM32F205 DFU升級(jí)問(wèn)題

    DFU升級(jí)的時(shí)候,進(jìn)入DFU模式正常,升級(jí)正常,最后Level DFU Mode的時(shí)候卡在49%的位置,動(dòng)不了。file:///C:/WI
    發(fā)表于 10-04 13:37

    stm32如何進(jìn)入dfu模式

    stm32如何進(jìn)入dfu模式,相當(dāng)部分的 STM32芯片都帶USB模塊,有時(shí)我們會(huì)考慮利用STM32芯片的USB模塊進(jìn)行程序代碼的下載或升級(jí)。USB協(xié)議中有專門針對(duì)設(shè)備固件升級(jí)的類
    發(fā)表于 08-09 08:09

    STM32芯片的DFU編程

    STM32芯片的DFU編程,相當(dāng)部分的 STM32芯片都帶USB模塊,有時(shí)我們會(huì)考慮利用STM32芯片的USB模塊進(jìn)行程序代碼的下載或升級(jí)。USB協(xié)議中有專門針對(duì)設(shè)備固件升級(jí)的類
    發(fā)表于 08-09 08:41

    關(guān)于Atmel AVR XMEGA USB CDC與DFU的特點(diǎn)介紹

    愛(ài)特梅爾AVR XMEGA USB CDC 和 DFU簡(jiǎn)介
    的頭像 發(fā)表于 07-10 03:21 ?3084次閱讀

    基于DfuSe工具從APP跳轉(zhuǎn)到DFU模式

    我們之前經(jīng)常有講到過(guò)如何通過(guò)USB的DFU方式來(lái)對(duì)固件進(jìn)行升級(jí),在示例中我們通常是通過(guò)一個(gè)按鍵來(lái)觸發(fā)APP跳轉(zhuǎn)到BOOT從而進(jìn)入到DFU升級(jí)模式。
    的頭像 發(fā)表于 02-02 17:41 ?8602次閱讀
    基于DfuSe工具從APP跳轉(zhuǎn)到<b class='flag-5'>DFU</b>模式

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

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

    megawin_USB_DFU_v1.22升級(jí)

    DFU(設(shè)備固件升級(jí))可以直接通過(guò)USB電纜執(zhí)行固件升級(jí),而無(wú)需通過(guò)執(zhí)行一些簡(jiǎn)單的步驟重新連接設(shè)備。
    發(fā)表于 06-21 15:35 ?2次下載
    megawin_USB_<b class='flag-5'>DFU</b>_v1.22<b class='flag-5'>升級(jí)</b>

    如何使用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 ?1452次閱讀

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

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

    STM32微控制器自舉程序中使用的USB DFU協(xié)議

    AN3156 STM32自舉程序中使用的USB DFU協(xié)議
    發(fā)表于 11-17 15:26 ?2次下載

    AN3156_基于STM32微控制器引導(dǎo)程序的USB_DFU協(xié)議應(yīng)用手冊(cè)

    AN3156_基于STM32微控制器引導(dǎo)程序的USB_DFU協(xié)議應(yīng)用手冊(cè)
    發(fā)表于 11-21 17:07 ?3次下載
    AN3156_基于STM32微控制器引導(dǎo)程序的USB_<b class='flag-5'>DFU</b><b class='flag-5'>協(xié)議</b>應(yīng)用手冊(cè)

    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> <b class='flag-5'>協(xié)議</b>
    24山的财位| 澳门百家乐官网娱乐场开户注册| 海燕百家乐官网论| 赌百家乐波音备用网| 威尼斯人娱乐城导航网| 水果机赌博| 百家乐官网博赌场娱乐网规则| 百家乐游戏单机牌| 永利线上娱乐| 百家乐官网大转轮真人视讯| 澳门百家乐常赢打法| 德州扑克算法| 百家乐官网赢钱心得| 百家乐庄闲的概率| 亿酷棋牌世界下载| 百家乐官网娱乐网真人娱乐网 | 大发888娱乐城存款| 百家乐官网投注打三断| 肯博百家乐游戏| 大发888娱乐网下 | 百家乐游戏筹码| 百家乐官网规则好学吗| 网上百家乐赌博犯法吗| 尊龙国际| 找真人百家乐官网的玩法技巧和规则 | 太阳城百家乐官网的分数| 新葡京百家乐娱乐城| 万豪网| 洛克百家乐官网的玩法技巧和规则| 威尼斯人娱乐城送钱| 真人百家乐官网赌博技巧| 百家乐官网哪条下路好| 百家乐博百家乐的玩法技巧和规则| 百家乐官网怎么玩啊| 百家乐ag厅投注限额| 遂溪县| 澳门百家乐几副牌| 尚志市| 百家乐系列抢庄龙| 百家乐官网技巧之写路| 十六浦百家乐官网的玩法技巧和规则|