NRF52832-DFU-Bootloader源码详解

NRF52832-DFU-Bootloader源码详解

Bootloader源码:nRF5_SDK_15.0.0_a53641aexamplesdfusecure_bootloaderpca10040_ble

 

Keil5中的C/C++中宏定义为:

APP_TIMER_V2 APP_TIMER_V2_RTC1_ENABLED BLE_STACK_SUPPORT_REQD BOARD_PCA10040 CONFIG_GPIO_AS_PINRESET FLOAT_ABI_HARD NRF52 NRF52832_XXAA NRF52_PAN_74 NRF_DFU_SETTINGS_VERSION=1 NRF_DFU_SVCI_ENABLED NRF_SD_BLE_API_VERSION=6 S132 SOFTDEVICE_PRESENT SVC_INTERFACE_CALL_AS_NORMAL_FUNCTION __HEAP_SIZE=0 uECC_ENABLE_VLI_API=0 uECC_OPTIMIZATION_LEVEL=3 uECC_SQUARE_FUNC=0 uECC_SUPPORT_COMPRESSED_POINT=0 uECC_VLI_NATIVE_LITTLE_ENDIAN=1

 

  1. main.c

ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);

这是将MBR区域(前0X1000)进行写保护, MBR是在FLASH中最靠前的一段程序。

ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE, false);这是将Bootloader程序区域进行写保护。

BOOTLOADER_START_ADDR由keil中的配置决定。BOOTLOADER_START_ADDR=0X70000

BOOTLOADER_SIZE = NRF_MBR_PARAMS_PAGE_ADDRESS-BOOTLOADER_START_ADDR

= 0X7E000 – 0X70000 = 0XE000

IRAM1的起始地址不能设置太小,得留够协议栈的RAM。

 

NRF_LOG_DEFAULT_BACKENDS_INIT();初始化RTT调试输出,此处是关闭的,空操作。

 

static void dfu_observer(nrf_dfu_evt_type_t evt_type)//这是DFU过程各种事件的回调函数。

nrf_bootloader_app_start();//如果不能进入升级处理流程,将会进入此函数,跳转到用户APP程序运行。

 

ret_val = nrf_bootloader_init(dfu_observer);//DFU升级最关键的函数就是这,dfu_observer是回调函数。

 

 

1、ret_val = nrf_dfu_settings_init(false);设置参数的初始化,进入此函数内部看看。

ret_code_t rc = nrf_dfu_flash_init(sd_irq_initialized);//此处是操作FLASH前的初始化。

NRF52832的FLASH操作有两种方式,一种是nrf_fstorage_sd,非阻塞方式, 一种是nrf_fstorage_nvmc,阻塞方式。

一般无协议栈时用nvmc方式,而有协议栈时用sd方式。如果已经初始化协议栈,仍使能nvmc方式,则程序可能会出现异常,具体原因还有待查找。此处因DFU程序还未初始化协议栈,所以使用的是nvmc方式。p_api_impl = &nrf_fstorage_nvmc;

memcpy((void*)&s_dfu_settings, m_dfu_settings_buffer, sizeof(nrf_dfu_settings_t));

将FLASH中的设置参数复制到RAM中然后进行CRC校验,如果正确则设置参数有效,不需要初始化,否则进行设置参数的初始化。

 memset(&s_dfu_settings, 0x00, sizeof(nrf_dfu_settings_t));

s_dfu_settings.settings_version = NRF_DFU_SETTINGS_VERSION;只是设置了一个设置参数的版本 。

rc = nrf_dfu_settings_write(NULL); 此函数会将FLASH中的设置参数与刚才初始化的参数进行比对,如果一样则跳过, 否则先按页(4K)擦除,然后更新s_dfu_settings的CRC值,最后写入FALSH中。

  1. activation_result = nrf_bootloader_fw_activate();//检查bank1是否有固件待更新

 先了解下NRF52832的FLASH布局情况。DFU升级使用的是双区升级。

 

NRF52832的FLASH布局如上图,MBR就是最先运行的那部分程序,是厂家固化FLASH中,无法擦除的,大小固定为0X1000。softdevice紧跟其后,大小与协议栈的版本有关,s132_nrf52_6.0.0_softdevice就占用152K,application紧跟协议栈,超始地址为0X26000。

对比下协议栈s132_nrf52_6.0.0与s132_nrf52_5.0.0可以看出,MBR好像又是包含在协议栈中的。

