nrf52832 sdk15.2.0 dfu升级攻略

step 1:  安装软件

  1. 安装coreutils-5.3.0.exe
  2. 安装gcc-arm-none-eabi-6-2017-q2-update-win32-sha2.exe
  3. 安装make-3.81.exe
  4. 安装Git-2.21.0-64-bit.exe
  5. 安装python-2.7.12.amd64.msi

step 2: 设置系统环境变量

在系统环境变量的path中加入以下路径后,再重启电脑使环境变量生效

C:\Program Files (x86)\GnuWin32;
C:\Program Files (x86)\GnuWin32\bin;
C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update\bin;
C:\Python27\Scripts;
C:\Python27\Lib;
C:\Python27;

step 3:   安装nrfutil

1.首先要保证电脑连上网络,打开命令行,输入python -m pip install nrfutil后回车,等待联网下载安装,过一会提示pip版本不够,然后在命令行中输入python -m pip install --upgrade pip后回车,等待联网更新pip

2.再次输入python -m pip install nrfutil后回车,等待联网下载安装,此时会安装成功

  • bootloader工程(SDK15.2.0的dfu目录下secure_bootloader工程)

step 1:  打开工程并编译,会报以下错误

*** Using Compiler 'V5.06 update 5 (build 528)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'nrf52832_xxaa_s132'
compiling nrf_crypto_ecdsa.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\crypto\nrf_crypto_ecdsa.c: 0 warnings, 1 error
compiling micro_ecc_backend_ecdh.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecdh.c: 0 warnings, 1 error
compiling dfu_public_key.c...
..\..\..\dfu_public_key.c(29): error:  #35: #error directive: "Debug public key not valid for production. Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate it"
  #error "Debug public key not valid for production. Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate it"
..\..\..\dfu_public_key.c: 0 warnings, 1 error
compiling micro_ecc_backend_ecdsa.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecdsa.c: 0 warnings, 1 error
compiling micro_ecc_backend_ecc.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.c: 0 warnings, 1 error
compiling nrf_crypto_ecc.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\crypto\nrf_crypto_ecc.c: 0 warnings, 1 error
compiling nrf_dfu_ver_validation.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\bootloader\dfu\nrf_dfu_ver_validation.c: 0 warnings, 1 error
compiling nrf_dfu_req_handler.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\bootloader\dfu\nrf_dfu_req_handler.c: 0 warnings, 1 error
compiling nrf_dfu_validation.c...
..\..\..\..\..\components\libraries\crypto\backend\micro_ecc\micro_ecc_backend_ecc.h(52): error:  #5: cannot open source input file "uECC.h": No such file or directory
  #include "uECC.h"
..\..\..\..\..\components\libraries\bootloader\dfu\nrf_dfu_validation.c: 0 warnings, 1 error
".\_build\nrf52832_xxaa_s132.axf" - 9 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed:  00:00:03

step 2: 解决uECC.h缺失问题

1.首先保证电脑连上网络,然后在nRF5_SDK_15.2.0_9412b96\external\micro-ecc\目录下,找到build_all.bat文件,双击

运行从github上复制micro-ecc文件包到nRF5_SDK_15.2.0_9412b96\external\micro-ecc\目录下,并会在nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52nf_keil\armgcc\目录下生成micro_ecc_lib_nrf52.lib文件

step 3: 解决Public key问题

1.再次编译工程,会提示缺少 public key.

2.在nRF5_SDK_15.2.0_9412b96\examples\dfu\的目录下打开命令行,输入nrfutil.exe keys generate private.pem回车,

会产生一个private.pem文件

3.再次在命令行中输入nrfutil.exe keys display --key pk --format code private.pem --out_file public_key.c回车,产生

一个public_key.c文件

4.打开public_key.c文件,将以下的内容复制

