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
- 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中。
- 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升級流程為:
- 進入bootloader,判斷相應標誌,決定是否等待DFU升級。
- 清相關標誌,等待DFU升級,接收初始化包,進行簽名校驗。
- 接收升級包存入bank1。
- 接收完成,軟體復位。
- 進入bootloader,檢查bank1是否有有效的APP韌體,有則複製到bank0.
- 清除bank1有效標誌,軟體復位。
- 進入bootloader,跳轉到bank0的APP程式執行。