Dfu bootloader的起始地址也不一定是0X78000,是可以调整的。而Dfu bootloader的起始地址前的3页(12K)是用户数据存储区。

Bootloader setting起始地址是0X7F000,大小0X1000,这是固定不变的。

MBR parameters 起始地址是0X7E000, 大小0X1000, 这也是固定不变的。

这样用户APP区大小为512-152-32-12=316K,再分为两个bank, bank0起始地址为:0X26000,保存的是最终的用户APP程序,bank1起始地址为bank0的结束地址,页对齐。Bank1在DFU升级时用来保存升级包数据,后续将会覆盖bank0。所以用户APP程序最大为158K。

进入到nrf_bootloader_fw_activate函数内部。

case NRF_DFU_BANK_VALID_APP: ret_val = app_activate();//bank1为有效的APP程序,则复制到bank0区域

case NRF_DFU_BANK_VALID_SD:ret_val = sd_activate();//bank1为有效的SD程序,则复制到协议栈起始地址0X1000处

case NRF_DFU_BANK_VALID_BL:ret_val = bl_activate();//bank1为有效的bootloader程序,则通过nrf_dfu_mbr_copy_bl函数来复制

case NRF_DFU_BANK_VALID_SD_BL:ret_val = sd_bl_activate();//bank1为有效的bootloader程序及SD程序,则先复制SD程序,后复制BOOT程序。

case NRF_DFU_BANK_INVALID://bank1无效

default:

return ACTIVATION_NONE; //则不处理,后续将跳转到用户APP程序。

 

nrf_dfu_bank_invalidate(p_bank);//清除bank1有效标志

ret_val = nrf_dfu_settings_write(flash_write_callback);//更新参数到FLASH中。下次重启将不会执行复制操作。

根据执行上面函数的返回值作相应处理:

case ACTIVATION_NONE:

initial_timeout = NRF_BL_DFU_INACTIVITY_TIMEOUT_MS;//DFU升级时间限定为120秒。

dfu_enter       = dfu_enter_check();//检查是否进入DFU升级

case ACTIVATION_SUCCESS_EXPECT_ADDITIONAL_UPDATE:

initial_timeout = NRF_BL_DFU_CONTINUATION_TIMEOUT_MS;

dfu_enter       = true;

//如果SD已更新,而bank0有效,则进入DFU升级,限时10秒。

case ACTIVATION_SUCCESS:

bootloader_reset();//前面从bank1的复制操作成功,软件复位。

case ACTIVATION_ERROR:

default:

return NRF_ERROR_INTERNAL;//出错将会产生软件复位

 

if (dfu_enter)//如果进入DFU升级流程

nrf_bootloader_wdt_init();//初始化看门狗

scheduler_init();//调度相关初始化

dfu_enter_flags_clear();//清除进入DFU的相关标志

//进入DFU的方式有:NRF_BL_DFU_ENTER_METHOD_GPREGRET根据电源备份寄存器的 //值, 按键NRF_BL_DFU_ENTER_METHOD_BUTTONLESS,复位键 //NRF_BL_DFU_ENTER_METHOD_PINRESET。

 

ret_val = nrf_dfu_init_user();//什么也没干。

nrf_bootloader_dfu_inactivity_timer_restart(initial_timeout, inactivity_timeout);//开启超时定时器。

ret_val = nrf_dfu_init(dfu_observer);//DFU的初始化,与蓝牙相关,进入函数看看。

 

 

ret_val = nrf_dfu_transports_init(dfu_observer);//初始化蓝牙传输,调用函数ble_dfu_transport_init.进入函数查看。

err_code = nrf_balloc_init(&m_buffer_pool);//初始化内存池

err_code = ble_stack_init();//初始化协议栈

if (nrf_dfu_settings_adv_name_is_valid())

     err_code = nrf_dfu_settings_adv_name_copy(&m_adv_name);

//如果setting中有广播名称则复制过来。

err_code = gap_params_init();//蓝牙通用连接参数初始化,如果setting中有蓝牙广播名称就用,没有就用默认的。设置最小最大连接间隔,从机延迟,超时时间。

 

err_code = ble_dfu_init(&m_dfu);//初始化设备固件更新服务

err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,

                                        &service_uuid,

                                        &(p_dfu->service_handle));

//增加基本服务0xFE59到协议栈,获取服务句柄service_handle。

 

err_code = sd_ble_uuid_vs_add(&base_uuid128, &p_dfu->uuid_type);