粘贴到dfu_public_key.c文件中(粘贴前先将#error这行删除掉)

5.再次编译工程将会通过

  • bootloader工程分析

1.main.c文件中的static void dfu_observer(nrf_dfu_evt_type_t evt_type)函数被传入nrf_bootloader_init函数的函数指针参数

/**
 * @brief Function notifies certain events in DFU process.
 */
static void dfu_observer(nrf_dfu_evt_type_t evt_type)
{
    switch (evt_type)
    {
        case NRF_DFU_EVT_DFU_FAILED:     //dfu 失败
        case NRF_DFU_EVT_DFU_ABORTED:            //dfu 中断
        case NRF_DFU_EVT_DFU_INITIALIZED:        //dfu 初时化完成
            bsp_board_init(BSP_INIT_LEDS);
            bsp_board_led_on(BSP_BOARD_LED_0);
            bsp_board_led_on(BSP_BOARD_LED_1);
            bsp_board_led_off(BSP_BOARD_LED_2);
            break;
        case NRF_DFU_EVT_TRANSPORT_ACTIVATED:    //dfu 传输激活
            bsp_board_led_off(BSP_BOARD_LED_1);
            bsp_board_led_on(BSP_BOARD_LED_2);
            break;
        case NRF_DFU_EVT_DFU_STARTED:    //dfu 开始
            break;
        default:
            break;
    }
}

     ret_val = nrf_bootloader_init(dfu_observer);   // bootloader初时化函数,传入dfu_observer函数作参数
     APP_ERROR_CHECK(ret_val);

2.在nrf_bootloader_init函数中,检测了各种进入dfu方式

ret_code_t nrf_bootloader_init(nrf_dfu_observer_t observer)
{
    NRF_LOG_DEBUG("In nrf_bootloader_init");

    ret_code_t                            ret_val;
    nrf_bootloader_fw_activation_result_t activation_result;
    uint32_t                              initial_timeout;
    bool                                  dfu_enter = false;

    m_user_observer = observer;

    if (NRF_BL_DFU_ENTER_METHOD_BUTTON)      //检查按键进入dfu方法
    {
        dfu_enter_button_init();            //按键初时化
    }

    ret_val = nrf_dfu_settings_init(false);        //检查设置区信息是否有正确
    if (ret_val != NRF_SUCCESS)
    {
        return NRF_ERROR_INTERNAL;
    }

    // Check if an update needs to be activated and activate it.
    activation_result = nrf_bootloader_fw_activate();

    switch (activation_result)
    {
        case ACTIVATION_NONE:        //未激活状态
            initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS);
            dfu_enter       = dfu_enter_check();    //dfu进入检查
            break;

        case ACTIVATION_SUCCESS_EXPECT_ADDITIONAL_UPDATE:    //DFU 附加信息更新
            initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_CONTINUATION_TIMEOUT_MS);
            dfu_enter       = true;            //DFU标志设为真
            break;

        case ACTIVATION_SUCCESS:            //DFU成功
            bootloader_reset();                //系统复位
            NRF_LOG_ERROR("Should never come here: After bootloader_reset()");
            return NRF_ERROR_INTERNAL; // Should not reach this.

        case ACTIVATION_ERROR:
        default:
            return NRF_ERROR_INTERNAL;
    }

    if (dfu_enter)
    {
        nrf_bootloader_wdt_init();     //wdt初时化

        scheduler_init();                //调度机制初时化

        // Clear all DFU stop flags.
        dfu_enter_flags_clear();        //清除标记

        // Call user-defined init function if implemented
        ret_val = nrf_dfu_init_user();        //用户实现功能初时化
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }

        nrf_bootloader_dfu_inactivity_timer_restart(initial_timeout, inactivity_timeout);

        ret_val = nrf_dfu_init(dfu_observer);  //dfu初时化
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }

        NRF_LOG_DEBUG("Enter main loop");
        loop_forever(); // This function will never return.
        NRF_LOG_ERROR("Should never come here: After looping forever.");
    }
    else
    {
        // Erase additional data like peer data or advertisement name
        ret_val = nrf_dfu_settings_additional_erase();   //擦除设置附加信息
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }

        m_flash_write_done = false;
        nrf_dfu_settings_backup(flash_write_callback);   //dfu设置flash写回调函数
        ASSERT(m_flash_write_done);

        nrf_bootloader_app_start();        //应用程序启动
        NRF_LOG_ERROR("Should never come here: After nrf_bootloader_app_start()");
    }

    // Should not be reached.
    return NRF_ERROR_INTERNAL;
}

宏NRF_BL_DFU_ENTER_METHOD_BUTTON决定是否使用按键方式进入dfu升级模式

#ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON
#define NRF_BL_DFU_ENTER_METHOD_BUTTON 1
#endif

 if (NRF_BL_DFU_ENTER_METHOD_BUTTON)
    {
        dfu_enter_button_init();            //按键初时化
    }

/**@brief Function for initializing button used to enter DFU mode.
 */
static void dfu_enter_button_init(void)
{
    nrf_gpio_cfg_sense_input(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN,
                             BUTTON_PULL,
                             NRF_GPIO_PIN_SENSE_LOW);
}

宏NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN定义了使用哪个gpio引脚

#ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN
#define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 16           //P0.16
#endif

如果不使用按键方式,将NRF_BL_DFU_ENTER_METHOD_BUTTON宏定义值由1改成0

#ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON
#define NRF_BL_DFU_ENTER_METHOD_BUTTON 0
#endif

检查设置区信息是否正确有效(设置配置hex文件由nrfutil命令生成)

 ret_val = nrf_dfu_settings_init(false);        //检查设置区信息是否有正确
    if (ret_val != NRF_SUCCESS)
    {
        return NRF_ERROR_INTERNAL;
    }

/*代码片段*/
ret_code_t nrf_dfu_settings_init(bool sd_irq_initialized)
{
    NRF_LOG_DEBUG("Calling nrf_dfu_settings_init()...");

    ret_code_t err_code = nrf_dfu_flash_init(sd_irq_initialized);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("nrf_dfu_flash_init() failed with error: %x", err_code);
        return NRF_ERROR_INTERNAL;
    }

    bool settings_valid        = settings_crc_ok();
    bool settings_backup_valid = settings_backup_crc_ok();

    if (settings_valid &&
        settings_backup_valid &&
        !settings_forbidden_parts_equal_to_backup(m_dfu_settings_buffer))
    {
        NRF_LOG_WARNING("Restoring settings from backup since the app has tampered with the "
                        "off-limit parts of the settings page.");
        memcpy(&s_dfu_settings,
               mp_dfu_settings_backup_buffer,
               sizeof(nrf_dfu_settings_t));
    }
    else if (!settings_valid)
    {
        if (settings_backup_valid)
        {
            NRF_LOG_INFO("Restoring settings from backup since the settings page contents are "
                         "invalid (CRC error).");
            memcpy(&s_dfu_settings,
                   mp_dfu_settings_backup_buffer,
                   sizeof(nrf_dfu_settings_t));
        }
        else
        {
            NRF_LOG_WARNING("Resetting bootloader settings since neither the settings page nor the "
                            "backup are valid (CRC error).");
            memset(&s_dfu_settings, 0x00, sizeof(nrf_dfu_settings_t));
            s_dfu_settings.settings_version = NRF_DFU_SETTINGS_VERSION;
        }
    }
    else
    {
        NRF_LOG_DEBUG("Settings OK");
        memcpy(&s_dfu_settings, m_dfu_settings_buffer, sizeof(nrf_dfu_settings_t));
        return NRF_SUCCESS;
    }

    err_code = nrf_dfu_settings_write(NULL);

    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("settings_write() failed with error: %x", err_code);
        return NRF_ERROR_INTERNAL;
    }

    return NRF_SUCCESS;
}

dfu进入检查

        dfu_enter    =  dfu_enter_check();  

                            dfu_enter_check函数中有5种方式进入DFU模式

/*代码片段*/
/**@brief Function for checking whether to enter DFU mode or not.
 */
static bool dfu_enter_check(void)
{
    if (!nrf_dfu_app_is_valid(crc_on_valid_app_required()))        //app crc方式进入DFU
    {
        NRF_LOG_DEBUG("DFU mode because app is not valid.");
        return true;
    }

//=========================================================================
//    #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON
//    #define NRF_BL_DFU_ENTER_METHOD_BUTTON 1
//    #endif
//==========================================================================
    if (NRF_BL_DFU_ENTER_METHOD_BUTTON &&          //是否按键方式进入DFU
       (nrf_gpio_pin_read(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN) == 0))
    {
        NRF_LOG_DEBUG("DFU mode requested via button.");
        return true;
    }
//============================================================================
//  #ifndef NRF_BL_DFU_ENTER_METHOD_PINRESET
//  #define NRF_BL_DFU_ENTER_METHOD_PINRESET 0
//  #endif
//=============================================================================

    if (NRF_BL_DFU_ENTER_METHOD_PINRESET &&        //是否复位方式进入DFU
       (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk))    
    {
        NRF_LOG_DEBUG("DFU mode requested via pin-reset.");
        return true;
    }

//================================================================================
//  #ifndef NRF_BL_DFU_ENTER_METHOD_GPREGRET
//  #define NRF_BL_DFU_ENTER_METHOD_GPREGRET 1
//  #endif
//=================================================================================

    if (NRF_BL_DFU_ENTER_METHOD_GPREGRET &&            //是否GPREGRET寄存器方式进入DFU
       (nrf_power_gpregret_get() & BOOTLOADER_DFU_START))
    {
        NRF_LOG_DEBUG("DFU mode requested via GPREGRET.");
        return true;
    }

//=================================================================================
//  #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTONLESS
//  #define NRF_BL_DFU_ENTER_METHOD_BUTTONLESS 0
//  #endif
//=================================================================================
    if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS &&       //是否buttonless方式进入DFU
       (s_dfu_settings.enter_buttonless_dfu == 1))
    {
        NRF_LOG_DEBUG("DFU mode requested via bootloader settings.");
        return true;
    }

    return false;
}
  •  bootloader的ROM和RAM空间设置(S132,nRF52832_xxAA) 

                       0x78000是用户程序结束地址

                       0x6000是bootloader的大小

                       0x1000是4K的setting配置信息

  • 用户程序增加DFU服务

step 1:   (ble_dfu_bonded.c暂时不用)     

sdk_config.h增加dfu相关宏,以及添加ble_dfu.c,ble_dfu_unbonded.cnrf_dfu_svci.c三个文件到工程   

step 2: 

                    在C/C++选项中添加2个NRF_DFU_TRANSPORT_BLE=1和BL_SETTINGS_ACCESS_ONLY宏定义

step 3:

        在C/C++选项中添加以下路径

step 4:

       在main.c中引入头文件#include "ble_dfu.h"并加入以下代码片段


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

step 5:

    在main.c中的services_init函数中添加DFU服务初时化代码

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

step 6:

 增加ble_bas.c和ble_dis.c两个文件到工程

step 7:

      在C/C++选项中添加以下路径

step 8:

   1.在main.c中引入头文件

          #include "ble_dis.h"
         #include "ble_bas.h"

2. 在main.c文件加定义m_bas

     BLE_BAS_DEF(m_bas);

 3. 在m_adv_uuids数组中添加uuid服务

static ble_uuid_t m_adv_uuids[]          =                                          /**< Universally unique service identifier. */
{
    {BLE_UUID_NUS_SERVICE,                BLE_UUID_TYPE_VENDOR_BEGIN},
    {BLE_UUID_BATTERY_SERVICE,            BLE_UUID_TYPE_BLE},
    {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE},

};

step 9:

           在main.c中增加bas_init和dis_init服务初时化代码,并在services_init函数中调用


/**@snippet [UART Initialization] */

/**@snippet [Handling the data received over BLE] */

/**@brief Function for initializing Battery Service.
 */
