Иногда возникает ситуация, когда аппаратных ресурсов микроконтроллера не хватает или требуемый ресурс в его составе отсутствует. Решить эту проблему можно двумя способами – заменить используемый микроконтроллер на другой или реализовать требуемый ресурс программно. Оба способа имеют свои достоинства и недостатки, однако второй вариант часто более предпочтительный, а иногда и единственно возможный. Например, как в случае с
1-Wire интерфейсом, который не поддерживается аппаратно ни в одном 8-ми разрядном микроконтроллере AVR.
Требования
Для использования кода программного 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() обработчик прерывания
Файлы
software-uart.rar