Программирование AVR Передача данных через UART (RS-232) Thu, March 28 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Передача данных через UART (RS-232) Печать
Добавил(а) microsin   

В статье описан аппаратный UART, его настройка и применение.

[Использование UART]

У AVR есть регистр UDR (UART Data Register). На самом деле это два разных регистра, но имеют один адрес. При записи данные попадают в один регистр (регистр передатчика), а при чтении данные вычитываются из другого регистра (регистр приемника).

Когда байт пришел в регистр UDR, может сработать прерывание по завершению приема (если конечно оно разрешено), которое вызывается сразу же, как только приемник получил все биты данных (обычно 8, но может быть и 9, в зависимости от настройки UART).

Поскольку передача во времени происходит не мгновенно (биты выходят из передатчика последовательно, друг за другом), то бездумно пихать данные в регистр UDR нельзя - нужно дождаться окончания передачи предыдущего байта. О том, что UDR пуст и готов к приему нового байта, сигнализирует бит UDRE, он же вызовет аппаратное прерывание по опустошению буфера.

Так что постоянно следить за UART вручную не нужно, все обслуживание можно повесить на прерывания, и он сам будет все делать. Можно в памяти организовать кольцевой буфер, и туда тупо пихать данные, а на прерывании по опустошению UDR следить - есть ли что в буфере, и если есть - отправлять. При приеме принцип аналогичный - по прерыванию записывать данные в кольцевой буфер SRAM [5], а фоновая программа (прокручивающаяся в цикле main) должна этот буфер отслеживать и обрабатывать.

[Настройка UART]

Все настройки приемопередатчика хранятся в регистрах конфигурации. Это UCSRA, UCSRB и UCSRС. Скорость задается в паре регистров UBBRH:UBBRL.

Досконально все расписывать не буду — на это есть даташит. Напишу лишь то, что жизненно необходимо.

Регистр UCSRA. Тут нам интересны только биты RXC и TXC, это флаги завершения приема и передачи соответственно. RXC устанавливается, когда непрочитанный байт находится в регистре UDR (заполнение регистра приемника), а TXC устанавливается, когда последний стоп-бит прошел, а новое значение в UDR не поступило (опустошение регистра передатчика). Также одновременно с этими флагами вызывается прерывание (если оно было разрешено). Сбрасываются биты RXC и TXC аппаратно — принимающий после чтения из регистра UDR, передающий при переходе на прерывание, либо программно (чтобы сбросить флаг программно, в него надо записать 1).

Бит UDRE сигнализирует о том, что регистр UDR передатчика пуст и в него можно записать новый байт. Сбрасывается он аппаратно после засылки в UDR какого либо байта. При опустошении регистра передатчика может генерироваться прерывание.

Бит U2X - бит удвоения скорости при работе в асинхронном режиме. Его надо учитывать при расчете значения в UBBRH:UBBRL

Регистр UCSRB. В первую очередь это биты RXEN и TXEN — разрешение приема и передачи. Стоит их сбросить, как выводы UART тут же становятся обычными ножками I/O.

Биты RXCIE, TXCIE, UDRIE разрешают прерывания по завершению приема, передачи и опустошению буфера передачи UDR.

Регистр UCSRC. Внимание! В разных моделях AVR этот регистр может быть организован по-разному. Например, для ATmega8 старший бит этого регистра URSEL определяет, к какому регистру - UCSRC или UBRRH - будет осуществляться доступ. В ATmega32U4 старшие биты UCSRC определяют режим работы USART.

В общем случае регистр UCSRC задает количество стоп-битов, наличие бита четности и способ его проверки. Если оставить все по умолчанию, то получится стандартный режим - 1 стоп-бит, без бита четности. Надо только задать количество бит в посылке (от 5 до 9). Делается это битами UCSZ0, UCSZ1, UCSZ2 (бит UCSZ2 находится в регистре UCSRB). Для стандартного формата фрейма 8 бит нужно установить комбинацию UCSZ2 UCSZ1 UCSZ0 в значение 011.

