Пролог

В этом тексте я бы хотел провести экскурс по использованию CLI в микроконтроллере. Расскажу про API той CLI, которая сформировалась у меня много лет назад. Стимулом к написанию текста послужило то, что при первом знакомстве с таким понятием как UART-CLI порой бывает даже не понято, что это такое и про что вообще идет речь. Это абсолютно нормальное явления, так как я и сам первые 7 лет опыта во флагманских предприятиях российской микроэлектроники не знал, что оказывается можно отлаживать микроконтроллерные прошивки через UART-CLI. А словосочетания UART-CLI, UART-Shell и диагностика были для меня просто пустой звук.

Прежде всего для подключения к UART на стороне PC я бы порекомендовал Вам бесплатную утилиту TeraTerm так как в ней есть возможность задавать паузу между отправкой байтов в UART. Это не позволит вашей прошивке захлебнуться от чрезмерно плотного потока байт.

UART-CLI можно делать по-разному. Прежде всего CLI - это первичный лог загрузки прошивки. Своеобразная лента черного ящика. Как в самолётах. Инициализация микроконтроллера - это многостадийный, многофазовый, многоступенчатый процесс, в котором многое может пойти не по плану. Какая-нибудь подсистема может при инициализации прошивки вернуть false. Например из-за абсурдного значения в конфигурационной структуре того или иного программного компонента. Поэтому первое, что Вы должны увидеть в UART консоли - это зеленый лог загрузки программы.

После отработки инициализации системы пуска Вы увидите курсор -->. Курсор это приглашение ввести и исполнить свою самую первую CLI команду.

Иной раз можно также перед курсором --> отображать up_time системы. Это позволяет визуально проверять корректность установки тактирования просто нажав и удерживая Enter убеждаться, что секунды в самом деде переключаются раз в секунду.

Все команды консоли можно разделить на две группы: Getter-ы и Setter-ы. Setter-ы что-то меняют в прошивке. Getter-ы просто и безопасно просматривают состояния прошивки под разными срезами. По хорошему getter-ов должно быть столько же сколько и setter-ов.

Самая первая CLI команда - это help. В терминале она сокращена до одной буквы h. Можно даже просто нажать TAB и появится тот же список доступных команд. Их обычно десятки.

У команды h есть два ключа. Это нужно для отображения только тех команд, в имени которых есть указанная в аргументах подстрока. Так среди сотен и тысяч CLI команд консоли можно найти одну конкретную команду даже не зная её точного названия.

Через UART-CLI Вы всегда сможете узнать версию прошивки, версию компилятора и git sha256 коммита, из которого была собрана данная прошивка. Для этого есть команда vi (version information)

Порой отлаживать прошивку можно просто анализируя лог. Это printf-отладка. Чтобы выбирать лог для каждого программного компонента есть команда ll (log level). Можно просмотреть какой уровень логирования назначен каждому модулю.

Удобно, когда есть команда, показывающая частоту ядра процессора.

и команда показывающая включенные тактирования подсистем

При bring-up платы очень полезно получить отчет о состоянии GPIO пинов. Это показывает команда gl (GPIO list)

CLI обладает низкоуровневыми командами. Команда rm (read_mem) позволяет читать произвольные куски памяти. Просто указываешь физический адрес, количество байт, нажимаешь enter и получаешь содержимое памяти в виде массива. Аналогично можно и прописать RAM память командой wm (write_mem).

Можно более осознано вычитать только сырые значение конкретных регистров (например аппаратного таймера 5) и сравнить значения битов с даташитом. Попять как настроен таймер. Это особенно помогает в поиске причин багов.

Благодаря UART-CLI можно читать не только внутренние регистры, но и внешние регистры. Вот тут удалось вычитать по I2C значения внутренних 9-битных регистров ASIC аудиокодека NAU8814 на той стороне I2C шины. Команда nrm (nau read reg map). Красота!

Можно просмотреть степень заполнения flash памяти. Команда fd ( flash_diag)

