1. 程式人生 > 其它 >nrf52空中升級(DFU)教程

nrf52空中升級(DFU)教程

一、概述

1.1 OTA DFU

DFU(Device Firmware Update)即韌體升級,而空中升級(Over The Air)就是通過無線連線的方式實現的韌體升級。只要是通過無線通訊實現的DFU,都可以稱為空中升級,包括WiFi/藍芽/NFC/Zigbee。也可以使用有線的方式進行空中升級,如使用串列埠、USB或者SPI實現。

1.2 DFU升級模式

DFU的升級模式有後臺式DFU和非後臺式兩種。後臺式DFU,就是跟手機升級版本類似,裝置在後臺下載新的韌體版本,在這個過程中,應用程式可以正常執行,就是該DFU升級不影響程式的正常執行,直到下載完成後,程式才回跳到BootLoader模式,進行升級。

非後臺式DFU,當程式發起DFU請求的使用,程式會從應用層進入BootLoader模式,通過BootLoader完成新版本下載及版本升級覆蓋操作。NRF52的空中升級方式就是使用非後臺式模式。

1.3 DFU Flash佈局

DFU又分雙區DFU(dual bank)和單區DFU(single bank)兩種方式。雙區DFU,新的韌體和舊的韌體各佔一份bank(儲存區),只有在新的韌體下載校驗成功之後,才會進入bootloader,然後擦除就韌體所在的bank,再把新韌體所在的bank複製到就韌體所在的bank,完成升級。單區DFU,新舊韌體共用一個bank,收到升級請求,程式會進入bootloader模式,先擦除老韌體,然後把新韌體下載到剛剛擦除的bank中,完成升級,如果校驗失敗,將重新升級。

由於Nordic晶片是純Flash產品,裡面沒有其他NVM,所有非易失性資料都放在Flash中,包括藍芽協議棧,所以Nordic藍芽協議棧也可以OTA。

1.4 升級檔案含義

bootloader.hex:裝置進入bootloader模式的韌體版本,可在替換掉公鑰的DFU官方樣例(SDK\examples\dfu\secure_bootloader)選擇對應的晶片編譯,並將生成的hex改成bootloader.hex。更多bootloader資訊

app.hex:自己工程生成的hex,也可以直接使用生成的名字,這個沒什麼要求。

setting.hex:用於引導裝置啟動後bootloader轉到應用層的設定檔案,相當於做個校驗。程式從協議棧進入bootloader之後,會檢查Settings中的 bank0_bank_code、bank0_img_crc,只有兩者正確,才會去執行application,否則會停留在bootloader中執行DFU。更多

Settings資訊

協議棧:升級包的時候回用到它的版本號,可在通過命令列的命令(nrfutil pkg generate --help)檢視、線上文件、官方論壇搜尋,也可以在SDK的協議棧版本的doc資訊中檢視(SDK\components\softdevice\s140\doc\s140_nrf52_7.2.0_release-notes.pdf)

升級包:裡面包括manifest.json(檔案清單)、nrf52833_xxaa.bin(新韌體)和nrf52833_xxaa.dat(init packet),其中init packet包含了meta資訊——新韌體的型別、大小、版本和簽名信息。

二、工具安裝

2.1 gcc-arm-none-eabi

作用:編譯micro-ecc。

下載地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

2.2 MinGW

作用:Windows 平臺下實現 Makefile。

下載地址:https://osdn.net/projects/mingw/releases/或者https://sourceforge.net/projects/mingw/files/latest/download?source=files

2.3 Python

作用:pc-nrfutil 需要用到 python-2.7 的環境。感覺2.7還是3.7都沒多大影響。

下載地址:https://www.python.org/downloads/

2.4 pc-nrfutil

作用:Nordic 釋出的 PC 端的工具,支援 DFU 和加 密功能。

下載地址:https://github.com/NordicSemiconductor/pc-nrfutil/

可以選擇命令列輸入pip install nrfutil安裝nrfutil、下載碼源裡面的python setup.py install安裝、也可以直接下載exe安裝程式。最後使用nrfutil version檢視是否安裝成功,有顯示錶示成功。

2.5 nrfgo(或者nrf connect的Programmer)

作用:燒錄hex,nrfgo可以識別得到的都可以使用nrfgo;否則需要使用Programmer,安裝它還需要更新nrf command line tools。

下載地址:nrfgonrf connectnrf command line tools

三、修改bootloader工程

3.1 建立公私鑰

::generate private key
nrfutil keys generate priv.pem
​
::generate public key related with private key: priv.pem
nrfutil keys display --key pk --format code priv.pem --out_file dfu_public_key.c

把生成的dfu_public_key.c替換掉SDK\examples\dfu資料夾下面的同名檔案。

3.2 micro-ecc-master 原始碼新增

把下載的 micro-ecc-master.zip 解壓, 解壓後拷貝到 SDK/external/micro-ecc 檔案中, 重新名為 micro-ecc, 如下圖所示:

3.3 micro_ecc_lib_nrf52.lib 檔案生成

具體檢視Secure DFU環境搭建步驟2.4節

3.4 使用無按鍵觸發bootloader

之後編譯程式,把hex改名為bootloader.hex

四、Application新增DFU

4.1 配置sdk_config檔案

使能DFU服務

NRF_SDH_BLE_VS_UUID_COUNT + 1,在原先基礎上加1

修改RAM空間,Start + 0x10,Size - 0x10

4.2 新增檔案

4.3 新增程式碼

標頭檔案

#if NRF_MODULE_ENABLED(BLE_DFU)
#include "nrf_power.h"      //dfu服務
#include "ble_srv_common.h"
#include "nrf_dfu_ble_svci_bond_sharing.h"
#include "nrf_svci_async_function.h"
#include "nrf_svci_async_handler.h"
#include "ble_dfu.h"
#include "nrf_bootloader_info.h"
#endif

新增DFU所需函式

#if NRF_MODULE_ENABLED(BLE_DFU)
​
static void disconnect(uint16_t conn_handle, void* p_context)
{
   UNUSED_PARAMETER(p_context);
​
   ret_code_t err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
   if(err_code != NRF_SUCCESS)
   {
     NRF_LOG_WARNING("Failed to disconnect connection. Connection handle: %d Error: %d", conn_handle, err_code);
   }
   else
   {
     NRF_LOG_DEBUG("Disconnected connection handle %d", conn_handle);
   }
}
​
static void advertising_config_get(ble_adv_modes_config_t* p_config)
{
   memset(p_config, 0, sizeof(ble_adv_modes_config_t));
​
   p_config->ble_adv_fast_enabled  = true;
   p_config->ble_adv_fast_interval = APP_ADV_INTERVAL;
   p_config->ble_adv_fast_timeout  = APP_ADV_DURATION;
}
​
/**@brief Function for handling dfu events from the Buttonless Secure DFU service
 *
 * @param[in]  event  Event from the Buttonless Secure DFU service.
 */
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
{
   switch(event)
   {
   case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
   {
//     NRF_LOG_INFO("Device is preparing to enter bootloader mode.");
​
     // Prevent device from advertising on disconnect.
     ble_adv_modes_config_t config;
     advertising_config_get(&config);
     config.ble_adv_on_disconnect_disabled = true;
     ble_advertising_modes_config_set(&m_advertising, &config);
​
     // YOUR_JOB: Disconnect all other bonded devices that currently are connected.
     //        This is required to receive a service changed indication
     //        on bootup after a successful (or aborted) Device Firmware Update.
     uint32_t conn_count = ble_conn_state_for_each_connected(disconnect, NULL);
       NRF_LOG_INFO("Disconnected %d links.", conn_count);
     break;
   }
​
   case BLE_DFU_EVT_BOOTLOADER_ENTER:
     // YOUR_JOB: Write app-specific unwritten data to FLASH, control finalization of this
     //      by delaying reset by reporting false in app_shutdown_handler
     break;
​
   case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
     NRF_LOG_ERROR("Request to enter bootloader mode failed asynchroneously.");
     // YOUR_JOB: Take corrective measures to resolve the issue
     //      like calling APP_ERROR_CHECK to reset the device.
     break;
​
   case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
     NRF_LOG_ERROR("Request to send a response to client failed.");
     // YOUR_JOB: Take corrective measures to resolve the issue
     //      like calling APP_ERROR_CHECK to reset the device.
     APP_ERROR_CHECK(false);
     break;
​
   default:
     NRF_LOG_ERROR("Unknown event from ble_dfu_buttonless.");
     break;
   }
}
​
/**@brief Handler for shutdown preparation.
 *
 * @details During shutdown procedures, this function will be called at a 1 second interval
 *      untill the function returns true. When the function returns true, it means that the
 *      app is ready to reset to DFU mode.
 *
 * @param[in]  event  Power manager event.
 *
 * @retval  True if shutdown is allowed by this power manager handler, otherwise false.
 */