static void bas_init(void)
{
     ret_code_t         err_code;
    ble_bas_init_t     bas_init;
 

    // Initialize Battery Service.
    memset(&bas_init, 0, sizeof(bas_init));

    bas_init.evt_handler          = NULL;
    bas_init.support_notification = true;
    bas_init.p_report_ref         = NULL;
    bas_init.initial_batt_level   = 100;

    // Here the sec level for the Battery Service can be changed/increased.
    bas_init.bl_rd_sec        = SEC_OPEN;
    bas_init.bl_cccd_wr_sec   = SEC_OPEN;
    bas_init.bl_report_rd_sec = SEC_OPEN;

    err_code = ble_bas_init(&m_bas, &bas_init);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing Device Information Service.
 */
static void dis_init(void)
{
     ret_code_t         err_code;
    ble_dis_init_t     dis_init;

    //    ble_dis_pnp_id_t pnp_id;
    //    ble_dis_sys_id_t sys_id;

    //    pnp_id.vendor_id_source = PNP_ID_VENDOR_ID_SOURCE;
    //    pnp_id.vendor_id        = PNP_ID_VENDOR_ID;
    //    pnp_id.product_id       = PNP_ID_PRODUCT_ID;
    //    pnp_id.product_version  = PNP_ID_PRODUCT_VERSION;
    
    // Initialize Device Information Service.
    memset(&dis_init, 0, sizeof(dis_init));

    
    ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *)MANUFACTURER_NAME);
    ble_srv_ascii_to_utf8(&dis_init.model_num_str, MODEL_NUMBER);
    ble_srv_ascii_to_utf8(&dis_init.serial_num_str, SERIAL_NUMBER);
    ble_srv_ascii_to_utf8(&dis_init.hw_rev_str, HARDWARE_VERSION);
    ble_srv_ascii_to_utf8(&dis_init.fw_rev_str, FIRMWARE_VERSION);
    ble_srv_ascii_to_utf8(&dis_init.sw_rev_str, SOFTWARE_VERSION);
     //    sys_id.manufacturer_id            = MANUFACTURER_ID;
    //    sys_id.organizationally_unique_id = ORG_UNIQUE_ID;
    //    dis_init.p_sys_id = &sys_id;
    //    dis_init.p_pnp_id = &pnp_id;

    
    dis_init.dis_char_rd_sec = SEC_OPEN;

    err_code = ble_dis_init(&dis_init);
    APP_ERROR_CHECK(err_code);
}
/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
     uint32_t                  err_code;
	  ble_nus_init_t     nus_init;
    nrf_ble_qwr_init_t        qwr_init  = {0};
    ble_dfu_buttonless_init_t dfus_init = {0};

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_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);
		
		 // Initialize NUS.
    memset(&nus_init, 0, sizeof(nus_init));

    nus_init.data_handler = nus_data_handler;

    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);
		
		bas_init();
		dis_init();
    
}

step 10:

     在main.c中定义以下宏

#define SOFTWARE_VERSION_STR				"1.0.0.0"
#define MODEL_NUMBER				"DFU_TEST"
#define SERIAL_NUMBER		"123456789"
#define HARDWARE_VERSION	"H001"
#define FIRMWARE_VERSION	"S001"
#define SOFTWARE_VERSION		SOFTWARE_VERSION_STR
#define MANUFACTURER_NAME		"NORDIC TECHOLOGY LTD"

step 11:

    修改sdk_config.h中的宏,如下
// <q> BLE_DIS_ENABLED  - ble_dis - Device Information Service

      #ifndef BLE_DIS_ENABLED
      #define BLE_DIS_ENABLED 1
      #endif
// <e> BLE_BAS_ENABLED - ble_bas - Battery Service
//==========================================================
      #ifndef BLE_BAS_ENABLED
      #define BLE_BAS_ENABLED 1
      #endif
// <e> BLE_BAS_CONFIG_LOG_ENABLED - Enables logging in the module.
//==========================================================
      #ifndef BLE_BAS_CONFIG_LOG_ENABLED
      #define BLE_BAS_CONFIG_LOG_ENABLED 0
     #endif
// <o> BLE_BAS_CONFIG_LOG_LEVEL  - Default Severity level
 
// <0=> Off 
// <1=> Error 
// <2=> Warning 
// <3=> Info 
// <4=> Debug 

    #ifndef BLE_BAS_CONFIG_LOG_LEVEL
   #define BLE_BAS_CONFIG_LOG_LEVEL 3
   #endif

// <o> BLE_BAS_CONFIG_INFO_COLOR  - ANSI escape code prefix.
 
// <0=> Default 
// <1=> Black 
// <2=> Red 
// <3=> Green 
// <4=> Yellow 
// <5=> Blue 
// <6=> Magenta 
// <7=> Cyan 
// <8=> White 

#ifndef BLE_BAS_CONFIG_INFO_COLOR
#define BLE_BAS_CONFIG_INFO_COLOR 0
#endif

// <o> BLE_BAS_CONFIG_DEBUG_COLOR  - ANSI escape code prefix.
 
// <0=> Default 
// <1=> Black 
// <2=> Red 
// <3=> Green 
// <4=> Yellow 
// <5=> Blue 
// <6=> Magenta 
// <7=> Cyan 
// <8=> White 

