046-Программный UART для ATtiny13.

Автор: | 01.07.2010

В заглавной статье по интерфейсам fkvfaydar обратил внимание на то, что неплохо было бы заиметь интерфейс UART в ATtiny13. Полностью согласен с fkvfaydar, более того, я считаю, что реализация программного UART для микроконтроллера ATtiny13 будет показательной в плане философии блога – удобные, недорогие устройства для Ваших проектов. Что может быть дешевле и проще ATtiny13? А значит, быть статье!

Программый UART в ATtiny13

ATtiny13 маленький и недорогой микроконтроллер, имеющий у себя на «борту» АЦП, что очень удобно для обслуживания датчиков и выносной периферии. Но у ATtiny13 есть один большой недостаток — отсутствие, каких либо, аппаратных интерфейсов для связи. Это сводит на нет все его достоинства. Будем это исправлять! Организуем программный интерфейс для ATtiny13!

UART, наверное, лучший интерфейс для программной реализации.
Причин несколько:
— интерфейс простой и по причине маленького размера памяти программ микроконтроллера это актуально;
— для работы интерфейса нужны только 2 линии, приемника — RxD и передатчика — TxD. А если нужен только прием или только передача будет задействована только одна ножка микроконтроллера (рабочих ножек у ATtiny13 всего 5, если не считать Reset), а значит 4 ножки остаются нам на наши нужды;
— ну и немаловажным есть то, что UART очень распространен и в микроконтроллере Вашего проекта он точно найдется.

На какие ресурсы ATtiny13 мы можем рассчитывать при реализации UART?
А рассчитывать нам особо не на что:
— размер памяти программ 512 слов (большая часть должна остаться для основной программы);
— один восьмибитный таймер (основная программа должна иметь возможность им пользоваться);
— прерывания по изменению уровня на ножках.
Вот, собственно, и все.

Что мы хотим получить от программной реализации интерфейса UART?
— код, по возможности, меньшего размера и с наименьшим потреблением ресурсов;
— по возможности, независимые прием и передачу;
— кроме того, должна оставаться возможность полноценного функционирования основной программы.

Нужно понимать, что программная реализация, хоть и простого, но все-таки интерфейса, потребует определенных «жертв». Для облегчения жизни микроконтроллеру откажемся от возможности задавать различные режимы работы UART. Это даже не «жертва» — это здравый смысл! Интерфейс будет работать в одном режиме (Скорость UART – 9600, количество бит данных – 8, бит четности – нет, стоп-бит – 1). Проверка на правильность передачи байта (бит четности), конечно, штука нужная, но для неответственных приложений (а у нас именно такие) необязательная. Убираем. Скорость работы UART (Baud Rate) может быть любой (частота задающего генератора контроллера это позволяет).

Вариантов реализации мною рассматривалось несколько, но самым удачным оказался вариант работы на прерываниях. Как прием, так и передача осуществляется в процедурах обработки прерываний по сравнениям счетчика Timer0 — TIM0_COMPA и TIM0_COMPB. Кроме того, для определения начала посылки используется прерывание по изменению уровня сигнала на ножке — PCINT0. Достоинством такого способа есть то, что работа интерфейса не мешает ходу основной программы и можно использовать, параллельно с UART, таймер (конечно не совсем полноценно – но хоть так).

Теория реализации интерфейса.
Обе процедуры обработки прерывания по сравнению таймера TIM0_COMPA и TIM0_COMPB вызываются непрерывно с частотой 9600 Гц (частота работы UART).
TIM0_COMPA используется для побитной передачи кадра UART (один бит кадра передается за одно прерывание). Процедура обработки прерывания TIM0_COMPA может параллельно использоваться основной программой (замеры временных интервалов, периодический опрос датчиков и др.) т.к. она постоянно вызывается с заданной частотой.
PCINT0 используется для обнаружения переднего фронта старт-бита принимаемого кадра. В прерывании PCINT0 происходит коррекция фазы вызова прерывания TIM0_COMPB таким образом, чтобы прерывания возникали в центрах битов принимаемого кадра, где однозначно можно определить значение принимаемого бита.
Описывать работу прерываний и программы в целом не буду (боюсь Вас занудить) – смотрите листинги программ (там комментариев больше чем самой программы :)).

