Nordic公司的nRF52芯片提供的DFU功能,能方便的实现固件升级。官方也提供了单独的OTA例程,文档说明,在ble工程中,添加OTA DFU服务的方法。
1. 添加源码文件
DFU需要到的文件包含ble_dfu.c
、ble_dfu_unbonded.c
、nrf_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);
......
}