Nordic的DFU分析

前面文章分析了OTA的实现架构、协议和存储问题,提到了一个细节,如果使用双程序更新模式,将Flash空间分成上下两部分,如下图所示,那么最后生成的BIN文件为了放在不同的位置,需要生成两份地址不同文件,然后根据芯片的实际使用情况,选择合适的文件进行烧写。

BLE OTA memory layout

偶然看到一个信息,Nordic做双程升级时候,只需要一个二进制文件,不需要提供两个BIN文件进行选择。Nordic是怎么办到的呢?

内存布局

Nordic芯片的Flash布局图如下:

Nordic_DFU_Memory_Layout

Flash地址从下向上增长,最底层的MBR(Master Boot Record)区域存放中断向量表的位置。芯片中的Bootloader、SoftDevice和Application都具有自己的向量表,MBR的寄存器UICR.NRFFW[1]指示系统该选择哪一个中断向量表。(参考

SoftDevice区域存放协议栈。

Application区域存放用户固件程序。

APP Data区域存放用户希望永久保存的数据,比如设备地址等。这块区域在升级过程中也可以保持不被擦写。

DFU Bootloader区域存放Bootloader程序,用以执行升级过程。

更新方案

Nordic提供了三种更新方案,分别是:(参考

  1. 双程DFU
  2. 单程DFU
  3. 更新BootLoader和SoftDevice

1. 双程DFU

双程DFU方案的操作流程如下:

Dual-Bank DFU memory layout

从左侧图中可以看到Application区域空间分成了两部分:Blank 0和Blank 1。

假如当前的程序放在Blank 0区域,那么新的程序将被复制到Blank 1区域,如中间的图。

大多数芯片厂的DFU过程到这里就结束了,只要通过Bootloader引导MCU进入到新的程序地址即可。这样必然会产生本文开头提到的问题,在升级程序的时候,需要确定旧的程序放置位置。由于生成的BIN文件包含了起始地址信息,这意味着要新的程序要准备两份功能相同、地址不同的BIN文件,按照实际情况选择合适的文件烧写空白区域。

Nordic使用了一个小技巧,即将新的程序(Blank 1)复制到旧的程序空间(Blank 0),这样就是的程序基地址永远在Blank 0,从而解决了上述困难。

由于Flash赋值是一个耗时操作,DFU做起来一定会很慢。仔细观察,会发现双程DFU至少需要执行两次擦除和两次烧写,分别是:

  • 烧写Blank 1
  • 擦除Blank 0
  • 烧写Blank 0
  • 擦除Blank 1

疑问:如果在执行右侧图的Flash赋值过程中途,发生了断电,该怎么办?

我猜测应该先将Blank 1的内容复制到Blank 0,当复制完毕校验通过,再擦除Blank 1,结束DFU过程。这样即使在复制中途断电,Blank 1的内容仍然有效,重启后可以重新进行复制操作。读源码应该可以确认这一点。

2. 单程DFU

单程DFU的操作流程如下:

Single-Blank DFU memory layout

单程DFU就是现将Application区域清空,再将新的程序写入到Application空间的基地址。

这种做法好处是充分利用Application的空间,而且只需要擦除一次,烧写一次,操作时间相对于双程DFU一定会有大幅度降低。

单程DFU的缺点是不能在线更新,因为用户程序需要被擦除,所以一定需要断开连接和重启。

3. BootLoader和SoftDevice

升级BootLoader和SoftDevice的操作流程如下:

BootLoader and SoftDevice DFU memory layout

这个图乍一看比较复杂。

(1)擦除旧用户程序

(2)将新的Bootloader和SoftDevice写入Application区域

(3)将新的Bootloader和SoftDevice复制到Bootloader和SoftDevice区域

So easy? ——不是的!

并不是每个厂商的芯片都能实现这个功能,因为第(2)步到第(3)步之间,MCU要擦除Bootloader区域,有些BLE厂商的MCU,Bootloader程序是唯一的启动器,Bootloader程序自身是不可以被擦除的,否则MCU无法正常启动。Nordic的芯片存在MBR,利用MBR引导MCU跳转到Bootloader或其他地方。

程序在第(2)步结束时设置MBR的寄存器,让MCU下次启动时候不直接跳转到Bootloader区域,而是跳转到Application区域,执行一个复制操作,将新Bootloader程序和SoftDevice程序复制到各自区域,然后再设置MBR寄存器,让MCU下次重启时候直接跳转到Bootloader区域。

虽然仅仅增加了几行Flash来存放MBR信息,但是对MCU的更新提供了极大的灵活性。

(完)