前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nRF5x系列蓝牙模块DFU升级服务移植 -- 4 应用移植

nRF5x系列蓝牙模块DFU升级服务移植 -- 4 应用移植

原创
作者头像
DN
修改2020-07-02 14:26:01
2K0
修改2020-07-02 14:26:01
举报
文章被收录于专栏:JETSON NANO AI

在操作应用移植时,请先掌握以下内容:

《nRF5x系列蓝牙模块DFU升级服务移植 -- 1 工具安装》

《nRF5x系列蓝牙模块DFU升级服务移植 -- 2 文件生成》

《nRF5x系列蓝牙模块DFU升级服务移植 -- 3 烧写升级》

此处使用实际的车位锁终端的工程项目进行移植说明,通过演示步骤,最终达到在任意应用程序中添加DFU升级功能。

4.1  配置文件使能

4.1.1  配置文件使能方法

在SDK12之后,代码中出现了一个专门的配置文件sdk_config.h,这个文件包含了全部需要使能的功能,由于其需要使能的功能非常的多,因此官方专门做了一个配置向导的编辑界面,如下图所示:

那么如何来进行相应功能的使能配置呢?下面介绍两种方法,一种是直接在sdk_config.c文件中把需要的配置参数由0修改成1,就可以完成使能,如下图所示:

另外一种方法,点击Configuration Wizard选型卡,在需要使能的服务或者功能上点击勾选,然后返回到Text Editor中,可以看到勾选后文件上对应的选项使能了,如下图所示:

4.1.2  DFU需要使能的选项

要使得应用程序添加DFU功能,需要使能的主要有以下描述的几个部分。

使能DFU功能,这个是核心部分,选择nRF_DFU选项下的BLE_DFU_ENABLED进行勾选,如下图所示:

添加绑定功能,绑定功能实际上是内存和设备管理相关,选择nRF_BLE选项下的PEER_MANAGER_ENABLED进行勾选,如下图所示:

备注:由于车位锁应用程序中已经启用绑定功能,所以此处实际上是没有操作的。

添加FDS功能和CRC功能,固件的存储必须使能FDS功能,错误校验必须使能CRC功能。选择nRF_Libraries选项下的FDS_ENABLED和CRC16_ENABLED进行勾选,如下图所示:

备注:由于车位锁应用程序中已经启用FDS功能和CRC功能,所以此处实际上是没有操作的。

协议栈初始化参数的改变,选择nRF_softDevice选项下的NRF_SDH_BLE_ENABLED下的BLE_Stack configuration下的NRF_SDH_BLE_SERVICE_CHANGED进行勾选;修改nRF_softDevice选项下的NRF_SDH_BLE_ENABLED下的BLE_Stack configuration下的NRF_SDH_BLE_VS_UUID_COUNT内容为2,如下图所示:

备注:由于车位锁应用程序中已经勾选NRF_SDH_BLE_SERVICE_CHANGED,所以此处实际上是没有操作的。

修改RAM空间,修改如下图所示:

备注:空间大小改写原则是每增加一个独立的128bit UUID服务,占用RAM空间0x10大小。因此,Start起始位0x20002470 + 0x10;应用空间就相应减少,Size为0xDB90 - 0x10。

4.2  工程文件添加

使能服务功能后,就需要添加服务与驱动函数文件,下面介绍需要添加的文件说明。

4.2.1  DFU功能支持文件添加

需要添加DFU功能文件,首先鼠标放在工程上点击右键,选择Manager Project Items工程管理选项,如下图所示:

新建一个文件夹nRF_DFU,在该文件夹里添加F:\YL-CWS_0.00.18\components\ble\ble_services\ble_dfu文件夹下的三个C文件,如下图所示:

添加后可在工程目录中找到,如下图所示:

将F:\YL-CWS_0.00.18\components\ble\ble_services\ble_dfu路径添加到Keil的Options for Target的C/C++选项中的Include Paths,如下图所示:

4.2.2  Peer绑定功能支持文件添加

添加文件到nRF_BLE文件夹中,结果如下图所示:

备注:由于车位锁工程中已经添加这些文件,所以没有进行添加文件的操作。

将F:\YL-CWS_0.00.18\components\ble\peer_manager路径添加到Keil的Options for Target的C/C++选项中的Include Paths,结果如下图所示:

备注:由于车位锁工程中已经添加该路径,所以此步操作没有做。

4.2.3  FDS和CRC支持文件添加

添加F:\YL-CWS_0.00.18\components\libraries\fds文件夹下的C文件到nRF_BLE中,结果如下图所示:

将F:\YL-CWS_0.00.18\components\libraries\fds路径添加到Keil的Options for Target的C/C++选项中的Include Paths,结果如下图所示:

备注:由于车位锁工程中已经添加该路径,所以实际没有操作此步。

新建一个文件夹nRF_SVC,在该文件夹里添加F:\YL-CWS_0.00.18\components\libraries\bootloader\dfu文件夹下的nrf_dfu_svci.c文件,如下图所示:

将F:\YL-CWS_0.00.18\components\libraries\bootloader\dfu和F:\YL-CWS_0.00.18\components\libraries\svc路径添加到Keil的Options for Target的C/C++选项中的Include Paths,结果如下图所示:

4.3  代码修改

4.3.1  头文件的添加

在main.c文件最开头处,需要添加如下的头文件:

#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 "peer_manager.h"

#include "fds.h"

#include "ble_conn_state.h"

#include "ble.h"

#include "nrf_power.h"

4.3.2  配对函数的添加

1、在main.c文件中添加配对绑定中需要设置的安全参数的配置,代码如下所示:

#define SEC_PARAM_BOND                  1                                           

/**< Perform bonding. */

#define SEC_PARAM_MITM                  0                                           

/**< Man In The Middle protection not required. */

#define SEC_PARAM_LESC                  0                                           

/**< LE Secure Connections not enabled. */

#define SEC_PARAM_KEYPRESS              0                                           

/**< Keypress notifications not enabled. */

#define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                        /**< No I/O capabilities. */

#define SEC_PARAM_OOB                   0                                           

/**< Out Of Band data not available. */

#define SEC_PARAM_MIN_KEY_SIZE          7                                           

/**< Minimum encryption key size. */

#define SEC_PARAM_MAX_KEY_SIZE          16

2、设置配对事件处理函数,配对绑定中需要设置的安全参数的配置,代码如下所示:

static void pm_evt_handler(pm_evt_t const * p_evt)

{

ret_code_t err_code;

switch (p_evt->evt_id)

{

case PM_EVT_BONDED_PEER_CONNECTED:

{

NRF_LOG_INFO("Connected to a previously bonded device.");

        } break;

case PM_EVT_CONN_SEC_SUCCEEDED:

        {

            NRF_LOG_INFO("Connection secured: role: %d, conn_handle: 0x%x, procedure: %d.",

                         ble_conn_state_role(p_evt->conn_handle),

                         p_evt->conn_handle,

                         p_evt->params.conn_sec_succeeded.procedure);

        } break;

case PM_EVT_CONN_SEC_FAILED:

        {

            /* Often, when securing fails, it shouldn't be restarted, for security reasons.

             * Other times, it can be restarted directly.

             * Sometimes it can be restarted, but only after changing some Security Parameters.

             * Sometimes, it cannot be restarted until the link is disconnected and reconnected.

             * Sometimes it is impossible, to secure the link, or the peer device does not support it.

             * How to handle this error is highly application dependent. */

        } break;

case PM_EVT_CONN_SEC_CONFIG_REQ:

        {

            // Reject pairing request from an already bonded peer.

            pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};

            pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);

        } break;

case PM_EVT_STORAGE_FULL:

        {

            // Run garbage collection on the flash.

            err_code = fds_gc();

            if (err_code == FDS_ERR_NO_SPACE_IN_QUEUES)

            {

                // Retry.

            }

            else

            {

                APP_ERROR_CHECK(err_code);

            }

        } break;