#ifndef BLE_BAS_CONFIG_DEBUG_COLOR
#define BLE_BAS_CONFIG_DEBUG_COLOR 0
#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

step 12:

        用户ROM和RAM配置

0x26000是协议栈s132_nrf52_6.1.0_softdevice.hex所占的ROM空间大小,即协议栈地址范围是0~0x26000

0x52000是用户程序可用ROM空间的大小

0x20002AA8是协议栈s132_nrf52_6.1.0_softdevice.hex所占的RAM空间大小,范围是0x20000000~0x20002AA8

0xD558是用户程序可能RAM空间的大小

如果不确定协议栈所占RAM的空间大小,可以使用以下方法来确定

1.在nrf_sdh_ble.c中定义变量startRam,ramSize;然后在nrf_sdh_ble_enable函数中添加2行代码后编译一下

2.在nrf_sdh_ble_enable函数中的最后一行设置一个断点,点击DEBUG并全速运行,待停到断点处后,将变量startRam,ramSize

加入到Watch 2中,startRam的值为用户程序RAM的起始地址(即协议栈的大小),ramSize的值为用户程序可用的RAM大小

  • 合并bootloader,用户程序,协议栈,配置信息文件成一个hex档

      烧录工具下载地址:https://download.csdn.net/download/mygod2008ok/11157992

step 1: 安装工具

安装nrfgostudio_win-64_1.21.2_installer.msi和nRF5x-Command-Line-Tools_9_6_0_Installer.exe两个烧录工具

step 2:设置环境变量

          在系统环境变量path中加入路径C:\Program Files (x86)\Nordic Semiconductor\nrf5x\bin\;然后重启电脑使设置生效

       step 3: 生成bootloader_setting

        将nrf52832_xxaa_s132.hex ,nrf52832_xxaa.hex和s132_nrf52_6.1.0_softdevice.hex用户程序hex文档复制到

        nRF5_SDK_15.2.0_9412b96\examples\目录下,并在此目录下打开命令行,输入nrfutil.exe settings generate --family

        NRF52 --application nrf52832_xxaa.hex --application-version 3  --bootloader-version 2 --bl-settings-version 1

       bootloader_settings.hex后回车生成bootloader_settings.hex配置文件

    step 4:合并bootloader和协议栈

      在命令行中输入mergehex.exe  --merge  nrf52832_xxaa_s132.hex  s132_nrf52_6.1.0_softdevice.hex     --output

      production_final1.hex后回车,生成production_final1.hex临时文件

    step 5:合并临时文件和用户程序

     在命令行中输入mergehex.exe  --merge  production_final1.hex   nrf52832_xxaa.hex  --output production_final2.hex 

   后回车,生成production_final2.hex 临时文件

  step 6: 合并临时文件和配置文件

   在命令行中输入mergehex.exe  --merge  production_final2.hex   bootloader_settings.hex  --output nrf52832_final.hex 

  后回车,生成nrf52832_final.hex最终烧录hex文档

   step 7:烧录nrf52832_final.hex到芯片

    1.在命令行中输入nrfjprog -f NRF52 --eraseall后回车擦除整个芯片

    2.在命令行中输入nrfjprog.exe  -f NRF52  --program  "nrf52832_final.hex" --verify后回车烧录最终程序到芯片

    3.在命令行中输入nrfjprog -f NRF52 --reset后回车复位并运行

如果为了方便操作,可以将上述操作整合到批处理文件中,在 nRF5_SDK_15.2.0_9412b96\examples\目录下新建一个

dfu.bat文件,打开编辑以下内容后保存

@echo off

:菜单
echo =======================
echo      批处理菜单
echo =======================

echo ====================================================
echo    1.生成bootloader_setting
echo    2.合并所有
echo ====================================================
echo    3.烧录所有
echo =======================

set /p 选择=请进入命令:
if %选择%==1 (goto :1生成bootloader_setting)
if %选择%==2 (goto  :2合并所有)
if %选择%==3 (goto  :3烧录所有)
::else (goto : 菜单) 

:1生成bootloader_setting
copy .\dfu\secure_bootloader\pca10040_ble\arm5_no_packs\_build\nrf52832_xxaa_s132.hex  .\
copy .\ble_peripheral\ble_app_uart\pca10040\s132\arm5_no_packs\_build\nrf52832_xxaa.hex  .\

