
Постановка задачи
Наладить по интерфейсу SWD интерфейс командной строки CLI (Shell). Чтобы посылать в прошивку команды и получать ответ. Чтобы можно было работать примерно как с UART. Чтобы прошивка в коде могла асинхронно получить текстовую строку от PC и отправить обратно строку в PC. SWO пин не предлагать, так как он у меня на плате даже не выведен на разъём программатора.
Реализация
Есть технология отладки, которая называется J-Link RTT. Появилась более 11 лет назад. Попробуем соединить RTT с CLI и посмотреть, что получится.
Прежде всего Вам понадобятся исходники программного компонента SEGGER_RTT, который реализует поддержку RTT на стороне микроконтроллерной программы. Их можно найти и скопировать из github по ключевым словам названий функций: SEGGER_RTT_Write SEGGER_RTT_Read. Там будет всего 4 файла. Я скачал исходные файлы для SEGGER_RTT из проекта робота собаки.
Название файла |
Пояснение |
Количество строк |
SEGGER_RTT.h |
API |
253 |
SEGGER_RTT.с |
Реализация |
1751 |
SEGGER_RTT_printf.c |
Реализация функции printf |
516 |
SEGGER_RTT_Conf.h |
Конфиг |
333 |
Первым делом в прошивке надо вызвать функцию SeggerInit(), которая настраивает нулевой экземпляр RTT структуры.
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; }
Затем на стороне прошивки надо просто периодически вызывать функцию SEGGER_RTT_Read. Как только PC пришлет данные функция SEGGER_RTT_Read вернет принятый массив.
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; }
Любая CLI всегда что-то отправляет в ответ. Всё, что транслируется на PC можно складировать в очередь TxFiFo, а затем вызывать функцию SEGGER_RTT_Write.
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; } } bool segger_rtt1_writer_transmit(void* base) { bool res = false; WriterHandle_t* Node = (WriterHandle_t*)base; if(Node) { strcpy((char*)Node->data, ""); uint32_t out_len = 0; Node->in_transmit = 0; res = fifo_pull_array(&Node->fifo, Node->data, sizeof(Node->data), &out_len); if(false == res) { Node->fifo.err_cnt++; } else { Node->in_transmit = out_len; } if(0 < Node->in_transmit) { if(Node->enable) { SeggerRttHandle_t* SeggerRtt = SeggerRttGetNode(1); if(SeggerRtt) { unsigned tx_cnt = SEGGER_RTT_Write(SeggerRtt->BufferIndex, (void* ) Node->data, (unsigned) Node->in_transmit); if(tx_cnt) { Node->tx_cnt += tx_cnt; res = true; } } } Node->in_transmit = 0; } } return res; }
На стороне LapTop PC для работы вам понадобится утилита J-Link RTT Viewer. После установки пакета утилит Segger J-link утилита J-Link RTT Viewer ( JLinkRTTViewer.exe ) оказывается в папке
C:\Program Files\SEGGER\JLink_V834
При пуске утилиты надо настроить подключение. Выбрать интерфейс связи между PC и программатором, выбрать целевое устройство, выбрать битовую скорость передачи данных и нажимаем OK.

Подключаться можно как по JTAG, так и по SWD в зависимости от схемотехники вашей платы.

Набирать команды для PCB надо в окне Terminal 0, так как там есть распознавание кодов цветов.

Иногда внутренние RTT FIFO могут переполниться. Поэтому часть ответа не придет на PC. В этом случае надо пересобрать прошивку с другим конфигом RTT. Делается это в файле SEGGER_RTT_Conf.h

Утилита JLinkRTTViewer также открывает TCP сокет на порте 19021
C:\Users\USER>netstat -ano | grep 19021 Proto Local Address Foreign Address State PID TCP 127.0.0.1:19021 0.0.0.0:0 LISTENING 15032
Поэтому вы можете подключится к консоли старым добрым TeraTerm или Putty.

И тогда ваша отладка по SWD-RTT визуально ничем не отличается от отладки по UART.

Достоинства Shell по SWD
++RTT работает параллельно пошаговой отладке.
++Утилите J-Link RTT Viewer не нужен ELF файл. То есть вы можете просто прошить плату и передать ее схемотехнику. И схемотехник при помощи обыкновенного дешевого J-link сможет произвести с платой любые манипуляции по RTT shell.
++Есть отдельный TCP сокет порт для подключения сторонними утилитами к порту 19021. Можно отлаживаться через TeraTerm хоть с мобильного телефона в той же WiFi сети.
++Позволяет масштабироваться. Можно создавать несколько экземпляров RTT терминала.
++Команды доставляются практически мгновенно. Аналогично ответ от MCU приходит тоже мгновенно. Так как данные передаются на мегагерцовых частотах шины SWD.
++Можно copy-paste выделенный в консоли RTT Viewer лог. Причем выделение подкрашивается приятным синим цветом.

