
Постановка задачи
Наладить по интерфейсу 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 |
