Главная » Delphi » Приемы программирования UART в микроконтроллерах на примере AVR

0

Задача этого раздела— показать, как можно разными способами запрограммировать микроконтроллер (МК) для асинхронного обмена данными с компьютером. Мы ограничимся микроконтроллерами AVR фирмы Atmel, т. к. для остальных их разновидностей (и даже при прямом программировании UART в самом компьютере) методика похожая. Мы рассмотрим программирование для семейства AVR Classic, некоторые особенности семейства AVR Mega будут рассмотрены параллельно. Всех подробностей я, разумеется, изложить не могу, так что для полного понимания придется заглянуть в руководство по AVR-контроллерам.

За портом UART в микроконтроллерах AVR зарезервировано три аппаратных прерывания и несколько регистров портов ввода/вывода, главными из которых являются два — регистр данных udr и регистр управления usr (ucsra для Mega). Регистры ubrr (ubrrl и ubrrh для Mega) и ucr (ucsrb и ucsrc) служат для установки скорости и режима обмена.

Начнем с установки скорости (9600) и режима (8п1). Для этого нужно выполнить такую процедуру (temp—? любой регистр общего назначения от г1б

до Г31):

ldi temp,25 /скорость передачи 9600 при 4 МГц out UBRR,temp ;устанавливаем ldi temp, (1<<RXENI1<<TXENI1<<RXB8I1<<TXB8) out UCR,temp ;разрешение приема/передачи 8 битов

Здесь мы выбрали частоту генератора МК равной 4 МГц. Смысл первого и второго оператора — задание скорости обмена через задание делителя частоты кварца. Этот делитель выбирается по таблице, имеющейся в каждом руководстве по AVR, или рассчитывается по формуле BAUD = fpe]/16(UBRR + 1). Для семейства Mega имеет смысл использовать более высокие частоты, и там процедура несколько усложняется, потому что скорость можно регулировать более точно:

ldi temp,103 ;9600 при 16 МГц out UBRRL, temp

; Enable Receiver and Transmitter ldi temp, (1«RXEN) I <1«TXEN) out UCSRB,temp

; Set frame format: 8data, 1 stop bit ldi temp, <1<<URSEL)I <3<<UCSZ0) out UCSRC,temp

При частоте кварца 4 МГц мы с приемлемой точностью можем получить скорости обмена не более 28 800 бод, правда, при выборе специального кварца 3.6864 МГц можно получить с нулевой ошибкой весь набор скоростей вплоть до 115 200. Для получения скоростей передачи выше указанных (стандартно СОМ-порт позволяет установить скорости, как указано ранее, до 256 Кбод) надо увеличивать частоту и подбирать под нее коэффициент деления. Так. при кварце 8 МГц и коэффициенте деления (UBRR) равном 1, мы получим скорость 250 000, что отличается от стандартных 256 000 на приемлемые 2,4%.

Теперь собственно о приеме/передаче. Если контроллер не перегружен, то хорошо работает упрощенный алгоритм, который вообще не использует прерывания. Он состоит в следующем: в начале программы (например, после перечисления векторов прерываний) вы записываете две коротких процедуры:

Out_com: /посылка Оайта из temp с ожиданием готовности

sbis USR,UDRE ;ждем готовности буфера передатчика rjmp out_com

out UDR,temp ;собственно посылка бвйта ret ;возврат из процедуры Out_com

In_com: ;прием байта в temp с ожиданием готовности

sbis USR,RXC ;ждем готовности буфера приемника rjmp in_com

in temp,UDR ;собственно прием байтов ret ;возврат из процедуры In_com

Для семейства Mega надо заменить все usr на ucsra. Обращение к процедуре in com при этом вставляется в пустой цикл в конце программы:

Cykle:

rcall In_com

<анализируем полученный в temp байт, и что-то с ним делаем, например, посылаем ответ через процедуру Out_com>

rjmp Cykle ;зацикливаем программу

При таком способе контроллер большую часть времени ожидвет приема, выполняя проверку регистра udr (в процедуре in com), этот процесс прерывается только на время выполнения настоящих прерываний. Прерывания все равно должны выполняться много быстрее, чем байт, в случае, если он пришел.

успевает в UDR смениться следующим, так что мы ничего особо не потеряем. А процедура посылки out ccm сама по себе может выполняться долго (если посылать несколько байтов подряд), но также в основном будет заключаться в том, что контроллер будет ожидать очищения udr, и т. к. это не прерывание, то процедура в любой момент может быть прервана настоящим прерыванием, и мы опять же ничего не теряем. Но чтобы ничего действительно не потерять, при таком способе нужно быть очень внимательным: так, нужно следить за использованием temp. Правда, если мы будем применять процедуру out com внутри процедуры прерывания, куда другое прерывание "влезть" не может, то temp меняться не будет, но тогда контроллер будет терять время на ожидание, потому лучше, кроме разве что расстановки контрольных точек при отладке программы, так не поступать.

С использованием прерываний процедура усложняется, но зато все будет послано и передано гарантированно и без потерь времени (в случае, конечно, если вычислительной мощности контроллера хватает). Мы покажем принцип организации такой процедуры для более-менее общей задачи: контроллер, как и ранее, все время ожидает команды от компьютера, и при ее получении должен послать в ответ некоторое количество байтов.

Сначала вы инициализируете прерывание "прием закончен" (для чего надо установить бит rxcie в регистре ucr). Возникновение этого прерывания означает, что в регистре данных udr имеется принятый байт. Процедура обработки этого прерывания тогда выглядит так:

UART_RXC:

in temp,UDR /принятый байт – в переменной temp

cbi UCR,RXCIE /запрещаем прерывание "прием закончен"

«анализируем команду, если это не та команда — опять разрешаем прерывание "прием закончен" и выходим из процедуры по метке END_R: sbi UCR,RXCIE rjmp END_R

В противном случае готовим данные, самый первый посылаемый байт должен быть в переменной temp>

sbi UCR,UDRIE /разрешение прерывания "регистр данных пуст"

END_R: reti

Далее у нас почти немедленно возникает прерывание "регистр данных пуст".

Обработчик этого прерывания состоит в том, что мы посылаем байт, содержащийся в переменной temp, и готовим следующие данные:

UART_DRE:

out UDR,temp ;посылаем байт

cbi UCR,UDRIE ;запрещаем прерывание "регистр данных пуст"

«готовим данные, следующий байт – в temp. Если же был отправлен последний нужный байт, то опять разрешаем прерывание "прием закончен" и далее rjmp на метку END_, иначе выполняем следующий оператор:>

sbi UCR,UDRIE /разрешаем прерывание "регистр данных пуст"

END_: reti

Обратим еще раз внимание на то, что переменная temp не должна в промежутках между прерываниями использоваться еще где-то, в противном случае ее надо сохранять, например, в стеке, но удобнее все же отвести для этого дела специальный регистр.

Сравните данные методы с процедурами для Windows в главе 20, и вы, несомненно, найдете определенное идеологическое сходство.

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии