Главная > AVR > Краткое пособие по микроконтроллерам AVR. Часть 3.

Краткое пособие по микроконтроллерам AVR. Часть 3.

UART.

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

Естесственно, последовательный обмен можно реализовать программно. Например, проследовательный вывод байта через PB0 можно записать так:

void ShiftByte(uint8_t byte)
{
  uint8_t i,msk=1;

  for (i=0; i<8; ++i)
  {
    if (byte & msk)
      PORTB|=(1<<PB0);
    else
      PORTB&=~(1<<PB0);

    msk=msk << 1;
  }
}

Ясно, что такой обмен будет занимать много процессорного времени МК. Немного поправить ситуацию можно, используя прерывания.

К счастью, в состав практически всех современных контроллеров входит блок под названием UART (Universal Asynchronous Receiver/Transmitter) — универсальный асинхронный приемопередатчик. Асинхронный потому, что при обмене данными сигнал синхронизации не передается явно — расчет делается на достаточную для безошибочного обмена стабильность тактовых генераторов (синхронизация производится только в начале обмена). Кроме того, встречается блок USART (в ATmega48 присутствует именно такой модуль) — Universal Synchronous/Asynchronous Receiver/Transmitter. Такие блоки имеют возможность синхронной работы (с передачей синхронизирующего сигнала) для большей надежности обмена.

Практика показывает, что для большинства практических применений вполне достаточно асинхронного режима. Поэтому в данном пособии будет рассмотрен именно он. Информацию об остальных режимах можно беcпрепятственно найти в документации на микроконтроллер.

Итак, физически U(S)ART представляет собой два сдвиговых регистра (на прием и передачу).
Последовательный вход/выход этих регистров подключен к выводам МК, и активен при соответствующей настройке этого блока. Программно данные UART доступны в качестве одного регистра — UDR0. При чтении из него данные будут прочитаны из регистра-приемника; при записи — записаны в регистр-передатчик (и сразу же переданы).

Наиболее распространеным режимом асинхронной передачи является т.н. 8N1 — передаваемый символ имеет размер 8 бит, контроль четности не используется, посылка имеет в конце один стоповый бит.


Контроль четности (проверка четности, parity check) — простейший метод контроля целостности передаваемых данных. Практика показывает, что обычно можно обойтись без него.

Временная диаграмма подобной передачи показана ниже:

Вначале простаивающая (idle) линия имеет высокий уровень. Посылка начинается с т.н. стартового бита (ST, всегда
ноль)
. Далее передаются биты данных, начиная с младшего. В конце передается стоп-бит (SP, всегда единица) — фактически, это возвращение линии в состояние idle.

Настройка модуля USART для асинхронной работы.

Наиболее удобно работать с модулем UART используя его прерывания. В частности, UART может генерировать прерывание каждый раз, когда принят новый символ.

Эти настройки находятся в регистре UCSR0B:

Включим приемник, передатчик, разрешим прерывание по завершению приема:

UCSR0B=(1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);

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


UBRR0 является шестнадцатибитным регистром; МК AVR — восьмибитный. Поэтому на самом деле UBRR разбит на две восьмибитных части: UBRR0L и UBRR0H. Однако компилятор С учитывает это, и на прикладном уровне можно записывать значение в UBRR0, как если бы он был по-настоящему шестнадцатибитным.

В документации приведена формула расчета значения этого регистра в зависимости от тактовой частоты МК и желаемой скорости передачи данных. Однако, удобнее пользоваться таблицами, расчитаными для стандартных частот передачи и тактирования, также приведенными в документации на МК (начиная со стр. 194 даташита на ATmega48):


Бит U2Xn (U2X0) отвечает за удвоение скорости передачи, что иногда бывает полезно. В приводимых примерах он всегда неактивен (0), поэтому необходимо ореинтироваться на значения UBRR из левого столбца.

Очевидно, в некоторых случаях установить точную частоту передачи данных не представляется возможным. Поэтому в таблице приведены значения ошибки. Приемлемыми можно считать значения менее 1% — 2% (по абсолютному значению).

Например: установим значение скорости передачи равным 38400Bps при частоте тактирования МК равной 8MHz — из таблицы видно, что в этом случае значение UBRR должно равняться двенадцати:

UBRR0=12;

На этом в простейшем описанном случае конфигурация блока UART завершается. Для передачи байта его необходимо записать в регистр UDR0; при приеме байта будет вызвано прерывание; принятый байт будет находиться в том же UDR0.

Пример: обратная отсылка принятого байта (эхо):


Некоторые детали предлагаемого примера требуют пояснения.

В примере реализована функция SendByte(), передающая символ и ожидающая конца его передачи. Ожидание конца передачи реализовано проверкой бита UDRE0 в регистре UCSR0A — этот бит устанавливается в единицу, как только регистр UDR0 готов принять следующий символ.

Сообщение для пользователя (msg) хранится в памяти программ, для чего использован макрос PROGMEM, объявленный в avr/pgmspace.h. Это необходимо для того, чтобы сообщение не занимало места в оперативной памяти (с целью ее экономии). Следует помнить, что доступ к таким переменным возможен только с помощью специальных функций (в данном случае используется pgm_read_byte()).


#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdint.h>

#define MSG_LEN    20

uint8_t msg = PROGMEM "Character received: ";

void SendByte(uint8_t byte)
{
  UDR0=byte;
  while (!(UCSR0A & UDRE0));
}

void SendMsg(void)
{
  uint8_t i;
  
  for (i=0; i<MSG_LEN; ++i)
    SendByte(pgm_read_byte(&(msg[i])));
}

ISR (USART_RX_vect)
{
  uint8_t tmp=UDR0;

  SendMsg();
  SendByte(tmp);
  SendByte('\n');
}

void main(void)
{  
  DDRD=(1<<PD1);

  UCSR0B=(1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);
  UBRR0=12; //38400BPs

  sei();

  while (1);
}
Рубрики:AVR
  1. Dice
    27/03/2013 в 23:03

    Отличная статья!
    Мне нужно ,было реализовать отправку/прием 9-битных посылок через UART, нарыл несколько примеров, но не мог понять что все этот птичий язык значит… теперь приходит осознание ;-))

    • YS
      27/03/2013 в 23:07

      О-о-о, рад, что кому-то помог мой труд.🙂

  1. No trackbacks yet.

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

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s