nrf52——DFU升级OTA升级方式详解(基于SDK开发例程)

在我们开始前,默认你已经安装好了一些基础工具,如nrfutil,如果你没有安装过请根据官方中文博客去安装好这些基础工具,连接如下:Nordic nRF5 SDK开发环境搭建(nRF51/nRF52芯片平台) - iini - 博客园 (cnblogs.com)

本文只是对整个升级过程进行一次完整操作,以及其中可能出错的地方进行记录,如果你想知道具体原理,那么请你看官方中文博客的两篇文章,如果仅仅只是基于SDK,那么你参考第一篇即可,如果你需要在NCS上进行请参考第二篇,相信你在看了该博客后会对DFU有更深刻的理解,连接如下:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com)  ,nRF Connect SDK(NCS)/Zephyr固件升级详解 – 重点讲述MCUboot和蓝牙空中升级 - iini - 博客园 (cnblogs.com)

整体升级流程(本文目录):

1、生成秘钥

2、生成BootLoader程序(需要算法库和秘钥)

3、生成带DFU——OTA(基于蓝牙方式)的APP工程

4、合成初始的固件、并下载到相应的硬件

5、生成需要更新的APP固件

6、合成升级包固件,一般为ZIP文件。

7、利用手机端的官方APP——nrf connect连接设备并上传升级固件

8、官方例程中的跳转处理

9、如有需要单区升级怎么更改

10、APP为有绑定(bond)的应用,如何更改APP和bootloader

特别注意,以下三点可能是你升级不成功的原因:

  • 在升级时不同的芯片有许多细节问题,可能导致升级不成功,大部分都已经以错误解决的方式进行了记录(文中标红部分),如果你发现你升级不成功,请先参看这些解决方案,一般都会得到解决,如果在官方SDK没有你当前使用的芯片的BootLoader例程可以直接使用,你可以参考后一篇文章(DFU升级——uart/usb方式),在其中也列举了一些关于DFU时可能遇到的错误,如果在本文中没,可以转到该文章进行参考,说不定 就解决了。
  • 本次测试采用的是官方DK版,是兼容SDK程序的,如果你在测试时是用自己打板的板子,那么可能由于LED灯按键等引脚的外部电路连接不同,导致程序中相关函数的返回值不正确(在官方例程中基本所有函数都有返回值,并会对返回值进行检查,如果不成功会进入错误中断),当发生错误时,无法正常运行,那么请去注释掉相关点亮LED或者按键判断的函数,在去进行测试。这里在举一个例子,比如你app中使用uart,但是你RX引脚连接了后续电路,那么在uart的官方初始驱动中会检查RX电平是不是高电平,如果不是那么就会出现返回值错误,芯片直接不跑了的情况。
  • 还有就是自己打的板子上没有低速晶振的,一定要把app和bootloader中的宏修改为使用内部RC,只要有一个地方不修改在使用ble的时候就会出现问题。原因是什么呢,只要看过官方dome的同学都知道,nordic的官方dome都是基于他们自己的DK板的,上面是有两个晶振的,低速晶振会提供时钟源给RTC,ble在运行的时候需要一个timer和RTC。如果板子上没有低速晶振,那RTC就没有办法工作(默认是使用外部低速晶振)。所以一定要改为内部RC,app和bootloader中都要改,不要只改一处。

一、秘钥生成

在开发环境搭建完毕后,新建一个文件夹DFU(任意命名),在DFU中生成秘钥,打开新建文件夹后,按住Shift键2秒左右单击右键选择powershell窗口,运行下面两条命令生成私钥和秘钥(必须保存好,后期升级都要用到)。

  • 私钥生成命令:nrfutil keys generate priv.pem (priv.pem就是私钥)
  • 公钥生成命令:nrfutil keys display --key pk --format code priv.pem --out_file dfu_public_key.c (dfu_public_key.c就是公钥)

 运行完毕后生成了如下两个秘钥文件:

问题解决:

这一步生成秘钥有多重要我就不说了,反正秘钥前后不一样,会导致程序一直在bootloader中无法跳转到app。那么除非使用jlink再次下载固件,不然设备就是变相的相当于变砖了。有些时候你发现你app始终在bootloader中,那么可能就是这的问题。

所以如果你在后续升级过程中,程序一直在bootloader中,无法跳转到APP,那么可能就是你秘钥没有按照3.1的方式替换,或者你做原始固件的秘钥丢失了,你在生成了一份秘钥做了一个app,而这个APP使用的签名秘钥和你最开始烧写到nrf设备中的固件使用的签名秘钥更不是一个。

一个很直观的判断程序是在bootloader中还是在APP的方式是,你设备的蓝牙广播名为“DfuTarg”那么就是在bootloader中,并没有跳转到APP,APP中的蓝牙名如果你没有更改,那么一般是Nordic_UART。当然这种方式只针对于你bootloader和APP都有ble的情况下。

二、算法库生成(micro-ecc算法库)——这算法库只需要生成一次就行。

官方的sdk从12.0开始,采取bootloader升级会先验证固件的签名,应用程序和bootloader的验证签名一致才能后续继续升级;反之失败。官方加入密匙验证的校验,需要添加算法校验lib来生成对应的key。

2.1、算法库生成环境

请参看如下连接:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com)。在环境正确的前提下,我们打开SDK的如下目录:external\micro-ecc,可以看到如下文件和一个.bat的文件,我们只需要双击运行他就行。会自动生成算法库文件lib文件。

 2.2、打开build_all.bat文件如下:

 可以看到,这个文件中需要去到github上获取一个算法文件,所以需要保证你的网络良好。

(当然如果你有这个文件那么可以不用去加载,但是要把这文件放到external\micro-ecc目录下,然后可以注释掉如下图红框中的几个指令)

 然后就可以运行一下build_all.bat脚本了。

2.3、问题解决:

有时候如果你原本使用的是SDK17,但是有需要用到其余版本SDK,如SDK15版本做DFU,这时在执行前面的过程中,发现怎么都不能在相应的工具链目录中生产.lib文件;如下图所示选择keil生成nrf52的.lib文件,在目录:external\micro-ecc\nrf52nf_keil\armgcc 下查看,运行build_all.bat并没有生如图所示.lib文件。

这个时候你可以进行如下几个操作,任选其一就行:

①、在你的cmd命令窗口中定位到external\micro-ecc目录去执行一下build_all.bat中的命令(复制粘贴运行就行),

②、或者在external\micro-ecc中按住键盘shift键后2s点击鼠标右键,在选项框中打开PowerShell窗口运行build_all.bat中的指令。

③、又或者打开build_all.bat脚本,在后面加入pause保持在运行,不要让其运行后就关闭。

运行之后,可以查看到如下报错,找不到相关工具链需要的文件,我测试的版本是缺少7 2018-q2-update文件(各位读者缺少的可能是其余文件),且查看相关报错提示的目录C:/Program Files (x86)/GNU Tools ARM Embedded下没有这个版本。

这个时候我可以去下载提示缺少的GCC编译文件,如果找不到,那么我们可以选择原本有的进行使用,那这种方式怎么做呢?

解决方式:

第一步:在编译生成.lib文件失败的SDK中的components\toolchain\gcc 目录下,有下面几个文件,由于我PC机是windows,所以我打开Makefile.windows。

 第二步:更改,可以看到,这个文件确定生成.lib库时使用的gcc版本,我只要更改了和我原本SDK使用相同的版本就行,可以在相同的目录下去查看版本号,或者直接根据你的安装的版本去更改,

 完成以上步骤,后直接运行build_all.bat 后在打开external\micro-ecc\nrf52nf_keil\armgcc  可以看到已经生成了.lib的库文件。

在这会有一个问题,那就是你现在的环境没有GCC,这个时候你可以去gcc官网下载一个GCC下来安装好后做更改就行,我下面就来演示一下:

 如图是我在官网上下载下来的windows环境下运行的ARM版本的GCC,注意一定要是ARM版本的GCC。然后我安装一下,我在安装过程中更改了安装路径,放在了我的E盘下,除此之外基本一路的next下去就行,可以在百度上参看相关安装教程:

在安装环境下的bin中就有稍后要使用的arm-GCC;

 然后在这个安装的目录下打开CMD命令窗口(请根据你自己的安装目录自行打开,不要死板的找和我一样的路径):

 然后使用GCC的版本查看命令( arm-none-eabi-gcc.exe -v),确定我刚刚安装的GCC的版本是什么,由截图可以确定,我安装的是10.3.1版本:

 由此我们就知道了GCC的路径和版本,把这两个东西告诉我们的脚本:

对应修改好后,保存。就可以去运行lib库编译脚本了。

 

 

 

三、bootloade程序生成

 3.1、公钥替换

把第一步生成的dfu_public_key.c替换SDK包examples\dfu路径下的自带公钥,然后打开examples\dfu\secure_bootloader目录,选择你的芯片和想要的升级方式,以及是否包括debug功能的BootLoader程序,并编译运行。芯片和SDK包对照如下表:

 

3.2、编译以及错误解决(uECC.h错误)

在打开的工程中如果编译后有下列错误出现,说明你SDK中没有算法库存在(继续去确定第二点SDK中的算法库是否有生成并存在)

 关于这问题,上面的第二点说过算法库是需要生成一次,但是为什么你以前有升级成功,经过一段时间后想要再次跑升级包生成流程还是出现这个问题呢,原因可能是你上使用SDK包和这个SDK包可能不一样了,你下载了新版本的SDK包,或者从新解压过一个SDK包覆盖了原本那个。这时你需要从新去生成算法库文件。

如果编译没有报错,那么我们就找到生成的.hex文件,把它放到DFU文件夹中,重命名一下,可以直接复制粘贴脚本生成相应文件,也可以自己定义,记住更改名字后在执行脚本命令名字要对应。

 

四、APP程序生成

编译application代码。这一步如果仅仅是进行测试:那么我们可以直接用例程中的带有DFU的蓝牙例程进行测试,但是实际开发中如果有自己的工程,那么就需要去移植DFU功能,移植方式看官方中文博客,路径如下:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com)

4.1、官方DFU例程

如果仅仅只是测试那么在SDK中有一个专门的例程是已经添加了DFU蓝牙服务工程,直接编译例程即可,但是在其中只有52810,52811,52832,52840的工程的例程:

SDK根目录 \examples\ble_peripheral\ble_app_buttonless_dfu\pca10040\s132\arm5_no_packs,将生成的hex文件改名为:app.hex   ,放入DFU文件夹。

 4.2、自定义DFU工程添加

如你使用的芯片不是这几个型号,那么就没有测试例程,只能自己添加例程,下面我就以52820为例进行例程添加(参资料考:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com) )

1)、选择例程:由于一般开发都是基于从机的透传例程进行开发所以我们选项目录examples\ble_peripheral\ble_app_uart下的工程进行添加,这个例程下的板子也是最全的

2)、在NUS(透传例程)添加一个DFU服务

  第一步:.c文件和.h头文件路径 添加

  在工程中新建一个DFU文件,添加四个.c文件(路径:components\ble\ble_services\ble_dfu 和 components\libraries\bootloader\dfu)

 

  添加如下四个头文件路径:

  

  第二步:添加全局宏定义

DFU_ADD DEBUG BL_SETTINGS_ACCESS_ONLY NRF_DFU_SVCI_ENABLED NRF_DFU_TRANSPORT_BLE=1 

  添加后如图所示:

  

3)、 修改sdk_config.h文件

  第一步:使能DFU模块,就是修改sdk_config.h中的宏定义  

#define BLE_DFU_ENABLED 1
#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 0

  第二步:由于我们需要添加一个服务,所以ATT table size也要变大,同时增加一个DUF的UUID,原本只有NUS服务,所以UUID为1,增加一个变为2

#define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1600
#define NRF_SDH_BLE_VS_UUID_COUNT 2

  添加修改如下图:绿色框处为修改后的值。

  

  第三步:预留足够的RAM空间给蓝牙服务建立运行

  由于我们添加了一个服务,增大了ATT table size的大小,那么原本预留个蓝牙服务建立运行的RAM空间就不够啦,所以我们要修改APP在RAM的运行起始地址(增大),在RAM前面留够足够的RAM空间给到蓝牙服务进行缓冲,同时需要注意的是每一块芯片的RAM是不同的,需要在设置时需要根据芯片的RAM大小去对应Size的参数,如下为nrf52820的修改

  

  由于nrf52820的RAM为8000在图中红框柱部分相加为8000,如果正在实际使用中RTT报错为存储空间不够(报错打印为4)如后面4.3小结中RTT所示的错误,那也请来按照调整第二步和第三步的大小。很大概率就解决了。

4)、代码添加

  第一步:头文件添加,在main.c中加入头文件

#ifdef DFU_ADD
#include "ble_dfu.h"
#include "nrf_bootloader_info.h"
#include "nrf_power.h"
#endif

  第二步:然后在services_init()中添加ble dfu服务

#ifdef DFU_ADD
ble_dfu_buttonless_init_t dfus_init = {0};
dfus_init.evt_handler = ble_dfu_evt_handler; err_code = ble_dfu_buttonless_init(&dfus_init); APP_ERROR_CHECK(err_code);
#endif

  添加后如下图所示,(ble_dfu_evt_handler为DFU回调函数)

  

   并在函数前添加如下代码(在DFU回调中你还可以加入一些你需要的其余处理):

#ifdef DFU_ADD
static void disconnect(uint16_t conn_handle, void * p_context)
{
    UNUSED_PARAMETER(p_context);
ret_code_t err_code = sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); if (err_code != NRF_SUCCESS) { NRF_LOG_WARNING("Failed to disconnect connection. Connection handle: %d Error: %d", conn_handle, err_code); } else { NRF_LOG_DEBUG("Disconnected connection handle %d", conn_handle); } } static void advertising_config_get(ble_adv_modes_config_t * p_config) { memset(p_config, 0, sizeof(ble_adv_modes_config_t)); p_config->ble_adv_fast_enabled = true; p_config->ble_adv_fast_interval = APP_ADV_INTERVAL; p_config->ble_adv_fast_timeout = APP_ADV_DURATION; } static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event) { switch (event) { case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE: { NRF_LOG_INFO("Device is preparing to enter bootloader mode."); // Prevent device from advertising on disconnect. ble_adv_modes_config_t config; advertising_config_get(&config); config.ble_adv_on_disconnect_disabled = true; ble_advertising_modes_config_set(&m_advertising, &config); // Disconnect all other bonded devices that currently are connected. // This is required to receive a service changed indication // on bootup after a successful (or aborted) Device Firmware Update. uint32_t conn_count = ble_conn_state_for_each_connected(disconnect, NULL); NRF_LOG_INFO("Disconnected %d links.", conn_count); break; } case BLE_DFU_EVT_BOOTLOADER_ENTER: // YOUR_JOB: Write app-specific unwritten data to FLASH, control finalization of this // by delaying reset by reporting false in app_shutdown_handler NRF_LOG_INFO("Device will enter bootloader mode."); break; case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED: NRF_LOG_ERROR("Request to enter bootloader mode failed asynchroneously."); // YOUR_JOB: Take corrective measures to resolve the issue // like calling APP_ERROR_CHECK to reset the device. break; case BLE_DFU_EVT_RESPONSE_SEND_ERROR: NRF_LOG_ERROR("Request to send a response to client failed."); // YOUR_JOB: Take corrective measures to resolve the issue // like calling APP_ERROR_CHECK to reset the device. APP_ERROR_CHECK(false); break; default: NRF_LOG_ERROR("Unknown event from ble_dfu_buttonless."); break; } } #endif

  在main()函数的最前面加入修改BootLoader广播名字的代码,(由于iOS DFU的时候默认就会去改广播名字,为了兼容iOS):

#if DFU_ADD
uint32_t err_code; err_code
= ble_dfu_buttonless_async_svci_init(); APP_ERROR_CHECK(err_code);
#endif

  添加完成,我们编译生成hex文件,将生成的hex文件改名为:app.hex   ,放入DFU文件夹。

4.3、错误解决:

实际工程添加中,你可能会出现出现无法运行的情况,那是由于我以52820为例添加的DFU例程,但是如果你采用其余芯片,可能导致留给协议栈的RAM空间并不够,所以你在发现第6步烧写原始固件后无法找到蓝牙广播,那请打开你的RTT,看是否有提示你RAM空间不够的错误,如果有该提示,请按照4.2中的方式去调整RAM的大小,如提示图中RTT打印提示请由2BA8,增大到2BB8。特别注意,如果没有RTT打印,请多次连接一下,我们的官方例程都是自带RTT打印的,如果没有,可能没有连接正确。

五、生成BootLoader settings page

采用官方博客中说的版本2方式生成settings.hex文件。命令如下:(如果文件中没有按照脚本中的方式命名,请根据自己生成的文件名进行更改)

nrfutil settings generate --family NRF52 --application app.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

六、原始固件烧写

这一步就是我们一个项目开发完成后,把要写到出厂产品的中固件烧写到芯片中,后期如果有需求需要去做APP升级,那么就可以通过DFU的方式升级了。

如中文博客中的方式一样运行后续的命令:

6.1、固件合成

将上文生成的3个hex文件和softdevice hex文件merge成一个文件,然后通过nrfjprog或者nRF Connect桌面版进行烧写,相关命令如后续所示:

6.1.1、复制一个协议栈文件(softdevice hex)到DFU文件目录下,我们可以打开SDK如下目录:components\softdevice 这里有各种版本的协议栈,当然我上面编译的APP工程协议栈栈为112的6.1.1版本,如下图所示,那么就打开找到相同名的hex文件复制到DFU目录下。

 这时我们的DFU目录下有了以下这些文件:

开始合并hex文件,脚本命令:

mergehex --merge bootloader.hex settings.hex --output bl_temp.hex
mergehex --merge bl_temp.hex app.hex s112_nrf52_6.1.1_softdevice.hex --output whole.hex

6.2、烧写固件

烧写hex文件命令(以nrfjprog为例),也可以用nRF connect(如果没有nRF connect请参看中文博客开发环境搭建篇进行环境搭建):

nrfjprog --eraseall -f NRF52
nrfjprog --program whole.hex --verify -f NRF52
nrfjprog --reset -f NRF52

6.3、错误解决

错误1:

在使用一些版本的SDK进行DFU升级时,如果在合成安装包后,在烧写阶段出现如下错误(以nrf52810为例):

命令行方式:数据位域有效区域外,或者有效区域没有数据。

 J-flash方式:不符合所选闪存扇区或不符合程序目标

然后查看生成的whole.hex文件,发现BootLoader的数据在地址0007 F000开始的地址,如下图所示,但是对于有些芯片(如nrf52810)flash只到60000,显然对于该芯片这个地址已经超过了FLASH的是存储容量,所以在烧写的时候会报错,那是因为合成的最终HEX固件已经超过了flash的容量。

 问题解决:

在生成settings.hex文件时,更改一下生成命令:

原来的生成命令如下(执行后,默认开始地址为nrf52832的地址),也就是0x0007F000:

nrfutil settings generate --family NRF52 --application app.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

 但是我们工程中BootLoader的地址为0x0002F000(对于nrf52810)

 针对于这个问题,只要修改一下生成BootLoader文件的指令即可:如下为nrf52810的,那如果是其余系列的芯片有相关问题,解决方式一样,在生成BootLoader时把默认的NRF52,改为NRF52xxx即可。

 nrfutil settings generate --family NRF52810 --application app.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

命令执行后可以看到执行情况如下,开始地址已经变为和工程中一样适用于nrf52810的地址,然后在去下载就不会报错了。

 针对以上这类问题,如你发现在下载时使用6.2所列出的烧写命令居然不能进行烧写,那么你可以尝试把nrf52改为nrf52xxx(对应的芯片型号)进行测试,说不定就烧写成功了。

特别注意:针对该问题总结一下替换命令:

setting.hex生成命令 对应芯片型号
NRF51 nrf51系列芯片
NRF52810 nrf52810
NRF52QFAB nrf52820
NRF52 nrf52832、nrf52833
NRF52840 nrf52840

实测发现:如果你是52840的芯片,然后你在制作setting的时候使用的是52832的指令也就是,合并语句的--family 项为:--family NRF52 而不是 --family NRF52840的时候,烧写合并固件后会一直在bootloader中,无法跳转到app中,这是由于setting的地址设置错误,校验环节出现问题导致bootloader认为app是一个不合法的固件,因此无法跳转,如果你打印了log,会有如下打印:

错误2:

如果你自己打板,在经过上面的步骤后发现固件无法运行,手机设备搜索不到APP中设置的名字,只能搜索到BootLoader中的名字,那说明程序没有正确跳转到APP端,这样就算升级后,也依然无法跳转APP执行,这是由于BootLoader程序中选择了需要按键确认跳转的方式,而你自己的硬件并没有设计该按键,导致程序一开始执行,就由于GPIO口电平不正确,错误判断直接进去到DFU模式(程序运行在BootLoader中),无法正确跳转到APP,去执行正确的升级流程。

解决方式:在编译BootLoader时,更改如下宏定义——取消勾选,这样就可以正确跳转APP,进行升级了。

6.4 下载固件到芯片后可以用nRF connect手机版搜索到我们的设备,连接后有如图所示的界面,可以看到右上角有一个DFU的图标,说明我们的原始固件具有了DUF(OTA方式)功能。

七、固件升级

7.1、新固件升级包合成

如果要进行新固件的升级,那么就需要准备好一个.zip升级包,然后在利用手机APP进行升级。那这个.zip如何进行生成呢?下面我们就来一步一步的的进行,

7.1.1、生成新的APP程序

因为我这是做测试,就只改变一下设备的名字,把名字改为“Nordic_DFU_NEW”,然后编译工程,找到新生成的HEX文件复制到我们的DFU文件夹,重命名为app_new.hex;

7.1.2、确定协议栈版本号

两种方式:

1)、直接查看SDK中相应协议栈的PDF文档获取

SDK的components\softdevice目录下,有各个版本协议栈的资料,我们根据自己使用的版本来选择确定值,我使用的是s112的版本,所以在如下目录下components\softdevice\s112\doc有一个PDF文档,我们打开该文档,可以看到如图所示的协议栈版本号:

 

 所以在如下命令中我使用的协议栈版本号,要设置为00B8,所以在开发自己的DFU例程时,要根据使用的协议栈去确定生成.zip 升级包命令中的协助栈版本号是什么,如果出错,你 会发现,不管怎么升级,你的新固件都是无法升级成功并运行的。

2)、通过工具命令获取

直接在CMD窗口运行:

nrfutil pkg generate --help

列举的协议栈版本号:

7.1.3、升级包合成

升级包生成命令如下,请修改版本号为你使用的协议栈版本号:

nrfutil pkg generate --application app_new.hex --application-version 2 --hw-version 52 --sd-req 0x00B8 --key-file priv.pem app_s112_new.zip

运行命令后你会发现在DFU文件夹中生成了一个app_s112_new.zip压缩包,这就是我们要通过手机APP发给我们设备的升级包

 7.2、升级

先把7.1步骤生成的固件包上传个手机,然后打开手机APP,我使用的是nordic官方APP(nrf connect),然后连接好设备然后点击DFU的升级标识。

然后在接下来的界面选择ZIP升级方式,然后点击OK。

 然后在手机中找到发个手机的ZIP升级压缩包并确定:

 接下来你会看到我们的升级界面如下,这里注意一点,升级过程中会从新进行连接,连接后为BootLoader的名称,既如图显示的DFUTARG,请切换为原来的蓝牙NORDIC——UART才能看到升级包传输界面:

 升级完成后,复位一下设备,我们重新在手机APP上搜索一下,可以发现蓝牙广播名已经变为我们新固件的名字了:

 到此,升级完毕!

 7.3、错误解决

有些时候会出现这种情况,在1到6的过程都没有问题,但是还是升级失败,那可能是你APP版本太低,那请去nordic官网下载最新的APP重新安装一下,之后在去重复升级流程。

八、官方例程中的跳转处理

任何MCU在从一个程序区域跳转到另一个区域时,都要先清除中断,释放掉使用的资源,并且更改好跳转的中断向量表,那么nordicbootloader是怎么做的呢?

在官方例程中有这样一个处理函数:

nrf_bootloader_app_start()
在其中直接往中断控制寄存器中写1完成对禁用中断的处理:
    NVIC->ICER[0]=0xFFFFFFFF;
    NVIC->ICPR[0]=0xFFFFFFFF;

然后是中断向量偏移,需要在20000000的首地址直接放置需要跳转的app的首地址即可,这里要注意的是有没有协议栈的用法,

设置完成后使用函数app_start()进行跳转;

 

总结为下面几点:

1MCU启动从0x0000000地址启动,这个区域属于协议栈部分,具体的话是协议栈的MBR,在第十章节的图中有标识,你可以上翻看一下,这个MBR具体有芯片默认的中断向量表,并且肯定也是最先执行的。

2、协议栈如何判断有没有bootloder,答案是通过检查一个寄存器是否有地址值判断的,该寄存器就是UICR寄存器,这个寄存器在前面我们也有讲过,当你有bootloader时,在bootloader中有一个代码就是往这个寄存器中写入你bootloader应用程序的入口地址。这也就是使用program工具连接上芯片后,去看我们的bootloader生成的HEX文件时为什么会提示你的hex超过了flash大小,这个寄存器的地址不在flash的地址范围内,这就是根本原因。没有值就跳转到app就行。

3、在没有bootloader时,MBR(协议栈的MBR)会将中断映射到app,且跳转到app,这一部分我们是不知道的,因为协议栈是不开源的。我们要注意的是,把app开始的地方接着协议栈后面放就行,在keil的魔术棒中有一个地方设置ROM,和RAM的地址和大小就是确定这些。

4、有bootloader时,如第1点,你有bootloader,那么就会修改UICR寄存器的值,好让协议栈知道要跳转到bootloader,这个写寄存器值是预编译的,所以就算先运行协议栈,由于在下载程序的时候已经把地址写到UICR中设置好了,所以协议栈肯定是知道在去运行完毕后要跳转到哪里的。

5、在有bootloader时,在跳转app时肯定是要处理中断和再次映射新的中断向量表的。处理方式也就是前面的图解部分了。

6、官方解释有如下两点:

  • 当引导加载程序启动并且软设备更新未进行时,它将告诉 MBR 将中断转发到软设备向量表(使用SD_MBR_COMMAND_INIT_SD选项调用sd_mbr_command)。然后引导加载程序告诉softdevice使用sd_softdevice_vector_table_base_setBOOTLOADER_REGION_START;将中断转发回引导加载程序的向量表。
  • 当引导加载程序需要启动应用程序时,它会告诉软设备开始使用 sd_softdevice_vector_table_base_setCODE_REGION_1_START) 将中断转发给应用程序

