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 文件生成

具体查看步骤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

青风论坛

nrf52832学习笔记(6)——ota dfu接口使用

SYQ博客

下载地址