case PM_EVT_PEERS_DELETE_SUCCEEDED:

        {

            advertising_start(false);

        } break;

        case PM_EVT_PEER_DATA_UPDATE_FAILED:

        {

            // Assert.

            APP_ERROR_CHECK(p_evt->params.peer_data_update_failed.error);

        } break;

case PM_EVT_PEER_DELETE_FAILED:

        {

            // Assert.

            APP_ERROR_CHECK(p_evt->params.peer_delete_failed.error);

        } break;

        case PM_EVT_PEERS_DELETE_FAILED:

        {

            // Assert.

            APP_ERROR_CHECK(p_evt->params.peers_delete_failed_evt.error);

        } break;

case PM_EVT_ERROR_UNEXPECTED:

        {

            // Assert.

            APP_ERROR_CHECK(p_evt->params.error_unexpected.error);

        } break;

        case PM_EVT_CONN_SEC_START:

case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:

        case PM_EVT_PEER_DELETE_SUCCEEDED:

        case PM_EVT_LOCAL_DB_CACHE_APPLIED:

        case PM_EVT_LOCAL_DB_CACHE_APPLY_FAILED:

            // This can happen when the local DB has changed.

        case PM_EVT_SERVICE_CHANGED_IND_SENT:

        case PM_EVT_SERVICE_CHANGED_IND_CONFIRMED:

        default:

            break;

}

}

3、添加配对初始化函数,初始化匹配绑定功能代码如下所示:

static void peer_manager_init()

{

    ble_gap_sec_params_t sec_param;

    ret_code_t           err_code;

    err_code = pm_init();

    APP_ERROR_CHECK(err_code);

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.

    sec_param.bond           = SEC_PARAM_BOND;

    sec_param.mitm           = SEC_PARAM_MITM;

    sec_param.lesc           = SEC_PARAM_LESC;

    sec_param.keypress       = SEC_PARAM_KEYPRESS;

    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;

    sec_param.oob            = SEC_PARAM_OOB;

    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;

    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;

    sec_param.kdist_own.enc  = 1;

    sec_param.kdist_own.id   = 1;

    sec_param.kdist_peer.enc = 1;

    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);

    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);

    APP_ERROR_CHECK(err_code);

}

4、在update_beacon.c文件中添加设置解绑功能,因为重新广播的时候需要解除绑定,代码如下所示:

static void delete_bonds(void)

{

    ret_code_t err_code;

    NRF_LOG_INFO("Erase bonds!");

    err_code = pm_peers_delete();

    APP_ERROR_CHECK(err_code);

}

5、重新编写广播开始函数,广播的时候添加解绑功能,注意重新广播函数会在app触发DFU功能函数里调用,所以需要进行声明,代码如下所示:

extern void advertising_start(bool erase_bonds);

void advertising_start(bool erase_bonds)

{

if (erase_bonds == true)

    {

        delete_bonds();

        // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.

    }

    else

    {

        uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);

        NRF_LOG_DEBUG("advertising is started");

    }

}

4.3.3  服务初始化DFU服务的声明

该部分分为三个部分进行操作:1、在服务初始化函数中,添加DFU服务初始化;2、添加DFU事件处理函数;3、添加APP触发DFU功能转换函数。

1、添加DFU服务初始化,因为DFU属于独立于其他服务的128bit主服务,需要独立的引出对DFU服务进行初始化声明,代码如下所示:

static void services_init(void)

{

ret_code_t         err_code;

//定义排队写入初始化结构体变量

nrf_ble_qwr_init_t qwr_init = {0};

ble_dfu_buttonless_init_t dfus_init = {0};

//排队写入事件处理函数

qwr_init.error_handler = nrf_qwr_error_handler;

//初始化排队写入模块

err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);

//检查函数返回值

APP_ERROR_CHECK(err_code);

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

/*添加设备信息的代码*/

ble_dis_init_t dis_init;

memset(&dis_init, 0x00, sizeof(dis_init));

ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, "YL");

ble_srv_ascii_to_utf8(&dis_init.hw_rev_str, "01.00.00");

