Программный UART для любого микроконтроллера. Ч1

01/02/2012 - 22:15
 
   Иногда возникает ситуация, когда аппаратных ресурсов микроконтроллера не хватает или требуемый ресурс в его составе отсутствует. Решить эту проблему можно двумя способами – заменить используемый микроконтроллер на другой или реализовать требуемый ресурс программно. Оба способа имеют свои достоинства и недостатки, однако второй вариант часто более предпочтительный, а иногда и единственно возможный. Например, как в случае с 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

Comments   

# Vfrc 2012-02-02 05:29
Жду продолжения! В свое время делал софтовый уарт на pic16 - нужно было сразу два, долго с таймингами мучился... В итоге лучший результат дал пример, поставляемый с компилятором с asm вставками.
# Muchachosss 2012-02-02 09:03
Спасибо за статью. Когда-то возникала такая необходимость. Будем реализовывать!
# Misha 2012-02-23 13:23
Ув. Pashagan, скажите мне пожалуйста для чего нужен данный код, ведь Ваш код кольцевого буфера вполне удобен! Чем данный код отличается от других кодов UART?
# Петр123 2012-02-27 07:34
Это же программный уарт, а там были аппаратные.
# 789 2012-03-16 11:08
Более правильно было бы использовать для программного УАРТ-а вход захвата таймера. Но почему-то таких реализаций в сети не видно.
# McAron 2012-12-17 18:55
Не могу понять почему не работает симуляция в proteus.
Создал 2 МК Attiny85, соединил крест накрест ножки (PB3 первого МК к PB4 второго и наоборот) В Вашем заголовочном файле изменил F_CPU: 8000000->165000 00, IN_BUF_SIZE: 32->6, RX_PIN: 1->4, TX_PIN: 0->3
Первый работет приемником, второй передатчиком.
На передатчике :

int main( void )
{
SUART_Init();
sei();
while(1)
{
SUART_PutChar(b uf);
buf++;
}
return 0;
}

На приемнике:
int main( void )
{
SUART_Init();
sei();
while(1)
{
buf=SUART_GetCh ar();
}
return 0;
}



Приемник ничего не получает! Помогите разобраться начинающему.

Кстати запустил Ваш проект в протеусе, на осцилографе нету изменений. Может я чего-то не понимаю.
# McAron 2012-12-17 21:48
Пардон, с осцилографом разобрался. Случайно не на ту частоту поставил.
а arriny85 так и не пересылает данные (точнее пересылает, но любой символ приходит как 0.
# Pashgan 2012-12-18 06:15
В Протеусе в настройках микроконтроллер а установите биты CKSEL на нужную тактовую частоту и CLKDIV8 - 1 (отключение деления тактовой частоты), тогда все заработает.
# McAron 2012-12-18 19:40
При установке CKSEL=0010 Продолжает слать ерунду. После установки CLKDIV8 - 1 не шлет ничего. Скиньте пожалуйста рабочий проэкт в протеусе с исходниками.
# McAron 2012-12-18 19:52
Дико Извиняюсь за дезинформацию, все работает! Спасибо!
# ZiperRUS 2012-12-28 17:37
ну так если работает у тя всё может сам выложис симуляцию в протеусе с исходникамию или чё те жалдко чтоль???
# ave! 2015-05-02 18:26
Для STM32F030F4P6 незаменимая штука! Там всего 1 UART.
В комментариях на этой странице мой рабочий код для STM.
http://we.easyelectronics.ru/STM32/vcp-soft_uart_x3-modifikaciya-stm32vldiscovery.html
# al 2018-05-24 05:24
Хотел написать автору, но не нашел контактов на сайте. Эта реализация UART имеет один неприятный баг - нельзя передавать на такой UART байтики подряд - будет каша после первого символа, нужно делать паузу в один символ после передачи каждого. Ну и еще кой чего по мелочи. А так автору спасибо, сэкономил много времени!!!!

У вас недостаточно прав для комментирования.