static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
   switch(event)
   {
   case NRF_PWR_MGMT_EVT_PREPARE_DFU:
     break;
​
   default:
     // YOUR_JOB: Implement any of the other events available from the power management module:
​
     return true;
   }
​
//   NRF_LOG_INFO("Power management allowed to reset to DFU mode.");
   return true;
}
​
/**@brief Register application shutdown handler with priority 0.
 */
NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);
​
static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void* p_context)
{
   if(state == NRF_SDH_EVT_STATE_DISABLED)
   {
     // Softdevice was disabled before going into reset. Inform bootloader to skip CRC on next boot.
     nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC);
​
     //Go to system off.
     nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
   }
}
​
/* nrf_sdh state observer. */
NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) =
{
   .handler = buttonless_dfu_sdh_state_observer,
};
​
#endif

在services_init()中新增DFU服務

#if NRF_MODULE_ENABLED(BLE_DFU)     
    ble_dfu_buttonless_init_t dfus_init = {0};  
    // Initialize dfu.
   dfus_init.evt_handler = ble_dfu_evt_handler;
   err_code = ble_dfu_buttonless_init(&dfus_init);
   APP_ERROR_CHECK(err_code);
#endif

如果是用IOS升級的,需要在主函式main開始時,新增一下函式

err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

編譯報錯

在工程設定選項下 C/C++選項下: Preprocessor Symbols 中新增這兩個定義 NRF_DFU_TRANSPORT_BLE=1 BL_SETTINGS_ACCESS_ONLY

五、升級包

1.生成zip包

根據上圖官方的資訊,好像只有BL+APP不能同時升級,其他好像都可以,根據自己需求選擇即可,不過大多數開發只需要升級APP就可以。

部分升級命令(nrf52832為例):

協議棧

nrfutil pkg generate --hw-version 52 --sd-req 0x0101 --softdevice s132_nrf52_7.3.0_softdevice.hex --sd-id 0x0124 --key-file priv.pem dfu_softdevice.zip
//REMACK:--sd-req 0x0101指在晶片執行的協議棧版本,--sd-id 0x0124即你要升級的協議棧ID

bootloader

nrfutil pkg generate --hw-version 52 --bootloader bootloadery.hex --bootloader-version 2 --sd-req 0x0124 --key-file priv.pem dfu_bootloader.zip

application

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

2.DFU測試(Android)

使用SDK17.1串列埠透傳例子,協議棧版本s140_nrf52_7.3.0_softdevice,晶片nrf52833進行dfu測試,過程如下

選擇DFU升級按鈕,或者在DFU服務中傳送升級請求。

等待升級完成。

對比升級之前,我已經改了名字。

六、腦圖

七、參考連結

Getting started with Nordic's Secure DFU bootloader, a step by step guide

詳解藍芽空中升級(BLE OTA)原理與步驟

青風論壇

nrf52832學習筆記(6)——otadfu介面使用

SYQ部落格

下載地址