Nordic nRF51 の serial DFU が動かない問題

Found a problem in Nordic SDK 7.2.0 serial DFU (Device Firmware Update) and a workaround.

久々に Nordic の Bluetooth LE(BLE)評価ボード nRF51-DK を引っ張りだし、シリアルポートを使った DFU を評価してみました。DFU(Device Firmware Update)というのは、J-Link Debug Probes 等を使わずに Bluetooth LE や UART 経由で内部のファームウェアをアップデートする仕組です。

私のボードには nRF51422 QFACA10 というリビジョンの IC が載っていて、公式(?)には SDK 7.2.0 までしかサポートされていないようなので、古い SDK で評価しています。

無事に BLE では DFU できたのですが、なぜかシリアル版がうまく動作しません。(ちなみに、BLE DFU の評価には、できたら Android 版の nRF Toolbox を使うことをお勧めします。iPhone 版よりもエラーの診断表示が分かりやすいように思います。)

評価ボード上の UART ピンにオシロのプローブを立てて調べてみました。ちゃんと TXD にも RXD にも信号は流れているようです。フロー制御の問題かと思い RTS, CTS 信号もモニタしてみましたが、信号が変化している気配はないので、フロー制御の問題でもなさそうです。

Master Control Panel に含まれる nrfutil.exeという Windows 版のツールだと動きがよく分からないので、SDK 7.2.0 に含まれる hci_dfu_send_hex.py というツールでデバッグしてみました。nRF51 側で動作する DFU ブートローダにブレークポイントを張ったりして調べてみたところ、process_dfu_packet() で START_PAKET や INIT_PACKET のルートは通るのに DATA_PACKET のルートに入らないことが分かりました。試しに、hci_dfu_send_hex.py でバイトを 1バイトずつウェイトを入れながら送信したところ、うまく DFU できるようになりました。

しかし、もしデータを送るのが早すぎるのなら、どうしてフローコントロールが働いて RTS がネゲートされないのでしょうか。nRF51 の UART ライブラリにはデータを取りこぼすことがあるという噂ががあるので、もう少し調べてみたいと思います。

参考になる方も、もしかしたらあるかも知れないので、hci_dfu_send_hex.py のパッチを上げておきます。0.01秒のウェイトは長すぎると思いますが、とりあえず。

diff --git a/hci_dfu_send_hex.py b/hci_dfu_send_hex.py
index 4724d3d..2970604 100755
--- a/hci_dfu_send_hex.py
+++ b/hci_dfu_send_hex.py
@@ -339,7 +339,10 @@ class Controller(object):
             last_ack = None
             packet_sent = False
             while packet_sent == False:
-                uart.write(pkt.data)
+                # uart.write(pkt.data)
+                for i in range(len(pkt.data)):
+                    uart.write(pkt.data[i])
+                    time.sleep(0.01)
                 attempts += 1
                 ack = get_ack_nr(uart)
                 if last_ack == None:

これで無事に DFU できるようになりました。

$ python hci_dfu_send_hex.py --file ../nrf51822_xxac_s110.hex --baud 38400 --port /dev/tty.usbmodem14421 --flowcontrol --verbose
Sending file ../nrf51822_xxac_s110.hex to /dev/tty.usbmodem14421, flow control = True

SoftDevice update: False
    > SD start address: 0x00000000
    > SD end address: 0x00000000
    > SD size: 0x00000000
Bootloader update: False
    > BL start address: 0x00000000
    > BL end address: 0x00000000
    > BL size: 0x00000000
Application update: True
    > APP start address: 0x00016000
    > APP end address: 0x00019cd4
    > APP size: 0x00003cd4

Total number of HCI packets: 34

Progress: [##############################] 100%

Success!

今日はここまで。