Постановка задачи

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

https://habr.com/ru/articles/1013008/

Почему Нам Нужен Shell? (или Добавьте в Прошивку Гласность)

https://habr.com/ru/articles/694408/

Дебаггинг в реальном времени через JTAG/SWJ-DP для микроконтроллеров на ядре ARM Cortex-M @Indemsys

https://habr.com/ru/articles/259205/

SEGGER RTT Console

https://mynewt.apache.org/latest/tutorials/tooling/segger_rtt.html

Полезные утилиты RTT Viewer и System Viewer @Evgeny_E

https://habr.com/ru/articles/1011650/

Дебаггинг в реальном времени через JTAG/SWJ-DP для микроконтроллеров на ядре ARM Cortex-M

https://habr.com/ru/articles/259205/

rtt-console @Mcublog

https://github.com/Mcublog/rtt-console

Какой Может быть CLI в Микроконтроллере (или Курс Молодого Бойца)

https://habr.com/ru/articles/980280/

Как наш shell похорошел @Katbert

https://habr.com/ru/companies/whoosh/articles/978192/

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы работали с J-Link RTT?
66.67%да6
33.33%нет3
Проголосовали 9 пользователей. Воздержавшихся нет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы создавали shell через J-link RTT?
36.36%да4
63.64%нет7
Проголосовали 11 пользователей. Воздержавшихся нет.