【Nordic】52840 为工程添加OTA

在上一篇添加GATT Service的基础上,继续添加OTA。


1代码启动流程

nRF52840的代码区块可以分为三块:SoftDevice,Application,Bootloader。如图 1.1‑1所示:

【Nordic】52840 为工程添加OTA_第1张图片

图 1.1‑1 nRF52840 Flash分配

SoftDevice即BLE协议栈,只要用到蓝牙相关功能,则必须烧写。SoftDevice是Nordic提供的,需要注意其版本。例如S112,S113和S132是支持nRF52832,S140是支持-nRF52840的。一般来说应该使用最新版本的协议栈,但如果与SDK配合不一致的话,可能会出现问题,可以适当降低版本测试。例如本例使用SDK15.2与最新的协议栈S140 v7.0.1实验时会导致开发板跑不起来,而用S140 v6.1.1则没有问题。

Application即应用程序,用来实现主要功能。

BootLoader主要用于实现Device Firmware Update(DFU)。芯片在上电后,首先跑SoftDevice,然后查看是否存在BootLoader,如果没有则去跑Application,如果有则先去跑BootLoader,在BootLoader中会检查Application是否有更新,如果有更新则将新固件搬移倒Application区域。

这里涉及到Dual Bank的升级模式,在Application区域有两个bank,如图 1.1‑2所示,一个正常跑的Application区域,一个用于DFU的swap区域。当有在DFU模式时,先往Swap区域中写入新的Firmware,当重启时进入BootLoader,将Swap中的Image复制到Application中,执行新的程序。

【Nordic】52840 为工程添加OTA_第2张图片

图 1.1‑2 Dual Bank模式

2DFU服务

nordic默认的DFU服务UUID为0xFE59,包含两个characteristic。一个是control point,其subUUID是0x0001;另一个是packet characteristic,其subUUID是0x0002。

DFU的过程如图所示,主机在DFU连接成功后,首先使能DFUctrlPoint的Notification,之后告知设备要开始DFU了,请做好准备。然后将新固件的大小告知设备,设备会检查是否有足够空间,如果有则准备好并通知主机。主机收到通知后会向设备发送Init Packet,在Init Packet中会包含设备类型,固件版本号,CRC校验码等信息,设备收到后会检查对应的信息是否满足更新条件,如果可以更新则通知主机可以开始发送固件了。设备通过Packet characteristic开始并不断接收新的固件,直到主机通过Ctrl Point向设备发送Validate Firmware,如果设备检查完CRC发现没有问题,则回复主机正常。之后设备会重启进入BootLoader,将新的固件搬移到Application区域,并跳转过去。

【Nordic】52840 为工程添加OTA_第3张图片

图 3.1‑1 DFU流程

3添加BootLoader

这里直接使用SDK自带的secure_bootloader, 首先确保python版本为2.7,且nrfutil安装在python2.7下。

1.在路径/examples/dfu下, shift+右键打开cmd,键入

nrfutil keys generate private.key

得到私钥private.key

2.键入

nrfutil keys display --key pk --format code private.key --out_file public_key.c

得到公钥‘public_key.c’

3.复制micro-ecc文件夹到/external/micro-ecc下

4.进入nrf52hf_keil/armgcc,在此处打开cmd,make编译一下。由于这里使用的是mingW,所以这里的命令是mingw32-make。

5.在\examples\dfu\secure_bootloader\pca10056_ble_debug\arm5_no_packs下打开工程,用刚生成的‘public_key.c’替换掉工程中原有的公钥,编译工程,得到’nrf52840_xxaa_s140.hex’

4添加dfu_service

这里添加的是buttonless_dfu。

1. 首先添加相关文件:

1) 在nRF_BLE中添加:components\ble\peer_manager下所有.c文件

2) 在nRF_DFU中添加:ble_dfu.c, ble_dfu_bounded.c, ble_dfu_unbounded.c

3) 在nRF_SVC中添加:nrf_dfu_svc.c

4) 在sdk_config中使能BLE_DFU_ENABLE

2. 在main中添加相关头文件

#include "ble_dfu.h"
#include "nrf_bootloader_info.h"
#include "nrf_power.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "nrf_fstorage.h"
#include "fds.h"
#include "ble_conn_state.h"

 3. 在service_init()中初始化dfu service

