JLink経由で、nRF52をNRF_LOGでデバッグ

nRF52シリーズで組込みの開発を行っていると、デバッグを行いたくなることってありますよね。
ねっねっ。

簡単にデバッグを行う方法としてプリントデバッグがあり、ArduinoIDEのシリアルモニターを用いると手軽に開発できます。

nRF52系のArduino Core, Libraryは[Adafruitさんが公開してくださってい](https://github.com/adafruit/Adafruit_nRF52_Arduino)ます。

センサーをつないで測定値を表示してみるといったものなら、このデバッグ方法でも十分でしょう。

しかし、**Bootloader起動時の処理内容が知りたい**、**電源投入後にひたすらリセットがかかる原因を究明したい**、**ArduinoのCoreにはない機能を実装してデバッグしたい[^1]**といった要望には応えられないでしょう。


なんかいい方法ないかな。。。

そんな時に大活躍するのが、NRF_LOG()です。

今回は、nRF5_SDK15.3.0を前提に、NRF_LOG()の使い方を紹介します。


# nRF5_SDK_15.3.0のダウンロード

[こちら](https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK/Download)から、nRF5_SDK_15.3.0やs140_nrf52_6.1.1などを選択してダウンロードします。


# サンプルプログラムの準備

適当なサンプルプログラムを使用します。

[YOUR_DOWNLOAD_PATH]/nRF5_SDK_15.3.0_59ac345/examples/peripheral/pwm_driver/main.c

を用いて、Lチカしてみます。

pca10040とpca10056のどちらを選択するかは、お手元の環境に合わせてください。

他のボードを使用する場合は、ピン配置を適切に変更すれば、とりあえず動作はすると思います。

ピン配置を変更するには、

[YOUR_DOWNLOAD_PATH]/nRF5_SDK_15.3.0_59ac345/components/boards/pca10056.h

あたりに手を加えれば、ひとまずなんとかなります。


# サンプルプログラムの書き込み

[JLink本体](https://www.segger.com/products/debug-probes/j-link/)を用意します。

/examples/peripheral/pwm_driver/pca10056/blank/armgcc

に移動して、

```
$ make
$ make erase
$ make flash
```
と実行していけば、ビルドされたソースコードがnRF52840に書き込まれます。

書き込みに成功すれば、User Button(DFU Switch)を押すたびに、ビルトインのLEDの光り方が変わるはずです。


# NRF_LOG() の有効化

/examples/peripheral/pwm_driver/pca10056/blank/config/sdk_config.h

の設定ファイルを編集していきます。

```
// <e> NRF_LOG_ENABLED - nrf_log - Logger
//==========================================================
#ifndef NRF_LOG_ENABLED
#define NRF_LOG_ENABLED 1
#endif
```

という#defineがあるはずなので（なければ適当なsdk_config.hからnRF_Logに関係している部分をコピーしてください）、 **#define NRF_LOG_ENABLED 1** と有効にされていることを確認してください。


# NRF_LOG() の出力先の設定

JLink経由での出力とUART出力の２種類から選べます。

## JLink経由でのデバッグ

JLink経由でデバッグする場合は、

```JLink
//==========================================================
// <e> NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend
//==========================================================
#ifndef NRF_LOG_BACKEND_RTT_ENABLED
#define NRF_LOG_BACKEND_RTT_ENABLED 1
#endif
```

という箇所を探し出し、 **#define NRF_LOG_BACKEND_RTT_ENABLED 1** として有効にしてください。


## SEGGER_RTT()を用いたデバッグ

JLink経由でのデバッグには、JLinkが必須となります。

となると、SEGGER_RTT()を用いた[リアルタイムでのデバッグ](https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/)も可能となります。

SEGGER_RTT()に関する定義と実装は

/nRF5_SDK_15.3.0_59ac345/external/segger_rtt/SEGGER_RTT.h

などを見れば、わかります。

SEGGER_RTT()を使うためには、2ステップ必要です。

### Makefileの編集

/examples/peripheral/pwm_driver/pca10056/blank/armgcc/Makefile

を編集します。


```
# Source files common to all targets
SRC_FILES += \
~~~中略~~~
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT.c \
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c \
  $(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_printf.c \

INC_FOLDERS += \
~~~中略~~~
  $(SDK_ROOT)/external/segger_rtt \
```

というように、SEGGER_RTT関連のソースファイルもコンパイル、リンクするように追記します。

### SEGGER_RTT.hのインクルード

```
SEGGER_RTT_WriteString(0, "Hello World!");
SEGGER_RTT_printf(0, "LED_NEOPIXEL pin: %lu\n", LED_NEOPIXEL);
```

のような関数を呼び出しているファイル(main.cなど)に

```
#include "SEGGER_RTT.h"
```

を追加します。


## UART経由でのデバッグ

UART経由でデバッグする場合は、

```UART
// <e> NRF_LOG_BACKEND_UART_ENABLED - nrf_log_backend_uart - Log UART backend
//==========================================================
#ifndef NRF_LOG_BACKEND_UART_ENABLED
#define NRF_LOG_BACKEND_UART_ENABLED 0
#endif
```

という箇所を探し出し、 **#define NRF_LOG_BACKEND_UART_ENABLED 1** と有効にしてください。


# JLink経由でのデバッグ (SEGGER_RTTを使ってみる)

JLink経由でデバッグを行うためには、[JLink本体](https://www.segger.com/products/debug-probes/j-link/)が欠かせません。

必要なソフトウェア一式を、[こちら](https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack)で、**J-Link Software and Documentation Pack**を選択してダウンロードします。

ダウンロードしたソフトウェアの中に、**JLinkRTTViewer** というものがあると思います。

nRF52840を使用しているならば、このままOKを押下します。
<img width="501" alt="J-Link_RTT_Viewer_nRF52840.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317198/bf255d0c-2f10-26c8-7d73-82b218f18506.png">

ファームウェアの書き込みに成功していれば、以下のような画面になるはずです。

<img width="752" alt="J-Link_RTT_Viewer_NRF_LOG_RTT.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317198/5d604a56-60be-d0c7-e32b-a761e0b0e7c9.png">

```
NRF_LOG_INFO("Hello World!);
SEGGER_RTT_WriteString(0, "Hello World!);
```

などで、出力した文字列が表示されているかと思います。

以上が、SEGGER_RTTを使用した、JLink経由でのデバッグの紹介です。


# UART経由でのデバッグ

**#define NRF_LOG_BACKEND_UART_TX_PIN 6** で指定されたピンに出力されているので、いい感じにUARTの出力を受け取ればデバッグできるでしょう。


# まとめ

ログを出力するために編集するファイルが多いので、慣れるまでは設定漏れが発生するかもしれません。

JLinkを用意すれば、**NRF_LOG_INFO()**だけでなく、**SEGGER_RTT_WriteString()**も使用できるので、リアルタイムでのデバッグも可能となるでしょう。


[^1]: AdafruitさんのArduino CoreはnRF5_SDK11系をもとに作成されています。最新のSDKはnRF5_SDK16系であり、nRF5_SDK11から大きく仕様が変わっているので、最新の機能を使うためにはArduinoのCore, Libraryに[各バージョンの機能を移植・実装する必要があります](https://devzone.nordicsemi.com/f/nordic-q-a/36013/migration-guide-from-sdk-11-to-sdk-15)。