Setting Up a CLI Shell via J-Link RTT for ARM Cortex-M
To implement a CLI over SWD without UART, use the SEGGER_RTT library. Download the source files: SEGGER_RTT.h (API, 253 lines), SEGGER_RTT.c (implementation, 1751 lines), SEGGER_RTT_printf.c (printf, 516 lines), SEGGER_RTT_Conf.h (configuration, 333 lines).
Start by initializing the zero RTT channel:
void SeggerInit() {
SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
SEGGER_RTT_SetTerminal(0);
}
bool segger_rtt_init_custom(void) {
bool res = true;
SeggerInit();
return res;
}
Call SeggerInit() at firmware startup to configure non-blocking transmit and receive buffers.
Processing Incoming Commands
Periodically poll the channel via SEGGER_RTT_Read in the main loop or a timer:
bool segger_rtt_proc_one(uint8_t num) {
bool res = false;
LOG_PARN(SEGGER_RTT, "RTT%u,Proc", num);
SeggerRttHandle_t* Node = SeggerRttGetNode(num);
if(Node) {
if(Node->RxBuffer) {
if(Node->buffer_size) {
unsigned rx_byte = SEGGER_RTT_Read(Node->BufferIndex,
Node->RxBuffer,
Node->buffer_size);
if(rx_byte) {
res = segger_rtt_writer(num);
LOG_DEBUG(SEGGER_RTT,"RxData:[%s]=[%s],rxByteCnt:%u Bytes",
ArrayToStr(Node->RxBuffer,rx_byte),
ArrayToAsciiStr(Node->RxBuffer,rx_byte),
rx_byte);
res = cli_process_data(Node->cli_num,
Node->RxBuffer,
rx_byte);
memset(Node->RxBuffer, 0, rx_byte);
}
}
}
}
return res;
}
The function returns the number of bytes received. Pass the data to the CLI handler: cli_process_data().
Sending Responses from Firmware
To transmit strings, use SEGGER_RTT_Write. Implement functions for putc/puts and a FIFO buffer:
void segger_rtt1_puts(void* stream_ptr, const char* str, int32_t len) {
SeggerRttHandle_t* Node = SeggerRttGetNode(1);
if(Node) {
if(str) {
if(len) {
unsigned tx_cnt = SEGGER_RTT_Write(Node->BufferIndex,
(void* ) str,
(unsigned) len);
(void)tx_cnt;
}
}
}
}
void segger_rtt1_putc(void* stream_ptr, char ch) {
SeggerRttHandle_t* Node = SeggerRttGetNode(1);
if(Node) {
unsigned tx_cnt = SEGGER_RTT_Write(Node->BufferIndex,
(void* ) &ch,
(unsigned) 1);
(void)tx_cnt;
}
}
Extract data from the Tx FIFO and write it to the RTT channel. This ensures asynchronous transmission without data loss.
- FIFO Advantages: Buffering prevents overflow during peak loads.
- Monitoring: Log
tx_cntto debug any missed transmissions. - Scaling: Support for multiple channels (num > 0).
Configuring J-Link RTT Viewer on PC
Install the SEGGER J-Link package. Run JLinkRTTViewer.exe from C:\Program Files\SEGGER\JLink_V834.
Configure the connection:
- Select SWD or JTAG.
- Specify the target device (ARM Cortex-M).
- Set the speed (up to megahertz).
- Confirm the connection.
Enter commands in Terminal 0 — supports color highlighting. If FIFO overflows, edit SEGGER_RTT_Conf.h to increase buffer sizes.
Alternative Access via TCP
RTT Viewer opens a socket on 127.0.0.1:19021 (check with netstat -ano | grep 19021).
Connect using PuTTY or Tera Term:
- Host: 19021
- Type: TCP
This emulates a UART terminal. Works remotely over a WiFi network.
Advantages and Limitations of RTT CLI
Advantages:
- Parallel operation with step-by-step debugging.
- No ELF file required: flash and pass to J-Link.
- Instant transmission at SWD speeds.
- Copy-paste logs with highlighting.
- Multiple terminals.
Disadvantages:
- Dependency on J-Link (not for AVR/ESP32/RISC-V).
- Disconnection on MCU reset — requires manual reconnection.
- Separate input window (solved via TCP).
- No support for \r in Viewer (available in Tera Term).
| Aspect | RTT via SWD | UART |
|--------|-------------|------|
| Speed | MHz SWD | 115200+ |
| Pins | 3-4 | 2+ |
| Debugging | Parallel | No |
| Reset | Disconnection | Stable |
Key Points
- Initialize RTT before main() for early logging.
- Use NO_BLOCK_SKIP to avoid hangs.
- Monitor FIFO overflow via tx/rx_cnt logs.
- TCP 19021 solves UI Viewer issues.
- Suitable for Cortex-M with J-Link support.
— Editorial Team
No comments yet.