nRF52-Note(09)-添加DFU服务

本文详细介绍了如何在nRF5SDKv15.3.0环境下,结合IAR或Keil开发工具,实现nRF52840芯片的OTA DFU功能。步骤包括:准备开发环境、修改应用和Bootloader工程、安装相关软件、生成升级文件和烧录。此外,还涉及了安全升级所需的公私钥生成以及使用nRFUtil工具进行固件打包和烧录。

一、开发环境及工具

  1. nRF5 SDK v15.3.0
  2. IAR For ARM V7.80.4 / Keil uVision5(MDK-ARM) V5.33
  3. 此工程源码下载路径 : https://gitee.com/amx/nrf52xx-project

二、移植DFU功能前准备

  1. DFU(Device Firmware Update),即设备固件升级。可以有多种方式,如OTA、UART、USB等,这里我们介绍OTA
  2. nRF52xx进行OTA升级需要烧录3种固件:softdevice、bootloader、application,我们需要编译bootloader、application工程
  3. 了解添加DFU功能后Flash带协议栈和Bootloader布局,更多可以查看nRF52-Note(04)-Memory Layout文章

在这里插入图片描述

下面我们演示如何在nRF5 SDK v15.3.0版本上一步步实现OTA功能。


三、安装相关工具软件

名称描述
J-LinkV6.81d
nRF Connect For DesktopV3.6.1,可选
nRF-Command-Line-ToolsV10.9.0
PythonPython2.7或Python3.7或更高版,可选
nrfutilV6.1.0
nRF Connect for Mobile手机端APP
nRF Toolbox App手机端APP
  1. 安装nRF Connect For Desktop (可选),用来烧录固件,这里我们使用最新版本V3.6.1:

在这里插入图片描述

  1. 安装nRF-Command-Line-Tools,用来合成和烧录DFU固件,这里我们使用10.9.0版本:

在这里插入图片描述

(1)将其安装路径添加进系统环境变量中:

在这里插入图片描述

(2)在PowerShell中输入nrfjprog --versionmergehex --version验证是否安装成功:

在这里插入图片描述

  1. 安装Python2.7或Python3.7或更高版本(可选),目的是使用其命令来安装nrfutil

  2. 安装nrfutil,用来生成密钥和打包升级文件,这里有两种安装方式:

    (1)直接下载使用nrfutil.exe文件,简单方便(我们使用此方法

    在这里插入图片描述

    (2)使用python中的pip命令安装,此方法较为麻烦,可能你会遇到其它一些奇怪的安装问题,这里不讲解。


    我们将nrfutil.exe放到nRF-Command-Line-Tools的安装路径下,在PowerShell输入nrfutil version查看版本:

    在这里插入图片描述
    在这里插入图片描述


四、修改Application工程

  1. 添加DFU相关文件:

在这里插入图片描述

  1. 添加宏定义

    BL_SETTINGS_ACCESS_ONLY
    NRF_DFU_SVCI_ENABLED
    NRF_DFU_TRANSPORT_BLE=1
    
  2. 配置sdk_config.h中的宏

    #ifndef BLE_DFU_ENABLED
    #define BLE_DFU_ENABLED 1		// 使能DFU
    #endif
    
    #ifndef NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS
    #define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0	// 无绑定升级方式
    #endif
    
    // <o> NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple of 4. 
    #ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE
    #define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1600	// 增加Attribute Table空间
    #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		// 这里用到了NUS和DFU两个自定义的UUID
    #endif
    
  3. 将RAM起始地址由0x20002a98改为0x20002ba8

在这里插入图片描述

  1. 在项目中添加DFU相关函数和服务并编译,具体请查看源代码

五、编译Bootloader工程

  1. 编译安装micro-ecc加密库:

    这里我们演示使用安全式空中升级方式(相对与开放式升级),需要下载使用加密库micro-ecc,解压到external\micro-ecc路径下,并编译安装。如果用户需要自行安装,还需要在Windows上额外的安装Git和GCC编译器,再运行build_all脚本完成编译。用户也可以使用他人编译好的加密库文件,直接替换掉原来的目录文件。我们项目代码中已经编译安装好micro-ecc,可以打包参考使用。

在这里插入图片描述

  1. 通过nrfutil生成公私钥对:

    (1)进入examples\dfu目录下,将dfu_public_key.c复制备份成dfu_public_key_backup.c

    (2)在该目录下,按住键盘Shift键,然后单击鼠标右键选择“在此处打开PowerShell窗口”,打开PowerShell

在这里插入图片描述

(3)输入命令生成私钥:nrfutil keys generate private_key.pem

(4)输入命令生成公钥:keys display --key pk --format code private_key.pem --out_file dfu_public_key.c

在这里插入图片描述

(5)这时你会发现目录下多了private_key.pem文件,dfu_public_key.c也被修改:

在这里插入图片描述

注意:请务必保管好自己的private_key.pem文件,以后每次升级新固件时,都会通过它进行签名,一旦丢失或者被暴露,DFU将无法进行或者变得不安全

  1. 编译Bootloader工程:

    (1)进入examples\dfu\secure_bootloader打开对应的ble工程并,编译生成bootloader的hex文件

    在这里插入图片描述

    (2)此时我们也可以查看下Bootloader工程的RAM和Flash的空间分配

    在这里插入图片描述


六、生成升级文件和烧录文件

  1. 在examples\dfu目录下新建文件夹secure_dfu_images,将工程编译好的application、bootloader文件拷贝到此目录,分别重新命名为nrf52xx_app.hex、nrf52xx_bootloader.hex

  2. 将softdevice文件和生成的私钥private_key也拷贝到此目录

  3. 在目录下创建脚本文件1_build.bat,在里面添加如下内容,符号::后面为注释内容:

   ::generate settings page for current image
   nrfutil.exe settings generate --family NRF52 --application nrf52xx_app.hex --application-version 3 --bootloader-version 2 --bl-settings-version 2 bootloader_settings.hex
   
   ::merge bootloader,softdevice,app and settings
   mergehex.exe --merge nrf52xx_bootloader.hex s132_nrf52_6.1.1_softdevice.hex --output production_final1.hex
   mergehex.exe --merge production_final1.hex nrf52xx_app.hex --output production_final2.hex
   mergehex.exe --merge production_final2.hex bootloader_settings.hex --output nrf52xx_firmware.hex
   
   ::generate upgrade pack
   nrfutil pkg generate --hw-version 52 --sd-req 0xB7 --application-version 0xff --application nrf52xx_app.hex --key-file private_key.pem nrf52xx_app_update.zip
   
   del production_final1.hex
   del production_final2.hex
   pause

上述命令选项中0xB7的值为对应的softdevice版本的值,可以通过nrfutil pkg generate --help命令查看,我们使用s132_nrf52_6.1.1_softdevice版本:

在这里插入图片描述

更多关于nrfutil和mergehex命令介绍可以访问https://infocenter.nordicsemi.com/index.jsp查看文档:

在这里插入图片描述

  1. 双击运行1_build.bat,生成烧录文件nrf52xx_firmware.hex和升级文件nrf52xx_app_update.zip,前者用于烧录MCU,后者用于OTA升级

在这里插入图片描述

  1. 新建脚本文件2_program.bat,用于烧录固件,加入以下内容:

    nrfjprog -f NRF52 --eraseall
    nrfjprog -f NRF52 --program "nrf52xx_firmware.hex" --verify
    nrfjprog -f NRF52 --reset
    
    pause
    

    双击运行2_program.bat,将固件nrf52xx_firmware.hex烧录到芯片中。

    也可以使用nRF Connect For Desktop进行烧录:

在这里插入图片描述


七、手机APP升级测试

  1. 在手机端安装nRF Toolbox软件
  2. nrf52xx_app_update.zip升级文件发送到手机端,比如通过微信
  3. 在文件打开方式中选择nRF Toolbox
  4. nRF Toolbox使用步骤及nRF Connect查看截图:

在这里插入图片描述


好文推荐: https://www.cnblogs.com/iini/p/9314246.html


int main(void) { // Populate Boot Address and MBR Param into MBR if not already // MBR_BOOTLOADER_ADDR/MBR_PARAM_PAGE_ADDR are used if available, else UICR registers are used // Note: skip it for now since this will prevent us to change the size of bootloader in the future // bootloader_mbr_addrs_populate(); // Save bootloader version to pre-defined register, retrieved by application // TODO move to CF2 BOOTLOADER_VERSION_REGISTER = (MK_BOOTLOADER_VERSION); board_init(); bootloader_init(); PRINTF("Bootloader Start\r\n"); led_state(STATE_BOOTLOADER_STARTED); // When updating SoftDevice, bootloader will reset before swapping SD if (bootloader_dfu_sd_in_progress()) { led_state(STATE_WRITING_STARTED); bootloader_dfu_sd_update_continue(); bootloader_dfu_sd_update_finalize(); led_state(STATE_WRITING_FINISHED); } // Check all inputs and enter DFU if needed // Return when DFU process is complete (or not entered at all) check_dfu_mode(); // Reset peripherals board_teardown(); /* Jump to application if valid * "Master Boot Record and SoftDevice initializaton procedure" * - SD_MBR_COMMAND_INIT_SD (if not already) * - sd_softdevice_disable() * - sd_softdevice_vector_table_base_set(APP_ADDR) * - jump to App reset */ if (bootloader_app_is_valid() && !bootloader_dfu_sd_in_progress()) { PRINTF("App is valid\r\n"); if (is_sd_existed()) { // MBR forward IRQ to SD (if not already) if (!_sd_inited) mbr_init_sd(); // Make sure SD is disabled disable_softdevice(); } // clear in case we kept DFU_DBL_RESET_APP there (*dbl_reset_mem) = 0; // start application PRINTF("Starting app...\r\n"); bootloader_app_start(); } NVIC_SystemReset(); } static void check_dfu_mode(void) { uint32_t const gpregret = NRF_POWER->GPREGRET; // SD is already Initialized in case of BOOTLOADER_DFU_OTA_MAGIC _sd_inited = (gpregret == DFU_MAGIC_OTA_APPJUM); // Start Bootloader in BLE OTA mode _ota_dfu = (gpregret == DFU_MAGIC_OTA_APPJUM) || (gpregret == DFU_MAGIC_OTA_RESET); // Serial only mode bool const serial_only_dfu = (gpregret == DFU_MAGIC_SERIAL_ONLY_RESET); bool const uf2_dfu = (gpregret == DFU_MAGIC_UF2_RESET); bool const dfu_skip = (gpregret == DFU_MAGIC_SKIP); bool const reason_reset_pin = (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk) ? true : false; // start either serial, uf2 or ble bool dfu_start = _ota_dfu || serial_only_dfu || uf2_dfu || (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && reason_reset_pin); // Clear GPREGRET if it is our values if (dfu_start || dfu_skip) NRF_POWER->GPREGRET = 0; // skip dfu entirely if (dfu_skip) return; /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ // DFU button pressed dfu_start = dfu_start || button_pressed(BUTTON_DFU); // DFU + FRESET are pressed --> OTA _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) dfu_start = 1; // App mode: Double Reset detection or DFU startup for nrf52832 if (!(just_start_app || dfu_start || !valid_app)) { #ifdef NRF52832_XXAA /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup * to support auto programming from Arduino IDE * * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. */ bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL, false); #else // Note: RESETREAS is not clear by bootloader, it should be cleared by application upon init() if (reason_reset_pin) { // Register our first reset for double reset detection (*dbl_reset_mem) = DFU_DBL_RESET_MAGIC; // if RST is pressed during this delay (double reset)--> if will enter dfu NRFX_DELAY_MS(DFU_DBL_RESET_DELAY); } #endif } if (APP_ASKS_FOR_SINGLE_TAP_RESET()) { (*dbl_reset_mem) = DFU_DBL_RESET_APP; } else { (*dbl_reset_mem) = 0; } // Enter DFU mode accordingly to input if (dfu_start || !valid_app) { if (_ota_dfu) { led_state(STATE_BLE_DISCONNECTED); if (!_sd_inited) mbr_init_sd(); _sd_inited = true; ble_stack_init(); } else { led_state(STATE_USB_UNMOUNTED); usb_init(serial_only_dfu); } // Initiate an update of the firmware. if (APP_ASKS_FOR_SINGLE_TAP_RESET() || uf2_dfu || serial_only_dfu) { // If USB is not enumerated in 3s (eg. because we're running on battery), we restart into app. bootloader_dfu_start(_ota_dfu, 3000, true); } else { // No timeout if bootloader requires user action (double-reset). bootloader_dfu_start(_ota_dfu, 0, false); } if (_ota_dfu) { disable_softdevice(); } else { usb_teardown(); } } }帮我分析一下这个代码,然后帮我修改物理按钮检测这个逻辑,我两个按键都是高电平有效,帮我加上BUTTON_FRESET跟BUTTON_DFU检测逻辑,逻辑是:保持BUTTON_DFU按下高电平,然后BUTTON_FRESET搞定电平复位系统,如果BUTTON_FRESET双击时间小于500毫秒就进去DFU
07-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AizBot

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

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

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

打赏作者

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

抵扣说明:

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

余额充值