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

摘要:在前面的nrf52——DFU升级OTA升级方式详解(基于SDK开发例程)一文中我测试了基于蓝牙的OTA,本文将开始基于UART和USB(usb_cdc_acm)进行升级测试。

整体升级流程:

整个过程希望你和我一样采用有log的bootloader进行。可以看到执行流程,也能在出错时进行检查。

1、生成秘钥(一定要保留好)

2、生成USB或者UART的BootLoader程序(需要算法库和秘钥)

3、生成APP工程

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

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

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

7、利用USB连接设备并上传升级固件

8、uart升级(接连第7节)

9、更改进入bootloader的方式,从APP触发进入

10、无直接的bootloader例程,如何升级

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

12、uart升级MCU版主机数据传输协议

 特别注意:在这过程中依然会遇到各种各样的细节问题,如果你在升级时有遇到问题,可以着重看一下是否和你遇到的问题类似,每一节遇到的问题,我都会以一个小节的方式放到后面供大家参考。

还是那句话,在操作前我默认您已经安装好了相关测试环境和基础工具,如果没有请参看如下的官方中文链接去安装:Nordic nRF5 SDK开发环境搭建(nRF51/nRF52芯片平台) - iini - 博客园 (cnblogs.com),同时本文也只是对整个升级流程进行一次完整操作,并记录一些遇到的问题及解决方式,如果你想知道具体原理,请参看如下的官方链接:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com)  原理是一样的,只是数据传输方式选择不一样了而已。那么接下我们就正式开始,有些步骤可能和蓝牙OTA篇重叠,可以选择性观看。

开始之前先把我们要用到的基础指令全部贴出来:

//私钥生成命令:
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就是公钥)

//settings包生成命令
nrfutil settings generate --family NRF52 --application app.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

//生成初始固件包命令
mergehex --merge bootloader.hex settings.hex --output bl_temp.hex 

mergehex --merge bl_temp.hex app.hex s140_nrf52_7.2.0_softdevice.hex --output whole.hex

//擦除芯片命令
nrfjprog --eraseall -f NRF52
 
//下载命令
nrfjprog --program whole.hex --verify -f NRF52

//硬件复位命令
nrfjprog --reset -f NRF52 

//生成升级包命令
nrfutil pkg generate --application app_new.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu.zip

//USB升级触发命令
nrfutil dfu usb-serial -pkg dfu.zip -p COM14 -fc 0 -b 9600

//UART升级触发命令
nrfutil dfu serial -pkg dfu.zip -p COM14 -fc 0 -b 9600

一、秘钥生成

还是熟悉的配方,先生成私钥,在任意一个位置建立一个文件夹,如DFU_USB,打开后直接在路径栏输入cmd回车打开我们命令窗口(其他方式打开也一样,但是请定位到刚刚这给文件夹)。

在命令框依此运行下面两条命令:

  • 私钥生成命令: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就是公钥)

 运行完毕后生成了如下两个文件文件priv.pem和dfu_public_key.c,具体如图所示:

二、算法库生成(micro-ecc算法库)。

如果你已经使用过蓝牙OTA方式升级,那么你的算法库已经存在了,不需要再去生成了。如果你是第一次测试DFU升级,那么依然需要进行算法库生成:可以参考我上一篇文章的生成流程,链接如下:nrf52——DFU升级OTA升级方式详解(基于SDK开发例程) - 星辰_start - 博客园 (cnblogs.com),如果你环境没有问题,或者没出现过修改,那么直接运行SDK目录:external\micro-ecc下的build_all.bat脚本即可,有问题再去参考解决

三、bootloade程序生成

 把我们第一步我们生成的密钥替换SDK中的密钥,这一步如果你前面已经参考过蓝牙OTA例程,那么就可以用当时生成的密钥,不用在替换,如果没有是第一次进行DFU升级,那么请替换(路径:examples\dfu):

 替换完成打开同目录下文件secure_bootloader,你会看到许多的例程bootloader,这些例程基本已经涵盖了nrf52系列全部的芯片和升级方式(BLE/USB/UART),在里面你会看到两种区别,有debug和无debug的,区别只是有debug的是有log打印信息的,在运行时你可以知道程序运行到哪里的,对于研发调试时很有必要的,但是相对的生成的hex文件就大,也就需要更多的flash和RAM,你可以自行选择。

选择芯片和你想要的升级方式打开后选择编译方式生成bootloader,但是如果想要的没有怎么办,比如你想使用nrf52833的USB,没有怎么办,不要慌,我会在后面的章节进行讲解,先让我们走一遍正常流程。

芯片对于如下:

 那我就来选择一个进行升级把,我选择nrf52840,先生成USB的选择如下工程:pca10056_usb_debug,编译生成hex文件。如果你编译时出现uECC,h的报错,说明前面算法库生成,你可能只是简单的双击了build_all.bat脚本,但是执行出错了,并没有完全生成算法库,那请去看我关于蓝牙OTA的例程进行解决。在正确生成hex文件后把这个文件复制到DFU_USB文件夹中并改名为bootloader_usb(为了一会和UART区分开),

四、APP程序生成

这一步没有蓝牙OTA那样麻烦,我们直接任意找一个蓝牙程序即可,我选择最经典的NUS程序(透传)路径如下:examples\ble_peripheral\ble_app_uart,直接打开编译即可,获得一个蓝牙APP的HEX文件,放到DFU_USB文件中,改名为app,同时注意你工程的协议栈版本是多少,可以这样查看:

 然后我们去如下目录找到对应版本的协议栈HEX文件,复制到我们的DFU_USB中,路径:components\softdevice。然后在更改一下APP中的蓝牙名称,默认为Nordic_UART,编译在获得一个APP程序,复制到DFU_USB中改名为app1,便于接下来制作升级包时使用。

五、生成BootLoader settings page

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

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

 

 由于我采用的硬件芯片为52840,所以命令中标红字段为nrf52840,如果是其余系列请参考下表:

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

 在执行了命令后会有结果显示如下,可以看一下start Address是否和芯片定义的地址一致,

 由以上两图对比,可以看到地址一致,没有问题。

六、原始固件烧写

6.1、固件合成

然后开始合成固件,脚本命令(主意文件名要和你实际的一样,不然会出错):

mergehex --merge bootloader_usb.hex settings.hex --output bl_temp.hex
mergehex --merge bl_temp.hex app.hex s140_nrf52_7.2.0_softdevice.hex --output whole.hex

6.2、烧写固件

烧写hex文件命令(以nrfjprog为例),也可以用nRF connect(如果没有nRF connect请参看官方的中文博客:开发环境搭建篇 进行环境搭建,我这给出博客地址:https://www.cnblogs.com/iini/p/9043565.html):

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

 用手机APP搜索可以看到我们蓝牙:

七、固件升级

7.1、新固件升级包合成

 确定协议栈版本是必不可少的一步,如果不知如何确定,请看蓝牙OTA升级篇。

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

nrfutil pkg generate --application app1.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu_usb.zip

确定上面命令中的0x0100这协议栈版本号有两种方式:

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

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

 

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

2)、通过工具命令获取

直接在CMD窗口运行:

nrfutil pkg generate --help

列举的协议栈版本号:

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

nrfutil pkg generate --application app1.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu_usb.zip

7.2、升级

 插入USB接口,确定USB端口接入,如果你是使用的DK板,那么按住按键4,紧接着复位一下,bootloader会检测到P0.25口电平,进入DFU升级模式,如果不是DK板,那么我们需要进行程序的修改,修改方式后续进行讲解。进入DFU后,你可以通过串口助手或者电脑的设备管理中查看,看到你的USB接口,记住端口号。(我的为31)

然后在进入DFU后,PC端输入升级命令,让PC端去扫描对应的端口(注意端口正确,你自己的端口),看是否有设备连接上,如果有就会按照协议进行数据发送:

nrfutil dfu usb-serial -pkg dfu_usb.zip -p COM31

-b为波特率(这是一个虚拟USB-CDC)。

可以看到升级成功

使用手机APP看一下,蓝牙名称是否改变:

 自此,USB升级完成。

7.3、问题解决(升级失败)

 在运行上一步时,你可能会遇到这样的问题,等了半天,没有升级,反而给我们报了许多错,这个在老的芯片如nrf52833,52832等UART或者USB(nrf52833、52820)时可能会经常遇到。

这个时候我们可以禁止流控,减小波特率。

运行下列指令:

nrfutil dfu usb-serial -pkg dfu_usb.zip -p COM31 -fc 0 -b 9600

其中-fc 0为禁止流控,-b为波特率(这是一个虚拟串口)。

 然后就可以升级成功了。

问题解决:

如果你使用的是UART升级的方式,到这如果还不行,那么可以直接使用串口命令的方式可能并没有正确更改到波特率和禁用流控,那么可以检查一下在bootloader程序中SDK_config中的宏定义,去修改一下禁用流控,和修改波特率,这一步相当于直接从源头上去确定好这些参数,你上位机端匹配设置的参数即可。

修改后相关的宏定义参数如下:

八、UART升级

通过前面的讲解,我们来快速实现一下UART升级,选择UART的bootloader,编译后改名为bootloader_uart。
然后命令如下:
生成setting:
nrfutil settings generate --family NRF52840 --application app.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

合成固件:

mergehex --merge bootloader_uart.hex settings.hex --output bl_temp.hex
mergehex --merge bl_temp.hex app.hex s140_nrf52_7.2.0_softdevice.hex --output whole.hex

烧写:

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

合成升级包:

nrfutil pkg generate --application app1.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu_uart.zip

查看端口号:

 

升级:

nrfutil dfu serial -pkg dfu_uart.zip -p COM3

等待升级成功:

 一样的问题,如果遇到无法升级的问题,请加上流控禁止和波特率。

命令:

nrfutil dfu serial -fc 0 -pkg dfu_uart.zip -p COM3 -b 115200

 

还有一个点,如果是自定义开发板,你可能不想使用官方默认的引脚作为uart升级,我们在哪里改呢?其实在bootloader_uart工程中去更改uart初始化定义的引脚即可:

 如,我现在想要使用串口27和26来升级,做如下修改就行。

九、更改进入DFU的方式(不使用按键)

在实际的项目中肯定不能说还要去按住一个按键然后复位才可以进入DFU进行USB或者UART升级。那我们如何改呢?方法很简单,只要修改DFU标志,然后触发软件复位即可。由于是测试,我这就利用软件定时器定时一定时间,然后修改DFU标志,修改完成后触发软件复位即可。实际中你可以是UART或者usb给到一条指令,通过解析指令去完成上诉操作。

下面列出DFU的触发方式,你可以进行搭配使用,用户设定的也只有1和2两种。

  1)、按键是否按下

  2)、保持寄存器GPREGRET1是否为0xB1

  3)、上次DFU过程是否还在进行中

  4)、应用程序校验是否通过

9.1、确定触发DUF的方式

按键触发定义在bootloader中,如果需要修改其余方式,我们要在bootloader中去修改。修改sdk_config中的宏定义,默认的定义如下图,使用了按键触发方式和寄存器方式:

9.2、在APP中加入软件DFU代码

下面来讲一下如何从APP中进入DFU,我们按照前面说的,添加一个软件定时器,在app广播开始20s后修改寄存器的值为0xB1,然后触发软件复位,从协议栈重新进入到bootloader去判断寄存器值,由于我们已经在软复位前更改寄存器值为0xB1,所以会进入DFU模式等待UART或者USB的升级指令,不在跳转到APP。

继续打开如下目录例程下nrf52840的透传例程:examples\ble_peripheral\ble_app_uart。然后加入如下代码:

//全局定义一个定时器别名
APP_TIMER_DEF(my_timer_id_DFU);//一个app定时器 

//main中初始化这个定时器
uint32_t err_code;
err_code = app_timer_create(&my_timer_id_DFU,APP_TIMER_MODE_REPEATED, my_timeout_handler);
APP_ERROR_CHECK(err_code);
err_code = app_timer_start(my_timer_id_DFU, APP_TIMER_TICKS(20000), NULL); 
APP_ERROR_CHECK(err_code);

//编写定时器回调,在回调中停止定时器,并修改寄存器值
#define BOOTLOADER_DFU_GPREGRET                 (0xB0)
#define BOOTLOADER_DFU_START_BIT_MASK           (0x01) 
#define BOOTLOADER_DFU_START    (BOOTLOADER_DFU_GPREGRET | BOOTLOADER_DFU_START_BIT_MASK) 