九、如有需要单区升级怎么更改 

  在SDK17.1版本中(这也是官方最新的SDK,以后不再更新,有更新都会再NCS上)官方默认的是双区升级:

 在bootloader的注释中可以看到,如果有足够的flash空间,那么就采用单区,否则就使用双区,默认是不使能的,升级时也是双区升级。那么怎么切换单区(single bank)呢?其实再bootloader中只要启用single bank的宏定义就可以了,bootloader会检查宏定义,确定是否使用单区升级。

 然后编译的bootloader就只支持单bank升级了,如果再升级的时候断电,如升级到60%,你的设备断电了,那么再次上电设备就只会再bootloader中了,无法回滚,并且一直等在bootloader中,直到你完成升级。这个时候你要搜索的蓝牙名称是DFUTAG这个名字,这是bootloader中蓝牙的名称,链接上后再次进行升级,升级流程走完,就可以执行新的app了。

十、APP为有绑定(bond)的应用,如何更改APP和bootloader支持DFU

  前面添加的dfu支持的是没有绑定的,也就是APP是不支持配对的。但是许多时候开发的蓝牙设备是需要绑定配对的,那这时候如果在APP中加入了配对绑定,在按照上面的流程进行ble-DFU是行不通的,没办法进行升级。我们需要修改一下宏定义让bootloader支持已配对的设备进行ble-DFU升级。

