Иногда возникает ситуация, когда аппаратных ресурсов микроконтроллера не хватает или требуемый ресурс в его составе отсутствует. Решить эту проблему можно двумя способами – заменить используемый микроконтроллер на другой или реализовать требуемый ресурс программно. Оба способа имеют свои достоинства и недостатки, однако второй вариант часто более предпочтительный, а иногда и единственно возможный. Например, как в случае с 1-Wire интерфейсом, который не поддерживается аппаратно ни в одном 8-ми разрядном микроконтроллере AVR.
В этой статье рассмотрена реализация программного UART`а, которую можно модифицировать под любой микроконтроллер, программируемый на Си. Во второй части статьи будет рассмотрена реализация программного UART`а модифицированного под микроконтроллер AVR. Материал написан на основе IAR`овского апноута, найденного на просторах интернета.
Для использования кода программного UART`а, необходимо выполнение следующих требований:
1. Наличие Си компилятора под используемый микроконтроллер
2. Наличие у микроконтроллера таймерного прерывания.
3. Наличие у микроконтроллера двух выводов, состояние которых можно программно читать и изменять.
В данном коде реализованы следующие функции.
1. void flush_input_buffer( void )
Очистка входного (приемного) буфера.
2. char kbhit( void )
Тестирование входного буфера на наличие данных.
3. char getchar( void )
Чтение символа из входного буфера.
Принимаемые данные сохраняются в кольцевом буфере, емкость которого задается с помощью макроопределения.
Если на момент вызова данной функции входной буфер пуст, функция ожидает поступление данных. При этом в цикле выполняется пользовательская функция idle(). Про нее написано ниже.
4. void turn_rx_on( void )
Разрешает прием данных по UART`у. По умолчанию прием данных разрешен.
5. void turn_rx_off( void )
Запрещает прием данных по UART`у.
Прием и передача данных выполняется в обработчике прерывания таймера. Запрещение приема данных уменьшает время выполнения обработчика.
6. void putchar( char )
Запись символа в последовательный порт
7. void init_uart( void )
Инициализация программного UART`a. Устанавливает внутренние флаги, настраивает выводы микроконтроллера и таймер.
Данная программная реализация UART`а основана на использовании прерывания таймера. Разрядность таймера и режим работы значения не имеют, главное чтобы частота его прерываний могла быть установлена в три раза выше требуемой скорости обмена.
Например, требуется скорость 9600 бит в секунду. Тогда прерывания таймера должны происходить с частотой 9600*3 = 28800 Гц. Или в пересчете на временной интервал – каждые 34,72 микросекунды.
Это накладывает скоростные ограничения на возможности применения программного UART`a, особенно в микроконтроллерах с низкой тактовой частотой.
Если установить слишком высокую скорость обмена, код обработчика прерывания таймера не будет успевать выполняться между вызовами прерываний, что приведет к ошибкам приема/передачи, а то и к полной потере связи.
Также высокая частота прерываний таймера будет негативно влиять на производительность остальной части программы. (Для аналогии можно привести ситуацию, когда вы пытаетесь выполнять свою работу, а вас постоянно отвлекают на посторонние дела.)
Код программного UART`a из IAR`овского апноута записан в сишном файле. Подключить его к проекту можно двумя путями:
– скопировать этот код в один из сишных файлов своего проекта
– написать к сишному файлу заголовочный и подключить к проекту полноценный модуль
Второй путь более грамотный, поэтому программный UART во второй части статьи я подключаю к проекту именно так.
Однако это еще не все.
Для переносимости кода в нем не реализовано несколько платформенно-зависимых функций, которые вам потребуется написать. Функции перечислены в списке ниже.
В принципе ничего сложного в этом нет.
1. void get_rx_pin_status( void )
Возвращает состояние приемного вывода (receive pin).
2. void set_tx_pin_high( void )
Устанавливает на передающем выводе (transmit pin) логическую единицу.
3. void set_tx_pin_low( void )
Устанавливает на передающем выводе логический ноль.
4. void idle( void )
Фоновая пользовательская функция. Выполняется, когда ожидается прием данный.
5. void timer_set( int BAUD_RATE )
Устанавливает частоту прерываний таймера. Частота прерываний должна быть в 3 раза выше требуемой скорости обмена!
Функции передается значение требуемой скорости обмена, а она должна устанавливать соответствующую частоту таймерных прерываний.
6. void set_timer_interrupt( timer_isr )
Разрешает прерывания таймера.
Для экономии памяти и увеличения производительности кода некоторые из этих функций можно заменить макросами. Например, функции 1, 2, 3 и 6.
Функцию 4 можно оставить пустой.
И последнее что нужно сделать, это поместить код функции void timer_isr(void) в прерывание таймера. Сделать это можно опять-таки двумя способами:
— поместить вызов функции timer_isr() в код обработчика прерывания
— сделать из самой функции timer_isr() обработчик прерывания
Второй вариант более грамотный, поскольку требует меньших накладных расходов – оперативной памяти и процессорного времени. Впрочем, ты и так должен знать, к чему приводит вызов функций из прерываний.
Программный UART вторая часть…
software-uart.rar
В этой статье давайте обсудим прерывания USART.
События прерывания, запускаемые периферийным устройством USART, перечислены на рис. 1, а в третьем столбце вы можете увидеть соответствующие управляющие биты.
В столбце флагов событий вы можете увидеть флаги, соответствующие каждому событию, которые будут установлены, когда соответствующее событие произойдет во время связи UART, и установка любого из этих флагов может генерировать прерывания, только если соответствующие биты управления включены.
Рисунок 1. Запросы прерывания USART.
Некоторые важные события прерывания, инициированные периферийным устройством USART, перечислены ниже:
На рис. 2 показана диаграмма сопоставления прерываний USART. В котором вы можете видеть, что от каждого периферийного устройства USART идет только одна линия, и она попадает в NVIC-движок процессора.
В I2C вы видели две линии, выходящие из каждого периферийного устройства USART. Один для ошибки, а другой для события. Но здесь все не так. Вы можете проанализировать схему отображения прерываний USART из справочного руководства.
Рисунок 2. Диаграмма отображения прерываний USART.Упражнение:
Курсы FastBit Embedded Brain Academy
Нажмите здесь: https://fastbitlab.com/course1
Теги: Лекции STM32 USART 900 14
Академия FastBit Embedded Brain Academy использует возможности Интернета, чтобы предлагать онлайн-курсы, связанные с программированием встроенных систем, операционной системой реального времени, встроенными системами Linux и т.
д., по очень низкой цене. Опираясь на большой опыт работы в отрасли, мы подготовили множество курсов, в которых приняли участие более 3000 клиентов в более чем 100 странах. В этом уроке я опишу, как:
[+] использовать U(S)ART микропроцессора STM32F103C8
[+] с кольцевым буфером DMA для Rx,
[+] обычный буфер DMA для Tx,
[+ ] и как использовать Idle Line Interrupt для получения сообщений неизвестной длины
Я использую макетную плату Bluepill с микропроцессором STM32F103C8T6, CubeMX для настройки оборудования, CubeIDE для редактирования и уровень аппаратной абстракции STM32 HAL вместе со следующим оборудованием:
[+] макетная плата и несколько перемычек
[ +] ST-Link V2 Clone
[+] простой преобразователь UART в USB, вы можете получить, например.
Используйте CubeMX и следуйте инструкциям в этом руководстве для настройки RCC, настроек часов, SWD и настройки проекта. В этом примере основная частота MCU составляет 72 МГц.
В этом уроке нам нужен PC13 в качестве выхода GPIO и USART1 с DMA-каналами для RX и TX и включенными глобальными прерываниями:
Цурюк
Вейтер
Убедитесь, что для канала RX DMA установлено значение «circular», а для канала TX DMA установлено значение «normal», и не забудьте включить глобальное прерывание для USART1.
Когда вы закончите настройку в CubeMX, распиновка MCU должна выглядеть так:
STM32F103C8 — Учебное пособие по циклическому прерыванию прерывания UART по DMA — Распиновка MCUВся функциональность реализована в файле main.c, кроме вызова прерывания простоя, который должен быть в файле stm32f1xx_it.c
Вы можете скачать оба файла (заархивированные) в конце этой статьи, если не хотите набирать их самостоятельно.
STM32F103C8 — Учебное пособие по циклическому прерыванию прерывания UART по DMA — main.c: частный включаетНужны и для sprintf () и strlen (). Правильное место для двух операторов include находится в разделе «Includes» файла main.c между тегами USER CODEIncludes.
STM32F103C8 — Учебное пособие по циклическому прерыванию прерывания UART по DMA — main.c: private определяетНам нужна пара глобальных переменных. Поместите их в раздел Частные переменные (код пользователя). Я сделал все необходимые переменные глобальными для этого примера, потому что их легче отслеживать с помощью отладчика. В реальном приложении вы, скорее всего, не сделали бы этого таким образом.
Нам нужны прототипы для функции обратного вызова UART RX Complete и функций обратного вызова UART TX Complete. Вы можете скопировать и вставить их из файла хедера stm32f1xx_hal_uart.h. Вы можете найти этот файл в папке Drivers -> STM32F1xx_HAL_Driver -> Inc вашего проекта CubeIDE. Поместите их в раздел «USER CODE 0» файла main.c.
Обе функции объявлены как «__weak» в файле stm32f1xx_hal_uart.c и будут перезаписаны вашей реализацией в файле main.c.
Обе функции вызываются обработчиком прямого доступа к памяти (автоматически). Так что вам не нужно заботиться о том, куда их вызывать и зачем…
STM32F103C8 — Учебное пособие по циклическому прерыванию прерывания UART по DMA — КОД ПОЛЬЗОВАТЕЛЯ 2: запустите UART1 в режиме DMAВ разделе КОД ПОЛЬЗОВАТЕЛЯ 2 (внутри функции int main (void)) мы должны:
LED1 переключается при каждой исходящей передаче.
бесконечный цикл >
> в то время как (1) { … } << в основной функции остается пустым. Всю «тяжелую работу» выполняют две функции обратного вызова DMA, которые реализованы в разделе «ПОЛЬЗОВАТЕЛЬСКИЙ КОД 4»: STM32F103C8 — Учебное пособие по циклическому прерыванию прерывания UART по DMA — main.c: Код пользователя 4 — Функции обратного вызова STM32F103C8 — Учебное пособие по циклическому DMA прерывания UART — UART Rx Полная реализация функции обратного вызоваНет необходимости вводить весь код самостоятельно. В конце этого урока вы можете скачать исходный код или перейдя по этой ссылке
Необходимо лишь небольшое изменение в stm32f1xx_it.c:
Когда вызывается общий обработчик IRQHandler UART, нам нужно проверить, является ли это Idle Interrupt, и если да, мы вызываем функцию обратного вызова RxComplete.
Этот код в целом работает следующим образом:
Существует 3 прерывания: прерывание по завершению приема (созданное DMA), прерывание по завершению передачи (также созданное DMA) и прерывание по «свободной линии», закодированное вручную в stm32f1xx_it.
Прерывание Rx Complete всегда происходит, когда буфер Rx переполняется. Чтобы продемонстрировать это, размер буфера Rx для этого примера был намеренно сделан очень маленьким (16 байт). Мы используем это прерывание для подсчета «Опрокидывания буфера». Буфер может перевернуться только один раз, прежде чем произойдет Idle Interrupt для корректной передачи. Если он переворачивается два и более раз, можно предположить, что какие-то данные были перезаписаны и передача повреждена.
Входящие данные проверяются при возникновении прерывания простоя. Начальная позиция нового входящего потока данных — это последняя конечная позиция, которую можно рассчитать, запросив регистр DMA «CNDTR» (который сообщает нам, сколько байтов осталось до заполнения / переполнения буфера) и вычитая это значение. от (известного) размера буфера. Эта информация должна быть непротиворечивой на протяжении всего времени работы программы, поэтому ее нужно хранить «в надежном месте» 😉 — не потерять…
С информацией о последней известной позиции буфера и текущей позиции буфера мы можем вычислить длину полученных данных.