static void my_timeout_handler (void * p_context)
{
    uint32_t err_code;
    //停止定时器
    app_timer_stop(my_timer_id_DFU);

    NRF_LOG_DEBUG("In     ble_dfu_buttonless_bootloader_start_finalize\r\n");
  //一定先要清除GPREGRET寄存器
    err_code = sd_power_gpregret_clr(0, 0xffffffff);
  APP_ERROR_CHECK(err_code);
  //再来修改GPREGRET寄存器的值为0xB1 
  err_code
= sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
  APP_ERROR_CHECK(err_code);
// Signal that DFU mode is to be enter to the power management module   
  nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
}

加入如上的代码后,我们就可以编译生成app了,并起名为app_soft作为原始app,然后在修改广播名为soft_Nordic_UART作为升级app。然后我们开始利用原来的bootloader

按照升级流程在来一遍。

1)、生成原始固件并下载

//settings包生成命令
nrfutil settings generate --family NRF52840 --application app_soft.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 settings.hex

//生成初始固件包命令
mergehex --merge bootloader_usb.hex settings.hex --output bl_temp.hex 
mergehex --merge bl_temp.hex app_soft.hex s140_nrf52_7.2.0_softdevice.hex --output whole.hex

//擦除芯片命令
nrfjprog --eraseall -f NRF52

//下载命令
nrfjprog --program whole.hex --verify -f NRF52

//硬件复位命令
nrfjprog --reset -f NRF52

下载完成,依然可以看到我们的广播名为Nordic_UART,等待20s后,我们的定时时间到后会进入DFU模式,(如果你使用的是DK板,你会发现你的板子LED1和LED2亮起,接入USB,后LED1和LED2亮起,注意进入DFU后在bootloader中才会初始化USB)。

 2)、升级

制作升级包命令:

//生成升级包命令
nrfutil pkg generate --application app_soft_new.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu_soft.zip

//USB升级触发命令
nrfutil dfu usb-serial -pkg dfu_soft.zip -p COM54

然后可以看到我们升级成功,注意看你的USB端口号是什么,这里注意一点,USB端口号要在进入bootloader后初始化完成才能看到。

 蓝牙名称如下,我们升级成功。

 经过上面的实验,可以确定在实际过程中,你可以通过串口发送一条指令或者中断等方式去修改寄存器值然后软复位进入DFU。经过第8章节UART升级的讲解,和USB的区别只是替换为USB的bootloader然后,最后命令换一下,在这就不在列举了。

十、无直接的bootloader例程,如何升级

 在SDK中是没有52833的USB或者UART升级的bootloader的,因此如果你需要这些在SDK中没有的bootloader时怎么办。

在开始前,来看一下我们前面制作的固件是怎么放置的,下面是我使用工具programmer读取到的关于最后的whole文件的地址存放信息,如果你也想看到下图的存储地址,你可以把我们生成的文件一个一个的放到programmer中查看,可能不同的芯片由于flash大小不一样,会导致起始地址不一样,但是每一个文件在最终合成固件中放置的相对位置是不会变的。我们接下来的对每一个固件组成部分的地址也是按照这个的格式来制作的,需要严格遵守,否则会出现如合并错误(后面会给出该错误的截图,或者你走运地址没有冲突合并成功,但是会发现,程序无法完整运行起来(PC指针跳转到的地址没有程序,导致死机等情况))。

下面是官方给出的flash存储和我们的对照图,其中backup page就是setting的MBR(我把它叫做这个名字,是为了和后面程序代码对应,你可以理解为都是一个东西),setting page和backup page内容是一样的,都是一页,就是4K大小,为什么要这样设置,因为你传输数据肯定要校验是否正确啊,你不校验怎么敢让你升级的APP跑,如果你想看详细解释去看官方的解释,我就不在废话了,官方博客链接如下:详解蓝牙空中升级(BLE OTA)原理与步骤 - iini - 博客园 (cnblogs.com)

 10.1、bootloader制作

10.1.1、setting地址修改(重要)

 选择一个相近系列的芯片的uart或者usb的bootloader进行修改、没有近似系列的也没关系,直接打开一个相应的USB或者uart例程,我们直接进行修改。如针对52833,没有usb的bootloader,我们可以选择52820的bootloader来进行修改,因为52820是52833的子集。选择如下目录的例程打开:examples\dfu\secure_bootloader\pca10100e_usb_debug,在打开的例程中找到如下文件nrf_dfu_types.h

 在上面的截图中有两个地址,上面那个是标明了52820这款芯片对应程序校验参数存放部分,即setting(setting中的setting page)的存放起始地址和大小,下面的MBR也是setting,不过这个setting我们把它叫做“setting的MBR”(即backup page),他们的大小都是4K,也就是flash的一页,放在flash的最后两页,到这我也终于把这部分解释清楚了。