注意1:本章节讨论的重点不是绑定,前面使用的历程ble_app_uart是没有绑定功能的,如果你在测试ble-DFU前没有在ble_app_uart中加入配对绑定的功能,那么你可以直接使用如下的例程进行测试(依然要添加DFU功能,相关代码和第4章节的一样):路径如下examples\ble_peripheral\ble_app_hrs,这是一个心率例程。如果你使用的是nrf52833等在改例子中没有直接对应工程的芯片,你可以直接在进行修改,方法的话在我另一篇文章中,详细讲了如果SDK中没有直接可以使用的例子,我们怎么在原本的例子上进行简单修改,直接快速的获取一个可用的例子。链接如下:nrf52——DFU升级USB/UART升级方式详解(基于SDK开发例程) - 星辰_stars - 博客园 (cnblogs.com)

  整个修改过程很简单,就是在前面第四章节的ble-DFU功能的基础上启动几个宏定义,获得APP;在bootloader中启动几个宏定义获得bootloader,然后在用工具生成固件和升级包即可。

10.1、APP修改

注意2:本次APP的添加默认你的工程已经有了配对绑定功能,且已经在工程中已经加入了ble的DFU服务(即ble的OTA功能),无绑定功能的请参考本章的注意1,无OTA功能的请参看本文的第四章。

