開發環境:
OS:macOS Big Sur 11.6
IDE:CLion 2021.2.2
開發板:nRF52840
nRF5_SDK:17.1.0
目標:將ble_app_blinky範例程式加入Buttenless_DFU
摘要:ble_app_blinky沒有Buttenless_DFU;ble_app_buttonless_dfu已經做好Buttenless_DFU了,所以將該專案實現Buttenless_DFU的部分移植到ble_app_blinky中
ble_app_blinky路徑:
nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_blinky
ble_app_buttonless_dfu路徑:nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_buttonless_dfu
步驟:
1.複製ble_app_blinky改叫ble_app_blinky_DFU,並以它為基礎修改
2.打開ble_app_buttonless_dfu將所需的程式移植到ble_app_blinky_DFU中
移植項目:
1 在Makefile設定include函數庫路徑,.c檔案路徑,CFLAGS,ASMFLAGS
3 sdk_config.h的define設定
2 複製dfu所需的函數到main.c
可以把自己的專案放左邊,要抄的內容放右邊比較好操作
2.1 在Makefile設定include函數庫路徑,.c檔案路徑,CFLAGS,ASMFLAGS
2.1.1 新增 ble_dfu 資料夾路徑
$(SDK_ROOT)/components/ble/ble_services/ble_dfu \
2.1.2 新增 ble_dfu.c , ble_dfu_bonded.c , ble_dfu_unbonded.c 路徑
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu.c \
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu_bonded.c \
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu_unbonded.c \
2.1.3 新增 dfu 資料夾路徑
$(SDK_ROOT)/components/libraries/bootloader/dfu \
2.1.4 新增 nrf_dfu_svci.c 路徑
$(SDK_ROOT)/components/libraries/bootloader/dfu/nrf_dfu_svci.c \
2.1.5 新增CFLAGS(從ble_app_buttonless_dfu Makefile抄來)
CFLAGS += -DNRF_DFU_SVCI_ENABLED
CFLAGS += -DNRF_DFU_TRANSPORT_BLE=1
2.1.6 新增ASMFLAGS(從ble_app_buttonless_dfu Makefile抄來)
ASMFLAGS += -DNRF_DFU_SVCI_ENABLED
ASMFLAGS += -DNRF_DFU_TRANSPORT_BLE=1
2.2 sdk_config.h的define設定
2.2.1 BLE_DFU_ENABLED
= 1
2.2.2 NRF_SDH_BLE_VS_UUID_COUNT
數字+1
本來是10+1就變11
2.2.3 BLE_ADVERTISING_ENABLED
= 1
開啟藍芽廣播
2.2.4 開啟RTT,NRF_LOG_BACKEND_RTT_ENABLED
= 1
開啟後可以用RTT看到log
2.3 編輯main.c的內容
從ble_app_buttonless_dfu複製buttenless_dfu所需的程式
將ble_app_buttonless_dfu專案main.c第129–282行複製到自己專案前面(include和define之後)
需複製的函數列表:
app_shutdown_handler()
NRF_PWR_MGMT_HANDLER_REGISTER()
2.buttonless_dfu_sdh_state_observer()
NRF_SDH_STATE_OBSERVER()
3.advertising_config_get()
4.disconnect()
5.ble_dfu_evt_handler()
需複製的函數內容:
/**@brief Handler for shutdown preparation.
*
* @details During shutdown procedures, this function will be called at a 1 second interval
* untill the function returns true. When the function returns true, it means that the
* app is ready to reset to DFU mode.
*
* @param[in] event Power manager event.
*
* @retval True if shutdown is allowed by this power manager handler, otherwise false.
*/
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;
}//lint -esym(528, m_app_shutdown_handler)
/**@brief Register application shutdown handler with priority 0.
*/
NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);static void buttonless_dfu_sdh_state_observer(nrf_sdh_state_evt_t state, void * p_context)
{
if (state == NRF_SDH_EVT_STATE_DISABLED)
{
// Softdevice was disabled before going into reset. Inform bootloader to skip CRC on next boot.
nrf_power_gpregret2_set(BOOTLOADER_DFU_SKIP_CRC); //Go to system off.
nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
}
}/* nrf_sdh state observer. */
NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0) =
{
.handler = buttonless_dfu_sdh_state_observer,
};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;
}
}
2.3.1 在services_init()函數加入此函數
dfu_buttonless_init();
在services_init()
前定義dfu_buttonless_init()
函數
static void dfu_buttonless_init(void)
{
uint32_t err_code;
ble_dfu_buttonless_init_t dfus_init = {0}; // Initialize the async SVCI interface to bootloader before any interrupts are enabled.
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);
}
3.編譯ble_app_blinky_DFU
如果編譯過程發現main.c缺少include什麼檔案與ble_app_buttonless_dfu比較看要補什麼,補的同時也在Makefile中加入路徑及.c檔案
在按下Makefile中最下面的flash: default就可以編譯了
3.1 經過一番波折發現main.c所需要include以及define的有
#include "ble_dfu.h"
#include "nrf_power.h"
#include "nrf_bootloader_info.h"
#include "ble_advertising.h"
#include "ble_conn_state.h"
BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */
3.2 經過一番波折發現Makefile所需要補的路徑及.c檔有
#.c檔
#前面已經加過的
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu.c \
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu_bonded.c \
$(SDK_ROOT)/components/ble/ble_services/ble_dfu/ble_dfu_unbonded.c \
$(SDK_ROOT)/components/libraries/bootloader/dfu/nrf_dfu_svci.c \#後來補的
$(SDK_ROOT)/components/libraries/bootloader/nrf_bootloader_info.c \
$(SDK_ROOT)/components/ble/ble_advertising/ble_advertising.c \#路徑
#前面已經加過的
$(SDK_ROOT)/components/ble/ble_services/ble_dfu \
$(SDK_ROOT)/components/libraries/bootloader/dfu \#後來補的
$(SDK_ROOT)/components/libraries/bootloader \
$(SDK_ROOT)/components/ble/ble_advertising \
將以上內容補上後,就可以編譯成功了
4.將編譯完成後將產生的.hex檔與其他所需檔案合併為all.hex
合併項目:
- app.hex(自己專案的hex)
- bootloader.hex(用dfu範例程式編譯,需要公鑰)
- bootloader_setting.hex(用app.hex產生)
- softdevice.hex(SDK中已有提供,根據開發版型號選擇)
路徑:nRF5_SDK_17.1.0_ddde560/components/softdevice/開發板型號/hex
4.1 產生bootloader.hex
4.1.1 產生公鑰,私鑰
使用終端機執行以下指令產生private.pem
及dfu_public_key.c
# Generate a private key and store it in a file named private.pem
nrfutil keys generate private.pem# Display the public key that corresponds to the generated private key (in code format to be used with DFU)
nrfutil keys display --key pk --format code private.pem# Write the public key that corresponds to the generated private key to the file public_key.c (in code format)
nrfutil keys display --key pk --format code private.pem --out_file dfu_public_key.c
4.1.2 將dfu_public_key.c
放在nRF5_SDK_17.1.0_ddde560/examples/dfu
中
4.1.3 編譯secure_bootloader
專案
根據開發版型號選擇專案
nRF5_SDK_17.1.0_ddde560/examples/dfu/secure_bootloader/pca10056_s140_ble
4.2 產生bootloader setting.hex
用以下指令,輸入app.hex產生bootloader setting.hex
nrfutil settings generate --family NRF52840 --application app.hex --application-version 3 --bootloader-version 2 --bl-settings-version 1 bl-settings.hex
4.3 合併hex檔案
使用以下指令將app.hex,bootloader.hex,bootloader_setting.hex,softdevice.hex合併為all.hex
mergehex --merge app.hex bootloader.hex bootloader_setting.hex softdevice.hex --output all.hex
5. 將all.hex刷入開發板中
可以用以下指令:
#清除開發版內的資料
nrfjprog -f nrf52 --eraseall#刷入all.hex
nrfjprog -f nrf52 --program all.hex --sectorerasenrfjprog -f nrf52 --reset
或是用nRF Connect → Programmer
5.1 記憶體空間調整
在刷入hex後發現搜尋不到開發板藍牙,使用RTT Viewer查看錯誤訊息
打開RTT Viewer後發現Softdevice分配的RAM不足需要調整記憶體起始位址及空間大小
照RTT Viewer訊息在ble_app_blinky_gcc_nrf52.ld
中修改記憶體起始位址及空間大小
修改後重新編譯並刷入開發版就可以搜尋到開發版藍牙,並能夠使用開關燈的功能
6.產生DFU更新檔
將ble_app_blinky_DFU修改後重新編譯
將編譯後的.hex檔+私鑰產生升級.zip檔
可以用以下指令:
update.hex:重新編譯後的專案
private.pem:私鑰(4.1.1有提到)
update.zip:輸出的升級.zip檔
nrfutil pkg generate --application update.hex --application-version-string "1.1.0" --hw-version 52 --sd-req 0x100 --key-file private.pem update.zip
需要特別注意的是:
--sd-req 0x100
0x100是跟著開發板型號以及softdevice版本決定的,可以在SDK資料夾中的文件中查到
.\nRF5_SDK_17.0.2_d674dde\components\softdevice\s132\doc
或是在使用nRF Connect → Programmer刷機時也會顯示
7. 用手機 nRF Connect 連上開發版藍芽,選擇DFU,選擇升級.zip檔並升級
結果:升級後還能再使用DFU升級
參考文獻:
https://blog.csdn.net/m0_37621078/article/details/115541552
https://blog.csdn.net/m0_37621078/article/details/115771780
http://ops9.blogspot.com/2020/08/nordic-nrf52-ota-ble-dfu-part-iii.html