- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在 NCS 中有多种的 DFU 选择,强烈推荐使用 MCUboot ,当然如果你需要选择传统的 nrf_DFU 也是可以的,但是要用到官方修改的源文件.
关于 mcuboot ,原理性的东西在官网和官方博客中有讲,可以自行查看,后面只是简单的提一下 : MCUmgr — Zephyr Project Documentation (nordicsemi.com) 。
为什么要使用mcubooot,原因是在NCS使用mcuboot,可以很快的实现DFU的加入,可以同时加入ble-dfu和uart-dfu。就是加入几个宏定义的事情,就完成了在SDK中可能要花很久才能完成的工程移植,既有ble也有uart的升级.
这里是为了便于后续工程直接调用:
第一步:在 nrf\subsys 目录下的 CMakeLists.txt 文件中加入我们需要加入的库的目录文件 。
打开 CMakeLists.txt 文件在最后加入:
add_subdirectory_ifdef(CONFIG_NRF_DFU nrf_dfu) 。
add_subdirectory_ifdef(CONFIG_NRF_DFU_RPC_NET nrf_dfu) 。
第二步:在 nrf\subsys 目录下的 Kconfig 文件中加入对即将添加到 nrf 库的库文件 nrf_dfu 的宏定义配置文件应用:
打开 Kconfig :加入 rsource "nrf_dfu/Kconfig" ,如果不加入,当我们在工程项目的 .conf 文件中加如引用文件的宏时会找不的相关配置宏定义而报错:
第三步:在 nrf\subsys 目录下建立一个文件夹:名字和我们刚刚写到 CMakeLists.txt 和 Kconfig 文件中的名字一致,名为: nrf_dfu 。
。
第四步:把我们需要的 .c 和 .h 文件导入,那导入的是什么文件呢,也就是 SDK 中的 DFU 使用到的相关源文件,但是请注意,由于需要移植到 zephyr 中,不在是传统的 SDK 了,有些底层文件可能要做部分修改,当然这一部分已经不需要我们来做了,原厂已经帮我们修改好了这一部分代码,我们可以直接拿来用.
主要修改的有 nrf_dfu_ble.c 、 nrf_dfu_types.c 等文件,当然不是全部修改,但是修改起来很麻烦,你需要知道整个原理,还需要了解 nordic 的 nrf bootloader 升级流程和相关函数调用。本着已经有轮子,我们就不要去造轮子的原则,我直接拿来使用。那么如何获取官方开发人员已经修改好的文件呢?相关文件工程已经同步到了 github 上,连接如下: aiminhua/ncs_samples: nRF connect SDK samples (github.com) 。
。
Ncs_samples 目录下可以下载整个项目,包含了不止 DFU 的实例,下载后可以进入到 2 所显示的目录,看到 3 处显示的目录,这个是 NCS2.2 版本已经添加好的 nrf bootloader 方式升级的文件,你可以选择直接把这个 nrf_dfu 文件加入,然后替换掉 nrf\subsys 目录下的 CMakeLists.txt 和 Kconfig 文件,就直接使用.
但是我这还是麻烦的一下,不直接使用,只是获取到官方修改过的 .c 和 .h 文件然后再 NUS 的透传例程上进行直接添加得到一个 BLE 的 OTA 例子.
(总结:虽然我们不会造轮子,但是有了轮子,我们要知道这么把轮子更好的安在车上,让车跑起来,而不是只知道把轮子粗暴安上把车开起来就行,那么可能出问题的时候就一脸蒙了) 。
。
在 nrf_dfu 文件中建立两个文件夹,一个是 inc ,放置 nrf bootloader 所有使用到的 .h 文件,还有一个就是放置 .c 文件的 src 文件夹。并把从 github 上下载下来的文件中的 .c 和 .h 文件都放到对应的文件中,注意把有几个 .c 文件就放置在了 nrf_dfu 目录下,如果你想把他们也放到 src 中,一定要更改 CMakeLists.txt 中的路径配置,每一个库都要有 CMakeLists.txt 和 Kconfig ,便于编译时编译链找到你的文件.
所以整理一下目录结构如下:
第五 步:添加我们 nrf_dfu 库文件下的 CMakeLists.txt 和 Kconfig 文件内容.
1 )、对于 CMakeLists.txt 有 如下定义:
可以看到在 1 处我们使用 zephyr_include_directories 加入了相关头文件存放的文件夹(就和 keil 中加入头文件一样),在 2 处使用 add_subdirectory_ifdef 加入了会使用到的 .c 文件存放的文件夹; 3 处即为要在 src 文件夹中去找 slip 文件(或者在 src 文件夹中的 CMakeLists.txt 中那 slip.c 给加入,两种方式任选一种).
。
2 )、对 Kconfig 配置定义了我们可以使用到的一些 kconfig ,主要关注的是在 CMakeLists.txt 中我们定义的相关红定于是否在 kconfig 中做了申明:
编写 Kconfig 的部分规则:
config xxxx /* 本次配置的名称 */ xxxx /* 表示改配置的类型(bool、int、hex(十六进制)、string、tristate(三太类型)) */ type “xxxxxx” /* 简单描述 */ default x /* 初始值 */ depends on xxxx /* 依赖的选项 */ help “xxxx” /* 帮助信息,一般是注释 */
menu 语法以 menu 开始, endmenu 结束。中间包含若干项 config 配置 , 当然也可以包含其他语法.
例如:
menu " test menu " config TEST_MENU_A tristate " menu test A " config TEST_MENU_B bool " menu test B " default n endmenu
。
第六步:制作 .c 文件夹里面的 Kconfig 文件:
把 src 文件夹中所有的文件都加入到 Kconfig 中, 。
这里为什么少了一个起那么第 6 步移动的 slip.c 文件的加入,因为起那么说了,两种方式任选一种加入即可,这已经在外层文件的 CMakeLists.txt 中要应用时,加入了改文件,所以可以不用写 slip.c 了.
到这就算已经加入了 nrf bootloader 的相关库了.
打开 vs code ,找到 peripheral_uart 例子然后打开(如何打开相关操作可以看 nordic 的入门教程),然后我们编译一下,可以看到肯定是能编译成功的.
在 NUS 例程中已经有一个原始的 .conf 文件了,那么只要往里面计入宏定义即可 , 没有的话,自己添加一个和板子型号一致的名字命名的 .conf 文件.
这已经有了,我们打开后加入如下语句:
在截图中我们加入了两个宏定义
CONFIG_NRF_DFU=
y
CONFIG_NRF_DFU_BT
=y
那他们是哪里来的,为什么是这两个宏定义?回答是他们是我们刚刚制作 nrf_duf 库时自己亲手设置的。在第 2.1 的第六步中的添加的 CMakeLists.txt 文件中,有如下两条语句:
#添加使用的的.c文件所在的在的目录
add_subdirectory_ifdef(CONFIG_NRF_DFU src)
#如果定义 CONFIG_NRF_DFU_BT ,就加入ble的DFU
zephyr_library_sources_ifdef(CONFIG_NRF_DFU_BT nrf_dfu_ble.c)
可以看到分分别使用是两个语句加入了头文件和加入了 nordic 的 ble 的 DFU 的 .c 文件,他们都是说如果定义了什么什么就加入什么。到这就相信大家可以很好理解了,那么我们可以改嘛,当然可以,但是记住,如这里改了名字请在相应的 Kconfig 中也改对应,否则编译器还是不能正确找到并编译链接,以上两个宏定义在 Kconfig 中对应的定义如下:
加入后我们直接进行编译,记住保存后,点击全编译:
编译后没有编译正确,报了一个错:
说 zephyr\include\zephyr\dfu\flash_img.h:32 目录下的 flash_img.h 文件的 32 行居然没有定义,那直接在 .config 中加入该宏定义,定义为 4K 。发现居然打了波浪线,怀疑肯定有其依赖项没有加入,我们先编译一下看一下是否通过,编译后依然一样的报错,且查看编译后的 autoconf.h 文件也确实没有(他包含了工程中所有需要用到宏定义),每次加入,要从新全部构建,具体查看路径是在我们建立的工程目录下如下路径:
。
因此我们要确定其依赖项,报错说是 flash_img.h 文件找不到,且编译器也告诉我们路径在哪里了,那我们直接过去看一下该路径下有没有 Kconfig 文件:
。
发现居然我没有,却其上一级目录也没有,那可能是在 flash_img.c 文件处了。而不是在 flash_img.h 文件处,我直接在整个文件夹中去找 flash_img.c (这里一般使用工具查找,很快就可以找到).
找到 flash_img.c 文件
。
在去上一级的目录果然找到一个对应的 Kconfig 文件:
。
打开该文件,然后全局搜索 CONFIIG_IMG_BLOCK_BUF_SIZE, 注意这里要去掉 config 进行搜索,即使用 IMG_BLOCK_BUF_SIZE 进行搜索,果然我们找到了该定义:
可以看到 CONFIIG_IMG_BLOCK_BUF_SIZE 有一个依赖项 MCUBOOT_IMG_MANAGER ,所以我们还要加入 MCUBOOT_IMG_MANAGER 这一个依赖项,即加入 CONFIG_MCUBOOT_IMG_MANAGER 到 .conf 中,大家可以看出来了,在定义 Kconfig 中的定义时时要去掉前面的 CONFIG .
其中 depends on xxx 表示需要依赖于 xxx 的意思,前面有对 kconfig 语法进行了基本接收,不知道的请翻看前面.
在加入后依然没有去掉红色波浪线,且编译依然有问题,那可能 CONFIG_MCUBOOT_IMG_MANAGER 依然有依赖或者这个 Kcongig 文件有依赖.
查找后可以看到如图的定义:
需要定义 IMG_MANAGER 才能启用 MCUBOOT_IMG_MANAGER ,那么我们把 CONFIG_MCUBOOT_IMG_MANAGER 加入到 .cofig 中去,然后再次全编译.
。
没有报错,说明到目前为止我们加入正确,那么下面可以开始 main.c 中加入相关的 DFU 代码了.
。
添加头文件:
#include " nrf_dfu.h " #include " nrf_dfu_validation.h " #include <drivers/nrfx_errors.h> // 加入重启系统的头文件 #include <zephyr/sys/reboot.h>
加入系统重启的原因是,如果在 DFU 中出现错误而终止,应该复位系统,等待再次的 DFU .
代码加入:
/* *@brief Function for handling DFU events. */ static void dfu_observer(nrf_dfu_evt_type_t evt_type) { switch (evt_type) { case NRF_DFU_EVT_DFU_STARTED: case NRF_DFU_EVT_OBJECT_RECEIVED: break ; case NRF_DFU_EVT_DFU_COMPLETED: case NRF_DFU_EVT_DFU_ABORTED: LOG_INF( " resetting... " ); // 处理一条挂起的log,打印完成后才进行系统复位 while (log_process()); sys_reboot(SYS_REBOOT_WARM); break ; case NRF_DFU_EVT_TRANSPORT_DEACTIVATED: // Reset the internal state of the DFU settings to the last stored state. LOG_INF( " NRF_DFU_EVT_TRANSPORT_DEACTIVATED " ); nrf_dfu_settings_reinit(); break ; default : break ; } } int dfu_init( void ) { int ret_val; ret_val = nrf_dfu_settings_init( true ); if (ret_val != NRF_SUCCESS) { LOG_WRN( " dfu settings init err %d " , ret_val); } ret_val = nrf_dfu_init(dfu_observer); if (ret_val != NRF_SUCCESS) { LOG_WRN( " dfu init err %d " , ret_val); } return ret_val; }
可以看到,首先会使用 nrf_dfu_settings_init() 函数初始化校验页.
。
改名字后编译一个 APP ,在我们的 build_5340\zephyr ( build_5340 是我给我的编译目录起的名字,你也需要在你自己的建立目录在找到)目录下找到 merged.hex 名字的 hex 文件,复制一下,然后杂工程的首目录下建议一个文件件(我建立的名字为 updata ) 。
。
然后把刚刚的 merged.hex 放到该文件中,使用 nrfutil 进行升级固件制作.
建立一个 .bat 的脚本,或者在该目录( updata )中直接运行命令窗口,运行如下 。
nrfutil pkg generate --application merged.hex --application-version 2 --hw-version 52 --sd-req 0x00 new .zip pause
由此生成了一个压缩文件:
把这个文件给到手机,然后连接上蓝牙,点击 DFU ,选择 ZIP 格式,然后选择文件,我们就可以看到如下界面了:
。
等待更新完成即可,这里注意的是使用 nrf bootloader 最高速度只到 18KB/s, 由于还需要校验等步骤,只会更慢,在我的截图上最高只到了 14kB/s, 平均只有 5.9KB/s 。因此一般不建议使用这种升级协议。建议使用 MCUboot .
简单一点来说,一个 bootloader 应该包括两个部分,一个是 DFU 时数据传输协议部分、一个是对 image 的管理部分.
注意一点,使用MCUboot时,传输的升级固件是app_update.bin,后续生成固件后,路径如下: 编译生成文件件\zephyr 下。后续就不再说明了.
DFU 协议一般都是把升级的 image 文件分成一块一块的传给 bootloader ,在传输过程中, DFU 会完成校验每一块的数据对不对,出现错误处理等工作。值得注意的是 DFU 协议不管你物理通道是什么,你可以选择 ble 、 uart 、 USB 等。同时在 zephyr 中有许多的协议,只对 SMP 做一下简单介绍.
SMP- simple management protoco(简单管理协议), 在 nordic 的 NCS 支持包中, mcumgr 模块就是使用了 SMP 协议.
Mcumgr 模块使用命令组(我的立即这就是一组函数)的方式去操作 SMP 协议进行数据的传输。这种方式对于 mcumgr 来说,只管把他需要 SMP 做的事高数 SMP , SMP 会去完成组包,解包等操作, mcumgr 等着 SMP 返回的结果就.
在 mcumgr 中有两个命令组是和 DFU 有关的:
1) 、 img_mgmt : image 管理命令组,包括 3 个三个命令集 4 个具体的命令( 4 个函数):
。
2) 、 os_mgmt : OS (系统)管理命令组,包括三个命令集 4 个具体命令(函数):
。
在后续的例程中我们会使用两条宏定义去启动上面的两个命令组.
。
。
在 MCUboot 中是根据其定义的一个变量 swap_type ,其确定了是等待升级还是跳转 APP 。 swap_type 的值由三个参数决定,官网上有这样一张图,通过它们不同的组合,导致 swap_type 有 6 种不同的值,如下图所示:
。
1)、BOOT_SWAP_TYPE_TEST:
在升级后,必须调用boot_write_img_confirmed()来把image_ok 这个参数写为 1 。再次启动时 MCUboot 才会认为新的 image 正确,否则回滚.
。
2)、BOOT_SWAP_TYPE_ PERM:
在升级后不管 image_ok 的值,默认新的 image 是正确的,没有回滚.
。
3)、BOOT_SWAP_TYPE_ REVERT 。
一旦检测到,就直接进行回滚.
。
4)、BOOT_SWAP_TYPE_ NONE 。
不进行 DFU , MCU 直接跳转到 app 运行.
。
5)、BOOT_SWAP_TYPE_ FAIL 。
MCUboot 在进行对 primary slot 进行校验时没有通过,程序将一直死在 MCUboot .
。
6)、BOOT_SWAP_TYPE_ PANIC 。
当 MCUboot 启动时出现致命错误时,出现也将死在 MCUboot 。 我们要程序能运行到 APP ,那么就要保证 swap_type 的值为 BOOT_SWAP_TYPE_TEST和BOOT_SWAP_TYPE_ PERM,那么我们怎么设置这两种类型呢?需要使用函数boot_request_upgrade(bool choose)。 当参数 choose = flash时为 BOOT_SWAP_TYPE_TEST 当参数 choose = true时为 BOOT_SWAP_TYPE_ PERM 。
三个参数的取值如下:
m agic:全为FF和0x96f3b83d( Good ) 。
image_ok : 全为FF ( 或者 Any、Unset ) 和 0x 01 。
copy_done:全为FF ( 或者 Any、Unset ) 或者 0x 01 。
(any 表示可能是 0x11 ,等非 0x01 的值 ) 。
如下图就为 MCUboot 打印的信息,我们可以通过其判断现在是那种模式:
对应前面的第四种模式,红色框部分为当前 MCUboot 标志位的值,也不用我们一个一个去对,然后查表确定当前 MCUboot 的状态。只需要看 Swap_type ,也就是黄色框部分就知道是处于第几种模式,然后就可以对应当前是属于 6 中状态中的那种状态了。如果状态不对,就可以根据前面的状态解释来进行修改.
。
为什么要加一个安全,在 MCUboot 官网中有这样一个解释,为什么你在出门期间你信任你的家是安全的,因为你家只有一道门,由此有这样一个逻辑:
由此如果你口袋中的钥匙不见了,那么你将失去对门的信任.
在每次复位或者上电后运行安全 bootloader ,会通过验证 bootloader 中的下一个 image 的签名和元数据来建立一个信任根本,就像确定钥匙是不是唯一且在自己的口袋中,如果任意一次验证不通过,那么 bootloader 就好停止,由此保证 bootloader 中的下一个 image 在被篡改后不会启动。由此杜绝攻击者通过更改固件来入侵接管设备,以保证安全性.
总结:
1) 、不可变的: bootloader 是 flash 锁定的,如果不擦除整个设备,无法进行修改或者删除.
2) 、安全的:拥有一套验证机制,保证自己和用户 app 的安全,不会被攻击者篡改。因此要提供自己的秘钥,让 bootloader 进行签名验证(下一个 image 是否被篡改)和元数据( Metadata )校验(检测 image 是否兼容).
注意本章节添加的,不管是 ble 的还是 uart 的,在升级的时候都是后台式的,升级是你可以正常使用.
依然选择在 peripheral_uart 这个 NUS 例子中加入 MCUboot 的 DFU 功能。那具体要用到那些宏定义配置,如何查看:
1)、一是有参考例程:zephyr\samples\subsys\mgmt\mcumgr\smp_svr, 该路径的例子中有全部的 config 宏定义,只要对比加入就行 。
2)、参考官方网站的说明进行加入: Adding a bootloader chain — nRF Connect SDK 2.3.99 documentation (nordicsemi.com) .
这里值得注意的是,不同版本之间这些 CONFIG 配置是会变的,所以在自己加入时一定要去看上面提到的参考,而不是无脑的复制粘贴。不然很可能你的版本和本文的版本不一样,导致你初始的工程编译都通过不了,稍后我会列举一下 NCS2.1 和 NCS2.3 的区别.
本次在 NUS (透传)程序的基础上添加 MCUboot ,例子路径 :nrf\samples\bluetooth\peripheral_uart 。使用 VS code 建立一个该工程的映像,并且编译通过后,开始 DFU 的添加.
注 1:该全局宏定义版本是NCS2.1 。
# #~~~~~~~~MCUboot加入~~~~~~~~~~~~ #确保生成与MCUboot兼容的二进制文件 CONFIG_BOOTLOADER_MCUBOOT = y #启用mcumgr CONFIG_MCUMGR = y # ~~~~~ 启用大多数的核心程序,就是前面说的对SMP的命令组的启用,用于DFU传输~~~~~~ ## 用于DFU支持已添加到应用程序中,因此需要重置芯片,以将控制权交给MCUboot,MCUboot将验证新上传的图像并将其与旧映像交换。最后,BOOTLOADER_MCUBOOT将MCUboot作为子映像包含在内 ## #启用用于imager管理的处理程序(传输 /列举/ 校验确认imager) CONFIG_MCUMGR_CMD_IMG_MGMT = y #启用用于操作系统管理的处理程序(用于校验出错时的回滚,以及复位操作) CONFIG_MCUMGR_CMD_OS_MGMT = y # 允许传输大容量的数据包 CONFIG_BT_L2CAP_TX_MTU = 252 CONFIG_BT_BUF_ACL_RX_SIZE = 256 # 启用mcumgr SMP 传输,SMP是传输协议。 CONFIG_MCUMGR_SMP_BT = y #同时确保禁用掉加密传输和连接时的身份验证(如果有绑定等功能的一定要关闭) CONFIG_MCUMGR_SMP_BT_AUTHEN = n # 把堆栈从原来的2048改为4096,为某些需要大堆栈的命令提供空间 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE = 4096 #由于要加入DFU到主线程,增加一点主线程的堆栈空间 CONFIG_MAIN_STACK_SIZE = 2348 # # ~~~~~~~~MCUboot加入结束~~~~~~~~~~~~
。
注 2 :这对于 NCS2.3 及之后版本(本文编写时更新到 NCS2.3 版本):
除了在 NCS2.1 中加入的全局宏,还应该多加入一条全局宏.
## NCS为 NCS3. 1 以及以后需要加入,启用并注册一个SPM 进程,就不用在在main中添加代码了。 CONFIG_MCUMGR_CMD_STAT_MGMT =y
。
第二步:头文件加入:
注 3: 针对于 NCS2.1 。
头文件加入:
#include <zephyr/mgmt/mcumgr/smp_bt.h> #include " os_mgmt/os_mgmt.h " #include " img_mgmt/img_mgmt.h "
注 4: 如果是 NCS2.0 头文件加入是:
#include <mgmt/mcumgr/smp_bt.h> #include " os_mgmt/os_mgmt.h " #include " img_mgmt/img_mgmt.h "
注 5:如果你的是NCS2.3版本以后,已经不需要在加入头文件了.
。
第三步: DFU程序加入:
注 6: 针对于 NCS2. 及 NCS2.1 。
官方例程中需要加入的是函数有三个,只有加入他们到 main() 进行初始化,才可以在初始化时把 SMP 相关的代码加入,如果你不加入,会导致下面的第五步升级包选择后出现错误,由于错误无法进行数据的传输:
smp_bt_register(); os_mgmt_register_group(); img_mgmt_register_group();
注 7 : 针对于 NCS2. 3及以后 。
由于官方对底层驱动又做了升级,单独使用一个进程对 SMP 进行初始化,只要使用一条宏定义开启进程,就不用添加这一步分代码了,已经通过——注 2 所列出的全局宏加入了进程,如下图所示 , 使用 MCUMGR_HANDLER_DEFINE(), 添加了一个进程用于初始化,而且想对应于 NCS2.1 使用了 static 定义了 。
img_mgmt_register_group(); 函数,也无法在 main() 进行添加了.
然后就可以编译了,编译成功后,直接下载,就可以使用 nordic 的官方 APP —— nrf connect 搜索蓝牙广播的名字,连接后,可以看到已经有了 DFU 的服务.
。
在工程中修改一下蓝牙广播的名字改为 Nordic_NEW,然后重新构建工程:
。
然后你可以在你的工程文件的编译子文件中找到升级的 bin 文件。如我在建立工程时,给编译的子工程起的名字为 bulid_5340
。
那么在我的工程目录下的 zephyr 中会找到一个 app_updata.bin 文件,这就是我们新的 app .
传到手机上,在 nrf connect 中连接设备,然后点击右上脚的 DFU 图标,选择 app_updata.bin 文件,然后,如图进行操作:
。
等待升级完成,,断开连接,复位一下,就可以看到广播名字改变了.
。
打印信息也没有问题,先是 test ,进入 DFU 升级,升级完成后,复位板子, MCUboot 模式为 none ,表示正常启动,升级成功.
如果你使用的版本和我本文的不同,且按照上述步骤不能升级,很有可能就是官方做了升级,改变了部分流程,请一定一定去参看一下路径:zephyr\samples\subsys\mgmt\mcumgr\smp_svr下的例子,我以上的更改也是根据这个来更改的.
。
如果你使用过 SDK 开发,那么在 DFU 时第一步就是生成自己的秘钥并进行替换。同样在 NCS 中实际项目中也请使用自己的秘钥.
在前面的升级中使用了 NCS 中自带的秘钥,这是不安全的,你可以在编译结果中找到如下一个警告:“ MCUBOOT 的秘钥使用的是默认秘钥,不能用于生产使用。” 。
。
秘钥一定要使用自己生成的,而不是使用 NCS 中自带的,而且最好保证秘钥存储的位置不在你的项目中.
还有一点就是:测试的时候你如果使用默认秘钥,那么在每一次删除项目进行重新构建默认秘钥都是可能被更改的,由此导致你不能使用新的 APP 对使用老版本固件的设备进行升级,因为你的秘钥已经在构建过程中改变了。所以最开始的固件也最好使用新建立的工程从新烧录.
在开始前,我先把官方文档参考连接给出(注意,如果你使用的版本和我的( NCS2.3 )不一样,那么请切换到和你版本一致的问的文档):
Firmware updates — nRF Connect SDK 2.3.99 documentation (nordicsemi.com) 。
版本切换如下图:
值的注意的是虽然生成秘钥的加密算法有许多中,但是 NCS 中的只支持部分,所以我们并不能随意的选择.
NCS 中支持三种方式生成秘钥,分别是:
如下截图是官方文档中给出的不同的 bootloader 支持所支持的秘钥加密算法。生成的时候一定不能使用不支持。否则 DFU 会失败.
要使用 openssl生成秘钥,那么我们就需要有openssl这个插件,你可以在网络上直接搜索openssl安装,在你的PC端安装一个openssl。如果你电脑安装的有 Git、VMware、Strawberry 等,那么由于这些软件都自带 openssl,因此可以直接使用.
首先可以直接运行一下 win+R键运行CMD,打开命令行窗口,运行一下如下命令 。
openssl help
结果:
居然提示不能运行,但是我电脑确定已经装了 git 的,并且在 git 运行相同的命令是可以得到响应的。由此应该由于没有把 openssl 的路径加到环境变量中,导致电脑找不到 openssl 。因此打开 git 的安装目录在 usr/bin 文件下成功找到了 openssl 的 exe 文件:
。
注意如果你也是这种方式,那么请确定好你 git 安装的路径。 接下来我复制该路径,打开环境变量并把该路径加入带系统变量中,流程看下列截图,按照箭头的指示一路下来:
添加并确认完毕后,再次打开 cmd 命令行窗口,记得一定要在添加完成后再次打开,不然原本是窗口由于没有刷新,依然不能运行 openssl.exe 。 。
添加完毕再次运行命令:
openssl help
成功:
。
环境没有问题了,那么就可以开始制作秘钥了.
在我的工程文件中新建立一个名字为 key 的文件,然后再该文件中打开 cmd 窗口:
。
在打开的窗口运行一下指令确定依然可以在该路径下执行 openssl.exe .
。
MCUboot 支持的几种加密算法都是非对称加密的算法,会生成一对秘钥对,分别为私钥和公钥,公钥直接写入到 MCUboot 中,在 NCS 中我们只用把生成的私钥加入到我们的工程中,私钥或在程序的运行下生成公钥,并写入到 hex 中。同时也对固件进行加密.
选择 RSA-2048 算法来制作秘钥对:
私钥生成命令
openssl genrsa - out private .pem 2048
然后就可以在文件夹中看到生成的秘钥了:
。
imgtool是一个NCS库中已经包含了的脚本工具,具体路径为:
NCS目录/ bootloader/mcuboot/scripts/imgtool.py 。
因此要在想生成秘钥的文件夹中加入该脚本的绝对路径,如我在前一节的工程先建立的 key文件夹下建立另一个文件imgtool,然后确定绝对路径,之后开始制作秘钥 。
命令如下:
python ../../../../v2.3.0/bootloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k priv.pem 。
。
由于我电脑是 python ,没有 python3 ,所以我直接使用 python ,需要根据自己的环境进行选择:
Python 3 ../../../../v2.3.0/bootloader/mcuboot/scripts/imgtool.py keygen -t ecdsa-p256 -k priv.pem 。
命令如下:
python ../../../../v2.3.0/nrf/scripts/bootloader/keygen.py --private -o priv.pem 。
或者:
Python3 ../../../../v2.3.0/nrf/scripts/bootloader/keygen.py --private -o priv.pem 。
主要路径是 nrf/scripts/bootloader/keygen.py ,你要根据你需要生成的秘钥的文本去定位该路径的绝对路径.
。
然后可以在工程目录下创建一个文件夹(可以任意,只要找得到秘钥的即可),该文件夹名字必须定义为 child_image ,并且在其中定义一个 mcuboot.conf 文件 , 为什么要求这么严格,因为自动建立脚本已经把响应的子文件的名字都确定好了,所以为了可以在建立时被应用到,必须按照要求来,加入完毕目录结构如下( peripheral_uart_23 为项目的根目录):
然后再 mcuboot.conf 中加入如下的宏定义:
CONFIG_BOOT_SIGNATURE_KEY_FILE= " D:/nordic_NCS/work/peripheral_uart_23/key/priv.pem "
其中 D:\nordic_NCS\work\peripheral_uart_23\key 为我放置私钥的文件位置.
建立编译后可以在编译结果中找到如下的编译信息,证明替换没有问题.
。
先烧录刚刚的固件,然后改一改蓝牙广播名字,再次建立工程。然后依然是在建立的工程目录下 build_5340\zephyr ( build_5340 是我自定义的编译输出文件的名字,和自己定义的进行匹配),找到 app_update.bin ,发送到手机上,然后打开手机连接上设备.
可以看到设备现在名字是 NORDIC_TEST ,点击 DFU ,开始选择升级固件 。
。
选择固件:
。
然后开始升级等待,这个过程手机最好不要退出去,保持在这个界面,不然很可能导致失败:
。
升级完成,断开连接再次扫描,会看到设备的 ble 名字已经变了:
。
Rtt 打印信息如下:
由此测试完毕.
问题解决:在升级的过程中,有遇到有些手机会由于文件系统原因,导致发送失败,如果你有同样的现象,可以换一个文件管理系统进行测试.
由于我使用的是 Windows ,所以我下面制作的是 Windows 上的 mcumgr.exe .
需要使用 mcumgr 的功能,那么上位机端也应该支持 mcumgr 的传输协议 SMP ,否则没有办法进行传输。有由于支持 mcumgr 的上位机命令行工具是使用 go 语言开发的,所以第一步就是应该在 Windows 端安装 go .
1) 、下载 go 的安装包 。
https://golang.google.cn/dl/ 打开 go 的官网进行下载,根据你的系统选择对应的安装包进行下载.
由于我的是 win11 的 64 位机,所以下载第一个,如果你的系统是 macOS 或者是 linux ,那么请根据你自己最终运行的平台去选择适合的 go .
。
。
下载完成后点击 msi 文件进行安装,可以选择你的安装路径,然后一路 next 下去就行.
安装好后,确定系统环境变量中是否已经把 go 安装目录下的 bin 文件路径包含进去了:
。
然后我们 win+R 输入 cmd 后在命令行窗口输入 go 后回车:看到如下打印,说明 go 环境安装完成:
。
2) 、下载 mcumgr 。
开始使用 go 安装 mcumgr 的上位机工具 ( 相关命令参考为 zephyr 官网给出的文档,连接地址如下:
MCUmgr — Zephyr Project Documentation (nordicsemi.com) )
获取 MCUmgr 命令 ( 需要在有 go.mod 的目录运行,请看问题解决 ) :
go
get
github.com/apache/mynewt-mcumgr-cli/mcumgr
如果你无法获取,并报错,请参看后续问题解决,主要是网络原因.
下载完成后:还是无法运行 mcumgr ,请使用如下命令进行安装 。
go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest
如果 mcumgr 安装成功,那么在 go 的 bin 目录下会有 mcumgr.exe 文件 。
。
安装完成请确保已经把 go 的安装目录下的 bin 文件所在目录加到了环境变量中.
然后我们任意启用一个 cmd 命令窗口,输入 mcumgr ,回车,如下图,证明安装成功了.
问题解决 1- 缺少 go.mod 报错:
如果你安装的 go 改过默认路径或者版本原因,那么可能导致在安装文件中没有 go.mod 文件,在执行命令时会导致无法安装.
解决方式:在 go 安装路径下:
建立一个新的文件夹并命名为 mod 。
。
然后我们进入这个文件夹,并输入一个 cmd ,打开命令行窗口:
然后运行指令:
go mod init mod
。
。
然后就生成了运行时缺少的 go.mod 文件了:
。
然后继续运行 mcumgr 的安装命令进行安装即可.
。
问题解决 2- 网络原因下载失败:
解决了问题1,到这一步还可能会安装失败:
具体原因:默认 go 的代理网站是 GOPROXY=https://proxy.golang.org,direct ,是一个外网地址,国内访问不到 ( 开 VPN 可能访问到,但是我测试时,开 VPN 也没有用,改代理网站没有问题 ) , 。
1 )、第一种方式时修改代理镜像 。
代理网站更改命令:
go env -w GOPROXY=https: // goproxy.cn,direct
更改后,再去执行 go set 命令下载 mcumgr .
go
get
github.com/apache/mynewt-mcumgr-cli/mcumgr
2 )、第二种方式——直接拉取(前提是安装的有 git 环境) 。
先进入到 go 的安装目录下的 src 文件夹,然后再在 src 文件夹下打开 cmd 命令窗口,然后使用 git 。
直接在 github 上拉取工具的源码.
拉取命令如下:
Git clone github.com/apache/mynewt-mcumgr-cli/mcumgr 。
拉取完成,安装即可.
。
正常创建工程,只需要使用如下几个宏定义就行,就是加入到工程的 .conf 配置文件中配置就行了.
# #确保生成与MCUboot兼容的二进制文件 CONFIG_BOOTLOADER_MCUBOOT = y # #启用mcumgr CONFIG_MCUMGR = y # #启用用于操作系统管理的处理程序(用于校验出错时的回滚,以及复位操作) CONFIG_MCUMGR_CMD_OS_MGMT = y # #启用用于imager管理的处理程序(传输 /列举/ 校验确认imager) CONFIG_MCUMGR_CMD_IMG_MGMT = y # #启动uart传输的宏定义 CONFIG_MCUMGR_SMP_UART = y CONFIG_CONSOLE =y
这里的正常是什么意思呢?就是说你 APP 中没有使用到 UART 去做数据通讯, NUS 透传的例子( peripheral_uart ),原本已经使用了 UART0 作为应用中的数据通讯外设,这个时候不能再用 UART0 来作为 mcuboot 的升级传输口了,至少是现在的 NCS_2.3 版本是不行的,如果要做,那么工作量可能也会很大,不建议自己再去改底层.
由于我使用的测试工程就是 peripheral_uart ,已经默然使用 uart0 作为透传历程了,我只能进行更多的修改。 根据不同的需求有两种更改方式:分别是使用 UART0 作为 mcuboot 的升级串口,使用 UART1 或者 UART2 等其余串口作为 mcuboot 的串口,二种方式都需要芯片支持多个 uart ,直接在 app 中触发升级。还有就是直接使用一个串口,升级必须在 mcuboot 中.
这个的好处是可以实现回滚.
a、 修改 app 中的 UART 为 uart1 。
在工程中修改或者添加一个可引用的 overlay 文件,添加时可以选择命名为 app.overlay 或者 _Board_.overlay(_Board_ 表示板子的名字 ) .
打开我工程下的 app.overlay, 使能 uart1 ,设置引脚,并进行引用,修改完成后如下:
/ { chosen { nordic,nus -uart = & uart1; }; pinctrl: pin - controller { nus_uart: nus_uart { phandle = < 0xb > ; group1{ psels = < NRF_PSEL(UART_TX, 1 , 8 ) > ; }; group2{ psels = < NRF_PSEL(UART_RX, 1 , 6 ) > ; bias -pull- up; }; }; }; }; & uart1 { status = " okay " ; pinctrl - 0 = < &nus_uart > ; };
如果你直接修改,注意 phandle = < 0xb >;要确定和最终的zephyr.dts的不重复,就是一个文件不要有两个都为0xb.
以上添加方式不理解可以看如下官方给出的设备树说明博客: 详解 Zephyr设备树(DeviceTree)与驱动模型 - jayant97 - 博客园 (cnblogs.com) 。
由以上代码我们启动了 uart1,并配置的P1.08和P1.06作为uart1 的通讯引脚.
。
b、 加入宏定义,改变数据通讯 UART的通讯方式 。
在前面提到的 prj.conf文件中加入如下宏定义:
CONFIG_UART_ASYNC_API= y CONFIG_UART_1_ASYNC = y CONFIG_UART_1_INTERRUPT_DRIVEN =n
用于启动 uart1的异步API,禁止中断方式,使用异步的方式通讯,如果你不加入这个三个宏定义,还是采用原本例程默认的,那么在编译下载后,通过RTT打印,你会看到如下报错:
。
。
这是说,在设置 uart 的回调时,返回 -88 这个错误,表示系统不支持。具体原因我没有仔细确定,所以一定要进行修改。 到这一步第一种 uart 升级就算加入完成了,直接下载就行.
2) 、使用双 bank+ 双 uart 升级( uart1 做 smp 传输)这种方式修改的就比较多了,首先是你需要修改 MCUboot ,让其把默认的为 UART0 的传输,改变为 UART1 .
。
a、 工程目录下 .conf 中宏定义的修改 。
首先是 .conf 文件的修改,依然是在启动了 mcuboot 的基础上设置传输方式为 UART ,并且启动异步 API, 同时禁用掉 UART0 的中断回调方式,启动为异步回调
CONFIG_MCUMGR_SMP_UART= y CONFIG_CONSOLE = y CONFIG_UART_ASYNC_API = y CONFIG_UART_0_ASYNC = y CONFIG_UART_0_INTERRUPT_DRIVEN =n
b 、 app (应用程序)的 .overlay 的修改 。
然后是 APP 中的 .overlay 的修改,(也就是我们的主应用,把其他 mcuboot 程序,网络核程序叫做子 image )把 app 中触发升级的串口改为 UART1 ,在工程根目录下的 .overlay 中加入配置代码:
/ { chosen { zephyr,uart -mcumgr = & uart1; nordic,nus -uart = & uart0; }; pinctrl: pin - controller { dfu_uart: dfu_uart { phandle = < 0xb > ; group1{ psels = < NRF_PSEL(UART_TX, 1 , 8 ) > ; }; group2{ psels = < NRF_PSEL(UART_RX, 1 , 6 ) > ; bias -pull- up; }; }; }; }; & uart1 { status = " okay " ; pinctrl - 0 = < &dfu_uart > ; };
启动了 UART1 ,并配置 uart1 为 mcuboot 的 uart 传输通道.
c 、修改 mcuboot ——子 imger 的 overlay 。
打开工程目录,建立一个名字为 child_image 的文件夹:
然后建立一个名字为 mcuboot 的文件夹:
在进入并建立一个名字为 boards 的文件夹:
在再此文件夹中建立一个 -board-.overlay 的文件,其中 -board- 表示的是项目需要使用的板卡文件,可以简单理解为要使用那个系列的芯片就使用对应的名字:
我使用的是 5340 ,且 app 运行在应用核,所以使用 nrf5340dk_nrf5340_cpuapp 这个名字就行。如果你不知道你使用的芯片应该怎么命名,可以在 VS code 中进行查看:
完整的 overlay 文件存放目录如下:
项目文件 \child_image\mcuboot\boards 。
然后在 nrf5340dk_nrf5340_cpuapp.overlay 目录中加入如下的代码:
/ { chosen { zephyr,uart -mcumgr = & uart1; }; pinctrl: pin - controller { dfu_uart: dfu_uart { phandle = < 0xb > ; group1{ psels = < NRF_PSEL(UART_TX, 1 , 8 ) > ; }; group2{ psels = < NRF_PSEL(UART_RX, 1 , 6 ) > ; bias -pull- up; }; }; }; }; & uart1 { status = " okay " ; pinctrl - 0 = < &dfu_uart > ; };
到这一步修改就完成了,我们全编译一下,然后需要查看最终的 dts 文件,看我们进行的修改是否生效,只有生效了才说明是正确的.
。
d 、查看配置是否生效 。
在全编译完整后,在如下目录先可以找到主 image (也就是 app )的 dts 文件—— zephay.dts 查看:
其中 build_5340 是我建立工程时选择的生成文件所在目录的名字(根据你自己设置的文件名确认),可以看到在:建立目录 \zephyr 路径下的 zephyr.dts 文件中按照我们的修改正确配置了.
。
Mcuboot 子 inage 修改的查看:
在 创建目录 \mcuboot\zephyr 路径下的 zephyr 中也是修改成功.
。
到此为止 app 就算制作完了.
在制作完成固件后,你可以在生成一个 app ——修改一下蓝牙的广播名,然后获得升级固件 。
命令:
nrfjprog --com
mcumgr --conntype serial --connstring " COM24,baud=115200 " echo hello mcumgr --conntype serial --connstring " COM23,baud=115200 " image upload -e app_update.bin
把板子连上 nrf 设备后,在命令行窗口运行命令:
nrfjprog --com
获取端口的 COM 号,如图:
。
。
由于我使用的是 nrf5340 ,其有三个虚拟串口,从 UART 打印端口确定,使用的串口 号 为 COM23 .
在开始传输前必须使用测试命令确保 uart 已经准备好, 。
命令:
。
mcumgr --conntype serial --connstring " COM12,baud=115200 " echo hello
。
。
如果你是使用了第二中添加串口的方式,使用 nrfjprog --com 命令后可能不会把你串口列举出来,可以通过串口助手参看,确定好端口后:使用更行命令:
命令:
mcumgr --conntype serial --connstring " COM23,baud=115200 " image upload -e app_update.bin
等待升级完成:
。
传输完成后使用如下命令进行激活:
mcumgr --conntype serial --connstring " COM12,baud=115200 " image list
由返回可以确定,我们新上传了一个固件,放在 bank2 中,我们需要用到这个固件的 hash 值,本次测试升级固件的 hash : 73edc83fe5a2536bc0a5b9b3015841f6f7a88444ebd75d2dfe0248f6bb3323f4 。
即运行:
mcumgr --conntype serial --connstring " COM12,baud=115200 " image test 73edc83fe5a2536bc0a5b9b3015841f6f7a88444ebd75d2dfe0248f6bb3323f4
然后复位测试确定一下,是否可以跳转到新固件执行:
使用命令:
mcumgr --conntype serial --connstring " COM12,baud=115200 " reset
运行完成后要等待一下
可以看到新的 APP 运行成功。但是这不是最终的固件修改,需要如下指令来 固化 :
mcumgr -c acm0 image confirm 73edc83fe5a2536bc0a5b9b3015841f6f7a88444ebd75d2dfe0248f6bb3323f4
然后使用命令复位一下 。
nrfjprog --reset
等待一会,等程序运行起来:
使用命令:
mcumgr -c acm0 image list
可以看到激活的固件的 hash 是我们升级固件的 hash ,升级成功.
最后此篇关于nordic——NCS下的DFU(ble加uart)升级(基于NCS)的文章就讲到这里了,如果你想了解更多关于nordic——NCS下的DFU(ble加uart)升级(基于NCS)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我几乎不敢在这里问这个问题,因为它似乎应该很容易通过谷歌或 ravendb.net 获得。但是,我一直很难找到将我的 RavenDB 升级到新版本的正确方法。我目前正在运行 573 版并希望升级到 6
这周我需要升级当前版本的 DNN。我目前使用的是 2.1.1。我不想每件事都做两次,所以,我有几个问题。 是否有升级工具或某些脚本可以帮助我进行升级。 我最好安装 4.9 还是 5.0。这是生产。 如
将 Sugarcrm 从 6.2 升级到 6.3 版本时遇到问题。当我升级本地 Sugarcrm 安装时,它可以工作,但是当我开始升级我的 Sugarcrm 6.2 站点并上传升级包时,它不会上传。
有没有办法绕过 Meteor 的自动更新功能?我坚持 Downloading meteor-tool@1.3.0_3... \ 当我尝试运行现有项目,或创建一个新项目或只是运行“
我已将应用内集成到我的 Andorid 应用程序中,用于单个产品 productone。 为此,我在我的 Google Play 控制台中创建了不同的产品 ID,如下所示: 1。 productone
我在将 TeamCity 版本 2017.1.1 升级到 2017.1.2 时遇到问题。这个问题涉及 TeamCity 和 PostgreSQL 的工作。我的工作: 停止 teamcity 进程 /e
我寻找了这个问题的具体答案,但找不到——即使是在 WAMPSERVER 网站上也是如此。我确定我忽略了它。 我有 Wampserver 2.0、MySQL 5.0.51b、PHP 5.2.6 和 Ap
我使用 Ubuntu 软件中心默认的 Eclipse 3.7。 我想将 Eclipse 升级到 kepler 版本,所以我添加了 repository 我收到以下错误消息: Cannot comple
你好 我只想安装 mercurial,但对于它需要 python 2.6 的所有版本,我尝试使用 .rpm 文件,但我唯一得到的是很多充满错误的行,它告诉我:需要安装在 2.6 之前和 2.5 之后的
我完全知道 Gradle 网站上有一些页面说明了如何升级,但仅限于 4.x 及更高版本。 我正在尝试关注 tutorial制作一个简单的“我的第一个”Minecraft 模组。在其中,您被告知安装 f
我们想升级 Kerberos(服务器和客户端) 当前:1.6.3-133.27.1 目标:1.6.3-133.49.97.1 问题是如果我们用包管理器升级它,下面会发生什么? KDC 数据库 所有主要
背景 原计划 2019 年发布的 Vue3,又经过一年的再次打磨,终于于去年 9 月正式发布。随后,不少 UI 组件库都积极参与适配,去年 12 月,Element-plus(
我有一个版本为 2.3.4 的 grails 项目,我需要尽可能升级到最新版本。查看文档我意识到从 2.x 到 3.x 有巨大的变化。 问题是:从 2 到 3、从 3 到 4、从 4 到 5 逐步升级
我正在将 API 项目从 .net5 升级到 .net6 它以前工作,现在它崩溃 内部异常消息“抛出了‘Unity.Exceptions.InvalidRegistrationException’类型
我将我的项目从 expo 44 升级到 expo 45,现在我有无数这样的错误: The module 'MaterialIcons' can't be used as JSX component.
我已经升级了掌 Helm 模板(手动) 以前的片段depoloyment.yaml : apiVersion: apps/v1beta2 kind: Deployment metadata: na
我正在尝试将我的 Scala Play Framework 应用程序升级到 2.8,这涉及将 SBT 升级到 1.x。 在我的 build.propeties 我有 sbt.version=1.3.5
我想在我的 Windows 服务器上安装 PHPUnit 3.7。我遵循了各种说明 here并以 PHPUnit 3.4.1 结束。当我尝试使用以下方法再次安装它时: pear update chan
Microsoft.Net 4.5 即将推出,我想在 MS 发布最终版本时升级我的 clickonce 应用程序。 我的问题是:已经安装了 clickonce 应用程序(使用 .net 4.0)的用户
为了将 Angular 8 更新到 9,我正在按照官方文档升级。 这建议首先更新到最新版本的 angular 8,例如: ng update @angular/core@8 @angular/cli@
我是一名优秀的程序员,十分优秀!