虽然我们解释清楚了setting的组成部分,但是在升级过程中我们怎么告诉bootloader,说你在接收完升级固件,去哪里找校验值去验证密钥对不对等等(反正就是官方博客中的那些要校验的东西),看过来,在main->nrf_bootloader_init->nrf_dfu_settings_init->nrf_dfu_settings_reinit->settings_crc_ok,在settings_crc_ok();函数中设定了settings的大小和地址。

 跳转到m_dfu_settings_buffer定义的地方,我们可以看到如下代码(作用是在flash中从NRF_MBR_PARAMS_PAGE_ADDRESS地址开始保留一个页面(4k)):

    uint8_t m_mbr_params_page[NRF_MBR_PARAMS_PAGE_SIZE]
        __attribute__((at(NRF_MBR_PARAMS_PAGE_ADDRESS)))
        __attribute__((used));

/*其意义了一个buffer,并且设定了其大小,然后指定其存储从flash的NRF_MBR_PARAMS_PAGE_ADDRESS地址开始,也就是0x0003E000处。


其中__attribute__( at(绝对地址) ) :表示绝对定位,有两个作用绝对定位到flash,或者绝对定位到RAM,这由于0x0003E000为flash的地址,所以绝对定位到了flash。


__attribute__((used)):告诉编译器在目标文件中保留一个静态函数,即使它没有被引用。*/

下面是我在 52820的手册上截取的flash地址截图,看是不最后一页的地址和我们的setting(即前面的setting程序部分)地址一样,所以你如果要修改bootloader请根据你选择的芯片来修改好setting的存放地址。

由于这次我使用的是52833,那么修改有两步:

1)修改启动文件和setting page的地址

把原本52820的地址修改为52833flash地址的最后一页开始地址,给前面留出更多的空间存储给app使用,修改如下图把原本52820的启动文件改为52833,这样会把整个工程中的都切换过来:

第一步:

选择第一个选项:

 打开如下窗口操作如图:

 然后打开下图所示界面,并做切换

然后打开:

 在此勾选选择启动文件,保存:

2)修改backup page(setting的MBR)的起始地址

把全局宏定义中的NRF52820_XXAA 修改为NRF52833_XXAA 

 

完成上面两步,可以看都如下结果,两个地址都修改完成了,一个起始地址为0x0007F000,一个为0x0007E000,说明修改完成,你要问我为什么是这两个地址,那是因为52833flash空间最大就是80000,最后两页地址就是这么来的。

 

10.1.2、bootloader存放地址修改(重要)

前面我们告诉bootloader校验值在哪里去寻找了,那我们接下来解决一下bootloader自己的问题,(不能只考虑别人的问题,自己的也要解决一下对吧),下面是我截取的我使用的bootloader的大小有0xF593这么大,但是我们在他和setting之间还是要留一点空间的,那我们看看起始地址到setting有多大,计算一下为0x12000,那么相对于52833的setting地址计算一下,就是0x7E000-0x12000=0x6C000,由此就决定吧bootloder的相对地址放在0x6c000吧,这样就给app流出了足够空间了,如果后期你开发的app很大,还可以往后面在调整一下bootloader的大小,但是注意不要让bootloader内容去覆盖到setting,如果这些地址大小没有注意个很容易出现合并失败的错误。

 

什么是合并出错,那下面我们就来犯一次错,我在修改了setting后,直接编译,不去修改我们bootloder的地址,然后合并一下:

 然后会发现居然报错了,说hex文件在0地址处冲突,不能合并,不能合并我们就做不了升级了。如果你前面不注意app大小和bootloader的起始地址,你还可能出现在其余地址合并冲突的情况,不能说怎么你报错的地址和我的不一样,有这冲突就说明该去调整地址了,各个文件大小和地址你没有合理安排好,导致整个flash都被糟蹋了,还不能让人家报个错。这个时候如果要进行下去,可以用programmer去查看一下各给文件的大小和你现在生成文件的大小,然后调整一下,这调整的主要是app和bootloader,协议栈是固定大小的(当然不同版本的协议栈栈大小不一样,你可以换个小的协议栈),然后app紧跟着协议栈后面。如果发现app很大,bootloader已经都挨着setting放置了还是放不下app,那没办法,要不你裁剪一下app,要不你不要DFU了,要不就换一个小的协议栈,当然你就换一个更大flash的芯片也是可以的。