ble_srv_ascii_to_utf8(&dis_init.fw_rev_str, "s132");

ble_srv_ascii_to_utf8(&dis_init.sw_rev_str, "00.00.18");

dis_init.dis_char_rd_sec = SEC_OPEN;//设置访问权限

err_code = ble_dis_init(&dis_init);

APP_ERROR_CHECK(err_code);

/*添加自定义通信服务*/

ble_user_ser_init();

#if 1

//显示MAC地址

uint8_t my_device_mac[6] = {0,};

ble_gap_addr_t paddr;

err_code = sd_ble_gap_addr_get(&paddr);

APP_ERROR_CHECK(err_code);

uint8_t i = 0;

NRF_LOG_INFO("MAC");  

for(i = 0; i < 6; i++)

{

my_device_mac[i] = paddr.addr[5-i];

}

for(i = 0; i < 6; i++)

{

NRF_LOG_INFO("%02x", my_device_mac[i]);  

}

NRF_LOG_INFO("\n");

check_hander_init(my_device_mac, sizeof(my_device_mac));

//uint32_t sd_ble_gap_addr_set (ble_gap_addr_t const * p_addr); //如果需要重新设置设备MAC地址

#endif

}

2、添加DFU事件处理函数,代码如下所示:

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.");

            // YOUR_JOB: Disconnect all 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.

            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;

    }

}

3、添加APP触发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.");

            // YOUR_JOB: Get ready to reset into DFU mode

            //

            // If you aren't finished with any ongoing tasks, return "false" to

            // signal to the system that reset is impossible at this stage.

            //

            // Here is an example using a variable to delay resetting the device.

            //

            // if (!m_ready_for_reset)

            // {

            //      return false;

            // }

            // else

            //{

            //

            //    // Device ready to enter

            //    uint32_t err_code;

            //    err_code = sd_softdevice_disable();

            //    APP_ERROR_CHECK(err_code);

            //    err_code = app_timer_stop_all();

            //    APP_ERROR_CHECK(err_code);

            //}

            break;

        default:

            // YOUR_JOB: Implement any of the other events available from the power management module:

            //      -NRF_PWR_MGMT_EVT_PREPARE_SYSOFF

            //      -NRF_PWR_MGMT_EVT_PREPARE_WAKEUP

            //      -NRF_PWR_MGMT_EVT_PREPARE_RESET

            return true;

    }

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

    return true;

}

4.3.4  主函数的修改和宏的声明

1、主函数中,添加上peer_manager_init()配对初始化函数,代码如下所示:

int main(void)

{

//初始化log程序模块

log_init();

//初始化APP定时器

timers_init();

//初始化电源管理

power_management_init();

//初始化协议栈

ble_stack_init();

peer_manager_init();

//配置GAP参数

gap_params_init();

//初始化GATT

gatt_init();

//初始化服务..在这里添加了自定义的蓝牙通信层接口

services_init();

//连接参数协商初始化

conn_params_init();

//Update_Advertising();

#if 1

//应用流程初始化

Application_Init();

#else

//只广播不跑应用

Update_Advertising();

#endif

NRF_LOG_INFO("Started .");  

//主循环

while(true)

{

//处理挂起的LOG和运行电源管理

idle_state_handle();

}

}

2、在update_beacon.c文件中修改广播开始函数调用,代码如下所示:

void Update_Advertising(void)

{

bool erase_bonds;

if(Get_Conect_Status() == BLE_CONN_HANDLE_INVALID)//未连接

{

advertising_stop();

advertising_init();

advertising_start(erase_bonds);

}

else

{

//更新值

advertising_init();

}

}

3、添加两个宏定义,在Keil工程设置选项下的C/C++选项下的Define中添加两个定义NRF_DFU_TRANSPORT_BLE=1 BL_SETTINGS_ACCESS_ONLY,如下图所示:

添加完成后,编译生成应用hex文件。

备注:在编译时,提示头文件缺失问题,添加路径即可编译通过,如下图所示:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档