1. 程式人生 > 實用技巧 >NRF52832-DFU-Bootloader原始碼詳解

NRF52832-DFU-Bootloader原始碼詳解

NRF52832-DFU-Bootloader原始碼詳解

Bootloader原始碼:nRF5_SDK_15.0.0_a53641a\examples\dfu\secure_bootloader\pca10040_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,非阻塞方式。

一般無協議棧時用sd方式,而有協議棧時用nvmc方式。如果已經初始化協議棧,仍使能sd方式,則程式可能會出現異常,具體原因還有待查詢。此處因DFU程式還未初始化協議棧,所以使用的是sd方式。p_api_impl = &nrf_fstorage_sd;

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

//加入一個服務UUID128bit到協議棧,獲取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操作用nvmc非阻塞方式, 然後初始化簽名安全認證。如果已經接收了初始化資料包,則進行簽名認證。

繼續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_exchange_mtu_reply(m_conn_handle, mtu_reply);

case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST://資料長度更新請求

ble_gap_data_length_params_t const dlp =

{

.max_rx_octets = BLE_GAP_DATA_LENGTH_AUTO,//自動資料長度

.max_tx_octets = BLE_GAP_DATA_LENGTH_AUTO,//自動資料長度

};

//啟動或響應資料長度更新過程

sd_ble_gap_data_length_update(p_ble_evt->evt.gatts_evt.conn_handle,

&dlp, NULL);

case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST://資料長度更新過程引數

NRF_LOG_DEBUG("ReceivedBLE_GAP_EVT_DATA_LENGTH_UPDATE(%u, max_rx_time%u).", p_gap->params.data_length_update.effective_params.max_rx_octets, p_gap->params.data_length_update.effective_params.max_rx_time_us);

case BLE_GAP_EVT_SEC_PARAMS_REQUEST://請求提供安全引數

ble_gatts_value_t gatts_value =

{

.len = BLE_CCCD_VALUE_LEN,//CCCD值長度2

.p_value = (uint8_t*)&cccd

};

//獲取指定屬性的值

err_code=sd_ble_gatts_value_get(m_conn_handle, BLE_UUID_GATT_CHARACTERISTIC_SERVICE_CHANGED,//服務改變特性 0X2A05

&gatts_value);

//因未開啟密碼配對,應答不支援此功能

err_code=sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,

NULL,

NULL);

case BLE_GAP_EVT_CONN_PARAM_UPDATE://連線引數已更新

ble_gap_conn_params_t const * p_conn =

&p_gap->params.conn_param_update.conn_params;

NRF_LOG_DEBUG("max_conn_interval: %d",p_conn->max_conn_interval);

NRF_LOG_DEBUG("min_conn_interval: %d",p_conn->min_conn_interval);

NRF_LOG_DEBUG("slave_latency: %d", p_conn->slave_latency);

NRF_LOG_DEBUG("conn_sup_timeout: %d", p_conn->conn_sup_timeout);

case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST://連線引數更新請求 根據手機端的連線引數更新

//連線引數更新,將收到BLE_GAP_EVT_CONN_PARAM_UPDATE事件

err_code = sd_ble_gap_conn_param_update(m_conn_handle,

&p_gap->params.conn_param_update_request.conn_params);

case BLE_GAP_EVT_PHY_UPDATE://PHY請求結果

NRF_LOG_DEBUG("Received BLE_GAP_EVT_PHY_UPDATE (RX:%d, TX:%d, status:%d)",

p_gap->params.phy_update.rx_phy,

p_gap->params.phy_update.tx_phy,

p_gap->params.phy_update.status);

case BLE_GAP_EVT_PHY_UPDATE_REQUEST:

ble_gap_phys_t const phys =

{

.rx_phys = BLE_GAP_PHY_AUTO,//自動PHY

.tx_phys = BLE_GAP_PHY_AUTO,//自動PHY

};

//啟動或響應PHY更新過程

err_code = sd_ble_gap_phy_update(p_gap->conn_handle, &phys);

case BLE_GATTS_EVT_TIMEOUT://對方應答超時

//如果是ATT協議超時

if(p_ble_evt->evt.gatts_evt.params.timeout.src==BLE_GATT_TIMEOUT_SRC_PROTOCO)

err_code=sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);//遠端斷開連線

case BLE_EVT_USER_MEM_REQUEST://使用者記憶體請求

err_code = sd_ble_user_mem_reply(m_conn_handle, NULL);//申請使用者記憶體塊

case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST://寫請求操作(從應用程式請求寫入授權)

//此處是控制屬性的入口,用來控制升級過程。讀寫授權請求, 前提是使能了通知。

if (p_ble_evt->evt.gatts_evt.params.authorize_request.type//不是無效型別

!= BLE_GATTS_AUTHORIZE_TYPE_INVALID)

if (on_rw_authorize_req(&m_dfu, p_ble_evt))//GATT層面的自動應答

err_code=on_ctrl_pt_write(&m_dfu, &(p_ble_evt->evt.gatts_evt.params.authorize_request.request.write));

on_ctrl_pt_write函式後面詳解

case BLE_GAP_EVT_SEC_INFO_REQUEST://請求提供安全資訊

//應答安全資訊

err_code = sd_ble_gap_sec_info_reply(p_gap->conn_handle, p_enc_info, p_id_info, NULL);

case BLE_GAP_EVT_CONN_SEC_UPDATE://安全連線已更新

case BLE_GATTS_EVT_SYS_ATTR_MISSING://永久性系統屬性訪問掛起

//初始化永久系統屬性資訊

err_code = sd_ble_gatts_sys_attr_set(p_gap->conn_handle, NULL, 0, 0);

static void nrf_dfu_req_handler_req_process(nrf_dfu_request_t * p_req)

switch (p_req->request)

case NRF_DFU_OP_PROTOCOL_VERSION:// DFU協議版本訊息功能

on_protocol_version_request(p_req, &response);

//如果支援上傳DFU協議版本, 則應答DFU協議版本號1,否則應答不支援的操 作 NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED實際好像並未應答這些資料

case NRF_DFU_OP_HARDWARE_VERSION://硬體版本號

on_hw_version_request(p_req, &response);

//硬體資訊包括:硬體部分,硬體變數,ROM大小,RAM大小,ROM頁數,

//實際好像並未應答這些資料

case NRF_DFU_OP_FIRMWARE_VERSION://韌體版本,實際好像並未應答這些資料

on_fw_version_request(p_req, &response);

case NRF_DFU_OP_PING: //實際好像並未應答這些資料

on_ping_request(p_req, &response);

case NRF_DFU_OP_RECEIPT_NOTIF_SET://接收通知的設定 無額外資料應答

on_prn_set_request(p_req, &response);

case NRF_DFU_OP_MTU_GET://獲取MTU大小 p_res->mtu.size

on_mtu_get_request(p_req, &response);

case NRF_DFU_OP_ABORT://中止DFU程式

on_abort_request(p_req, &response);

//當前程式只有下面5種情況才會處理,其它情況只應答3個數據

case NRF_DFU_OP_OBJECT_CREATE: //建立

case NRF_DFU_OP_OBJECT_SELECT: //選擇

case NRF_DFU_OP_OBJECT_WRITE: //寫

case NRF_DFU_OP_OBJECT_EXECUTE: //執行

case NRF_DFU_OP_CRC_GET: //獲取CRC值

response_ready = nrf_dfu_obj_op(p_req, &response);

default:break;

if (response_ready)//應答

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

static bool nrf_dfu_obj_op(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)

static nrf_dfu_obj_type_t current_object = NRF_DFU_OBJ_TYPE_COMMAND;//靜態變數初始化

if ( (p_req->request == NRF_DFU_OP_OBJECT_SELECT)//選擇請求的物件在object_type

|| (p_req->request == NRF_DFU_OP_OBJECT_CREATE))//建立請求的物件在object_type

current_object = (nrf_dfu_obj_type_t)(p_req->select.object_type);

switch (current_object)

{

case NRF_DFU_OBJ_TYPE_COMMAND://初始化包相關

nrf_dfu_command_req(p_req, p_res);

break;

case NRF_DFU_OBJ_TYPE_DATA://升級資料包相關

response_ready = nrf_dfu_data_req(p_req, p_res);

break;

}

//初始化包相關處理

static void nrf_dfu_command_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)

switch (p_req->request)

case NRF_DFU_OP_OBJECT_CREATE:

//為傳輸初始化包做準備,設定包大小到setting中。

on_cmd_obj_create_request(p_req, p_res);

case NRF_DFU_OP_CRC_GET:

//應答初始化包在setting中的偏移及CRC值

on_cmd_obj_crc_request(p_req, p_res);

case NRF_DFU_OP_OBJECT_WRITE:

//儲存初始化包到setting中,但是不應答。

on_cmd_obj_write_request(p_req, p_res);

case NRF_DFU_OP_OBJECT_EXECUTE:

//簽名及版名校驗,並獲取升級包的大小及儲存地址。將當前setting(包含初始化包資料)儲存

on_cmd_obj_execute_request(p_req, p_res);

case NRF_DFU_OP_OBJECT_SELECT:

//應答物件資訊包含最大物件大小、偏移量和到目前為止整個物件的CRC32

on_cmd_obj_select_request(p_req, p_res);

升級資料包相關處理

static bool nrf_dfu_data_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)

switch (p_req->request)

case NRF_DFU_OP_OBJECT_SELECT:

//選擇升級包傳輸物件,應答最大位元組數4K,偏移量(已傳輸資料量)及CRC值

on_data_obj_select_request(p_req, p_res);

case NRF_DFU_OP_OBJECT_CREATE:

on_data_obj_create_request(p_req, p_res);

//必須已接收到初始化包,才能建立升級包單包傳輸。單包傳輸最大為4K,可分為多個小包傳輸。此處先擦除一頁,等待接收資料。應答成功

case NRF_DFU_OP_OBJECT_WRITE:

on_data_obj_write_request(p_req, p_res);//儲存接收資料到FLASH,bank1中。不需要應答。

case NRF_DFU_OP_CRC_GET:

//應答接收資料的偏移量及CRC值,客戶端會進行校驗。

on_data_obj_crc_request(p_req, p_res);

case NRF_DFU_OP_OBJECT_EXECUTE:

//校驗接收資料總量是否正確,

response_ready = on_data_obj_execute_request(p_req, p_res);

如果升級包全部接收完畢,呼叫 nrf_dfu_validation_post_data_execute(m_firmware_start_addr,m_firmware_size_req);

進行韌體hash值驗證,如果是APP,則bank1有效,並更新大小及CRC,清除升級進度資料,清除初始化包資料。並呼叫回撥函式on_dfu_complete,關閉連線,並軟體復位。

如果升級包未接收完,則應答執行成功。

DFU升級流程為:

  1. 進入bootloader,判斷相應標誌,決定是否等待DFU升級。
  2. 清相關標誌,等待DFU升級,接收初始化包,進行簽名校驗。
  3. 接收升級包存入bank1。
  4. 接收完成,軟體復位。
  5. 進入bootloader,檢查bank1是否有有效的APP韌體,有則複製到bank0.
  6. 清除bank1有效標誌,軟體復位。
  7. 進入bootloader,跳轉到bank0的APP程式執行。