Интерфейс реализован в двух вариантах:
046-T13-C-ProgUART.zip (8982 Загрузки)
046-T13-AB-РrogUART.zip (4890 Загрузок)

 

Управление работой, как приемника, так и передатчика осуществляется через две переменные.
Передатчик:
Tx_Byte – передаваемый байт. В эту переменную записываем байт который хотим передать;
Tx_Count – счетчик переданных бит. Для запуска передачи байта из Tx_Byte нужно обнулить Tx_Count. Дальше передача идет автоматически в прерываниях. Если Tx_Count равен 10 значит передача окончена, можно передавать следующий байт.
Приемник:
Rx_Byte –принятый байт;
Rx_Count – счетчик принятых бит. Если счетчик равен 10 значит прием окончен, можно забрать принятый байт из Rx_Byte. В Rx_Count ничего записывать не нужно, прием начинается автоматически по факту прихода на ножку RxD старт-бита.

Чтобы не морочиться с переменными, для работы с интерфейсом, в обоих вариантах реализации (Algorithm Builder и CodeVisionAVR), определены по две процедуры:
PutByte () – отправка байта по линии TxD. Если процедура вызвана во время передачи предыдущего байта, процедура будет ожидать окончания передачи и лишь потом инициирует новую передачу.
GetByte () – чтение, принятого по линии RxD, байта. Если процедура вызвана во время приема байта, процедура будет ожидать окончания приема и потом вернет принятый байт. Если принятый байт уже был прочитан, а новый еще не начал приниматься — процедура вернет значение – 157 (это сделано для того, чтобы в программе было видно, что новых байт по RxD не поступало). Если байт принялся с ошибкой (неправильный формат кадра), процедура вернет значение – 158.
В случае надобности процедуры TxByte и RxByte Вы всегда можете подкорректировать под себя.

В работе программного UART есть небольшой нюанс, который нужно учитывать при реализации его в каждом конкретном микроконтроллере. Этот нюанс — внутренний задающий генератор (RC – цепочка). Дело в том, что, в отличии от кварцевого генератора, частота его не очень стабильна и зависит от температуры, напряжения питания и других факторов. И может такое случиться, что в вашем микроконтроллере частота внутреннего генератора выйдет за пределы, допустимых для UART, 10% . Уход частоты от допустимой будет сопровождаться большим количеством ошибок при приеме-передаче (посылаем «Hello World», а принимаем «lsksadkfh»). Хотя такой большой уход и редкость, но нужно знать как решить эту проблему.
1 Официальный способ. У микроконтроллеров есть, так называемые калибровочные байты, которые и призваны подстраивать частоту внутреннего задающего генератора. Калибровочные байты доступны при программировании (правда, не все программы для программирования позволяют с ними работать). Чем больше значение в нем записано, тем меньше частота задающего генератора. Если Вы выбрали этот способ коррекции – почитайте сначала даташит.
2 Простой способ. Вместо того, чтобы корректировать частоту задающего генератора, можно подкорректировать частоту вызова прерываний таймера в самой программе. Для этого нужно поменять значение константы Baud Rate, которая используется для вычисления периода срабатывания прерываний. Менять значения нужно в пределах ±5-7% от стандартного (9600).

#define Baund_Rate  9600
// Частота работы UART (если прием-передача с ошибками -
// поменяйте на значение больше/меньше на 5%)

Подведем итоги.
Я считаю, что программный интерфейс UART получился удачным. Мы имеем приемник и передатчик которые могут работать параллельно, независимо друг от друга (полнодуплексная связь). Размер кода небольшой: приемник – 45 слов; передатчик – 20 слов (со всеми необходимыми инициализациями периферии это чуть больше 5 части памяти микроконтроллера). Прием и передача осуществляется в прерываниях, в фоновом режиме, незаметно для основной программы. Для реализации UART задействован единственный у ATtiny13 таймер, но основная программа не потеряла возможности его использовать для своих нужд. Реализация программного UART в микроконтроллере ATtiny13 позволит легко и дешево подключать различные удаленные периферийные устройства (в том числе и аналоговые) к Вашему проекту.

