Lorsqu’un module Bluetooth perd des connexions sur le terrain, consomme 3 mA au-dessus des specs en veille, ou echoue la certification sur le 2eme canal du balayage DTM a 40 canaux, la difference entre une reparation d’un jour et une impasse de deux semaines depend de si vous avez correctement instrumente le module pendant le developpement. Cet article couvre les interfaces de debogage physiques, les methodes de tracage en temps reel, les voies de verification RF et les outils post-mortem qui devraient etre concus des le premier jour du schema — pas ajoutes apres la premiere panne sur le terrain.
## 1. Panorama des Interfaces de Debogage : JTAG vs SWD vs cJTAG
La plupart des SoC BLE modernes (nRF52/53, CC2640, ESP32-C3/H2, DA1469x, BGMxx) exposent soit un JTAG complet a 5 broches (TCK, TMS, TDI, TDO, nTRST) ou le SWD a 2 broches specifique ARM (SWCLK, SWDIO) plus nRESET. Le tableau ci-dessous resume les compromis pour la conception de modules BLE :
| Parametre | JTAG (5 broches) | SWD (2 broches + RESET) | cJTAG (2 broches) |
|———–|————-|———————|—————-|
| Nombre de broches | 5 (ou 4 sans nTRST) | 2-3 | 2 |
| Horloge max | 10-50 MHz | 1-50 MHz | jusqu’a 50 MHz |
| Support trace | ETM (bus trace 4 broches) | ITM (SWO, 1 broche) | ITM (SWO) |
| Vitesse download flash (256 KB) | ~8-12 s | ~6-10 s | ~6-10 s |
| Limite longueur fil | ~15 cm (sans tampon) | ~30 cm (sans tampon) | ~20 cm |
| Support SoC | Universel (ARM + RISC-V) | ARM Cortex-M seulement | ARM Cortex-M33/55 |
| Courant typique (debug actif) | 2-5 mA | 1-3 mA | 1-3 mA |
Pour les modules BLE ou la surface PCB se mesure en millimetres carres, SWD est le choix par defaut. La serie nRF52/53 permet meme SWD sur les memes broches que GPIO P0.00/P0.01 pendant le developpement. On les route vers un micro-connecteur au pas de 0,05 pouce (Samtec FTSH-105 ou equivalent) et on le peuple pour le developpement ou on le laisse comme pastilles PCB nues pour la production. L’approche pastilles-seules est preferable car un connecteur peuple sur un module de production invite un acces non autorise.
### Considerations d’Integrite de Signal SWD
SWDIO est bidirectionnel et open-drain avec pull-up (typiquement 10 kΩ interne, 4,7 kΩ externe). La valeur du pull-up importe plus que la plupart des ingenieurs ne le pensent :
| Pull-up | Temps de montee SWDIO (tr) | SWCLK max | Notes |
|———|———————|———–|——-|
| 100 kΩ | 220 ns | 1 MHz | Marginal — perte de synchro occasionnelle |
| 47 kΩ | 104 ns | 4 MHz | OK pour fils courts (<15 cm) |
| 10 kΩ | 22 ns | 25 MHz | Fiable pour la plupart des configurations |
| 4,7 kΩ | 10 ns | 50 MHz | Prefere pour J-Link a haute vitesse |
| 1 kΩ | 2,2 ns | >50 MHz | Excessif, gaspille 3,3 mA a l’etat bas |
La formule est simplement un retard RC : tr ≈ 2.2 × R × C, ou C est la capacite totale trace + sonde (typiquement 10-20 pF sur un module avec un cable ruban de 15 cm). Si vous voyez des erreurs intermitentes « SWD DP read failed » sur votre J-Link, la premiere chose a verifier est si le pull-up externe est absent et le 10 kΩ interne lutte contre un long cable.
## 2. Console de Debogage UART : Conception pour le Debogage Reel
Une console de debogage UART est l’interface de debogage la plus precieuse en firmware de production, mais elle est systematiquement mal implantee. Les erreurs courantes :
1. **Debit baud trop eleve pour la priorite de la pile BLE.** A 1 Mbps UART avec FIFO de 16 octets, l’interruption se declenche toutes les 128 μs. Si la pile BLE maintient les interruptions pendant 150 μs lors de la preparation d’evenement de connexion, des caracteres sont perdus. Solution : utiliser 460800 bps ou 921600 bps avec DMA TX et tampon circulaire pour RX.
2. **printf bloquant la boucle principale.** Un `printf` bloquant qui ecrit 200 octets a 115200 bps prend 17,4 ms — plus long qu’un intervalle de connexion BLE de 7,5 ms. Utilisez un tampon circulaire sans verrou avec vidange DMA en idle :
« `c
// Tampon circulaire UART sans verrou (producteur unique, consommateur unique)
#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; // tampon plein, jeter caractere
dbg_buf[dbg_head] = tmp[i];
dbg_head = next;
}
// Declencher vidange DMA si TX idle
if (!(UART0->STAT & UART_STAT_TXBUSY)) {
uart_dma_flush();
}
}
« `
3. **Pas de filtrage de niveau de log.** Un module de production emettant des logs de niveau VERBOSE a 460800 bps pendant un evenement de connexion causera de la gigue de temporisation. Implementez un filtrage a la compilation et a l’execution :
| Niveau | Flag compilation | Runtime | Utilisation typique |
|——-|————-|———|————-|
| ERROR | `DBG_ERROR` | Toujours actif | Hard faults, echecs d’assertion |
| WARN | `DBG_WARN` | Toujours actif | Retentatives, performance degradee |
| INFO | `DBG_INFO` | Configurable | Connexion/deconnexion, changements d’etat |
| DEBUG | `DBG_DEBUG` | Off en production | Timing par evenement, dumps de paquets |
| VERBOSE | `DBG_VERBOSE` | Off en production | Planification par slot, RSSI par evenement |
4. **Pas de capacite de dump de crash binaire.** Quand le module a un hard fault, UART est votre seule bouee de sauvetage. Pre-allouez un gestionnaire de dump de crash de 1 KB qui dumpe les registres empiles (R0-R3, R12, LR, PC, xPSR) et les 16 premiers mots de la pile avant le reset du watchdog :
« `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); // Attendre watchdog
}
```
### Selection de Broches UART sur Modules BLE
Evitez P0.00/P0.01 (reservees pour SWD ou cristal 32 kHz), P0.02/P0.03 (souvent ADC ou analogique), et toute broche utilisee par la pile BLE pour le timing radio. Broches UART sures sur SoC courants :
| SoC | TX sur | RX sur | Eviter |
|-----|---------|---------|-------|
| 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. Tracage en Temps Reel : RTT vs ITM/SWO vs ETM
Le debogage UART base sur printf convient pour les evenements basse frequence, mais l'analyse de timing de pile BLE necessite une resolution sub-microseconde. Trois options existent, chacune avec des compromis differents :
### Segger RTT (Real-Time Transfer)
RTT utilise un tampon circulaire en RAM cible que le debogueur interroge via SWD a haute vitesse. Il requiert zero temps CPU cible par entree de log (le debogueur lit la RAM directement), ce qui le rend ideal pour le code sensible au timing :
| Metrique | UART @ 460800 | RTT | ITM/SWO @ 2 MHz |
|--------|---------------|-----|-----------------|
| Temps CPU par log 100 caracteres | ~2,2 ms | ~5 us | ~50 us |
| Debit | 46 KB/s | ~500 KB/s | ~200 KB/s |
| Latence (log → debogueur) | 2,2 ms | ~10 us | ~25 us |
| Broches requises | 2 (TX/RX) | 0 (utilise SWD) | 1 (SWO) + SWD |
| Tampon en RAM | 0 | 1-4 KB | 0 (FIFO materiel) |
| Support debogueur | Quelconque terminal | J-Link seulement | J-Link, ST-Link, CMSIS-DAP |
L'avantage principal de RTT est qu'il fonctionne via l'interface SWD existante sans broches supplementaires. Sa principale limitation est qu'il necessite un Segger J-Link. Pour le developpement, c'est acceptable ; pour le debogage sur le terrain, vous n'avez probablement pas de J-Link.
RTT supporte egalement la communication bidirectionnelle — le debogueur peut injecter des commandes dans la cible sans utiliser de broche 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] = ‘