[Скорость обмена]

Скорость передачи и приема задается в регистровой паре UBBRx. Вычисляется требуемое значение этой регистровой пары по формуле:

UBBR=XTAL/(16*baudrate)-1 для U2X=0
UBBR=XTAL/(8*baudrate)-1 для U2X=1

Где:

XTAL - рабочая тактовая частота контроллера.
baudrate - требуемая скорость бит/сек (baud rate, bit per second, bps).

[Пример кода на ассемблере]

Для начала инициализация 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 << RXEN)|(1 << TXEN)|(0 << RXCIE)|(0 << TXCIE)
; Прерывания запрещены, прием-передача разрешен.
      OUT UCSRB, R16
      LDI R16, (1 << URSEL)|(1 << UCSZ0)|(1 << UCSZ1)
; Формат кадра - 8 бит, пишем в регистр UCSRC, за это отвечает бит селектор
      OUT UCSRC, R16
      RET

Теперь посылка байта:

      RCALL uart_init     ; вызываем нашу процедуру инициализации.
      LDI                 ; загоняем в регистр код буквы «E»
uart_snt:
      SBIS UCSRA,UDRE
      RJMP uart_snt       ; ждем готовности - флага UDRE
      OUT UDR, R16        ; шлем байт!

Для приема надо сделать обработчик прерывания, который считает байт из UDR и решит, что с ним делать дальше.

[Ошибки]

К сожалению мир наш не идеален, поэтому возможны ошибки при приеме. За них отвечают флаги в регистре UCSRA.

FE - ошибка кадрирования. Т. е. ожидался стоп бит, а пришел 0.
OR - переполнение буфера. То есть данные поступают быстрее, чем программа успела их забрать из UDR.
PE - не совпал контроль четности.

[Работа с последовательным портом UART/USART AVR на языке C в AVR Studio]

Есть удобные библиотеки, которые позволяют настраивать асинхронный последовательный порт AVR на нужную скорость и параметры передачи/приема (наличие бита четности, количество бит в элементарной посылке, количество стоп-бит).

Исходники библиотек скачаны с сайта Osamu Tamura, проект AVR-CDC [4]. Можете также скачать готовый проект для AVR Studio, где хорошо видно, как библиотека используется (см. раздел "Ссылки").

Весь функционал сосредоточен в модулях uart.c, uart.h, uart-asm.S. Частота кварца должна быть задана в переменной F_CPU, тип процессора - MCU (см. файл Makefile). Настраивается порт простым вызовом (в этом примере скорость 115200 бит/сек, без четности, 1 стоп-бит, 8 бит данных): 

uartInit(115200, 0/*no parity*/, 1/*stopbits*/, 8/*databits*/); 

В проекте по ссылке 4 используется обработчик приема на ассемблере, но иногда проще его написать на C, и для обработки приема и передачи использовать кольцевые буферы. Например:

u8 rxbuf[RXBUF_SIZE];
u8 inrx, outrx;
u8 txbuf[TXBUF_SIZE];
u8 intx, outtx;
u8 rxtimeout = 0;

//обработчик прерывания приема USART
#if ((defined (__AVR_ATmega8__)) || (defined (__AVR_ATmega168__)))
ISR (SIG_USART_RECV)
#endif
#if (defined (__AVR_ATmega16__))
ISR (SIG_UART_RECV)
#endif
{
    rxbuf[inrx++] = UDR0;
    inrx &= RXBUF_MASK;
    rxtimeout = 0;
}

//подпрограмма определяет количество байт в буфере
u8 idxDiff (u8 idxIN, u8 idxOUT, u8 bufsize)
{
    if (idxIN >= idxOUT)
        return (idxIN - idxOUT);
    else
        return ((bufsize - idxOUT) + idxIN);
}

