Сегодня почти каждый приличный микроконтроллер, для связи с внешним миром, имеет на борту последовательный интерфейс передачи данных UART/USART (а некоторые МК — даже по два и более). Это касается и семейства AVR ATmega. В этой статье попробуем с ним разобраться.
UART или USART?
Не буду здесь расписывать много теории, т.к. много раз уже всё расписано и легко гуглится, но для начала определимся, с чем мы всё-таки имеем дело.
UART — от англ. Universal Asynchronous Receiver-Transmitter — Универсальный Асинхронный Приёмопередатчик. В простейшем случае имеет линии: RX (receiver — приём данных), TX (transmitter — передача данных) и общий провод.
USART — от англ. Universal Synchronous/Asynchronous Receiver-Transmitter — Универсальный Синхронный/Асинхронный Приёмопередатчик. Кроме линий передачи данных, может иметь отдельную линию для сигнала синхронизации. Главное отличие USART в том, что он может работать как в синхронном, так и в асинхронном режимах.
В синхронном режиме, для синхронизации приёмного и передающего устройств, с передающей стороны могут посылаться сигналы синхронизации по линии данных, либо может использоваться отдельная линия синхронизации. Синхронный режим обычно используется в специфических случаях (например, если необходимо обеспечить высокую скорость передачи данных).
В асинхронном режиме — синхронизация осуществляется только по стартовым битам, без каких-либо дополнительных сигналов синхронизации, поэтому, для успешной передачи данных, приёмник и передатчик заранее должны быть настроены на одинаковые скорость обмена и формат пакетов. Т.к. реализация асинхронного режима проще, то в подавляющем большинстве случаев, для простых задач, используют именно его.
Фактически, МК семейства ATmega имеют на борту USART интерфейс. Но в данном случае мы будем его рассматривать только в асинхронном режиме, т.е. как обычный UART.
Протокол обмена USART
Пакет данных USART состоит из:
- стартового бита;
- 5, 6, 7, 8 или 9 бит данных;
- бита контроля чётности (опционально);
- 1 или 2 стоп-бит.
Здесь:
- St — стартовый бит (логический 0);
- 0-8 — биты данных;
- P — бит контроля чётности — может быть проверка на чётность (Even), нечётность (Odd), или отсутствовать;
- Sp — стоп биты (логическая 1);
- IDLE — нет передаваемых данных (логическая 1).
Формат передачи данных устанавливается конфигурационными битами в регистрах UCSRB и UCSRC.
Бит контроля чётности (Even) вычисляется путём исключающего ИЛИ всех бит данных, для контроля нечётности (Odd) — полученное значение инвертируется.
Описание регистров
Рассматривать регистры конфигурации и контроля состояния USART будем на примере МК Atmega8. Во всах остальнах МК AVR — всё очень похоже, только названия и расположение бит и регистров может незначительно отличаться.
Регистр данных UDR
Буферы принимаемых и передаваемых данных тут расположены в одном адресном пространстве. При чтении из регистра UDR — данные считываются из буфера приёма, при записи в него — данные записываются в буфер отправки, соответственно. При отправке пакетов в формате 5, 6, или 7 бит данных — старшие, незначащие биты игнорируются передатчиком, а при получении — приёмник заполняет их нулями.
Запись данных в буфер передачи возможна только когда установлен флаг UDRE в регистре UCSRA, в противном случае — записанные данные будут проигнорированы передатчиком. После записи в буфер передачи, данные загружаются в передающий сдвиговый регистр и из него — на шину TX (пин TxD).
Регистр UCSRA
Bit 7 – RXC: USART Receive Complete
Этот бит (флаг) устанавливается, когда в приемном буфере есть непрочитанные данные, и очищается — когда буфер приема пуст (не содержит непрочитанных данных). Если приемник отключен, содержимое приёмного буфера сбрасывается, следовательно, бит RXC обнуляется. Флаг RXC может использоваться для генерирования прерывания по завершению приёма (см. описание бита RXCIE).
Bit 6 – TXC: USART Transmit Complete
Этот бит (флаг) устанавливается, когда все данные из сдвигового регистра были сдвинуты, а в буфере передачи (UDR) ещё нет новых данных. Флаг TXC автоматически сбрасывается при выполнении прерывания по завершению передачи, или может быть очищен путем записи в него единицы. Флаг TXC может использоваться для генерирования прерывания по завершению передачи (см. описание бита TXCIE).
Bit 5 – UDRE: USART Data Register Empty
Флаг UDRE указывает, готов ли буфер передачи (UDR) для записи в него новых данных. Если флаг UDRE установлен — значит буфер пуст и готов к записи. Флаг UDRE может использоваться для генерирования прерывания по опустошению регистра данных (см. описание бита UDRIE).
Bit 4 – FE: Frame Error
Этот бит устанавливается, если в очередном полученном пакете в приемном буфере обнаружена ошибка формата данных (то есть, когда первый стоп-бит равен нулю). Этот бит действителен до тех пор, пока не будет считан приемный буфер (UDR). Бит FE равен нулю, когда стоп-бит полученных данных равен единице. При записи в UCSRA, в этот бит всегда следует записывать ноль.
Bit 3 – DOR: Data OverRun
Этот бит устанавливается, если обнаружено переполнение приёмного буфера. т.е. когда приемный буфер заполнен (два символа), в приёмном сдвиговом регистре ожидает ещё один принятый символ, и обнаруживается новый стартовый бит. Этот бит действителен до тех пор, пока не будет считан приемный буфер (UDR). При записи в UCSRA, в этот бит всегда следует записывать ноль.
Bit 2 – PE: Parity Error
Этот бит устанавливается, если в очередном полученном пакете в приемном буфере обнаружена ошибка чётности, и проверка четности в данный момент включена (UPM1 = 1). Этот бит действителен до тех пор, пока не будет считан приемный буфер (UDR). При записи в UCSRA, в этот бит всегда следует записывать ноль.
Bit 1 – U2X: Double the USART transmission speed
При записи единицы в этот бит, делитель скорости передачи данных уменьшается с 16 до 8, таким образом, увеличивая вдвое скорость обмена в асинхронном режиме. Этот бит используется только в асинхронном режиме. В синхронном режиме — в этот бит должен быть записан ноль.
Bit 0 – MPCM: Multi-processor Communication Mode
Этот бит включает режим многопроцессорной связи. Когда бит MPCM установлен в единицу, все входящие пакеты, которые не содержат адресную информацию, будут игнорироваться приёмником USART. Передатчик — не зависит от настройки MPCM. (Подробнее об организации многопроцессорной связи см. в даташите.)
Регистр UCSRB
Bit 7 – RXCIE: RX Complete Interrupt Enable
Запись единицы в этот бит — включает прерывание по завершению приёма (по флагу RXC). Прерывание будет сгенерировано, если установлены: бит RXCIE, бит глобального разрешения прерываний I в SREG, и бит RXC в UCSRA.
Bit 6 – TXCIE: TX Complete Interrupt Enable
Запись единицы в этот бит — включает прерывание по завершению отправки (по флагу TXC). Прерывание будет сгенерировано, если установлены: бит TXCIE, бит глобального разрешения прерываний I в SREG, и бит TXC в UCSRA.
Bit 5 – UDRIE: USART Data Register Empty Interrupt Enable
Запись единицы в этот бит — включает прерывание по опустошению буфера данных (по флагу UDRE). Прерывание будет сгенерировано, если установлены: бит UDRIE, бит глобального разрешения прерываний I в SREG, и бит UDRE в UCSRA.
Bit 4 – RXEN: Receiver Enable
Запись единицы в этот бит — включает приемник USART. Приемник переопределит нормальную работу порта для пина RxD. При отключении приемника — происходит очистка приемного буфера, сброс флагов FE, DOR и PE.
Bit 3 – TXEN: Transmitter Enable
Запись единицы в этот бит — включает передатчик USART. Приемник переопределит нормальную работу порта для пина TxD. Отключение передатчика не вступит в силу до тех пор, пока не будут завершены текущие и ожидающие передачи (т.е. пока не будут переданы все данные из сдвигового регистра и буфера передачи).
Bit 2 – UCSZ2: Character Size
Бит UCSZ2, совместно с битами UCSZ1:0 регистра UCSRC (см. ниже) устанавливают количество бит данных в пакете (размер посылки) для приёмника и передатчика.
Bit 1 – RXB8: Receive Data Bit 8
В бит RXB8 записывается значение старшего бита (8) принятого пакета, при установленном размере посылки в 9 бит. Значение этого бита должно быть считано прежде, чем будут считаны младшие биты из приёмного буфера UDR.
Bit 0 – TXB8: Transmit Data Bit 8
В бит TXB8 записывается значение старшего бита (8) отправляемого пакета, при установленном размере посылки в 9 бит. Значение этого бита должно быть записано прежде, чем будут записаны младшие биты в буфер отправки UDR.
Регистр UCSRC
Bit 7 – URSEL: Register Select
Вот этот бит тут самый интересный. Дело в том, что Atmega8 и ещё некоторые подобные МК — несколько ущербны в плане адресного пространства. Так, регистры UCSRC и UBRRH тут находятся по одному адресу. И чтобы определить, в какой регистр мы будем записывать данные, как раз и используется бит URSEL. Для записи в UCSRC — в бит URSEL должна быть записана единица, для записи в UBRRH — соответственно, ноль. При чтении из UCSRC — бит URSEL возвращает единицу.
Считывание из данных регистров происходит следующим образом: при первом обращении будет возвращено значение регистра UBRRH, а при повторном обращении в следующем цикле — возвращается значение UCSRC. Таким образом, чтобы считать значение UCSRC, нужно дважды подряд произвести из него считывание. Причём, если между двумя считываниями произойдёт прерывание, то повторное считывание может и не сработать должным образом (считается опять содержимое UBRRH). Поэтому, желательно отключать прерывания во время считывания UCSRC. Надо заметить, что далеко не во всех АТмегах есть такой геморрой с битом URSEL и двойным считыванием. Например, в atmega168 и подобных МК — можно, не заморачиваясь, независимо обращаться к любому из этих регистров, как для чтения, так и для записи.
Bit 6 – UMSEL: USART Mode Select
Установкой этого бита выбирается режим обмена: 0 — асинхронный, 1 — синхронный.
Bit 5:4 – UPM1:0: Parity Mode
Установкой битов UPM1:0 выбирается режим проверки чётности:
- 00 — не проверять;
- 01 — не используется (в резерве);
- 10 — проверка на чётность (Even);
- 11 — проверка на нечётность (Odd).
Bit 3 – USBS: Stop Bit Select
Выбор количества стоп-бит: 0 — 1-бит, 1 — 2-бита.
Bit 2:1 – UCSZ1:0: Character Size
Биты UCSZ1:0, совместно с битом UCSZ2 регистра UCSRB устанавливают количество бит данных в пакете (размер посылки) для приёмника и передатчика:
- 000 — 5-бит;
- 001 — 6-бит;
- 010 — 7-бит;
- 011 — 8-бит;
- 100 — не используется (в резерве);
- 101 — не используется (в резерве);
- 110 — не используется (в резерве);
- 111 — 9-бит.
Bit 0 – UCPOL: Clock Polarity
Этот бит используется только в синхронном режиме и определяет, по какому фронту тактирующего сигнала будет производиться отправка/приём данных. В асинхронном режиме — в бит UCPOL нужно записать ноль.
Регистры UBRRL и UBRRH
Данные регистры отвечают за настройку скорости обмена:
Bit 15 – URSEL: Register Select
Значение бита URSEL определяет, в какой регистр мы будем записывать данные: UBRRH или UCSRC. Для записи в UCSRC — в бит URSEL должна быть записана единица, для записи в UBRRH — соответственно, ноль (подробнее — см. выше описание бита URSEL регистра UCSRC). При чтении из UBRRH — бит URSEL возвращает ноль.
Bit 14:12 – Reserved Bits
Биты 14…12 — не используются.
Bit 11:0 – UBRR11:0: USART Baud Rate Register
Биты с 11 по 0 — составляют 12-битный регистр, устанавливающий скорость обмена USART. Значение UBRR определяется следующим образом:
UBRR = Fcpu/(Baud х 16)-1
Или, в режиме удвоенной скорости (U2X = 1):
UBRR = Fcpu/(Baud х 8)-1
где:
- Fcpu — частота тактирования МК;
- Baud — требуемая частота обмена.
Пример использования
Ну и в завершение, приведу несколько функций, нагдядно иллюстрирующих приёмы работы с USART:
#include <stdint.h>
#include <avr/io.h>
// Инициализация
void USART_init(void)
{
uint16_t ubrr; // значение UBRR
ubrr = F_CPU/9600/16-1; // скорость обмена 9600 бит/с
UBRRH = (uint8_t) (ubrr >> 8); // старшие биты UBRR
UBRRL = (uint8_t) ubrr; // младшие биты UBRR
UCSRB = (1<<RXEN)|(1<<TXEN); // включаем приёмник и передатчик
UCSRC = (1<<URSEL)|(3<<UCSZ0); // асинхронный режим, размер посылки 8 бит, проверка чётности отключена, 1 стоп-бит
}
// Отправка ASCII символа
void USART_sendChar(char character)
{
while( !((UCSRA >> UDRE) & 1) ) ; // ожидаем очистки буфера передачи
UDR = character; // помещаем данные в буфер для отправки
}
// Отправка единичного HEX символа
void USART_sendHex(uint8_t number)
{
if ( number < 10 )
USART_sendChar(48 + (number & 15)); // отправка [0-9]
else if ( number < 16 )
USART_sendChar(55 + (number & 15)); // отправка [A-F]
}
// Отправка байта в HEX формате
void USART_sendByteHex(uint8_t number)
{
USART_sendHex(number >> 4); // старшие 4 бита
USART_sendHex(number & 15); // младшие 4 бита
}
// Отправка строки
void USART_sendLine(char *string)
{
while ( *string )
{
USART_sendChar(*string); // посимвольно отправляем строку
string++;
}
}
// Приём символа
char USART_receiveChar(void)
{
return ( (UCSRA >> RXC) & 1 ) ? UDR : 0; // возвращаем значение буфера приёма
}