第一步:c源文件加入

在SDK的如下目录下有这样一个文件:components\ble\ble_services\ble_dfu,其中包含两个ble_dfu的c文件,一个是booded(支持绑定的),一个是unbonded(不支持绑定的),人下图,需要确保在工程中有加入了ble_dfu_bonded.c这个文件,

第二步:宏定义使能

第一个宏定义是需要使能绑定方式DFU的宏定义:

#define NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS 1

 第二个:是勾选特征改变服务使能更新功能:

#define NRF_SDH_BLE_SERVICE_CHANGED 1

第三步:DFU程序添加位置确定

  这一步的目的是防止还没有初始化好PM模块,就直接使用PM模块的注册函数,这将导致错误,原因是在OTA代码加入的时候,会使用进行DFU初始化,区别于无绑定的初始化,有绑定的初始化,需要注册一个绑定回调给到OTA服务,如果你把这个OTA服务的功能加入放在了PM模块的初始化前,就是在没有初始化的模块上进行注册,这是不可行的,就像你GPIO口还么没初始化,你就想直接在GPIO口上进行电平翻转一样。

 正确的顺序如下;

 如果在PM模块初始化前你就初始化了你的OTA服务,那么你就会再log中得到一个错误[NRF_ERROR_INVALID_STATE] ,

 该错误说在main.c这个文件的607行发生了一个致命错误,错误为NRF_ERROR_INVALID_STATE,错误码是8,注意,如果你要想像我一样的打印出具体哪个文件那个函数运行错出现了错误,需要在全局宏定义中加入DEBUG,然后编译,且确保你的工程是支持log打印的。那么我们根据报错看一下:

 找到607行,可以看到确定是ble_dfu_buttonless_init函数执行出错了,然后在该函数中去继续定位,就会看到是pm_register返回的错误,我们在其定义处可以看到其错误说明是说,如果没有初始化PM模块时有该错误,所以一定要注意位置:

   完成上面的步骤,那么app就算制作完成了。

