Передача данных через UART для мк AVR
Автор DI HALT источник http://easyelectronics.ru
Почти каждый микроконтроллер имеет на борту универсальный последовательный интрфейс - UART. AVR тут не исключение и поддерживает этот протокол в полном обьеме полностью аппаратно. По структуре это обычный асинхронный последовательный протокол, то есть передающая сторона по очереди выдает в линию 0 и 1, а принимающая отслеживает их и запоминает. Синхронизация идет по времени — приемник и передатчик заранее договариваются о том на какой частоте будет идти обмен. Протокол
Вначале передатчик бросает линию в низкий уровень - это старт бит. Почуяв что линия просела, приемник выжидает интервал Т1 и считывает первый бит, потом через интервалы Т2 выковыриваются остальные биты. Последний бит это стоп бит. Говорящий о том, что передача этого байта завершена. Это в самом простом случае.
В конце байта, перед стоп битом, может быть и бит четности. Который получается если поксорить между собой все биты, для контроля качества передачи. Также может быть два стопа, опять же для надежности. Битов может быть не 8, а 9. О всех этих параметрах договариваются на берегу, до начала передачи. Самым же популярным является 8 бит, один старт один стоп, без четности.
Причем с самим протоколом можно не заморачиваться - все реализовано аппаратно. Разве что захочется завести второй UART, тогда придется делать его программно.
По такому же протоколу работает COM порт компьютера, разница лишь в разнице напряжений, поэтому именно этот протокол я буду использовать для связи микроконтроллера с компом. Для преобразования напряжений используется RS232-TTL конвертер.
Аппаратная часть
Ну тут все просто, соединяем крест-накрест приемник с передатчиком и готово.
Внутри же находится вначале накопительный сдвиговый регистр, в котором происходит сборка байта из битов и регистры данных UDR, куда этот бит передается. Такая структура исключает возможность считать не до конца полученный байт.
Буфер приема состоит из двух байт, что позволяет ему держать два в уме и один еще засасывать в сдвиговый регистр.
Использование UART’a
У AVR есть регистр UDR это UART Data Register. На самом деле это два разных регистра, но имеют один адрес. Просто на запись попадает в один (регистр передатчика), а на чтение берет из другого (регистр приемника). Таким образом достаточно просто пихать данные в этот регистр и они улетят приемнику, и, наоборот, считывать их оттуда по приходу.
О том, что байт пришел в регистр UDR нам скажет прерывание по завершению приема, которое вызывается сразу же, как приемник засосет в себя все биты (обычно 8, но может быть и 9, в зависимости от настройки).
Поскольку передача идет довольно медленно, то бездумно пихать данные в регистр UDR нельзя - нужно дождаться окончания передачи предыдущего байта. О том, что UDR пуст и готов к приему нового байта сигнализирует бит UDRE, он же вызовет аппаратное прерывание по опустошению буфера.
Так что постоянно следить за UART вручную не нужно, все обслуживание можно повесить на прерывания и он сам будет все делать. Можно в памяти организовать буфер и туда тупо пихать данные, а на прерывании по опустошению UDR следить за тем есть ли что в буфере и если есть - отправлять.
А по приему, не тупить, а также, по прерыванию, пихать данные в ОЗУ, но уже в буфер приема, откуда их считает уже программа.
Настройка UART
Все настройки приемопередатчика хранятся в регистра конфигурации. Это UCSRA, UCSRB и UCSRС. А скорость задается в паре UBBRH:UBBRL.
Досконально все расписывать не буду — на это есть даташит. Напишу лишь то, что жизненно необходимо.
Регистр UCSRA
Тут нам интересны только биты RXC и TXC это флаги завершения приема и передачи, соответственно. RXC встанет когда непрочитанный байт вылезет в регистр UDR, а TXC встает когда последний стоп-бит прошел, а новое значение в UDR не поступило. Т.е. после прохода всех байтов.
Также одновременно с этими флагами вызывается прерывание (если оно было разрешено). Сбрасываются они аппаратно — принимающий после чтения из регистра UDR, передающий при переходе на прерывание, либо программно (чтобы сбросить флаг программно в него надо записать 1)
Биты UDRE сигнализирует о том, что регистр UDR приемника пуст и в него можно пихать новый байт. Сбрасывается он аппаратно после засылки в UDR какого либо байта. Также генерируется прерывание “Регистр пуст”
Бит U2X - это бит удвоения скорости при работе в ассинхронном режиме. Его надо учитывать при расчете значения в UBBRH:UBBRL
Регистр UCSRB
Тут в первую очередь это биты RXEN и TXEN — разрешение приема и передачи. Стоит их сбросить как выводы UART тут же становятся обычными ножками I/O.
Биты RXCIE, TXCIE, UDRIE разрешают прерывания по завершению приема, передачи и опустошении буфера передачи UDR.
Регистр UCSRC
Самый прикол тут это бит селектора URSEL дело в том, что по неизвестной причине создаетели решили сэкономить байт адреса и разместили регистры UCSRC и UBRRH в одной адресном пространстве. А как же определять куда записать? А по старшему биту! Поясню на примере. Если мы записываем число у которого седьмой бит равен 1, то оно попадет в UCSRC, а если 0 то UBRRH.
Остальные биты задают число стопов, наличие и тип контроля четности. Если оставить все по дефолту то будет стандартный режим. Надо только выставить формат посылки. Делается это битами UCSZ0, UCSZ1 и UCSZ2 (этот бит тусуется в регистре UCSRB). Для стандартной 8ми битной посылки туда надо записать две единички.
Скорость обмена.
Тут все зависит от пары UBBRx
Вычисляется требуемое значение по формуле:
UBBR=XTAL/(16*baudrate)-1 для U2X=0
UBBR=XTAL/(8*baudrate)-1 для U2X=1
Где:
XTAL - рабочая тактовая частота контроллера.
baudrate - требуемая скорость (я люблю 9600 )
Пример кода:
Для начала инициализация UART
.equ XTAL = 8000000
.equ baudrate = 9600
.equ bauddivider = XTAL/(16*baudrate)-1
uart_init: LDI R16, low(bauddivider)
OUT UBRRL,R16
LDI R16, high(bauddivider)
OUT UBRRH,R16
LDI R16,0
OUT UCSRA, R16
LDI R16, (1< ; Прерывания запрещены, прием-передача разрешен.
OUT UCSRB, R16
LDI R16, (1< ; Формат кадра - 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор
OUT UCSRC, R16
RET
Теперь посылка байта:
RCALL uart_init ; вызываем нашу процедуру инициализации.
LDI R16,'E' ; загоняем в регистр код буквы «E»
RCALL uart_snt
; бла бла бла куча какого нибудь другого кода.
; Процедура отправки байта
uart_snt: SBIS UCSRA,UDRE ; Пропуск если нет флага готовности
RJMP uart_snt ; ждем готовности - флага UDRE
OUT UDR, R16 ; шлем байт!
RET
Ну, а для приема надо сделать обработчик прерывания, который считает байт из UDR и решит что с ним делать дальше.
Ошибки
К сожалению мир наш не идеален, поэтому возможны ошибки при приеме. За них отвечают флаги в регистре UCSRA
FE - ошибка кадрирования. Т.е. мы ждали стоп бит, а пришел 0.
OR - переполнение буфера. То есть данные лезут и лезут, а из UDR мы их забирать не успеваем.
PE - не совпал контроль четности.