Если Ваша прошивка испускает PWM сигналы, то в CLI можно просмотреть настройки PWM сигналов командой pwd.

Можно просмотреть состояние LED-ов lmd (led_mono_diag)

В микроконтроллерах очень много источников прерываний. Можно просмотреть настройки прерываний командой intd. Проверить включенные прерывания.

Состояние аппаратных таймеров можно посмотреть командой trd (timer_diag)

В CLI можно просматривать и задавать параметры NVRAM. Так Вы сможете пере конфигурировать прошивку без пере сборки всего проекта.

В CLI можно просмотреть процент заполнения стековой памяти по всем ядрам. Это команда cd (core_diag)

При работе с интерфейсом I2C CLI позволяет удобно просканировать всю шину. Вот так отображается карта I2C адресов, когда к шине подключен RTC DS3231 и еще какая-то eeprom. Это отработала команда i2cs (i2c scan )

Из CLI можно даже инициировать перезагрузку ядра (команда reboot).

Диагностика супер цикла показывает сколько итераций в секунду делает цикл while(1) в main.
Можно прочитать среднюю продолжительность одной итерации и многое другое. Это делает команда scd (super_cycle_diag)

на MCU
на MCU
на Win PC
на Win PC

В CLI можно открыть диспетчер задач и отдельной командой отключить одну из задач по её id. Прямо на лету без пересборки прошивки.

Из UART-CLI можно, например, пульнуть пакет в CAN. Это делается командой cs (can_send). В аргументах через пробел указываем номер CAN трансивера, ID пакета и данные в виде hex строки. Нажимаем Enter и пакет улетает в шину. Принятый же пакет отразится отдельной строчкой в UART логе (при включенном уровне логирования).

Можно отметить CLI команду repeat. Эта тестировочная команда повторяет конкретную команду, имя которой указано в аргументе N раз с периодом P миллисекунд. Команда repeat просто незаменима при отладке софта и железа. Её можно сравнить с 3ей рукой в нужный момент.

Через CLI можно инициировать пуск модульных тестов и увидеть отчет исполнения тестов. Можно запустить только те тесты, которые имеют в своем имени конкретную подстроку (например "i2c" или SPI ).

Калькуляторы

Когда у Вас в прошивке есть CLI, любое ваше устройство, помимо базового функционала, превращается в супер калькулятор покруче любого Casio. Только вместо клавиатуры и дисплея торчит два провода UART. Можно прямо в консоли TeraTerm / PuTTY попросить микроконтроллер расшифровать кусок Base64, RLE, AES256, вычислить SHA256, посчитать CRC16, решить задачку на поиск пересечения временных интервалов, определить точное количество дней между 2мя датами, вычислить расстояние между двумя GNSS координатами на сфере, вычислить коэффициенты линейной функции (ax+b=y) по двум точкам,

найти угол между векторами, решить линейное уравнение, вычислить формулу и прочее и прочее. По сути CLI это полноценный интерпретируемый язык программирования, который Вы создаете сами по мере надобности. Вот тут я в CLI обсчитываю делители напряжения.

Или вот, типичная ситуация. Есть у Вас два набора регистров. На одном наборе устройство работает. На другом наборе регистров устройство не работает. Надо понять в чем, собственно, дело. Какие именно биты отличаются? Надо узнать, что именно влияет на работу и куда копать. И тут Вам снова поможет CLI-ха. У меня есть команда (bd), которая автоматически вычисляет отличающиеся биты в двух 32-битных числах.

Или вот другой пример. Прошивка рассчитывает ёмкость керамического конденсатора по его трехбуквенной маркировке. Раньше это была головная боль, теперь - игра.

Конденсаторный калькулятор
Конденсаторный калькулятор

Буквально недавно опять выручила консоль. Вот я в консоли прошивки инициирую алгоритм вычисления коэффициентов PLL для желаемой частоты 220MHz. Это делает CLI команда clock_pll_calc 220000000. Можно менять частоту ядра налету.