//加入一个服务UUID 128bit到协议栈,获取UUID类型到uuid_type

 

  err_code = dfu_pkt_char_add(p_dfu);

  //增加数据包特性到服务,可写,DFU的升级数据包及初始化包通过此特性传输。

 

err_code = dfu_ctrl_pt_add(p_dfu);

//增加控制特性到服务 可写,通知功能,DFU升级的控制,通过此特性传输,通知功能用作应答。

 

err_code = advertising_start();//蓝牙广播的初始化及启动广播

//nrf_dfu_transports_init

ret_val = nrf_dfu_req_handler_init(dfu_observer);

//此函数中先nrf_dfu_flash_init(true); FLASH操作用sd非阻塞方式, 然后初始化签名安全认证。如果已经接收了初始化数据包,则进行签名认证。

 

继续nrf_bootloader_init函数:

loop_forever(); // This function will never return.

//进入死循环,根据事件回调,循环喂看门狗

Else

ret_val = nrf_dfu_settings_additional_erase();//清除掉setting最后部分,广播名称,参数。

nrf_bootloader_app_start();//开始执行APP程序。

 

关于回调函数dfu_observer 

//此函数为 nrf_bootloader.c中的回调

static void dfu_observer(nrf_dfu_evt_type_t evt_type)

{

    switch (evt_type)

{

//创建初始化包传输即认为是DFU升级开始

        case NRF_DFU_EVT_DFU_STARTED://在on_cmd_obj_create_request中

//on_data_obj_execute_request每次接收到数据都会重启超时定时器

        case NRF_DFU_EVT_OBJECT_RECEIVED:

            LCD_Display_Updata_Cur();

            nrf_bootloader_dfu_inactivity_timer_restart(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS, inactivity_timeout);

            break;

        case NRF_DFU_EVT_DFU_COMPLETED:

        case NRF_DFU_EVT_DFU_ABORTED:

            bootloader_reset();

            break;

        default:

            break;

    }

 

    if (m_user_observer)

    {

        m_user_observer(evt_type);

    }

}

//nrf_bootloader.c中调用, 因 nrf_dfu_init 在nrf_dfu.c中被重写

ret_val = nrf_dfu_init(dfu_observer);

 

//nrf_dfu.c 重写nrf_dfu_init

uint32_t nrf_dfu_init(nrf_dfu_observer_t observer)

{

    uint32_t ret_val;

 

//nrf_bootloader.c中的dfu_observer被用于nrf_dfu.c中的m_user_observer

    m_user_observer = observer;

 

    NRF_LOG_INFO(“Entering DFU mode.”);

 

    dfu_observer(NRF_DFU_EVT_DFU_INITIALIZED);

 

    // Initializing transports

    ret_val = nrf_dfu_transports_init(dfu_observer);

    if (ret_val != NRF_SUCCESS)

    {

        NRF_LOG_ERROR(“Could not initalize DFU transport: 0x%08x”, ret_val);

        return ret_val;

    }

 

    ret_val = nrf_dfu_req_handler_init(dfu_observer);

 

    return ret_val;

}

 

//此函数为nrf_dfu.c中的回调 dfu_observer,在 nrf_dfu_req_handler.c中被设置

ret_code_t nrf_dfu_req_handler_init(nrf_dfu_observer_t observer);

//nrf_dfu.c中dfu_observer被nrf_dfu_req_handler.c中的on_dfu_complete调用。

 

nrf_dfu.c  static void dfu_observer(nrf_dfu_evt_type_t event)

{

    switch (event)

    {

        case NRF_DFU_EVT_DFU_COMPLETED://所有固件传输完成

        {

#ifndef NRF_DFU_NO_TRANSPORT

            UNUSED_RETURN_VALUE(nrf_dfu_transports_close(NULL));//关闭连接

#endif

            break;

        }

        default:

            break;

    }

 

    /* Call user’s observer if present. */

    if (m_user_observer)

    {

        m_user_observer(event);

    }

}

 

 

 

 

//所有固件传输完成执行

nrf_dfu_req_handler.c static void on_dfu_complete(nrf_fstorage_evt_t * p_evt)

{

    UNUSED_PARAMETER(p_evt);

 

    NRF_LOG_DEBUG(“All flash operations have completed. DFU completed.”);

 

    m_observer(NRF_DFU_EVT_DFU_COMPLETED);

}

 

 