Недостатки Shell по J-link SWD
--Нужен отладчик J-link.
--Не все микроконтроллеры можно отлаживать при помощи RTT, а только те, что поддерживаются пакетом Jlink. Поэтому AVR, ESP32, некоторые RISC-V и Power-PC придется отлаживать по UART-CLI.
--Для ввода команды выделено отдельное окно.

Это неудобно, так как надо сперва кликнуть курсором в поле ввода каждый раз, когда надо набрать команду. Либо нажимать несколько раз TAB, прежде, чем выделение дойдет до строки отправки команды. Это раздражает при интенсивной работе с терминалом. Однако это легко решается, если подключаться к сокету со стороны через TeraTerm или PuTTY.
--При сбросе питания PCB SWD link обрывается. Надо заново активировать подключение (File->Connect ->OK (F2+Enter)) и настраивать конфигурацию подключения. Поэтому не ясно как получить ценнейший лог загрузки прошивки (power-on log).

--Терминал J-Link RTT Viewer не отображает управляющий символ возврата каретки [\r]. В результате каждая новая строка пишется на новой строке. При этом в Python и TeraTerm такой проблемы нет и там можно переписывать снова и снова одну и ту же строку. Делать бегущие строки и прочее.
--Документация на утилиты Segger не загружается на территории РФ.

Результат
Удалось настроить shell через SWD. Теперь для отладки системного ПО не нужен ни UART, ни ISO-TP поверх CAN. Достаточно только 3x-4х проводов интерфейса SWD. Это существенно расширяет список PCB, которые можно отлаживать через shell консоль.
Словарь
Акроним |
Расшифровка |
RTT |
Real Time Transfer |
PCB |
printed circuit board |
UART |
Universal asynchronous receiver/transmitter |
CAN |
Controller Area Network |
API |
application programming interface |
MCU |
Microcontroller Unit |
PC |
personal computer |
CLI |
Command-line interface |
FW |
Firmware |
SWO |
Serial Wire Out |
SWD |
Serial Wire Debug |
Ссылки
Название |
URL |
J-Link RTT – Real Time Transfer |
https://microsin.net/programming/arm/j-link-rtt-real-time-transfer.html?ysclid=mngade0qu961290142 |
robot_dog |
https://github.com/56696/robot_dog/tree/2e0d221710fea67c884d2fabdf8f69c5d749bff1 |
CLI через CAN по ISO-TP (или утилита CANshell) |
|
Почему Нам Нужен Shell? (или Добавьте в Прошивку Гласность) |
|
Дебаггинг в реальном времени через JTAG/SWJ-DP для микроконтроллеров на ядре ARM Cortex-M @Indemsys |
|
SEGGER RTT Console |
https://mynewt.apache.org/latest/tutorials/tooling/segger_rtt.html |
Полезные утилиты RTT Viewer и System Viewer @Evgeny_E |
|
Дебаггинг в реальном времени через JTAG/SWJ-DP для микроконтроллеров на ядре ARM Cortex-M |
|
Какой Может быть CLI в Микроконтроллере (или Курс Молодого Бойца) |
|
Как наш shell похорошел @Katbert |
Комментарии (6)

Mcublog
02.04.2026 19:48Кстати с RTT можно работать не только через JLink, например через дешевые отладчики для МК WCH32 -- WCH-LinkE. Еще минутка саморекламы, я об этом писал статью, может быть будет интересно https://habr.com/ru/articles/813669/
С тех пор как писал статью на WCH32 так и не появилось проекта, поэтому говорить на сколько удобнее или наоборот работать с RTT через WCH-LinkE не могу. Также не могу сказать на сколько это надежно.
Через JLink RTT работает как часы.

Sergey78
02.04.2026 19:48RTT это же просто буфер в памяти. Его можно любым отладчиком читать. Насколько помню, в openocd добавляли поддержку.
Я делал прототип устройства, которое подключалось по swd к целевому МК и вычитывало логи RTT. Была идея сделать логгер который будет писать длительные логи на sd для отлова редких событий.
Mcublog
Давно пользуюсь rtt очень удобная штука. Написал для себя консольную утилиту, аналог rtt viewer, по сути обмазка на питоне для dll от Segger. Может кому-нибудь тоже будет полезным https://github.com/Mcublog/rtt-console
aabzel Автор
Отлично!
А насколько сложно сделать rtt-console на Си, чтобы был только один *.exe файл?
Mcublog
Спасибо.
Да, кажется, что не сложно, на сколько помню segger поставляет динамическую библиотеку для управления JLink. Уже не помню название, соответственно из C можно к ней прилинковаться и дергать те же методы, что и в питоне.
Я использовал питон в первую очередь из за хорошей и простой библиотеки cli (история, автодополнения итд).
Ну из моего скрипта можно сделать бинарь, там по-моему даже для винды поверешелл скрипт лежит, способ в общем то стандартный с помощью pyinstaller. Или даже бинарь я в релизы клал, но потом в принципе на релизы поклал и там старая версия.
aabzel Автор
Удивляет то , что по rtt очень мало информации.