clock_pll_calc 220000000
clock_pll_calc 220000000

Калькулятор покажет какие реальные CAN ID будут фактически приняты для данной настройки маски и ID. Это команда cfim (can_filter; CanFilterIdAndMask).

Вам уже нравится UART-CLI? Можно и дальше перечислять другие полезные команды CLI. Я лишь показал основные команды.

Немного фактов про UART-CLI

--UART-CLI нужна в debug сборке. В релизе shell может и не быть.
--Количество команд можно варьировать в зависимости от объёма Flash на микроконтроллере
--CLI можно гонять по любому интерфейсу или протоколу. Не обязательно именно UART.
--Через CLI можно обновлять прошивку. Просто используя команды модуля flash и boot.
--UART-CLI не зависает, если перетыкать питание PCB, как SWD/JTAG link.
--Через UART-CLI можно калибровать FW.
--Постоянно включенная UART-CLI работает как заземление между электронной платой и компьютером.

Реализация UART-CLI

Буквально пару слов про то, как обычно работает CLI. Ничего сложного тут нет в помине. Любая приличная прошивка состоит из множества программных компонентов. UART-CLI - это тоже не один файл, а набор папок. Получается иерархическая связь между программными компонентами.

Из чего обычно состоит UART-CLI:

1.Драйвер UART. Приём байтов в прерывании и помещение символа в очередь.

2.Программный компонент string_reader - это по сути однострочный текстовый редактор. Чисто для того, чтобы позволить пользователю исправлять опечатки кнопкой backspace.

3.Парсер CSV строк В случае с CLI символом разделителя является обыкновенный пробел.

4.Набор функций для распознавания базовых типов данных из строчек ( вещественные числа) . По сути конечный автомат, который реализовывает конкретное регулярное выражение. Распознать float из текстовой строки.

5. Сам компонент CLI. Набор команд - это константный массив структур в Flash памяти, где в каждом элементе находится имя команды, короткое имя команды и указатель на функцию с двумя аргументами: argc, argv. Массив структур формируется на стадии препроцессора (cpp) #ifdef-ми. То есть при пуске main CLI уже частично проинициализирован.

Исходные коды с реализацией CLI

Ядро CLI
https://github.com/aabzel/trunk/tree/main/source/connectivity/protocols/cli\_drv
Однострочный текстовый редактор
https://github.com/aabzel/trunk/tree/main/source/connectivity/protocols/string\_reader
Умное логирование
https://github.com/aabzel/trunk/tree/main/source/connectivity/log
Для CLI нужна реализация протокола CSV.
https://github.com/aabzel/trunk/tree/main/source/connectivity/protocols/csv
Текстовое описание как работает код разбора CSV строчек.
https://habr.com/ru/articles/765066/

Итог

Вы конечно можете реализовать CLI по-своему. Добавить историю команд. Добавить авторизацию. Добавить авто дополнение и прочие крутые механизмы. Я лишь показал то, какая CLI сформировалась у меня. При формировании API для UART CLI я делал упор на лаконичность команд, переносимость кода и простоту. CLI не боится неверно введенных команд. В случае некорректных аргументов CLI сама красным текстом пишет, что не так. Чтобы научиться пользоваться CLI надо пробовать с ней работать.

Ссылки

Название

URL

Только консоль. Почему текстовый интерфейс настолько эффективен @ru_vds

https://habr.com/ru/companies/ruvds/articles/747396/

Command line interpreter на микроконтроллере своими руками @Corviniol

https://habr.com/ru/post/247507/

консоль в микроконтроллере с micro readline @Helius

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

Реализация многофункционального терминального интерфейса для МК AVR @R3EQ

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

Пишем терминальный сервер для микроконтроллера на С @Neoprog

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

embedded-cli

https://github.com/funbiscuit/embedded-cli

MCU-CLI-w25Q80-emulEEPROM

https://github.com/NetDm/MCU-CLI-w25Q80-emulEEPROM