P.S. Наверное по причине нетвердых знаний в программировании на С, процедуры обработки прерываний в CodeVisionAVR не получились адекватными, в плане размера кода. Поэтому я сделал их ассемблерными. В них сложно разобраться (для Сишников), но зато код небольшой.
Если у кого-то получиться сделать адекватные процедуры обработки прерываний на С – присылайте! Другим будет легче разобраться.

(Visited 22 095 times, 4 visits today)

046-Программный UART для ATtiny13.: 66 комментариев

  1. AVL

    Подскажите, пожалуйста, как понизить частоту обмена до 4800.
    Тут я исправил: #define Baund_Rate 4800.
    Понимаю, что внастройках таймера нудно понизить частоту тактирования 9600Кгц до 1200Кгц (єто в 8 раз понижение получится). За сч’т #define Baund_Rate 4800 — поднимится в два раза. Итого скорость изменится в 4 четіре раза, а надо в два раза уменьшить. «Перебор» нужно устранить начальной инициализацией сч’тного регистра, наверно.
    Тут уже затік у меня. Помогите, пожалуйста.
    Спасибо.

  2. mmavka

    Для того чтобы не лезло 157.
    Написано в коде Русским языком.
    if (Rx_Count == 11) return (157); // Если байт уже раз прочитали
    // то получаем 157 (за ненадобностью можно убрать)

  3. AVL

    Так и собираюсь сделать, но єто опять займёт пару дней, ибо программер с меня никакой и я постоянно спотікаюсь об банальніе вещи.

  4. GetChiper Автор записи

    На Си я не помошник.
    Но проектов на Си в сети куча — чего не взять готовый и не подредактировать под себя?

  5. AVL

    О, к стати, Ві за не большое денежное вознаграждение не перепишите под 2313? ибо я сделал ставку на єту заготовку, как оказалось она мне не подходит, а сроки уже меня поджимают.
    Спасибо!

  6. AVL

    Для 2313 не подскажете заготовку дод УАРТ, аналогичную, как для Тини13?
    Ибо я понадеялся на Т13, потратил пару дней и как оказалось, такое решение меня не устроит. Теперь под Тини2313 ищу, ибо есть у меня такая микросхема в запасе. По-бістрому надо мне на неё проект перенести. Спасибо!

  7. AVL

    Если ж убрать єтот фильтр то ПОСТОЯННО лезет 157 в приёмній буфер.

  8. AVL

    Они не принимаются даннім устройством.
    if ((R_byte!=157) & (R_byte!=158)) // если не ошибка и не повтор — обрабатываем
    Етот фильтр их не пропускает.

  9. GetChiper Автор записи

    А что с этими байтами не так?

  10. AVL

    Караул!!!
    Ложка дёгтя.
    Даное решение, на мой взгляд, носит только образовательній характер, ибо в реальной жизни оно не примет байті значением 157 и 158!!!
    Я тока-что наступил на ети грабли.
    Хотелось бі, что б я біл не прав.

  11. GetChiper Автор записи

    Обычно при такой небольшой скорости UART погрешности частоты не дают ошибок. Поэтому подстраивать нет особого смысла. Но, в случае чего, всегда можно подстроить скорость UART при инициализации самого модуля.

  12. AVL

    И ещё місль меня посетила по поводу ошибок скорости порта из-за температурного и прочего дрейфа внутреннего осцилятора.
    Вот у меня, к примеру, моя Тиня должна общаться по УАРТу с кварцованнім девайсом, соответственно и запрос от кварцованного будет стандартнім. Мі ж можем померять длительность имульса запорса и исходя из єтого подстроить тактовую частоту нашей Тиньки.
    Или ета процедура заберёт очень мого памяти?
    Спасибо!

  13. AVL

    Спасибо, с конфигурацией порта я спрвился, но странно мне что такие строки, сгенерированніе роднім МАСТЕРОМ не принимались компиляторм.
    // Input/Output Ports initialization
    // Port B initialization
    // Function: Bit5=In Bit4=In Bit3=Out Bit2=Out Bit1=Out Bit0=Out
    DDRB=(0<<DDB5) | (0<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
    // State: Bit5=T Bit4=T Bit3=1 Bit2=1 Bit1=1 Bit0=1
    PORTB=(0<<PORTB5) | (0<<PORTB4) | (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);

    Почему они не принимаются?

    Сейчас у меня проект при при'ме одного символа включает светодиод и отправляет назад другой символ, а при при'ме третьего символа віключает светодиод.
    Одним словом РАБОТАЕТ.
    Скорость обмена пока менять не пробовал. Тестирую в Протеусе.
    Спасибо!

  14. GetChiper Автор записи

    Кроме ножек задействованных под UART (PB3, PB4), все остальные ножки доступны для свободного использования.
    Порта настраиваются на выход как обычно (записью 1 в соответствующий DDR-порт).
    Частота приема/передачи равна частоте вызовов прерываний таймера0. Соответственно для того чтобы изменить частоту UART — меняйте частоту вызовов прерываний таймера.

  15. AVL

    Хотел я попробовать включать светодиоді при при’ме определ’нного символа, но получил затік.
    А именно, я не могу настроить порт на віход.
    Сейчас все порті кроме Тх настроені на вход. При попітке изменить, к примеру РВ0 на віход — получаю ошибку.
    Єто наверное из-за того, что все ноги задействовані как вході в прерівании?
    И ещ’, мнеконкретно нужна скорость 4800, а не 9600, думаю, что простім изменением в листинге єтого значения задача не решиться.
    Вопрос:
    Как настроить порті на віход и изменить скорость.
    Спасибо!

  16. AVL

    Всем привет!
    Я нашол ошибку в исходнике Кодевижина.
    Есть:
    // Pin change interrupt service routine
    interrupt [PCINT0] void pin_change_isr(void)
    А долно біть:
    // Pin change interrupt service routine
    interrupt [PC_INT0] void pin_change_isr(void)

  17. Phantom

    @mmavka
    Хотя сейчас уже не актуально, но большое спасибо!
    Пригодится для изучения, т.к. мне ближе С, ASM в последний раз изучал в универе.

  18. mmavka

    Еще раз привет!
    Нашел. Правки в ASM. Все через дефайны. Если внимательно все менять, заработает сразу.
    https://yadi.sk/i/pFWj70yqiaJt4

  19. mmavka

    Всем привет.
    Кстати я переделывал код под любые пины. Если кому нужно могу поискать в архиве.

  20. Phantom

    Всем привет!
    К сожалению, вариант автора под CodeVisionAVR не заработал (у меня для Rx и Tx другие ноги).
    Перелопатил весь исходник, вооружившись даташитом, изменил номера ног в соответствующих местах ассемблерных вставок, не помогло…
    Но за идею спасибо!
    Как говорится, хочешь во всем разобраться детально — сделай сам )))
    За пару дней реализовал алгоритм на С (под Atmel Studio) с буфером приемника в 16 байт.
    Размер получился побольше: 566 bytes (55,3 % Full), но если вырезать приемный буфер, то можно сэкономить.
    При изучении UART помогла вот эта статья: http://geektimes.ru/post/253786/
    Если кому-то нужен исходник: https://yadi.sk/d/umRP8CCaia8Bg.
    Для настройки на другие номера ног (у меня Tx=PB4, Rx=PB2) в файле Config.h достаточно поменять три значения — TxD, RxD, RxINT.
    В алгоритме осталась возможность использовать таймер, но при условии, что вы не будете менять значения регистра TCNT0, иначе сместятся временные интервалы UART, что приведет к ошибкам как приема, так и передачи.
    Задержка в основной части программы _delay_ms(20) обязательна (возможно поставить ее поменьше — не пробовал), иначе при приеме сразу нескольких байт подряд будут ошибки.

    Пример main.c:
    #include «Config.h»
    #include
    #include
    #include

    extern void send_ch(uint8_t data);
    extern void start_receive();
    extern volatile uint8_t receive_ready;
    extern volatile unsigned char get_Rx();
    extern volatile unsigned char read;

    ISR(PCINT0_vect)
    {
    start_receive();
    }

    int main(void)
    {
    Tx_init();
    Rx_init();
    read = 0;
    asm(«sei»);

    while(1)
    {
    if (receive_ready == 1)
    {
    send_ch((char)get_Rx());
    _delay_ms(20);
    }
    }
    }

  21. Gnusmas

    Обнаружил такой глюк. Ножку Rx (примник) действительно можно переназначить на любую, а вот ножка Tx (передатчика) может быть только на PortB.3. Если переназначить на любую другую — передатчик сам по себе все время шлет мусор. Настройки портов правильные. подтяжки тоже переназначил правильно. Проверено на 3 Тиньках. Будьте добры, проверьте кто-нибудь еще и отпишитесь.

  22. GetChiper Автор записи

    Да, наверное ничего особо и править не нужно. у тини13 и так все по минимуму у других МК должно все быть.

  23. Gnusmas

    Доброго времени суток. Подскажите, а что нужно переделать в исходнике, чтобы этот програмный юарт заработал на AtTiny26 ? Просто есть несколько таких контроллеров, и одним из их минусов по сравнению с Тини 2313 как раз есть отсутстве аппаратного юарта. Прикручивание програмного юарта, позволило бы мне получить сбалансированный 20 ногий контроллер с ацп, это моя мечта.

  24. mmavka

    Если интересно…
    Переделал проект в CVAVR для того что-бы можно было устанавливать Tx и Rx на произвольные ножки

  25. WingoPavel

    набросал небольшую програмку на тот случай,если нажно выводить текст через uart,генерирует код,выдающий нужные байты.
    Генерирует код вроде

    Tx_Byte=0x4B;
    putbyte(Tx_Byte);
    delay_ms(50);

    Tx_Byte=0x4C;
    putbyte(Tx_Byte);
    delay_ms(50);

    Написано на Hiasm,программа,в принципе,примитивная,но свои цели выполняет.
    Скриншоты:
    http://s017.radikal.ru/i438/1305/0f/3bba71b8e987.jpg
    http://i065.radikal.ru/1305/28/8b9e9b3cbf42.png
    Ссылка
    http://zalil.ru/34535561

  26. GetChiper Автор записи

    Да, есть такое дело. Молодой был, глупый… ))) Разрешите не править — пусть будет напоминанием о том что нужно всегда работать над собой.

  27. Lex.org100h

    Уважаемый Евгений. Прошу прощения, но у Вас есть некоторая неточность в написании «Baund_Rate». Оно пишется как Baud (бод — бит/с). Такая ошибка обнаружена в статье и в исходниках.

  28. ivdor

    @GetChiper
    Прошу прощения, если неясно выразился 🙂 С этим вопросом разобрался сам.
    Просто делал как и в 1 комментарии — сигнал от уарта дальше надо послать через RS-485. А их передатчики (SP485, ADM3483) для передачи требуют поднимать одну из ног (разрешение передачи).

    И вроде решил вторую возникшую проблему. С обработкой 6 переданных байтов 🙂

    Большое спасибо за программный уарт !

  29. GetChiper Автор записи

    Чего-то я не очень понял.

  30. ivdor

    Здравствуйте. Подскажите такой вопрос:
    делаю реализацию уарта дальше на рс-485. Так вот, при передаче должна одна нога подниматься на +, в конце, соответственно, на землю.

    Так вот — включить то сразу понятно — в функцию putbyte добавляю к примеру PORTB.2=1
    А вот как лучше опустить ?
    У меня только такой вариант в голову приходит, потому что ассемблер (да и тот на 8051) начисто забыл (

    // Timer 0 output compare B interrupt service routine
    interrupt [TIM0_COMPB] void timer0_compb_isr(void)
    {
    // Обработка принятых бит по линии RxD
    #asm
    …….
    pop r17
    pop r16

    #endasm
    if (Tx_Count==11) PORTB.2=0;
    }

  31. tonich

    И еще я не понял зачем RSTDISBL fuse сетить нужно было ?

  32. tonich

    Так номинально то всё крутиться, просто мусор лезет вместо данных =(

  33. GetChiper Автор записи

    Ух, сложно сказать… Запустите отладку и прощелкайте программы пошагово, может заметите ошибки.

  34. tonich

    Спасибо за Вашу работу!
    Помогите разобраться с адаптацией под Atiny25!

    TIMSK0=0x0C на TIMSK=0x18 поменял.
    частоту 8Mhz
    Baudrate поварьировал.
    но принимает мусор…

    не подскажите, что еще я пропустил ?

  35. GetChiper Автор записи

    В Си сделана вставка в асме, менять что-то будет проблемно. Может оставить как есть?

  36. mmavka

    @GetChiper
    Что то все равно не до конца понял что сконфить?
    Если дефайн, то он не на что не влияет.
    По поводу Tx вроде понял, там в асме нужно менять. а вот по поводу Rx не смог разобраться. Помогите плз.

  37. GetChiper Автор записи

    Если Вы про AB, то поменять в таблице переменных биты для переменных RxD и TxD. Да еще подтянуть RxD к 1, и TxD сконфигурировать на выход.

  38. mmavka

    Подскажите как поменять ножки Rx и Tx на другие! что то не понял как это сделать.
    Спасибо!

  39. anatoliy

    GetChiper :
    Насчет Modbus RTU – не знаю – довольно узкое применение, сложная реализация. Я так думаю, лучше пользоваться готовыми библиотеками от фирм-производителей компонентов использующие этот протокол. И уж конечно на на тиньках – не влезет код.

    Влезет ещё как влезет! Вопрос только где резать ужу хвост 😀 Т.e. какую функциональность нужно обеспечить.

  40. GetChiper Автор записи

    LCD обязательно будет. Не обещаю что скоро, но обещаю, что обязательно. Очень мне нравятся экранчики от мобилок — это просто находка для домашнего радиолюбительства — доступно, дешево и компактно. Хочется сделать что-то на них.

  41. don

    А вывод на LCD дисплей не планировали? Тем более, что видел трех-проводное управление на сдвиговом регистре, но делаю первые шаги с контроллерами и мне проще на рнр писать, чем на си, тем более с «микро» тараканами 🙂
    Просто было бы гениально — клава от старого телефона, ЛСД от какого-нить калькулятора и дитенку кассовый аппарат, что пикает и выводит циферки, как настоящий. Засунул в китайскую игрушку и забыл про дитятю 🙂

  42. GetChiper Автор записи

    Возможно отличия в версиях кодевижена…

  43. don

    Поковырялся в Даташите на тиньку13, в Cavr и переписал 43-ю строку так:
    interrupt [3] void pin_change_isr(void)
    После этого Кодевижн проглотил всё без ошибок 🙂

  44. don

    Попытался откомпилировать выложженный проект в Codevision 2.05 выдает полный бред:
    Error: usart.c(43): bad interrupt vector
    Error: usart.c(237): undefined symbol ‘TIMSK0’
    В чем грабли?

  45. GetChiper Автор записи

    А я и не использую PCINT0. Для приема используется прерывание по PCINT4.

    Да Вы правы, ножки могут быть любые.

  46. VoJak

    Как Вы используете прерывание PCINT0, если на эту ногу ничего не нацеплено? Еще не вникал в код, но судя по всему можно ножки приема-передачи назначать самому для удобства разводки платы?

  47. foxit

    Ждем с нетерпением простой сетевой интерфейс!

  48. GetChiper Автор записи

    Касательно простого сетевого интерфейса у меня есть определенные задумки на этот счет и в скором времени мы все вместе подумаем над его реализацией.

  49. GetChiper Автор записи

    RS-485 и есть UART только уровни сигнала -12 +12 плюс дифференциальная передача сигнала. В общем нужно просто прицепить на UART ATtiny13 микросхему-преобразователь и получится RS-485. Хотя не знаю — стоит ли оно того?
    Насчет Modbus RTU — не знаю — довольно узкое применение, сложная реализация. Я так думаю, лучше пользоваться готовыми библиотеками от фирм-производителей компонентов использующие этот протокол. И уж конечно на на тиньках — не влезет код.

  50. alexandershahbazov

    Можно ли к интерфейсам добавить про RS-485 с самым насколько можно
    простым практическим применением .
    Потом про Modbus RTU , но опять же с практической стороны и по возможности с самым простым применением .

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