nRF Connect SDK (NCS) / Zephyr固件升级,主要包括MCUboot和蓝牙空中升级。
随着nRF Connect SDK(NCS)/Zephyr固件升级,近期不少朋友在后台给Nordic君发来不少疑问: 为此,Nordic君特地找到一篇来自 Nordic 中国区 FAE 经理 Kevin 艾的原创博客文章对以上问题进行解答。目录
CONTENT
1.概述2. NCS中的Bootloader 2.1 nRF5 SDK Bootloader 2.2 MCUboot 2.3 B0,亦称nRF Secure Immutable Bootloader(NSIB)3. DFU协议 3.1 概述 3.2 SMP DFU协议 3.2.1 SMP包头和命令 3.2.2 SMP包payload和CBOR编码 3.2.3 SMP包详细解析示例 3.2.4 SMP DFU流程 3.3 nrf dfu协议4. NCS DFU升级步骤说明 4.1 SMP DFU升级步骤说明 4.2 nrf_dfu升级步骤说明 4.3 存储器分区(多image情况)5. 移植SMP DFU功能到peripheral_uart(NUS)6. 手机端DFU参考代码 ●1、概述 ●DFU概念
DFU(Device Firmware Update),就是设备固件升级的意思。OTA概念
OTA(Over The Air)是实现DFU的一种方式而已,准确说,OTA的全称应该是OTA DFU,即通过空中无线方式实现设备固件升级。只不过大家为了方便起见,直接用OTA来指代固件空中升级(有时候大家也将OTA称为FOTA,即Firmware OTA,这种称呼意思更明了一些)。 只要是通过无线通信方式实现DFU的,都可以叫OTA,比如4G/WiFi/蓝牙/NFC/Zigbee/NB-IoT,他们都支持OTA。DFU除了可以通过无线方式(OTA)进行升级,也可以通过有线方式进行升级,比如通过UART,USB或者SPI通信接口来升级设备固件。 不管采用OTA方式还是有线通信方式,DFU包括后台式(background)和非后台式两种模式。 1、后台式DFU,又称静默式DFU(Silent DFU),在升级的时候,新固件在后台悄悄下载,即新固件下载属于应用程序功能的一部分,在新固件下载过程中,应用可以正常使用,也就是说整个下载过程对用户来说是无感的,下载完成后,系统再跳到BootLoader程序,由BootLoader完成新老固件拷贝操作,至此整个升级过程结束。比如智能手机升级Android或者iOS系统都是采用后台式DFU方式,新系统下载过程中,手机可以正常使用哦。 2、非后台式DFU,在升级的时候,系统需要先从应用程序跳到BootLoader程序,由BootLoader进行新固件下载工作,下载完成后BootLoader继续完成新老固件拷贝操作,至此升级结束。早先的功能机就是采用非后台式 DFU来升级操作系统的,即用户需要先长按某些按键进入bootloader模式,然后再进行升级,整个升级过程中手机正常功能都无法使用。 下面再讲双区(2 Slot)DFU和单区(1 Slot)DFU,双区或者单区DFU是新固件覆盖老固件的两种方式。 后台式DFU必须采用双区模式进行升级,即老系统(老固件)和新系统(新固件)各占一块Slot(存储区),假设老固件放在Slot0中,新固件放在Slot1中,升级的时候,应用程序先把新固件下载到Slot1中,只有当新固件下载完成并校验成功后,系统才会跳入BootLoader程序,然后擦除老固件所在的Slot0区,并把新固件拷贝到Slot0中,或者把Slot0和Slot1两者的image进行交换。 非后台式DFU可以采用双区也可以采用单区模式,与后台式DFU相似,双区模式下新老固件各占一块Slot(老固件为Slot0,新固件为Slot1),升级时,系统先跳入BootLoader程序,然后BootLoader程序把新固件下载到Slot1中,只有新固件下载完成并校验成功后,才会去擦除老固件所在的Slot0区,并把新固件拷贝到Slot0区。 单区模式的非后台式DFU只有一个Slot0,老固件和新固件分享这一个Slot0,升级的时候,进入bootloader程序DFU模式后立马擦除老固件,然后直接把新固件下载到同一个Slot中,下载完成后校验新固件的有效性,新固件有效升级完成,否则要求重来。 跟非后台式DFU双区模式相比,单区模式节省了一个Slot的Flash空间,在系统资源比较紧张的时候,单区模式是一个不错的选择。不管是双区模式还是单区模式,升级过程出现问题后,都可以进行二次升级,都不会出现“变砖”情况。 不过双区模式有一个好处,如果升级过程中出现问题或者新固件有问题,它还可以选择之前的老固件老系统继续执行而不受其影响。而单区模式碰到这种情况就只能一直待在bootloader中,然后等待二次或者多次升级尝试,此时设备的正常功能已无法使用,从用户使用这个角度来说,你的确可以说此时设备已经“变砖”了。 所以说,虽然双区模式牺牲了很多存储空间,但是换来了更好的升级体验。 可参考下面三个图来理解上述过程 如果你是第一次接触nRF Connect SDK(NCS),那么建议你先看一下这篇文章:开发你的第一个NCS/Zephyr应用程序,以建立NCS的一些基本知识,然后再往下看以下章节。 ●2、NCS中的Bootloader ● 如果你的应用不需要DFU功能,那么Bootloader就可以不要;反之,如果你的应用需要DFU功能,Bootloader就一定需要。 Bootloader在其中起到的作用包括:一判断正常启动还是DFU升级流程,二启动并校验应用image,三升级的时候完成新image和老image的交换或者拷贝工作。Bootloader首先需要判断是进入正常应用程序启动流程还是DFU流程。
要启动应用image,Bootloader必须知道启动image的启动向量表在哪里。 要校验一个image,Bootloader必须知道这个image正确的校验值存在哪里。 要完成升级,Bootloader必须知道新image所在位置和老image所在位置,并执行一定的拷贝算法。 启动向量表可以放在image的最开始处,也可以放在其他地方,这就涉及到image的格式。 Image正确的校验值可以跟image合在一块存放,也可以单独放在一个flash page里面。如果image的校验值是跟image本身合在一块存放的,这里再次涉及到image的格式。 关于新image和老image存放位置,这就涉及到存储器分区问题。Bootloader的实现将直接决定image的格式,以及存储器的结构划分。 NCS支持MCUboot,B0和nRF5 Bootloader三种Bootloader,三个Bootloader选其一即可,一般推荐大家使用MCUboot。 由于很多读者对Nordic老的SDK,即nRF5 SDK比较熟悉,我们先以这个nRF5 Bootloader为例来讲解他们的Flash分区以及image格式,然后再讲MCUboot和B0,看看他们又是如何分区和定义image格式的。 注意:如果你只对其中某一个具体的Bootloader感兴趣,可以跳过其他章节,直接阅读相关章节,比如如果你只对MCUboot感兴趣,可以只看2.2节。2.1 nRF5 SDK Bootloader介绍
nRF5 Bootloader 是指:nRF5_SDK_17.1.0_ddde560examplesdfusecure_bootloader 这里面定义的Bootloader。 如果你的DFU想使用这个Bootloader,那么nRF5 SDK的存储区划分(双bank)是下面这样的: 在nRF Connect SDK(NCS)中,如果也使用nRF5 Bootloader,此时存储器的分区跟上面大同小异,我们用NCS中的语言重新组织如下: 当前固件(老固件)在Bank0里面执行,新固件接收后直接存放在Bank1,而且程序永远只执行Bank0里面的代码,Bank1的起始地址是动态的,其计算公式为:Bank0起始地址 + Bank0 image大小。 由于nRF5 Bootloader跳到Bank0的时候,直接跳到一个固定地址(0x1000),因此它不需要专门去找新image的启动向量,换句话说,如果使用nRF5 Bootloader的话,新image就是应用代码编译后的样子,不需要添加任何的头或者尾信息。 如果这样的话,image的SHA256或者签名校验怎么做? 在nRF5 Bootloader中,把正确的SHA256或者签名放在settings page里面,这样image就真得不需要任何头或者尾信息,当需要校验image的时候,从settings page中取出标准值,然后进行校验。 那这些标准的SHA256或者签名怎么从远程传过来呢? 答案是init包,所以nRF5 Bootloader升级的时候,需要把一个zip包传给目标设备,如下所示: 这个zip包除了新image本身,还包含一个dat文件,这个dat文件包含新image的大小,SHA256,签名等信息。至于升级拷贝,nRF5 Bootloader做法也很简单,先擦掉Bank0里面的内容,然后把Bank1里面的内容拷贝到Bank0,然后重新从Bank0启动,完成整个升级。 在拷贝之前,Bootloader会校验Bank1里面的image完整性,只有校验通过才会做下一步的拷贝工作,否则退出升级模式。 从上可以看出,虽然nRF5 Bootloader会校验image的完整性,但是如果出现发版错误(打个比方,Win11和Win7都是微软验签,因此完整性校验都可以通过,但是如果微软把Win11发到一台只能跑Win7的设备上,那么这台设备将无法运行),由于它没有新image确认操作,也不支持回滚操作,那么升级后系统有可能挂死在一个错误的版本里面。 说完了启动,校验和升级拷贝,最后说一下如何进入DFU模式。在nRF5 Bootloader里面,通过判断某些Flag(标志位)来决定要不要进入DFU模式,这些标志位有一个为真,进入DFU模式,否则正常启动app:- 特定按键是否按下
- 保持寄存器GPREGRET1是否为0xB1
- Settings page里面当前bank是否为Bank1
- 上次DFU过程是否还在进行中
- 应用程序校验是否通过
- 我们是通过把Settings page里面的当前bank设置为Bank1来触发DFU模式的。
- 由于是后台式DFU,我们只把DFU进度信息保存在RAM里面,没有将其保存在Settings page这个Flash页面中。
2.2 MCUboot
MCUboot位于如下目录:bootloader/mcuboot/boot/zephyr 在NCS中做DFU的时候,一般都推荐使用MCUboot。 MCUboot功能强大,兼容的芯片平台多,而且是一个久经考验的第三方开源Bootloader。 MCUboot把存储区划分为Primary slot和Secondary slot,而且primary slot跟secondary slot两者大小是一样的,程序默认在Primary slot中执行。 有一点需要大家注意,NCS对MCUboot进行了定制,在NCS中,程序只能在Primary slot中执行,Secondary slot只是用来存储新image,而且Secondary slot可以放在内部Flash,也可以放在外部Flash,这样在NCS中,存储器分区有如下两种典型情况: 注:MCUboot放在0x000000地址。 如前所述,Bootloader有四大功能:启动image,校验image,拷贝image以及DFU模式判断。 那么MCUboot是如何完成这4项功能的:- 启动image
- BOOT_SWAP_TYPE_TEST。MCUboot将进入DFU模式,而且为test目的的DFU。跟下面的BOOT_SWAP_TYPE_ PERM模式相比,BOOT_SWAP_TYPE_TEST的DFU过程与之一模一样,也就是说BOOT_SWAP_TYPE_TEST就是进行正常的真正DFU,只不过DFU完成后,MCUboot跳到新app,这个时候新app必须把secondary slot里面的image_ok字段写为1,即调用boot_write_img_confirmed()这个API来完成,否则再次复位进入MCUboot的时候,MCUboot会认为新image有问题(没有确认),从而执行回滚操作,重新把老image换到primary slot,然后继续跑老image(此时升级应该算失败)。
- BOOT_SWAP_TYPE_ PERM。如前所述,BOOT_SWAP_TYPE_ PERM跟BOOT_SWAP_TYPE_TEST DFU过程一模一样,唯一区别的是,一旦设为PERM(永久)模式,哪怕新image没有去写image_ok字段,再次复位进入MCUboot,MCUboot也不会去执行回滚操作,而强制认为升级已成功。
- BOOT_SWAP_TYPE_ REVERT,回滚操作。前述的回滚操作,swap_type就是BOOT_SWAP_TYPE_ REVERT。一旦检测到BOOT_SWAP_TYPE_ REVERT,MCUboot将进行回滚操作。
- BOOT_SWAP_TYPE_ NONE。正常启动模式,MCUboot将直接跳到app,而不是进入DFU模式。
- BOOT_SWAP_TYPE_ FAIL。当MCUboot校验primary slot里面的image失败时,就会报BOOT_SWAP_TYPE_ FAIL,此时程序将死在MCUboot里面。
- BOOT_SWAP_TYPE_ PANIC。当MCUboot启动过程中出现了致命错误,就会报BOOT_SWAP_TYPE_ PANIC,此时程序将死在MCUboot里面。
2.3B0=nRF Secure Immutable Bootloader(NSIB)
NSIB(nRF Secure Immutable Bootloader),亦称B0,位于nrf/samples/bootloader,这个是Nordic自己开发的一个不可升级的Bootloader。 b0把存储区划分成slot0和slot1,并且slot0大小等于slot1大小,s0_image跑在slot0,s1_image跑在slot1,B0根据s0_image和s1_image的版本号来决定跑哪一个image,如果s0_image的版本号高于或等于s1_image的版本号,那么B0启动的时候就会跳到s0_image;反之,如果s1_image的版本号高于s0_image的版本号,那么B0启动的时候就会跳到s1_image。 由于s0_image和s1_image都有可能被执行,所以s0_image和s1_image必须都放置在内部Flash,也就是说slot0和slot1必须都在nRF设备内部Flash中。 B0将存储区划分成如下模样: 如前所述,Bootloader有四大功能:启动image,校验image,拷贝image以及DFU模式判断。 那么b0是如何完成这4项功能的: 1. 启动image B0通过读provision区域信息,得到s0_image和s1_image信息,provision属于B0的一部分,下面为provision的定义及一个示例:(感兴趣的读者,仔细看一下结构体各个字段定义,并对应image hex进行解读) 从上面示例可以看出,s0_address为0x9000,0x9000即为s0_image的起始地址,s1_image起始地址可以用同样道理获得。得到S0_image或者S1_image的起始地址后,就可以得到两个image的fw_info,fw_info定义及示例如下所示: 通过fw_info就可以找到boot_address,从而跳转到相应app。 2、校验image B0也支持SHA256或者签名验签,SHA256或者签名放在image的最后,称为fw_validation_info,其定义及示例如下所示: B0通过magic字段找到hash和signature,然后进行校验。 3、拷贝image B0没有拷贝image的操作,所谓升级,就是执行高版本image,具体来说,如果s1_image版本比s0_image版本高,则执行s1_image;否则执行s0_image。 4、DFU模式进入 B0不存在DFU模式,也就不存在所谓进入DFU模式判断。每次复位B0都去读s0_image和s1_image的版本,那个image版本高就执行那个image。 基于b0的DFU,有一点需要特别注意,由于S0_image和S1_image两者的偏移或者启动向量不一样,因此即使S0_image和S1_image两者功能一模一样,他们的image内容也不一样,这也意味着slot0和slot1对应的升级image是不一样的。一般来说,手机app或者其他主机并不知道设备当前正在运行哪个slot里面的image,因此DFU的时候,手机app或其他主机需要先跟设备沟通,获知设备当前正在执行哪个image。如果S0_image在运行,就给它传S1_image(signed_by_b0_s1_image.bin)并放置在slot1中;如果S1_image在运行,就给它传S0_image(signed_by_b0_s0_image.bin)并放置在slot0中。 升级image接收完毕,系统复位,B0自动选择高版本image执行,至此整个升级完成。从上可知,DFU的升级文件必须同时包含signed_by_b0_s0_image.bin 和signed_by_b0_s1_image.bin,实际中我们一般使用如下zip文件: 这里我们做了一个基于b0的DFU例子:https://github.com/aiminhua/ncs_samples/tree/master/nrf_dfu/ble_intFlash_b0 大家感兴趣的话,可以自己去看一下(按照里面的readme来操作)。下面是B0正常启动的一个示例,可以看出B0选择了slot0里面的s0_image进行装载,校验和跳转。 ●3、DFU协议 ● 3.1 概述 前面说过,为了实现固件升级,需要把新image放在secondary slot(以MCUboot为例),如何把新image传输到secondary slot?这就是DFU协议要做的事情,一般来说,DFU协议需要把image文件分块一块一块传给设备端,然后设备端按照要求将image块写入secondary slot,并回复写入结果给主机。 期间有可能还需要校验传输的image对不对,或者告知每次image块写入的偏移地址。最后DFU协议还有可能涉及一些管理操作,比如image块写入的准备工作,读取设备状态,复位设备等。 这里需要特别强调一下,DFU协议是脱离于传输层的,也就是说,同样的DFU协议可以跑到不同的传输层,比如蓝牙,WiFi,UDP,USB CDC,UART等,千万不要把DFU协议跟特定的传输层混为一谈。 nRF Connect SDK包含多种DFU协议,最著名的就是SMP DFU协议,除此之外,还有其他DFU协议,比如http_update,hid_configurator,USB DFU class,PCD DFU,以及从nRF5 SDK移植过来的nrf_dfu协议。 不同的应用场景有不同的DFU协议需求,大家需要根据自己的情况选择合适的DFU协议,就像前述的Bootloader一样,这些DFU协议选择一个适合自己的就可以,不需要全部都要会用。下面着重讲一下smp dfu和nrf_dfu两个dfu协议。 3、2 SMP DFU协议 smp 全称simple management protocol(简单管理协议),它是设备管理协议的一种,在NCS中,mcumgr模块实现了smp协议,或者说,smp协议按照mcumgr的要求对相应的传输数据进行编码,这样mcumgr里面注册的命令组(command group)可以直接对传输数据进行解析。 mcumgr实现的功能比较多,smp DFU只是其中一种,除此之外,它还有很多其他功能,比如shell管理,日志管理等。这里我们只对DFU相关命令组进行介绍,其他命令组就不在这里讲了。 3.2.1SMP包头和命令 mcumgr里面有两个命令组跟DFU有关: 1、img_mgmt,即image管理命令组,该命令组又具体包括3个命令集4个具体命令,详细定义如下: 2、os_mgmt,即OS管理命令组,该命令组又具体包括3个命令集4个具体命令,详细定义如下:(实际上,DFU只用到了os_mgmt_reset这个命令) smp协议把数据包(packet)分成两部分:包头(header)和有效载荷(payload),包头每一个字节正好对应如下结构体的每一个字段,即第一个字节代表nh_op(操作类型),第二个字节代表nh_flags,第三和四个字节代表nh_len,第五和六个字节代表nh_group(命令组编号),第7个字节代表nh_seq,第8个字节代表nh_id(命令在该命令组中的编号)。 这样我们就可以通过SMP的包头找到相应的handler,比如包头00 00 00 02 00 01 00 00,即对应命令组1的0号命令集的00操作(读命令),最终找到img_mgmt_state_read这个handler。我们会在3.2.3节对此示例的解析做详细说明。 3.2.2SMP包payload和CBOR编码 SMP payload采用CBOR编码,CBOR将一连串二进制数据分成多个data item,如下所示: 从上可知,每个data item第一个字节包含2部分:数据类型和数据长度,数据类型定义如下:- 0,正数
- 1,负数
- 2,字节串(byte string)
- 3,UTF-8字符串(text string)
- 4,数组
- 5,map(又称字典)
- 6,tag(这个用得少)
- 7,浮点数或者特殊类型,其中特殊类型将short count 20–23定义为 false, true, null和undefined
-
如果长度为0–23,则直接用short count的5 bits来表示,从第2个字节开始表示data payload
-
如果short count为24(0x18),则表示第2个字节代表长度,从第3个字节开始表示data payload
-
如果short count为25(0x19),则表示第2和第3个字节合起来表示长度,从第4个字节开始表示data payload
-
如果short count为26(0x1A),则表示第2,第3,第4和第5个字节合起来表示长度,从第6个字节开始表示data payload
-
如果short count为27(0x1B),则表示第2至第9个字节合起来表示长度,从第10个字节开始表示data payload
-
如果short count为31(0x1F),则表示长度为未定义,从第2个字节开始表示data payload,直到遇到停止符:0xFF
- 00 00 00 02 00 01 00 00 bf ff
- int img_mgmt_state_read(struct mgmt_ctxt *ctxt)
-
签名升级image。注:app_update.bin已经是签过名的image了
-
上传image,即把app_update.bin传送到目标设备
-
列出image以获得image的hash值
-
测试image,即写magic字段,以让MCUboot进入DFU模式
-
复位设备,以重新进入MCUboot,从而MCUboot进入DFU模式,并执行相应的swap操作,并完成两个slot image之间的交换或者拷贝动作
-
Confirm image,即新image启动成功后,对其image_ok字段进行置1操作
-
mcumgr conn add myCOM type="serial" connstring="dev=COM13,baud=115200,mtu=256" (Note: change the COM if needed)
-
mcumgr -c myCOM image upload app_update.bin
-
mcumgr -c myCOM image list
-
mcumgr -c myCOM image test
-
mcumgr -c myCOM reset
-
mcumgr -c myCOM image confirm
蓝牙DFU流程解读
当采用BLE作为传输层的时候,上面命令都被手机app打包成二进制数据包直接下发给设备端,但解析出来之后,你会发现蓝牙DFU流程跟上面说明的流程基本上一模一样。比如前面的00 00 00 02 00 01 00 00 bf ff,就是手机发给设备的第一条DFU命令或者说请求(request)。 我们再举一个例子:上传image命令(request),它的第一个数据包示例如下所示: 从包头02 00 00 eb 00 01 00 01可以看出,这个数据包将触发handler: img_mgmt_upload,我们再来看数据包payload的前面8个字节:bf 64 64 61 74 61 58 cc,bf表示后面是map数据,即key/value数据对,0x64,表示后面是text string数据,长度为4,从而得到64这个data item对应的payload为:64 61 74 61,即key=”data”; 从0x58开始,就表示value这个data item了,0x58表示这个item为字节串并且长度为下一个字节:0xcc,也就是说”data”这个key对应的value包含了0xcc个数据的字节流,这样第一个key/value对解析完毕。然后再解析63 6c 65 6e 1a 00 02 05 a8,0x63,表示此item为text string数据,长度为3,从而得到payload为6c 65 6e,即key = ”len”; 0x1a表示此item为正数,count为后面4个字节,也就是说”len”这个key对应的value为0x000205a8,至此第二个key/value对解析完毕。以此类推,我们后面又可以解析出”sha”和”off”两个key以及他们各自的value,最后碰到停止符:0xFF,整个map item结束。 前面说过,整个数据包的payload会通过参数传给img_mgmt_upload作为实参,img_mgmt_upload的函数声明为:- img_mgmt_upload(struct mgmt_ctxt *ctxt)
- 03 00 00 0d 00 01 00 01 bf 62 72 63 00 63 6f 66 66 19 09 40 ff
- 02 00 00 32 00 01 00 00 BF 67 63 6F 6E 66 69 72 6D F4 64 68 61 73 68 58 20 47 7C C8 4B 52 27 23 03 DA 27 41 F1 1D 38 46 0F 11 AE DB 5E 75 A2 D3 25 0C 6E DE EF 15 84 24 49 FF,大家可以仿照上面的做法来解析一下这个数据包,它解析的结果是:调用img_mgmt_state_write,并写入magic字段,同时将swap类型设为BOOT_SWAP_TYPE_TEST
- 02 00 00 02 00 00 00 05 BF FF,这个包解析的结果是:调用os_mgmt_reset,对设备进行复位
3.3 nrf dfu协议
nrf dfu协议就是nRF5 SDK使用的DFU协议,相信很多读者都很熟悉它。 nrf dfu协议定义了两个角色:controller和target,controller发request,target回response,一来一往,完成DFU传输过程。 nrf dfu定义了如下request命令以及他们的response。 Request命令的格式是:Opcode + parameters Response的格式是:60 + Opcode + parameters 比如编码:01 02 00 10 00 00,通过上面解析可以知道它是一个创建数据对象命令NRF_DFU_OP_OBJECT_CREATE,而这条命令的响应是:60 01 01,可以看出也符合上面的定义。 nrf dfu用到了对象概念,什么叫对象(object)? 对象分两种:command object和data object,其中init包是command对象,而image chunk(image块)是data对象。 我们可以进一步提炼一下,nrf dfu协议主要涉及的命令是如下几个:- 选择对象(NRF_DFU_OP_OBJECT_SELECT),用来选择init包或者image包
- 创建对象(NRF_DFU_OP_OBJECT_CREATE),用来创建init包或者一个image 4kB块
- 写对象(NRF_DFU_OP_OBJECT_WRITE),即传输实际数据。由于蓝牙将命令和数据分成两个不同characteristic,写对象其实就是写数据,是一个专门的characteristic:packet characteristic,因此发送写对象命令时,就没有必要加上Opcode,而是直接把数据写到packet characteristic上。由于串口只有一个RX线,因此通过串口DFU的时候,写对象命令还是有Opcode的。
- 获取对象的CRC(NRF_DFU_OP_CRC_GET),用来获取前面init包或者4kB image块的CRC值
- 执行对象(NRF_DFU_OP_OBJECT_EXECUTE),即把数据真正写入Flash中
- 选择init对象
- 创建init对象
- 执行init对象
- 选择image data对象
- 建第一个4kB data对象
- 写对象,即设备(target)循环接收主机发过来的image chunk,直至4kB
- 计算4kB image块的CRC,并返回给主机(controller)以供其校验
- 执行4kB image块对象,即将其写入到Flash中
- 循环往复,直至整个image写入完毕
- 写DFU标志,并复位设备
- 复位后进入Bootloader DFU模式,Bootloader完成后续的拷贝工作,至此整个DFU过程宣告结束
- 进入项目目录:cd zephyrsamplessubsysmgmtmcumgrsmp_svr
- 编译:west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p -- -DOVERLAY_CONFIG="overlay-bt.conf"(根据你自己手上的板子情况,把nrf52840dk_nrf52840换成其他DK,比如nrf5340dk_nrf5340_cpuapp)
- 烧写:west flash -d build_nrf52840dk_nrf52840,此时设备将广播“Zephyr”
- 修改原始工程,比如广播名字(CONFIG_BT_DEVICE_NAME="NEW_DFU"放在overlay-bt.conf中)再重新编译,然后拷贝以下字段到手机版nRF Connect“build_nrf52840dk_nrf52840/zephyr/app_update.bin"
- 用手机nRF Connect连接设备,成功后,点击右上角的“DFU”图标,选择前面的“app_update.bin”文件,然后选择“Test and Confirm”,DFU开始
- 升级文件传输完毕,系统将重启
- MCUboot完成swap操作,并跳到新app,广播将变成“NEW_DFU”
- 手机nRF Connect连接新app,并发送confirm命令
- 至此整个升级结束
- 准备。a. 安装PC版nrfutil。nrfutil安装有两种方式,一种是直接下载exe文件,一种是以Python的方式进行安装。nrfutil.exe直接下载链接为:https://github.com/NordicSemiconductor/pc-nrfutil/releases,记得把nrfutil.exe所在目录放在Windows环境变量中。Python方式安装nrfutil步骤如下所示: i. 安装Python,下载地址//www.python.org/downloads/,安装成功后请确保Windows环境变量包含Python目录 ii. 通过pip安装最新版的nrfutil,即打开Windows命令行工具CMD,输入如下命令:pip install nrfutil,即可以完成nrfutil的安装。安装完成后,在Windows命令行工具输入:nrfutil version,如果可以正确显示版本信息,说明安装已经成功对于Windows用户,nrfutil运行需要几个特殊的DLL库,而这几个库有些Windows机器是没有的,如此,可往:https://www.microsoft.com/en-us/download/details.aspx?id=40784下载b. 进入nrf_dfu/ble_intFlash/sdk_change目录,选择你的SDK版本。比如ncs_v1.8.0,把nrf_dfu/ble_intFlash/sdk_change/ncs_v1.8.x下面内容直接覆盖nrf仓库目录c. 建议大家对照例子里面的readme看一下还有没有其他准备工作
- 进入项目目录:cd nrf_dfu/ble_intFlash
- 编译:west build -b nrf52840dk_nrf52840 -d build_nrf52840dk_nrf52840 -p(根据你自己手上的板子情况,把nrf52840dk_nrf52840换成其他DK,比如nrf5340dk_nrf5340_cpuapp)
- 烧写:west flash -d build_nrf52840dk_nrf52840此时设备将广播“Nordic_DFU”
- 修改原始工程:比如广播名字(CONFIG_BT_DEVICE_NAME="NEW_DFU"),再重新编译,然后拷贝“build_nrf52840dk_nrf52840/zephyr/ app_signed.hex”到update目录
- 双击update目录中的zip_generate.bat,将生成ble_intFlash.zip,将ble_intFlash.zip拷贝到手机nRF Connect中
- 用手机nRF Connect连接设备,成功后,点击右上角的“DFU”图标,选择前面的“ble_intFlash.zip”文件
- 升级文件传输完毕,系统将重启
- MCUboot完成swap操作,并跳到新app,新app自动完成image confirm操作
- 此时广播已经变成“NEW_DFU”,至此整个升级结束
- mcuboot_primary大小必须等于mcuboot_secondary,而且CONFIG_BOOT_MAX_IMG_SECTORS最好也等于他们大小/4096
- 如果使用了一个region(flash_primary这个region除外),那么这个region每一块区域都要属于一个分区名字,不能出现某块区域没有分区名字情况。比如上面重新定义了external_flash region,根据regions.yml文件定义,external_flash总共有8Mbytes,那么这8Mbytes都必须有一个分区名字,而我们定义的littlefs_storage和mcuboot_secondary两个分区的确包含了全部8MB区域。如果我们定义littlefs_storage所在区域为0x0~0x700000,而mcuboot_secondary所在区域为0x710000~0x800000,那么系统就会报错,因为这里还有一个空隙(gap):0x700000~0x710000是没有取分区名字的。解决这个问题有两个办法:一个就是上面的方法把0x700000~0x710000划到littlefs_storage分区,一个就是给这块区域专门取一个名字,比如:my_unused_area(见下面示意),也是可以解决问题的。对于flash_primary这个region,由于系统默认认为必须要有一个“app”分区,所以它可以存在而且只能存在一个空隙(gap),这样系统默认这个gap就是“app”分区。当然你也可以把flash_primary所有区域都分好区,包括“app”分区。
- regions.yml文件里面各个存储器的物理大小必须符合实际,这个通过修改dts文件来保证的。这里面最容易出错的就是external_flash,external_flash的大小在regions.yml文件里面是以字节为单位(在kconfig文件里面也是以字节为单位的),但是external_flash对应的设备树,比如MX25R64,它在dts文件里面是以bit为单位的,所以当大家使用其他外部Flash的时候,请仔细检查这些size对不对
- settings_storage,即settings使用的分区,大家可以将分区名改成:storage,这是其一,其二settings系统最终使用的最大flash区域大小是由CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE决定,而不是settings_storage分区本身大小决定,所以建议大家把CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE的值设为settings_storage分区大小。
- 至于RAM分区,道理也是一样的。这里需要注意的是,RAM各个分区的大小大家可以直接到dts文件里面去调整,而无需在pm_static.yml文件里面调整。当然,大家在pm_static.yml里面调整也是可以的,殊途同归,达到目的就好了。对于nRF52系列,只有一个sram_primary分区,这个没什么好讲的;对于nRF53系列,除了sram_primary这个分区,它还有rpmsg_nrf53_sram分区以及pcd_sram分区,其中rpmsg_nrf53_sram是用来蓝牙协议栈host和controller之间进行双核通讯的,而pcd_sram是用来升级网络核image的。
大家可以下载下来参考或者测试一下。 从上述例子我们可以看出,在NCS中移植一个例子非常方便,它不需要去添加c文件和头文件,也不需要去修改编译选项,还不需要去修改传统的头文件进行配置,仅仅修改conf文件和初始化函数,就轻轻松松完成了整个移植,这也是NCS非常大的一个好处。 https://github.com/aiminhua/ncs_samples/tree/master/smp_dfu其实链接下面包含的例子都同时具备smp和nus两个服务,并且区分各种不同情形下的DFU情况,比如secondary slot在外部Flash,通过串口传输image等,同时其对peripheral_uart例子进行了小小改动,以更符合某些实际应用场景,建议大家好好看一下,相信对大家理解MCUboot和SMP会帮助不少。 ●6、手机端DFU参考代码 ● Nordic不仅提供设备端的DFU参考代码,同时提供手机端的参考代码。Nordic分别开发了Android版和iOS版的DFU库,大家可以直接拿过来使用,集成到自己的移动端app中,这两个库都放在github上,其中smp dfu对应的DFU库链接如下所示:
- Android版SMP DFU库:https://github.com/NordicSemiconductor/Android-nRF-Connect-Device-Manager
- iOS版SMP DFU库:https://github.com/JuulLabs-OSS/mcumgr-ios
- Android版nrf dfu库:https://github.com/NordicSemiconductor/Android-DFU-Library
- iOS版nrf dfu库:https://github.com/NordicSemiconductor/IOS-DFU-Library
- Android版nRF Toolbox源代码及开发说明请参考:https://github.com/NordicSemiconductor/Android-nRF-Toolbox
- iOS版nRF Toolbox源代码及开发说明请参考:https://github.com/NordicSemiconductor/IOS-nRF-Toolbox
原文标题:【Nordic博文分享系列】nRF Connect SDK(NCS)/Zephyr固件升级详解来啦!
文章出处:【微信公众号:Nordic半导体】欢迎添加关注!文章转载请注明出处。
-
固件升级
+关注
关注
0文章
34浏览量
12032 -
NCS
+关注
关注
1文章
8浏览量
9033 -
Nordic
+关注
关注
9文章
150浏览量
47071
原文标题:【Nordic博文分享系列】nRF Connect SDK(NCS)/Zephyr固件升级详解来啦!
文章出处:【微信号:nordicsemi,微信公众号:Nordic半导体】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论