ble_dfu_buttonless_init_t dfus_init = {0};

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

 4. 从buttonless_dfu例程中拷贝以下代码到我们的程序中

static bool app_shutdown_handler(nrf_pwr_mgmt_evt_t event)
{
    switch (event)
    {
        case NRF_PWR_MGMT_EVT_PREPARE_DFU:
            NRF_LOG_INFO("Power management wants to reset to DFU mode.");
            
            break;

        default:

            return true;
    }

    NRF_LOG_INFO("Power management allowed to reset to DFU mode.");
    return true;
}

//lint -esym(528, m_app_shutdown_handler)
/**@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,
};

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

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

// YOUR_JOB: Update this code if you want to do anything given a DFU event (optional).
/**@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)
{
	NRF_LOG_INFO("ble_dfu_evt_handler,evt:%d",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);

            // 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
            NRF_LOG_INFO("Device will enter bootloader mode.");
            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 Function for handling Peer Manager events.
 *
 * @param[in] p_evt  Peer Manager event.
 */
static void pm_evt_handler(pm_evt_t const * p_evt)
{
    pm_handler_on_pm_evt(p_evt);
    pm_handler_flash_clean(p_evt);
}

 5. 在main()中添加

// Initialize the async SVCI interface to bootloader before any interrupts are enabled.
err_code = ble_dfu_buttonless_async_svci_init();
APP_ERROR_CHECK(err_code);

 6.因为增加了一个GATT service,所以需要告知SDK多申请一部分RAM来处理。在sdk_config.h中,将NRF_SDH_BLE_VS_UUID_COUNT的值改为2

#ifndef NRF_SDH_BLE_VS_UUID_COUNT
#define NRF_SDH_BLE_VS_UUID_COUNT 2
#endif

 7. 编译,解决掉剩下的error。编译成功后,得到‘nrf52_ble_sean_service.hex’。

5生成settings.hex

将之前得到的‘private.key’和‘nrf52_ble_sean_service.hex’放到同一个文件夹下,打开cmd,键入

nrfutil settings generate –-family NRF52840 –-application nrf52_ble_sean_service.hex –-application-version 1 –-bootloader-version 0 –-ble-settings-version 1 bootyloader_settings.hex

6打包新的软件

将我们原有的软件更改设备名为SEAN_SERVICE_3,编译后用以下指令打包

nrfutil pkg generate --hw-version 52 --application-version 3 --application nrf52_ble_sean_service.hex --sd-req 0xB6 --key-file private.key sean_dfu_package.zip

7将新的软件OTA到开发板中

首先我们先烧写未改名的固件,如图所示打开nRF connect -> Programmer,在左上角连接好板子,然后依次将

  • settings: ‘bootloader_setting.hex’
  • BootLoader: ‘nrf52840_xxaa_s140.hex’
  • Application: ‘nrf52_ble_sean_service.hex’
  • SoftDevice: ‘s140_nrf52_6.1.1_softdevice.hex’

拖入右侧区域,点击‘Erase&write’将所有程序烧写进板子。

【Nordic】52840 为工程添加OTA_第4张图片

图 3.6‑1 烧写固件

在手机端nRF Connect上找到我们的开发板’SEAN SERVICE’,点击’’connet’,在’Service’页面可以看到我们已经添加好的’DFU Service’,如图 3.6‑2所示。

【Nordic】52840 为工程添加OTA_第5张图片

图 3.6‑2 DFU Servicce

向左滑动进入’’DFU’页面,找到我们打包好的’’‘sean_dfu_service.zip’,如图 3.6‑3所示。

【Nordic】52840 为工程添加OTA_第6张图片

图 3.6‑3 找到新固件

 

然后等待一段更新的动画,如图 3.6‑4所示。

【Nordic】52840 为工程添加OTA_第7张图片

图 3.6‑4 更新动画

最后页面会显示DFU更新成功,如图 3.6‑5所示,可以搜索到新名称的板子。

【Nordic】52840 为工程添加OTA_第8张图片

【Nordic】52840 为工程添加OTA_第9张图片

图 3.6‑5 更新成功

 

你可能感兴趣的:(蓝牙)