Почему Нам Нужен UART-Shell? 

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

https://www.youtube.com/watch?v=Fkmt7N6he5U

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

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

Комментарии (16)


  1. ECRV
    10.01.2026 00:14

    Вообще восторг. Честно говоря, не думал что в cli можно добавить калькулятор чисел и емкости, идея прикольная конечно. Большинство функций невероятно полезные, а часть непонятно зачем делать на контроллере. Идейно ничего не мешает, а с другой стороны что-то в голове против такого подхода для использования другими людьми, то есть не для разработки.

    Я стараюсь двигаться в сторону web-морды прямо на мк если это esp32, или в сторону кастомного оконного gui на pyqt или js если ресурсов меньше. Программистам легко обьяснить как работать в терминале, но разрабатываемые мной устройства редко попадают в руки программистов. Обычным людям нужно что-то попроще, чтобы был какой-то пароль на входе и кнопочки чтобы потыкать.

    У вас понятная правильная статья про разработку и она заслуживает плюсик. А расскажете что делаете для юзеров, а не для себя?


    1. aabzel Автор
      10.01.2026 00:14

      Так уж вышло, что в основном пишу прошивки для приборов категории B2B.

      Целевая аудитория тоже разработчики и интеграторы.


    1. rukhi7
      10.01.2026 00:14

      вот web-морда для мк будет действительно классным решением! На веб-морде можно сделать в том числе вкладку в которой будет консоль, а можно сколько нужно (сколько в мк определено категорий) вкладок по категориям регистров...


      1. aabzel Автор
        10.01.2026 00:14

        Этих вкладок будет сотни


        1. rukhi7
          10.01.2026 00:14

          а какая альтернатива, держать эти сотни в голове, сотни адресов/категорий?

          Но на самом деле при правильной организации интерфейса сотен там не будет, будут десятки, ДО сотни, примерно, может чуть больше если это все таки десктопный процессор. А для проца, действительно, уровня МК будет не больше пары-тройки десятков. А в реальной работе, для реальной задачи никогда не используется больше десятка, а значит нет софта для отображения большего количества категорий, а в браузере надо отображать только то что МК поддерживает, а не все подряд на все случаи жизни.


  1. Saiho
    10.01.2026 00:14

    Все эти возможности прекрасны, но сколько же надо места и тактов на МК для их реализации? Разве не было бы идейно правильно оставить в МК только интерфейс доступа к информации (фактически API дебаггера), а сам интерпретатор и команды CLI хранить и исполнять на компе, формируя и передавая поток в стандартный эмулятор терминала?

    Не слишком навороченно сформулировал? Вечно пытаюсь всё впихнуть в одну фразу...


    1. aabzel Автор
      10.01.2026 00:14

      Тогда пришлось бы фактически писать две программы: прошивку и клиентскую утилиту.

      А в случае с uart cli достаточно написания одной только прошивки.


      1. Saiho
        10.01.2026 00:14

        Хочу отдать должное - команды лаконичные и набор их удобный, насколько могу судить, хэлп - вообще песня. Работать с этим, наверняка, удобно и приятно. Статья ведь именно об интерфейсе, а не о реализации, правильно понимаю? Так вот, в этом смысле точно всё хорошо.


    1. aabzel Автор
      10.01.2026 00:14

      Все эти возможности прекрасны, но сколько же надо места и тактов на МК для их реализации?

      Дак сейчас и МК тоже мощные. У меня сейчас МК с мегабайтным флешем. До этого был МК 6 мегабайт .

      Cli хороша тем, что не можно насыпать по одной команде .

      Гибко настраивается под каждую сборку.


      1. Saiho
        10.01.2026 00:14

        Понятно. Ожидаемый ответ, конечно. Но это уже не эмбеддерство, как по мне. У вас под рукой то, что в конце 80-х называлось мэйнфрэймом, только подключен один терминал, а не несколько десятков. И теперь это микроконтроллер, видите ли. Только вот маленькие микроконтроллеры по-прежнему в ходу. И там хоть тоже уже не считают байты, но для полноценной системы по-прежнему места нет и каждый килобайт на счету. Вот там эмбеддерство, а ваше направление надо уже как-то по-другому называть - совсем другой стиль и подход.

        Не так давно встретил описание поилки для котика, который не хочет пить из миски, а хочет лизать струйку. История реальная. Нужно было сделать так, чтобы, когда котик пришёл пить, включался насосик и выдавал струйку воды. Думаете чел поставил микрик под полом водопоя? Неееет! Поставил Pi с камерой, натаскал нейронку... А что, говорит, у меня пишка и камера без дела валялись, а дощечку пилить надо, в интерьер вписывать.


        1. aabzel Автор
          10.01.2026 00:14

          Если бы я был котом ,то пил бы только из струек с ламинарным потоком.


    1. rukhi7
      10.01.2026 00:14

      Ну учитывая что у нас мк нынче многоядерные, и я подозреваю работают на ГИГА-герцах:

      В CLI можно просмотреть процент заполнения стековой памяти по всем ядрам. Это команда cd (core_diag)

      места и тактов на таких МК некуда девать! Каких то 25 лет назад мы на более слабых МК играли в Qвейк по сети под виндой :) ! А теперь мы не стесняемся программирование под них называть эмбеддед разработкой и называть их Микроконтроллерами :) , хотя на самом деле это среднего уровня десктопные процы, просто с подключением узко-специальных последовательных шин/ аппаратных портов/... специальной переферии.

      Что называется: зажрались эмбеддед разработчики :) .


  1. redfox0
    10.01.2026 00:14

    Надеюсь, это всё отключается флагами условной компиляции и не попадает в релизную прошивку?


    1. aabzel Автор
      10.01.2026 00:14

      Да. Uart-cli нужна только в debug версии прошивки.


  1. tormozedison
    10.01.2026 00:14

    Довольно много проектов по высовыванию в CLI интерпретаторов Бейсика, похожих на классические. Работают на железках начиная с Nano 328p. Для RP2040 сделали CLI, имитирующий COMMAND.COM с возможностью запуска BAT-файлов (синтаксис во многом совпадает) и обращения оттуда к Python-скриптам и файлам на Бейсике, интерпретатор для которых тоже предусмотрен.


  1. kulhaker478
    10.01.2026 00:14

    Или вот, типичная ситуация. Есть у Вас два набора регистров. На одном наборе устройство работает. На другом наборе регистров устройство не работает. Надо понять в чем, собственно, дело.

    У меня подобную роль исполняет gdb'шная консоль отладчика и дубовый printf, разницу не посчитает конечно, но регистры в удобоваримом виде получить можно:

    000000000000  0000.00.00 00:00:00.000 ETH Main >Boot message: 0.
    
    Breakpoint 6, main () at Core/Src/Commons/main.c:236
    236    InitRS485();
    source dump_usart.batch
    LPUART1 CR1=0x0000000d, UE=1, TE=1, RE=1, M=0, PCE=0
    LPUART1 CR2=0x00000000, STOP=0
    LPUART1 CR3=0x00000300, RTSE=1, CTSE=1
    LPUART1 BRR=0x00022b8e
    LPUART1 RDR=0x00000000
    LPUART1 TDR=0x00000000
    LPUART1 ISR=0x006000d0, TXE=1, TC=1, RXNE=0, ORE=0
    
    
    LPUART2 CR1=0x0000000d, UE=1, TE=1, RE=1, M=0, PCE=0
    LPUART2 CR2=0x00000000, STOP=0
    LPUART2 CR3=0x00000000, RTSE=0, CTSE=0
    LPUART2 BRR=0x000d0555
    LPUART2 RDR=0x00000000
    LPUART2 TDR=0x00000000
    LPUART2 ISR=0x006000d4, TXE=1, TC=1, RXNE=0, OR

    Но после статьи серьёзно задумался насчёт интеграции подобного cli в проекты