Это краткое описание примера UART_CLI_Test, в котором тестируется драйвер UART_CLI. В примере переключаются светодиоды по командам с компьютера, которые передаются программой-терминалом pyComPort через UART в микроконтроллер. UART работает через DMA на прием и передачу для снижения нагрузки ядра.
Терминал реализован на Python с использованием компонентов QT, предоставляемых проектом PySide2. Выбор обусловлен, Википедия:
PySide — привязка языка Python к инструментарию Qt, совместимая на уровне API с PyQt. В отличие от PyQt, PySide доступна для свободного использования как в открытых, так и закрытых, в частности, коммерческих проектах, поскольку лицензирована по LGPL.
Кроме этого Python позволяет налету менять функционал программы, без необходимости пересобирать exe, подключать библиотеки и прочее и прочее. Это позволяет адаптировать терминал к различным потребностям в разных проектах работающих через UART. Да и в целом, Питон - это очень комфортная и простая экосистема для разработки. Использование компонентов Qt дает надежду, что тот же самый терминал заработает и на Linux.
Это вариант под Windows, под Linux должно быть аналогично.
Полезные ссылки - примеры:
Документация:
Код программы на GitHub: pyComPort
Иногда возникает необходимость поуправлять чем-нибудь с РС через COM-порт. Чтобы не писать каждый раз реализацию такой работы по UART, был реализован унифицированный драйвер, который в составе библиотеки MDR_Pack_V6 назван MDR_UART_CLI. В библиотеке уже есть подобный пример Пример приема команд по UART, но потребовалось создать что-то менее ресурсоемкое и более масштабируемое. Поэтому в данном драйвере для передачи данных используется DMA.
Протокол обмена написан так, что обмен инициализирует всегда только мастер - РС. Он запускает на исполнение команду с набором параметров и ждет ответа-подтверждения от МК. Таким образом, обмен работает только как вопрос-ответ.
UART микроконтроллера принимает и передает данные через DMA в блочном режиме (BReq). Первые два байта составляют заголовок. В этих байтах передается количество байт в данном сообщении (10 бит) и команда (6 бит). Эти поля позволяют завести 64 команды для управления и передавать для команды до 1023 параметров.
Если длина сообщения имеет четное количество, то досылается дополнительный служебный нечетный байт. Этот байт необходим чтобы сработало событие Uart_TimeoutRx, по которому отслеживается окончание приема сообщения от РС. (Это последнее слово вычитывается DMA по SReq.) Прерывания по таймауту приема могут срабатывать несколько раз по мере передачи USB адаптером всего сообщения. Окончание приема сообщения отслеживается по сравнению длины в заголовке и фактически принятым количеством байт.
Чтобы не тратить время на вход-выход в прерывание, наличие флага события Uart_TimeoutRx проверяется в функции запроса активной команды - MDR_CLI_GetCommand().
CLI_CMD_e MDR_CLI_GetCommand(uint8_t *lenAckParams, uint8_t **pCmdParams)
Команда из заголовка возвращается как значение из перечисления CLI_CMD_e, которое должно быть задано в файле MDR_Config.h. Параметры к команде и их количество возвращаются в аргументах функции. Для экономии памяти и сохранения быстродействия, функция передает ссылку на внутренний DMA-буфер с принятыми данными, откуда начинаются параметры (т.е. начиная с InpData[2], т.к. первые два байта заняты заголовком.)
Если длина в заголовке разошлась с фактической длиной сообщения, или если код команды не входит в перечисление CLI_CMD_e, то функция возвращает команду cliCMD_ERROR. Пользовательское приложение, которое использует драйвер UAR_CLI само должно решить, что делать в таком случае с данными. В примере ниже, в случае cliCMD_ERROR просто отсылаются обратно в РС, реализуя таким образом режим это (ECHO).
На примере реализации управления светодиодами были добавлены следующие пользовательские команды:
typedef enum { // Системные команды, зарезервированы cliCMD_NONE = 0, cliCMD_ERROR = 1, // Команды задаваемые пользователем под конкретный проект // В данном случае - это управление светодиодами и эхо cliCMD_LedShow, cliCMD_LedHide, cliCMD_LedOut, cliCMD_Echo, // Not for use - количество команд cliCMD_LEN } CLI_CMD_e;
Если функция MDR_CLI_GetCommand() возвращает cliCMD_NONE, то это значит, что команд от РС не поступало, обрабатывать нечего.
На каждую пришедшую команду от MDR_CLI_GetCommand() необходимо послать ответ функцией
void MDR_CLI_SetResponse(CLI_CMD_e cmd, uint8_t lenAckParams);
В поле cmd необходимо передать команду, на которую выдается ответ. А так-же задается количество данных в ответе. Сами данные ответа предлагается сразу писать в выходной буфер DMA, ссылку на который можно получить через вызов функции MDR_CLI_GetResponceBuf() аргумент pAckBuf. Размер буфера под параметры возвращается функцией.
uint16_t MDR_CLI_GetResponceBuf(uint8_t **pAckBuf);
Типовой цикл обработки команд:
CLI_CMD_e cliCMD; uint8_t cliParamLen; uint8_t *pCliParams; while (1) { cliCMD = MDR_CLI_GetCommand(&cliParamLen, &pCliParams); switch (cliCMD) { case cliCMD_NONE: break; case cliCMD_LedShow: ... обработка команды MDR_CLI_SetResponse(cliCMD, 0); break; case cliCMD_LedHide: ... обработка команды MDR_CLI_SetResponse(cliCMD, 0); break; case cliCMD_LedOut: ... обработка команды MDR_CLI_SetResponse(cliCMD, 0); break; case cliCMD_Echo: case cliCMD_ERROR: default: // Send Echo MDR_CLI_SetResponse(cliCMD, cliParamLen); } }
Для проверки работы драйвера UART_CLI и терминала PyComPort была реализована программа UART_CLI_Test входящая в состав примеров MMR_Pack_v6. Пример проверялся на отладочной плате 1986ВЦ1Т при подключении к РС через USB COM-адаптер.
Последовательность действий для запуска примера:
Для управления светодиодами посылаем через питоновский терминал команды:
На отладочной плате наблюдаем переключение светодиодов.
Большой плюс в использовании Python в том, что протокол обмена можно поменять в любой момент. Например, можно ввести расчет CRC. Достаточно поправить один текстовый файл и не надо связываться с проприетарными IDE с замороченными лицензиями на использование.
Но это наше первое знакомство с Python и возможно работа с потоками реализована не достаточно грамотно. Проект будет обновляться по мере освоения Python.