gpt4 book ai didi

nordic——NCS下的DFU(ble加uart)升级(基于NCS)

转载 作者:我是一只小鸟 更新时间:2023-06-14 22:31:50 28 4
gpt4 key购买 nike

1、简介

  在 NCS 中有多种的 DFU 选择,强烈推荐使用 MCUboot ,当然如果你需要选择传统的 nrf_DFU 也是可以的,但是要用到官方修改的源文件.

关于 mcuboot ,原理性的东西在官网和官方博客中有讲,可以自行查看,后面只是简单的提一下 : MCUmgr — Zephyr Project Documentation (nordicsemi.com) 。

  为什么要使用mcubooot,原因是在NCS使用mcuboot,可以很快的实现DFU的加入,可以同时加入ble-dfu和uart-dfu。就是加入几个宏定义的事情,就完成了在SDK中可能要花很久才能完成的工程移植,既有ble也有uart的升级.

2、 nRF bootloader 升级程序制作

2.1 、在 nrf 的库中加入 nRF bootloader 库文件

这里是为了便于后续工程直接调用:

第一步:在 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 的相关库了.

2.2 、程序添加

2.2.1、配置.conf文件

打开 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 代码了.

  。

2.2.2、在main中加入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() 函数初始化校验页.

  。

2.2.3、制作新的固件APP和升级

改名字后编译一个 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 .

3、 MCUboot

简单一点来说,一个 bootloader 应该包括两个部分,一个是 DFU 时数据传输协议部分、一个是对 image 的管理部分.

注意一点,使用MCUboot时,传输的升级固件是app_update.bin,后续生成固件后,路径如下: 编译生成文件件\zephyr 下。后续就不再说明了.

3.1 DFU 数据传输协议

DFU 协议一般都是把升级的 image 文件分成一块一块的传给 bootloader ,在传输过程中, DFU 会完成校验每一块的数据对不对,出现错误处理等工作。值得注意的是 DFU 协议不管你物理通道是什么,你可以选择 ble 、 uart 、 USB 等。同时在 zephyr 中有许多的协议,只对 SMP 做一下简单介绍.

3.1.1 SMP dfu 协议

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 个具体命令(函数):

  。

在后续的例程中我们会使用两条宏定义去启动上面的两个命令组.

3.2 image 管理部分

接下来是在NCS中推荐使用的DFU方式——MCUboot,其功能强大,兼容的芯片多,不仅仅是nordic,是一个开源的第三方bootloader。MCUboot的存放地址是从0x00000000开始。MCUboot把存储区域分为了两块,分别是主存储区(Primary slot)和第二存储区(Secondary slot),对于nordic的NCS中,应用程序(app)只能在主存储区运行,第二存储区(Secondary slot)可以位于芯片内(flash),也可以位于外部的flash(external flash)。

  。

  。

在 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 中状态中的那种状态了。如果状态不对,就可以根据前面的状态解释来进行修改.

  。

3.3 、一级不可升级的安全 bootloader

为什么要加一个安全,在 MCUboot 官网中有这样一个解释,为什么你在出门期间你信任你的家是安全的,因为你家只有一道门,由此有这样一个逻辑:

  1. 你信任一扇门,因为你信任锁
  2. 你信任锁,是因为你信任唯一的钥匙
  3. 你信任钥匙,是因为它正在你的口袋里

由此如果你口袋中的钥匙不见了,那么你将失去对门的信任.

在每次复位或者上电后运行安全 bootloader ,会通过验证 bootloader 中的下一个 image 的签名和元数据来建立一个信任根本,就像确定钥匙是不是唯一且在自己的口袋中,如果任意一次验证不通过,那么 bootloader 就好停止,由此保证 bootloader 中的下一个 image 在被篡改后不会启动。由此杜绝攻击者通过更改固件来入侵接管设备,以保证安全性.

总结:

1) 、不可变的: bootloader 是 flash 锁定的,如果不擦除整个设备,无法进行修改或者删除.

2) 、安全的:拥有一套验证机制,保证自己和用户 app 的安全,不会被攻击者篡改。因此要提供自己的秘钥,让 bootloader 进行签名验证(下一个 image 是否被篡改)和元数据( Metadata )校验(检测 image 是否兼容).

注意本章节添加的,不管是 ble 的还是 uart 的,在升级的时候都是后台式的,升级是你可以正常使用.

3.3.1、加入MCUboot

依然选择在 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) .

3.3.2、工程添加修改(ble-OTA)

这里值得注意的是,不同版本之间这些 CONFIG 配置是会变的,所以在自己加入时一定要去看上面提到的参考,而不是无脑的复制粘贴。不然很可能你的版本和本文的版本不一样,导致你初始的工程编译都通过不了,稍后我会列举一下 NCS2.1 和 NCS2.3 的区别.

本次在 NUS (透传)程序的基础上添加 MCUboot ,例子路径 :nrf\samples\bluetooth\peripheral_uart 。使用 VS code 建立一个该工程的映像,并且编译通过后,开始 DFU 的添加.

第一步: .config 中加入全局宏:

注 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下的例子,我以上的更改也是根据这个来更改的.

  。

3.3.3、使用自定义秘钥进行固件进行签名

如果你使用过 SDK 开发,那么在 DFU 时第一步就是生成自己的秘钥并进行替换。同样在 NCS 中实际项目中也请使用自己的秘钥.

在前面的升级中使用了 NCS 中自带的秘钥,这是不安全的,你可以在编译结果中找到如下一个警告:“ MCUBOOT 的秘钥使用的是默认秘钥,不能用于生产使用。” 。

  。

秘钥一定要使用自己生成的,而不是使用 NCS 中自带的,而且最好保证秘钥存储的位置不在你的项目中.

还有一点就是:测试的时候你如果使用默认秘钥,那么在每一次删除项目进行重新构建默认秘钥都是可能被更改的,由此导致你不能使用新的 APP 对使用老版本固件的设备进行升级,因为你的秘钥已经在构建过程中改变了。所以最开始的固件也最好使用新建立的工程从新烧录.

在开始前,我先把官方文档参考连接给出(注意,如果你使用的版本和我的( NCS2.3 )不一样,那么请切换到和你版本一致的问的文档):

Firmware updates — nRF Connect SDK 2.3.99 documentation (nordicsemi.com) 。

版本切换如下图:

第一步:秘钥生成

值的注意的是虽然生成秘钥的加密算法有许多中,但是 NCS 中的只支持部分,所以我们并不能随意的选择.

NCS 中支持三种方式生成秘钥,分别是:

  • 使用 openSSL 生成秘钥
  • l 使用 imgtool 生成秘钥
  • 使用 python 生成秘钥

如下截图是官方文档中给出的不同的 bootloader 支持所支持的秘钥加密算法。生成的时候一定不能使用不支持。否则 DFU 会失败.

1) OpenSSL 方式

要使用 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
                          
                        

然后就可以在文件夹中看到生成的秘钥了:

  。

2) 、使用 imgtool

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 。

3) 、使用 python

命令如下:

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 打印信息如下:

由此测试完毕.

问题解决:在升级的过程中,有遇到有些手机会由于文件系统原因,导致发送失败,如果你有同样的现象,可以换一个文件管理系统进行测试.

3.3.4、UART-DFU工程添加修改

这里要说一下 , 我真正需要的仅仅是 mcumgr.exe, 如果说你已经有了那么直接在环境变量中添加好 mcumgr.exe 的路径,就可以在 PC 端的任意路径下 cmd 命令启动,并使用 mcuboot 的串口升级。如果你是需要其他平台上可以运行的 mcumgr 的可执行文件,也要自己去生成一个对应平台的可执行的文件。打个比方说,如果我想在 Linux 系统运行 mcumgr.exe 去进行升级,那么我怎么获取一个在可以在 Linux 上可运行的 mcumgr.exe 。是用 linux 版本的 go 去制作。

由于我使用的是 Windows ,所以我下面制作的是 Windows 上的 mcumgr.exe .

第一步: PC 端上位机环境搭建

需要使用 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 中.

1) 、使用双 bank+ UART 升级( uart0 smp 传输通道)

这个的好处是可以实现回滚.

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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com