来我们看一下为什么0地址报错,打开下面截图的地方,你会发现,怎么地址是0,这是由于你在从52820切换到52833时修改了原本例程设置的值,就是前面的0x0002C000,所以这要改一下,不改你就和协议栈冲突了,协议栈就是从0地址开始放的。

这一步有两点需要注意需要更改ROM和RAM的值,ROM处填写的是bootloader的存储初始地址,在程序中会引用到这个值:在main->nrf_bootloader_flash_protect();函数的第一个参数为BOOTLOADER_START_ADDR,其代码如下:

#define CODE_START ((uint32_t)&Load$$LR$$LR_IROM1$$Base)

#define BOOTLOADER_START_ADDR (CODE_START)

1)flash值设定

它设置为我们前面计算的值6c000(为什么是这个值,前面已经一步一步计算过了,没看到明白可以在研究一下),然后size也要改,怎么改,根据我们选择芯片的flash来,0x80000(52833)-0x6C000=0x14000,填写0x14000即可;

2)数据RAM地址修改(有两个RAM,data RAM和code RAM)

这两个地址如下图(52833数据手册上截取),起始地址要为0x20000008,大小用data RAM减去8即可。

最后我们的设置如下图,如果你要修改为其他芯片,那么请对应去更改。

 结论:更改启动文件后,修改ROM和data RAM的地址(IRAM1),去除IRAM2,不要使用,如果你勾选了,虽然编译没有问题,但是你合并下载后会发现程序无法启动运行的。

到这我们就搞定了bootloder,编译一下,然后找到52833的app工程编译一下获得相关hex文件。

10.2、升级验证

 下面我们来简单制作一下升级的APP,也就是在第9节基础上改一改名字,就叫SW_Nordic_UART,编译后移动APP,改名,完成。原始的APP依然是那个。

原始固件制作:

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

mergehex --merge bootloader_33.hex settings_33.hex --output bl_temp_33.hex

mergehex --merge bl_temp_33.hex app33.hex s140_nrf52_7.2.0_softdevice.hex --output whole_33.hex

烧写:

nrfjprog --eraseall -f NRF52 

nrfjprog --program whole_33.hex --verify -f NRF52 

nrfjprog --reset -f NRF52 

然后芯片运行起来,20s后进入DUF,在20内,我们看到广播名为soft_Nordic_UART。

新固件制作:

nrfutil pkg generate --application app_SN.hex --application-version 2 --hw-version 52 --sd-req 0x0100 --key-file priv.pem dfu_SN.zip

然后在进入DFU后,插上USB,PC端输入升级命令,让PC端去扫描对应的端口(注意端口正确,你自己的端口),看是否有设备连接生,如果有就会按照协议进行数据发送:

nrfutil dfu usb-serial -pkg dfu_SN.zip -p COM30

然后复位一下,你会看到你的蓝牙广播名变为了SW_Nordic_UART。

10.3、UART方式

这和前面的流程一样,主要时修改bootloader,一样的方式,不在赘述了。

所有的升级讲解完成。

十一、官方例程中的跳转处理

任何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) 将中断转发给应用程序

十二、uart升级MCU版主机数据传输协议

  在有些应用场景无法直接使用PC端的串口给到设备进行升级。可能你主设备连接了一个NB模块,NB模块接收数据后,直接通过串口给nrf设备升级,这就设计到了一个问题,传输协议是什么,在上面的升级中使用nordic官方提供的PC端工具nrfutil,里面已经集成了uart传输协议,可以什么都不用管的进行升级,但是为了使用MCUnrf设备的串口升级,uart升级协议是绕不开的问题。

官方提供了一个主机的例程(也是NRF设备),但是其中很好的展示了uart发送时的协议,我们需要做的是参考并移植到直接使用的MCU上即可:

下面给出下载的地址:

(1) Getting started with Nordic's Secure DFU bootloader, a step by step guide - Software Development Kit - nRF5 SDK guides - Nordic DevZone (nordicsemi.com)

 

 

 注意事项:

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

 

 

 

posted @ 2022-08-11 20:45  星辰_stars  阅读(6225)  评论(34编辑  收藏  举报