Bluetoothモジュールがフィールドで接続を切断したり、スリープ時にスペックより3 mA多く消費したり、DTMスイープの40チャネル中2番目のチャネルで認証に失敗したりする場合、1日の修正と2週間の迷走の違いは、開発段階からモジュールを正しく計装していたかどうかにかかっています。本記事では、回路図設計の初日から組み込むべき物理デバッグインターフェース、リアルタイムトレーシング手法、RF検証パス、ポストモーテムツールについて解説します。
## 1. デバッグインターフェース概要:JTAG vs SWD vs cJTAG
最新のBLE SoC(nRF52/53、CC2640、ESP32-C3/H2、DA1469x、BGMxx)は、フル5ピンJTAG(TCK、TMS、TDI、TDO、nTRST)またはARM専用の2ピンSWD(SWCLK、SWDIO)+ nRESETを提供しています。BLEモジュール設計におけるトレードオフを以下の表にまとめます:
| パラメータ | JTAG (5ピン) | SWD (2ピン+RESET) | cJTAG (2ピン) |
|———–|————-|———————|—————-|
| ピン数 | 5 (nTRSTなしで4) | 2-3 | 2 |
| 最大クロック | 10-50 MHz | 1-50 MHz | 最大50 MHz |
| トレースサポート | ETM (4ピントレースバス) | ITM (SWO、1ピン) | ITM (SWO) |
| フラッシュ書き込み速度 (256 KB) | 約8-12秒 | 約6-10秒 | 約6-10秒 |
| 配線長制限 | 約15 cm (バッファなし) | 約30 cm (バッファなし) | 約20 cm |
| SoCサポート | 汎用 (ARM + RISC-V) | ARM Cortex-Mのみ | ARM Cortex-M33/55 |
| 典型的な電流 (デバッグ動作時) | 2-5 mA | 1-3 mA | 1-3 mA |
BLEモジュールではPCB面積が平方ミリメートル単位で測られるため、SWDがデフォルトの選択です。nRF52/53シリーズでは、開発中にSWDをGPIO P0.00/P0.01と同じピンで使用できます。0.05インチピッチのマイクロヘッダー(Samtec FTSH-105等)に配線し、開発用に実装するか、量産用にはむき出しのPCBパッドとして残します。量産モジュールに実装済みヘッダーがあると不正アクセスを招くため、パッドのみのアプローチが推奨されます。
### SWD信号品質の考慮事項
SWDIOは双方向でプルアップ(通常10 kΩ内部、4.7 kΩ外部)付きのオープンドレインです。プルアップ値は多くのエンジニアが予想する以上に重要です:
| プルアップ | SWDIO立ち上がり時間 (tr) | 最大SWCLK | 備考 |
|———|———————|———–|——-|
| 100 kΩ | 220 ns | 1 MHz | 限界的—同期ロスが時々発生 |
| 47 kΩ | 104 ns | 4 MHz | 短い配線(<15 cm)でOK |
| 10 kΩ | 22 ns | 25 MHz | ほとんどのセットアップで信頼性あり |
| 4.7 kΩ | 10 ns | 50 MHz | J-Link高速動作で推奨 |
| 1 kΩ | 2.2 ns | >50 MHz | 過剰、Low時に3.3 mA消費 |
計算式は単純なRC遅延:tr ≈ 2.2 × R × C、Cは配線+プローブの合計容量(15 cmリボンケーブルで通常10-20 pF)。J-Linkで断続的な”SWD DP read failed”エラーが出る場合、最初に確認すべきは外部プルアップが未実装で内部10 kΩが長いケーブルと戦っているかどうかです。
## 2. UARTデバッグコンソール:実世界のデバッグに向けた設計
UARTデバッグコンソールは量産ファームウェアで最も価値のあるデバッグインターフェースですが、 routinely不適切に実装されます。よくある間違い:
1. **BLEスタック優先度に対してボーレートが高すぎる。** 1 Mbps UARTで16バイトFIFOの場合、割り込みは128 μsごとに発生します。BLEスタックが接続イベント準備中に割り込みを150 μs保持すると、文字が失われます。解決策:DMA TXで460800 bpsまたは921600 bpsを使用し、RXはリングバッファで処理。
2. **printfがメインループをブロック。** 115200 bpsで200バイト書くブロッキング`printf`は17.4 msかかります—7.5 msのBLE接続間隔より長いです。アイドル時のDMAフラッシュ付きロックフリーリングバッファを使用:
“`c
// ロックフリーUARTデバッグリングバッファ (単一プロデューサー、単一コンシューマー)
#define DBG_BUF_SIZE 2048
static volatile uint16_t dbg_head = 0, dbg_tail = 0;
static char dbg_buf[DBG_BUF_SIZE];
void dbg_printf(const char *fmt, …) {
va_list ap;
va_start(ap, fmt);
char tmp[256];
int len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
va_end(ap);
for (int i = 0; i < len; i++) {
uint16_t next = (dbg_head + 1) & (DBG_BUF_SIZE - 1);
if (next == dbg_tail) break; // バッファフル、文字破棄
dbg_buf[dbg_head] = tmp[i];
dbg_head = next;
}
// TXアイドル時にDMAフラッシュトリガー
if (!(UART0->STAT & UART_STAT_TXBUSY)) {
uart_dma_flush();
}
}
“`
3. **ログレベルフィルタリングなし。** 量産モジュールが接続イベント中に460800 bpsでVERBOSEレベルログを出力するとタイミングジッターが発生します。コンパイル時と実行時のレベルフィルタリングを実装:
| レベル | コンパイルフラグ | 実行時 | 典型的な用途 |
|——-|————-|———|————-|
| ERROR | `DBG_ERROR` | 常にオン | ハードフォールト、アサーション失敗 |
| WARN | `DBG_WARN` | 常にオン | 再試行、性能低下 |
| INFO | `DBG_INFO` | 設定可能 | 接続/切断、状態変化 |
| DEBUG | `DBG_DEBUG` | 量産でオフ | イベントごとのタイミング、パケットダンプ |
| VERBOSE | `DBG_VERBOSE` | 量産でオフ | スロットごとのスケジューリング、RSSI |
4. **バイナリクラッシュダンプ機能なし。** モジュールがハードフォールトを起こした時、UARTが唯一の命綱です。ウォッチドッグリセット前にスタックレジスタ(R0-R3、R12、LR、PC、xPSR)とスタックの最初の16ワードをダンプする1 KBのクラッシュダンプハンドラを事前確保:
“`c
void HardFault_Handler(void) {
uint32_t *stk;
__asm volatile(“tst lr, #4
”
“ite eq
”
“mrseq %0, msp
”
“mrsne %0, psp
” : “=r”(stk));
dbg_printf(”
*** HARD FAULT ***
“);
dbg_printf(“R0: 0x%08X R1: 0x%08X R2: 0x%08X R3: 0x%08X
“,
stk[0], stk[1], stk[2], stk[3]);
dbg_printf(“R12: 0x%08X LR: 0x%08X PC: 0x%08X PSR: 0x%08X
“,
stk[4], stk[5], stk[6], stk[7]);
dbg_printf(“BFAR: 0x%08X CFSR: 0x%08X HFSR: 0x%08X
“,
SCB->BFAR, SCB->CFSR, SCB->HFSR);
for (int i = 0; i < 16; i++) {
dbg_printf("SP+%02d: 0x%08X
", i*4, stk[i]);
}
while(1); // ウォッチドッグ待機
}
```
### BLEモジュールのUARTピン選択
P0.00/P0.01(SWDまたは32 kHz水晶用予約)、P0.02/P0.03(ADCやアナログ)、BLEスタックが無線タイミングに使用するピンは避けてください。主要SoCの安全なUARTピン:
| SoC | 安全なTX | 安全なRX | 回避 |
|-----|---------|---------|-------|
| nRF52832 | P0.06 | P0.08 | P0.00/01 (XL1/XL2), P0.09 (NFC) |
| nRF52840 | P1.08 | P1.10 | P0.00/01, P0.09/10 (NFC), P0.24-25 (USB) |
| ESP32-C3 | GPIO2 | GPIO3 | GPIO0 (boot), GPIO2 (strapping) |
| CC2640R2 | DIO2 | DIO3 | DIO0-1 (boot), DIO7-8 (JTAG) |
| DA1469x | P0_06 | P0_07 | P0_00 (XTAL32M), P0_01-03 (SWD) |
## 3. リアルタイムトレーシング:RTT vs ITM/SWO vs ETM
printfベースのUARTデバッグは低頻度イベントには適していますが、BLEスタックのタイミング分析にはサブマイクロ秒の分解能が必要です。3つのオプションがあり、それぞれ異なるトレードオフがあります:
### Segger RTT (Real-Time Transfer)
RTTはターゲットRAMのリングバッファをデバッガがSWD経由で高速ポーリングします。ログエントリごとのターゲットCPU時間ゼロ(デバッガがRAMを直接読み取り)で、タイミングクリティカルなコードに最適:
| 指標 | UART @ 460800 | RTT | ITM/SWO @ 2 MHz |
|--------|---------------|-----|-----------------|
| 100文字ログあたりのCPU時間 | 約2.2 ms | 約5 μs | 約50 μs |
| スループット | 46 KB/s | 約500 KB/s | 約200 KB/s |
| レイテンシ (ログ→デバッガ) | 2.2 ms | 約10 μs | 約25 μs |
| 必要ピン数 | 2 (TX/RX) | 0 (SWD使用) | 1 (SWO) + SWD |
| RAM内バッファ | 0 | 1-4 KB | 0 (ハードウェアFIFO) |
| デバッガサポート | 任意のターミナル | J-Linkのみ | J-Link, ST-Link, CMSIS-DAP |
RTTの主な利点は既存のSWDインターフェースで追加ピン不要で動作することです。主な制限はSegger J-Link(またはJ-Linkファームウェア互換デバッグプローブ)が必要なことです。開発には問題ありませんが、フィールドデバッグではJ-Linkを持っていない可能性があります。
RTTは双方向通信もサポート—デバッガはUARTピンを使用せずにターゲットにコマンドを注入できます:
```c
#include "SEGGER_RTT.h"
void rtt_command_loop(void) {
char cmd[64];
int len = SEGGER_RTT_Read(0, cmd, sizeof(cmd) - 1);
if (len > 0) {
cmd[len] = ‘