10.2、bootloader修改

   bootloader中不用添加什么文件,直接启用两个宏定义即可。

第一个是:

NRF_DFU_BLE_REQUIRES_BONDS

第二个是:

NRF_SDH_BLE_SERVICE_CHANGED

加上就行。然后就可以得到支持绑定后进行DFU的bootloader了。

10.3、测试

  烧写固件后,我们先绑定,并确定好我们已经绑定好了,

 然后连接并进行升级,可以看到升级正常进行:

 

 注意事项:

# 研发生产测试方向
1、 预留好射频测试的串口测试点作为串口 TX 和 RX 做 DTM 测试用,任意 GPIO 即可 (首推空引脚),方便研发调试或者方便和工装对接
2、 开发调试的时候使用 Jlink 进行下载代码和 Debug;工厂批量烧录如下,主要是下载速 率和稳定性的问题,因为影响工厂生产效率
3、 芯片射频匹配电路根据芯片型号和封装和线路图参考手册上面的参数进行配置;射频 走线为 50 欧姆,走线光滑过渡自然,背面等有完整的参考地面,走线附近多地孔 等;天线匹配根据实际天线设置匹配参数;研发阶段射频必须做频偏校准,通过调节 高频晶振负载电容控制 CH0 /CH19/CH39 通道的频偏在 0KHz 附近,同时射频区域和晶 振匹配电容的器件选用小封装、高精度、低温飘的器件,以保批量时候的产品一致性
 
posted @ 2022-05-03 17:16  星辰_stars  阅读(6416)  评论(6编辑  收藏  举报