Программирование STM32. Часть 17: Драйвер UART

В прошлой части мы познакомились с таким интересным блоком в STM32, как UART. В этой статье мы не будем разбираться с простыми примерами (но это пока), а стразу познакомимся с библиотекой, которая позволяет удобным способом взаимодействовать с любым UART-ом в микроконтроллерах STM32F103xx. Ссылка на проект в конце статьи.

Предыдущая статья здесь, все статьи цикла можно посмотреть тут: http://dimoon.ru/category/obuchalka/stm32f1.

Краткое описание

Данная библиотека была создана в результате профессиональной деятельности, направленной на разработку ПО для очередного проекта на микроконтроллере 🙂  Она состоит из 2-х заголовочных и 2-х си-файлов:

  • uart.h
  • uart.c
  • RingFIFO.h
  • RingFIFO.c

Основные возможности:

  1. Работает на микроконтроллерах stm32f103xx;
  2. Позволяет обмениваться данными через UART1, 2, 3, 4. Обмен данными через UART5 пока не реализован (не было необходимости, но это исправим 😉 );
  3. Возможность переинициализировать UART прямо во время выполнения кода;
  4. Возможность задействовать и настроить длину FIFO-буфера на приемник и передатчик;
  5. Незадействованные в данном проекте участки кода не будут компилироваться в проект (реализовано через #ifdef);
  6. Для работы использует только CMSIS;
  7. Не перегружена лишним кодом (по возможности) и требует не очень много ресурсов (разумная цена за удобство использования).

Рассмотрим содержимое uart.h. В самом начале там есть вот это:

С помощью этих define-ов мы подключаем куски кода, которые отвечают за работу с соответствующим модулем UART. Тут следует обратить внимание на тот факт, что если в выбранном МК данный UART отсутствует, то необходимо закомментировать соответствующий define, иначе код не скомпилируется.

Далее идет следующее:

Тут куча define-ов, которые отвечают за тонкую настройку функциональности библиотеки.

Если мы хотим использовать кольцевой буфер, то нужно раскомментировать #define UARTn_USE_RING_BUFF, n — номер UART-a. Далее, можно настроить длину кольцевого буфера для приемника и передатчика через #define UARTn_TXBUFF_LENGHT и #define UARTn_RXBUFF_LENGHT, в данном примере длины буферов равны 16 байт.

Затем идет структура инициализации:

и прототипы функций библиотеки:

UART_Init() служит для инициализации, ее надо вызывать перед началом работы с UART-ом. UART_PutC() — отправить байт в UART, UART_GetC() — прочитать.

Далее идут функции работы с кольцевыми буферами.

UART_BytesToRead() — возвращает количество байт в буфере приемника, которые можно прочитать функцией UART_GetC().

UART_BytesToWrite() — этой функцией можно получить количество байт в буфере передатчика UART.

UART_ReadBuffClear() и UART_WriteBuffClear() — очистить буфер приемника и передатчика соответственно.

Вот и все функции 😉

Внутреннее устройство

Полное описание библиотеки проводить не буду, остановлюсь только на функции инициализации UART. Она устроена примерно так:

Для примера рассмотрим процесс инициализации UART1:

Первой инструкцией в условии if(id == 1) { … } идет включение тактирования модуля UART1:

После этого преступаем к инициализации пинов, к которым подключены линии RX и TX:

С помощью функции TxPinInit() мы инициализируем пин TX, а RxPinInit() пин RX. Данные функции получились не очень удобными, но у меня есть мысли, как это можно улучшить (хочу сделать нечто такое, как у DiHalt-а: http://easyelectronics.ru/udobnaya-rabota-s-gpio-na-stm32.html). По-умолчанию, используются пины без ремапа, если есть необходимость использовать ремап-пины, всю настройку нужно делать в этом блоке кода. Конечно, это не очень удобно, но пока как есть 😉.

Далее следует инициализация UART-а:

Первым делом выполняем сброс модуля UART через регисты RCC. Это сделано для «чистой» повторной инициализации модуля UART. Куча asm(«nop») на всякий случай, чтоб периферия успевала за стремительным выполнением программы в CPU. Мы же не используем разные HAL-ы, у нас же программа выполняется быстро? 😉

После этого вызываем функцию _uart_init(), и в случае неудачи экстренно выходим с кодом возврата -1.

Затем выполняем инициализацию кольцевого буфера, если он используется для данного UART-а:

Ну и последний штрих: разрешаем работу UART и выходим с кодом возврата 0:

На этом все, остальные UART-ы инициализируются схожим образом.

Пример работы с библиотекой

Изначально данная библиотека была написана для работы на микроконтроллере stm32f103c8, затем была доработана уже на stm32f103ve, и для написания статьи вновь был создан проект для stm32f103c8, при этом ни каких проблем это не вызвало. Скорее всего, проблем не будет при переносе этой библиотеки на любой микроконтроллер stm32f103.

Итак, перейдем к коду. Вот main.c:

Первым делом мы объявляем структуру, в которой содержится информация для инициализации UART-а, причем я объявил ее как const, чтоб она размещалась во flash-памяти и не отнимала место в ОЗУ микроконтроллера. Значение параметров структуры следующее:

  • bus_freq — частота шины, к которой подключен модуль UART, в данном случае 36МГц
  • baud — скорость передачи данных
  • data_bits — количество бит данных, 8 или 9
  • stop_bits — количество стоп-бит, 1 или 2
  • parity — контроль четности, 0 — нет, 1 — even, 2 — odd.

Тут есть небольшой нюанс. Если используется контроль четности (even или odd), и количество передаваемых бит равно 8, то data_bits надо выставить в 9 (8 бит данных + 1 бит четности, итого 9). Или это особенность STM32, или так принято у всех, я докапываться не стал, однако, данный момент описан в Referens Manual-е, хотя и найти его удалось только после того, когда знал, что искать 😁

Переходим к main(). Первым делом инициализируем тактовый генератор с помощью ClockInit() на максимальную частоту работы. После этого инициализируем UART1:

UART_Init(1, &UARTInitStr);

В качестве аргументов передаем номер UART-а и структуру инициализации.

После этого очищаем буферы приемника и передатчика (это не обязательно), и в бесконечном цикле выполняем опрос приемника UART с помощью функции UART_GetC(). Если пришел какой-либо символ, то функция возвратит значение, отличное от -1, которое мы тут же отправляем назад с помощью UART_PutC(). Вот и весь пример 😊

Заключение

На этом пока все, поздравляю всех с наступающим 0x07E4 годом! Желаю всем всего наилучшего, творческих успехов и удачи! До новых встреч! 🎄🎉🎉

Ссылки

Проект на GitHub: https://github.com/DiMoonElec/STM32UARTDriver

Метки: , , , , . Закладка Постоянная ссылка.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *