Главная arrow Программирование arrow AVR arrow Передача данных через UART (RS-232) Tuesday, May 30 2017  
ГлавнаяКонтактыАдминистрированиеПрограммированиеСсылки
UK-flag-ico.png English Version
GERMAN-flag-ico.png Die deutsche Version
map.gif карта сайта
нашли опечатку?

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

Поделиться:

Передача данных через UART (RS-232) Версия для печати
Написал microsin   
23.09.2008

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

[Использование 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<<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 в AVRStudio]

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

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

Весь функционал сосредоточен в модулях 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. AVR. Учебный курс. Передача данных через UART.
2. USART. Сопряжение МК с компьютером через COM-порт. Теория. Пример практической реализации.
3. Апноут AVR306: Using the AVR® UART in C. Исходники к апноуту avr306.zip.
4. Проект CDC-232, портированный на AVR-USB-MEGA16 (ATmega16 с кварцем на 16 МГц).
5. Сайт AVR-CDC Osamu Tamura @ Recursion Co.
6. Работа с кольцевым буфером.

Последнее обновление ( 23.08.2010 )
 

Комментарии  

  1. #3 Slut
    2010-04-2519:14:28 большое спасибо) оч полезная инфа
  2. #2 Егор
    2010-03-1011:01:50 У меня проблема с 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 настраивается на любую скорость и формат передачи, тип микроконтроллер а не имеет значения (код легко портируется на любой чип). Дописал статью, надеюсь она Вам пригодится.
  3. #1 Shvayzer
    2009-11-2417:55:14 Хотелось бы по-подробнее

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

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

:D:lol::-);-)8):-|:-*:oops::sad::cry::o:-?:-x:eek::zzz:P:roll::sigh:

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

< Пред.   След. >

Top of Page
 
microsin © 2017