nRF52 BLE工程中添加 OTA DFU服务

Nordic公司的nRF52芯片提供的DFU功能,能方便的实现固件升级。官方也提供了单独的OTA例程,文档说明,在ble工程中,添加OTA DFU服务的方法。

1. 添加源码文件

DFU需要到的文件包含ble_dfu.cble_dfu_unbonded.cnrf_dfu_svci.c文件,添加源码文件到工程。

sdk\components\ble\ble_services\ble_dfu\ble_dfu.c
sdk\components\ble\ble_services\ble_dfu\ble_dfu_unbonded.c
sdk\components\libraries\bootloader\dfu\nrf_dfu_svci.c

同时包含如下头文件路径。

sdk\components\libraries\bootloader
sdk\components\libraries\bootloader\ble_dfu
sdk\components\libraries\bootloader\dfu
sdk\components\libraries\svc

2. 修改全局宏

在IDE中添加如下如下宏代码。第1个用于调试,第2个用于DFU功能的宏使能,后3个为DFU功能必须。

DEBUG
DFU_SUPPORT
BL_SETTINGS_ACCESS_ONLY
NRF_DFU_SVCI_ENABLED
NRF_DFU_TRANSPORT_BLE=1

3. 初始化DFU服务

添加完文件后,需要在main.c 文件中初始化DFU相关服务。

main.c 文件中,包含DFU头文件。

#ifdef DFU_SUPPORT
#include "ble_dfu.h"
#endif

DFU服务初始化,代码如下所示。

#ifdef DFU_SUPPORT

    ble_dfu_buttonless_init_t dfus_init = {0};

    // Initialize the async SVCI interface to bootloader.
    err_code = ble_dfu_buttonless_async_svci_init();
    APP_ERROR_CHECK(err_code); 

    dfus_init.evt_handler = ble_dfu_evt_handler; 

    err_code = ble_dfu_buttonless_init(&dfus_init);
    APP_ERROR_CHECK(err_code);

#endif

DFU服务回调函数ble_dfu_evt_handler,复制ble_app_buttonless_dfu例程中的即可。

#ifdef DFU_SUPPORT

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

/**@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);

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

#endif

4. sdk_config.h 修改

sdk_config.h 文件的修改主要有两点:使能DFU;修改softdevice配置。

使能DFU宏代码如下所示。

#ifndef BLE_DFU_ENABLED
#define BLE_DFU_ENABLED 1
#endif
 
// <q> NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS  - Buttonless DFU supports bonds.

#ifndef NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS
#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0
#endif

修改softdevice配置。现在BLE服务,额外增加了DFU服务,所以要增大 VS_UUID_COUNT 的值。相应地ATT table size也要变大,然后应用程序RAM起始地址也需要跟着变,如下所示。(注:这里的attr_tab_size设置得稍稍偏大)

// <o> NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple of 4.
#ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE
#define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1600
#endif

// <o> NRF_SDH_BLE_VS_UUID_COUNT - The number of vendor-specific UUIDs.
#ifndef NRF_SDH_BLE_VS_UUID_COUNT
#define NRF_SDH_BLE_VS_UUID_COUNT 2
#endif

5. 修改程序RAM起始地址

参考串口、RTTLog, ble_app_buttonless_dfu 工程设置,修改当前工程RAM的起始地址。

:BootLoader每次启动会验证应用程序CRC,调试阶段可在Bootloader中增加如下语句跳过CRC验证。

int main()
{
    ......

    // skip crc check
    NRF_POWER->GPREGRET2 |= BOOTLOADER_DFU_SKIP_CRC_BIT_MASK;
    
    ret_val = nrf_bootloader_init(dfu_observer);
    APP_ERROR_CHECK(ret_val);

    ......
}

附A: 参考链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。