nrf_dfu_req_handler.c static void on_data_obj_execute_request_sched(void * p_evt, uint16_t event_length)

{

    UNUSED_PARAMETER(event_length);

 

    nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);

 

    /* Wait for all buffers to be written in flash. */

    if (nrf_fstorage_is_busy(NULL))

    {

        ret_code_t ret = app_sched_event_put(p_req,

                                             sizeof(nrf_dfu_request_t),

                                             on_data_obj_execute_request_sched);

        if (ret != NRF_SUCCESS)

        {

            NRF_LOG_ERROR(“Failed to schedule object execute: 0x%x.”, ret);

        }

        return;

    }

 

    nrf_dfu_response_t res =

    {

        .request = NRF_DFU_OP_OBJECT_EXECUTE,

    };

 

    nrf_dfu_flash_callback_t dfu_settings_callback;

 

    /* 所有固件传输完成 */

    if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req)

    {

        NRF_LOG_DEBUG(“Postvalidation of firmware image.”);

 

        res.result = nrf_dfu_validation_post_data_execute(m_firmware_start_addr, m_firmware_size_req);

        res.result = ext_err_code_handle(res.result);

 

        /* Callback to on_dfu_complete() after updating the settings. */

        dfu_settings_callback = (nrf_dfu_flash_callback_t)(on_dfu_complete);//回调函数在保存后setting后执行

on_dfu_complete->nrf_dfu.c::dfu_observer(关闭连接)->nrf_bootloader::dfu_observer(重启)

    }

    else

    {

        res.result = NRF_DFU_RES_CODE_SUCCESS;

        /* No callback required. */

        dfu_settings_callback = NULL;

    }

 

    /* Provide response to transport */

    p_req->callback.response(&res, p_req->p_context);

 

    /* Store settings to flash if the whole image was received or if configured

     * to save progress information in flash.

     */

    if ((dfu_settings_callback != NULL) || NRF_DFU_SAVE_PROGRESS_IN_FLASH)

    {

        ret_code_t ret = nrf_dfu_settings_write(dfu_settings_callback);//升级完成需要恢复且保存setting

        UNUSED_RETURN_VALUE(ret);

    }

 

    NRF_LOG_DEBUG(“Request handling complete. Result: 0x%x”, res.result);

}

 

 

 

 

 

 

蓝牙事件回调函数

Nrf_dfu_ble.c

static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)

 

case BLE_GAP_EVT_CONNECTED: //建立连接后的处理

m_conn_handle = p_gap->conn_handle;//保存连接句柄

err_code = sd_ble_gap_conn_param_update(m_conn_handle, &m_gap_conn_params);//更新连接参数

case BLE_GAP_EVT_DISCONNECTED: //断开连接,自动广播

m_conn_handle = BLE_CONN_HANDLE_INVALID;//连接句柄无效

//数据包接收完成后会置位此标志,停止传输,不需要广播。

if (!(m_flags & DFU_BLE_RESETTING_SOON))     

err_code = advertising_start();

case BLE_GATTS_EVT_WRITE: //蓝牙特性值写操作

on_write(&m_dfu, p_ble_evt);

//只有是写数据包特性才处理

if (p_write_evt->handle != p_dfu->dfu_pkt_handles.value_handle)

//申请缓存池

uint8_t * p_balloc_buf = nrf_balloc_alloc(&m_buffer_pool);

//将蓝牙接收数据复制到用户内存空间。

memcpy(p_balloc_buf, p_write_evt->data, p_write_evt->len);

 nrf_dfu_request_t request =

    {

        .request      = NRF_DFU_OP_OBJECT_WRITE,//对象写操作,无应答

        .p_context    = p_dfu,

        .callback     =

        {

            .response = ble_dfu_req_handler_callback,//回调函数,用来应答

            .write    = on_flash_write,//写入FLASH

        }

    };

ret_code_t rc = nrf_dfu_req_handler_on_req(&request);//触发用户层的事件回调

调用nrf_dfu_req_handler_req_process,此函数后面详解。

 nrf_balloc_free(&m_buffer_pool, p_balloc_buf);释放内存池

case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST://更新当前的MTU值到手机端

//交换MTU请求,手机端发来一个MTU值,收到后比对是否小于MTU最大值247,如果小于,则去掉头部3字节,剩下的按4字节对齐,否则MTU值不变,最后应答当前的MTU值。

err_code = sd_ble_gatts_ex

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年9月7日
下一篇 2020年9月7日

相关推荐