//Процедура отслеживает прием и его таймауты, и в зависимости от приема
// сбрасывает буфер либо переключает режим работы. Подпрограмма должна
// вызываться в бесконечном цикле main.
void usartPool (void)
{
    u8 rxcnt = idxDiff (inrx, outrx, RXBUF_SIZE);
    if (0 == rxcnt)
    {
        //в буфере ничего нет
        if (rxtimeout >= BLOCK_TIMEOUT)
        {
            //в буфере ничего нет давно
            ...
        }
    }
    else if (rxcnt < BLOCK_SIZE)
    {
        //в буфере что-то есть, но мало
        if (rxtimeout >= BYTE_TIMEOUT)
        {
            //прошла слишком большая пауза между байтами,
            // сбрасываем буфер приема
            outrx = inrx;
        }
    }
    else
    {
        //в буфере корректно приняты BLOCK_SIZE байт,
        // обрабатываем данные
        ...
    }
}

Передают байты, записывая их в регистр UDR0, предварительно проверяя флаг (UDRE), что предыдущий байт уже отправлен. Если не нужна максимальная скорость передачи, можно передавать байты через фиксированные промежутки времени достаточной длительности (гарантирующие, что байт уже ушел).

Для наиболее точной настройки скорости передачи данных выбирайте кварц из ряда 1.8432 МГц, 3.6864 МГц, 7.3728 МГц, 11.0592 МГц, 14.7456 МГц, 18.4320 МГц, 22.1184 МГц. Подробности см. например в даташите на микроконтроллер ATmega16, раздел "Examples of Baud Rate Setting", таблицы 68..71.

[Ссылки]

1. Передача данных через UART site:easyelectronics.ru.
2. AVR306: Using the AVR® UART in C site:atmel.com. Исходники к апноуту см. avr306.zip site:atmel.com.
3. Проект CDC-232, портированный на AVR-USB-MEGA16 (ATmega16 с кварцем на 16 МГц).
4. AVR-CDC: виртуальный COM-порт через Low-Speed USB (используется библиотека V-USB).
5. Работа с кольцевым буфером.

 

Комментарии  

 
+3 #3 isairon 12.01.2015 22:59
Лучше бы готовый примерчик с отправкой и получением, и светодиодиком ;-)
А то функции классные, кольцевой буфер элегантно описан, а понять новичку довольно сложно.
Цитировать
 
 
0 #2 Егор 10.03.2010 14:01
У меня проблема с USART на ATMega168. Вот из файла определений для ATMega168 "m168def.inc":

; ***** I/O REGISTER DEFINITIONS *************** *************** ***********
; NOTE:
; Definitions marked "MEMORY MAPPED"are extended I/O ports
; and cannot be used with IN/OUT instructions
.equ UDR0 = 0xc6 ; MEMORY MAPPED

Таким образом IN/OUT я не могу здесь использовать.
Тогда такой код будет правильным или нет?

USART0_Transmit :
; Wait for empty transmit buffer
lds r16,UCSR0A
sbrs r16,UDRE0
rjmp USART0_Transmit
; Put data (r17) into buffer, sends the data
lds r16,UDR0
mov r16,r17
sts UDR0,r16
ret

microsin: часть статьи про ассемблер довольно старая, и к сожалению правильность Вашего ассемблерного кода я не могу проверить. В последнее время использую готовые примеры кода для WinAVR/GCC при работе с последовательны м портом (на языке C). Скачайте проект для AVR Studio из статьи "Датчик давления и температуры MS5541B компании Intersema" http://microsin.ru/content/view/1100/44/. Код код очень простой и логичный, порт USART/UART настраивается на любую скорость и формат передачи, тип микроконтроллер а не имеет значения (код легко портируется на любой чип). Дописал статью, надеюсь она Вам пригодится.
Цитировать
 
 
0 #1 Shvayzer 24.11.2009 20:55
Хотелось бы по-подробнее :-*

microsin: откройте исходники к апноуту AVR306 (3-я ссылка). Там все просто, четко и подробно.
Цитировать
 

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


Защитный код
Обновить

Top of Page