nrfutil.exe settings generate --family NRF52 --application nrf52832_xxaa.hex --application-version 3 --bootloader-version 2 --bl-settings-version 1 bootloader_settings.hex
echo 已生成bootloader_setting...
:pause>nul
goto 菜单


:2合并所有
copy .\dfu\secure_bootloader\pca10040_ble\arm5_no_packs\_build\nrf52832_xxaa_s132.hex  .\
copy .\ble_peripheral\ble_app_uart\pca10040\s132\arm5_no_packs\_build\nrf52832_xxaa.hex  .\

nrfutil.exe settings generate --family NRF52 --application nrf52832_xxaa.hex --application-version 3 --bootloader-version 2 --bl-settings-version 1 bootloader_settings.hex

mergehex.exe  --merge  nrf52832_xxaa_s132.hex  s132_nrf52_6.1.0_softdevice.hex     --output production_final1.hex 
mergehex.exe  --merge  production_final1.hex   nrf52832_xxaa.hex  --output production_final2.hex 
mergehex.exe  --merge  production_final2.hex   bootloader_settings.hex  --output nrf52832_final.hex 
del production_final1.hex
del production_final2.hex
:pause>nul
goto 菜单

:3烧录所有
::nrfjprog.exe  -f NRF52  --program "app_test.hex" --verify
::nrfjprog.exe  -f NRF52  --program "dfu_bootloader.hex" --verify
::nrfjprog.exe  -f NRF52  --program "bootloader_settings.hex" --verify
cls
nrfjprog -f NRF52 --eraseall
::nrfjprog --family NRF52 --recovers 

nrfjprog.exe  -f NRF52  --program  "nrf52832_final.hex" --verify
nrfjprog -f NRF52 --reset
echo ......
:pause>nul
goto 菜单

:无效命令
echo 无效的指令 指令范围(1-20)重新选择
goto 菜单
  •  生成DFU升级文件                 

1.将private.pem和nrf52832_xxaa.hex文件复制到nRF5_SDK_15.2.0_9412b96\examples\目录下,在此目录下打开命

令行输入nrfutil pkg generate --hw-version 52 --sd-req 0xaf --application-version 0x03 --application nrf52832_xxaa.hex --key-file

private.pem nrf52832_xxaa_app.zip后回车会生成nrf52832_xxaa_app.zip蓝牙DFU升级包

nrfutil pkg generate --hw-version 52 --sd-req 0xaf --application-version 0x03 --application nrf52832_xxaa.hex --key-file 

private.pem nrf52832_xxaa_app.zip

如果不知道sd req,可以通过在命令行输入以下命令得到:  |s132_nrf52_6.1.0|0xAF|

           nrfutil pkg generate --help

E:\jzhou>nrfutil pkg generate --help
Usage: nrfutil pkg generate [OPTIONS] ZIPFILE

  Generate a zip package for distribution to apps that support Nordic DFU
  OTA. The application, bootloader, and SoftDevice files are converted to
  .bin if supplied as .hex files. For more information on the generated
  package, see: http://developer.nordicsemi.com/nRF5_SDK/doc/

  The following combinations are supported by this command:

  * BL only: Supported.

  * SD only: Supported (SD of same Major Version).

  * APP only: Supported (external or internal).

  * BL + SD: Supported.

  * BL + APP: Not supported (use two packages instead).

  * BL + SD + APP: Supported.

  * SD + APP: Supported (SD of same Major Version).

Options:
  --debug-mode                    Debug mode switch, enables version check
                                  skipping.
  --application TEXT              The application firmware file.
  --application-version INTEGER   The application version.
  --application-version-string TEXT
                                  The application version string, e.g
                                  "2.7.31".
  --bootloader TEXT               The bootloader firmware file.
  --bootloader-version INTEGER    The bootloader version.
  --hw-version INTEGER            The hardware version.
  --sd-req TEXT                   The SoftDevice requirements. A comma-
                                  separated list of SoftDevice firmware IDs
                                  (1 or more) of which one must be present on
                                  the target device. Each item on the list
                                  must be a two- or four-digit hex number
                                  prefixed with "0x" (e.g. "0x12", "0x1234").
                                  A non-exhaustive list of well-known values
                                  to use with this option follows:
                                  |s112_nrf52_6.0.0|0xA7|
                                  |s112_nrf52_6.1.0|0xB0|
                                  |s112_nrf52_6.1.1|0xB8|
                                  |s130_nrf51_1.0.0|0x67|
                                  |s130_nrf51_2.0.0|0x80|
                                  |s132_nrf52_2.0.0|0x81|
                                  |s130_nrf51_2.0.1|0x87|
                                  |s132_nrf52_2.0.1|0x88|
                                  |s132_nrf52_3.0.0|0x8C|
                                  |s132_nrf52_3.1.0|0x91|
                                  |s132_nrf52_4.0.0|0x95|
                                  |s132_nrf52_4.0.2|0x98|
                                  |s132_nrf52_4.0.3|0x99|
                                  |s132_nrf52_4.0.4|0x9E|
                                  |s132_nrf52_4.0.5|0x9F|
                                  |s132_nrf52_5.0.0|0x9D|
                                  |s132_nrf52_5.1.0|0xA5|
                                  |s132_nrf52_6.0.0|0xA8|
                                  |s132_nrf52_6.1.0|0xAF|
                                  |s132_nrf52_6.1.1|0xB7|
                                  |s140_nrf52_6.0.0|0xA9|
                                  |s140_nrf52_6.1.0|0xAE|
                                  |s140_nrf52_6.1.1|0xB6|
  --sd-id TEXT                    The new SoftDevice ID to be used as --sd-
                                  req for the Application update in case the
                                  ZIP contains a SoftDevice and an
                                  Application.
  --softdevice TEXT               The SoftDevice firmware file.
  --sd-boot-validation TEXT       The method of boot validation for
                                  Softdevice. Choose from:
                                  NO_VALIDATION
                                  VALIDATE_GENERATED_CRC
                                  VALIDATE_GENERATED_SHA256
                                  VALIDATE_ECDSA_P256_SHA256
  --app-boot-validation TEXT      The method of boot validation for
                                  application. Choose from:
                                  NO_VALIDATION
                                  VALIDATE_GENERATED_CRC
                                  VALIDATE_GENERATED_SHA256
                                  VALIDATE_ECDSA_P256_SHA256
  --key-file FILE                 The private (signing) key in PEM fomat.
  --external-app                  Indicates that the FW upgrade is intended
                                  to be passed through (not applied on the
                                  receiving device)
  --zigbee BOOLEAN                Create an image and distribution package
                                  for Zigbee DFU server.
  --zigbee-manufacturer-id INTEGER
                                  Manufacturer ID to be used in Zigbee OTA
                                  header.
  --zigbee-image-type INTEGER     Image type to be used in Zigbee OTA header.
  --zigbee-comment TEXT           Firmware comment to be used in Zigbee OTA
                                  header.
  --zigbee-ota-hw-version INTEGER
                                  The zigbee OTA hw version.
  --zigbee-ota-fw-version INTEGER
                                  The zigbee OTA fw version.
  --help                          Show this message and exit.

E:\jzhou>
  • DFU升级工具升级操作

一键式升级工具下载地址:https://download.csdn.net/download/mygod2008ok/10538088

step 1:

  安装一键式升级升级工具(适合用android 7及以前的手机)

step 2:

  将nrf52832_xxaa_app.zip升级包复制到手机,打开APP连接上蓝牙,点开始DFU升级按键加载nrf52832_xxaa_app.zip升级

  文件后,稍后APP会自动进行蓝牙升级,升级百分比进度会显示在APP上

如果工具不适用,你也可以从官网下载nRF Connect工具进行升级

  • nRF Connect工具进行升级

1.打开nRF Connect扫描设备

2.连接设备

3.打开DFU服务notify开关

4.点击notify左边的按钮打开写数据界面,打send发送使设备进入bootloader 模式

5.重新扫描

6.连接DfuTarg后打开notify开关

 7.点击右上角的DFU按钮打开加载升级文件页面,点击OK键去加载nrf52832_xxaa_app.zip升级包

8.升级包加载完后会开始DFU升级

  •  Demo下载地址:

https://download.csdn.net/download/mygod2008ok/11158400

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风雨依依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值