AVR: настройка uart/usart

UART — это Universal Asynchronius Receiver Transmitter, универсальный асинхронный приемо-передатчик.  Но если говорить проще, это обычный последовательный порт (он же COM-порт, он же RS-232),  которые раньше  часто  использовались в персональных компьютерах для подключения мыши, модема и прочих периферийных устройств. С повсеместным распространением USB последовательный порт потерял свою популярность, но в микроконтроллерах он все еще довольно часто используется, так как это один из самых простых способов связать два устройства. При помощи uart к микроконтроллеру можно подключить GSM модем, GPS приемник, или другой микроконтроллер. В зависимости от используемых преобразователей уровня можно получить rs232 или rs485. Так же существуют конвертеры rs232-USB, rs232-ethernet, rs232-bluetooth которые позволяют подключить устройство на микроконтроллере с rs232 используя более современные интерфейсы.

В UART используются две линии (вывода микроконтроллера): rx — для приема и tx — для передачи. В USART добавлена дополнительная линия clk для синхронизации приемника и передатчика, что позволяет увеличить скорость обмена.

Данные по uart передаются в следующем формате:

Старт-бит — служит для определения начала посылки
Биты данных — может быть от 5 до 9 бит, но наиболее часто используется 8 бит — один байт.
Бит контроля четности — позволяет проверить не произошел ли при передаче сбой.
Стоп биты — 1 или 2 бита, служат для определения окончания посылки.

Для uart в avr atmega настраивается несколько параметров:

  1. скорость обмена
  2. количество бит данных
  3. контроль четности
  4. количество стоп-бит

Рассмотрим настройку uart/usart для  avr atmega8.

Настройка скорости обмена по uart в avr

Для настройки скорости используется регистр UBRR  (Usart Boud Rate Register)

Для установки скорости в этот регистр надо записать значение рассчитанное по формуле:

UBRR = ( F /( B * 16 ) ) - 1

Где:

  • F — тактовая частота, на которой работает микроконтроллер avr, например 11059200 для кварца  11.0592 МГц
  • B — требуемая скорость, например 115200 (бит/с)

Пример расчета для кварца 11.0592 МГц и скорости 115200 бит/с:

( 11059200/( 115200*16 ) ) - 1 = 5

Полученный результат записываем в UBRR, т.к. этот регистр 16-битный то доступ организован через два 8-ми битных регистра

  UBRRH = 0; //старший байт
  UBRRL = 5; //младший байт

Если в результате вычисления UBRR получалось не целое число, то в регистр записывается округленное значение, частота обмена при этом будет отличаться от той, что была заданна при расчете. Расхождение в частоте может приводить к сбоям при обмене данными, по этому надо выбирать такой кварц, при котором для заданной скорости обмена UBRR будет целым числом.

Также в регистре UCSRA имеется бит U2X, позволяющий удвоить скорость обмена. В случае когда этот бит выставлен, для расчета значения UBBR используется следующая формула:

UBRR = ( F /( B * 8 ) ) - 1

 

Настраиваем количество бит данных для uart в avr

Зависимость количества бит данных от состояния бит UCSZx  регистра UCSRC для uart/usart  в atmega8

UCSZ2 UCSZ1 UCSZ0 количество бит данных
0 0 0 5
0 0 1 6
0 1 0 7
0 1 1 8
1 1 0 9

Пример настройки uart/usart avr atmega8 на 8 бит данных:

UCSRC = ( 1 << URSEL ) | ( 0 << UCSZ2 ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
* Для доступа к регистру UCSRC был выставлен бит URSEL.

Настройка контроля четности для uart в avr

Зависимость режима контроля четности от состояния бит UPMx регистра UCSRC для uart/usart в atmega8

UPM1 UPM0 контроль четности
0 0 запрещен
1 0 дополнение до четности
1 1 дополнение до нечетности

Пример настройки uart/usart avr atmega8 на режим с отключенным контролем четности:

UCSRC = ( 1 << URSEL ) | ( 0 << UPM1 ) | ( 0 << UPM0 ) ;
* Для доступа к регистру UCSRC был выставлен бит URSEL.
 

Настройка количества стоп-бит для uart в avr

USBS количество стоп-бит
0 1
1 2

Пример настройки uart/usart avr atmega8 на режим с одним стоп-битом:

UCSRC = ( 1 << URSEL ) | ( 0 << USBS );
* Для доступа к регистру UCSRC был выставлен бит URSEL.
 

Разрешение приема и передачи данных по uart

Для того что бы разрешить прием и передачу данных надо выставить соответствующие биты в регистре UCSRB:

UCSRB = ( 1 << TXEN ) | ( 1 << RXEN );

Обобщенная функция настройки uart для avr atmega8

Так как настройка количества бит данных, контроля четности и количества стоп бит производится  через регистр UCSRC, то установку всех необходимых бит объединим в одну строку. Также не будем указывать биты которые сбрасываются в 0.

Пример настройки uart/usart avr atmega8 на 8 бит данных, 1 стоп-бит, отсутствие контроля четности, скорость обмена 115200 бит/с:

void uart_init( void )
{
  //настройка скорости обмена
  UBRRH = 0;
  UBRRL = 5;
  //8 бит данных, 1 стоп бит, без контроля четности
  UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
  //разрешить прием и передачу данных
  UCSRB = ( 1 << TXEN ) | ( 1 <<RXEN );
}

Передача байта по uart в avr

Для передачи надо записать данные в регистр UDR, но перед этим надо убедиться что предыдущий байт уже отправлен.

Пример функции для передачи байта по uart/usart avr atmega8:

void uart_putc( char c )
{
  //ждем окончания передачи предыдущего байта
  while( ( UCSRA & ( 1 << UDRE ) ) == 0  );
  //передача данных
  UDR = c;
}

 

Прием байта по uart в avr

Для того что бы определить принят ли байт или нет используется бит RXC регистра UCSRA.
Так же в этом регистре находятся флаги говорящие о том что произошла ошибка при приеме данных:

  • бит FE (Frame Error ) — ошибка кадрирования. То есть вместо стоп бит пришел 0.
  • бит PE (Parity Error) — ошибка контроля четности. То есть не совпали принятый и рассчитанный биты четности.
  • бит OR (OveRflow)- переполнение буфера. То есть следующий байт уже принят а предыдущий еще не считан из регистра UDR.

Пример функции для приёма байта по uart/usart avr atmega8:

unsigned char uart_getc( void )
{
  //ждем приема байта
  while( ( UCSRA & ( 1 << RXC ) ) == 0  );
 
  //считываем принятый байт
  return UDR;
}

Простая программа для avr

Теперь можно написать простую программу, которая выводит в uart «Hello world» а потом переходит в режим  «эха» — отправляет обратно принятые символы.

#include <avr/io.h>
 
void uart_init( void )
{
  //настройка скорости обмена
  UBRRH = 0;
  UBRRL = 5;
  //8 бит данных, 1 стоп бит, без контроля четности
  UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
  //разрешить прием и передачу данных
  UCSRB = ( 1 << TXEN ) | ( 1 <<RXEN );
}
 
unsigned char uart_getc( void )
{
   //ждем приема байта
   while( ( UCSRA & ( 1 << RXC ) ) == 0  );
   //считываем принятый байт
   return UDR;
}
 
void uart_putc( char c )
{
  //ждем окончания передачи предыдущего байта
  while( ( UCSRA & ( 1 << UDRE ) ) == 0 );
  UDR = c;
}
 
void uart_puts( char *str )
{
  unsigned char c;
  while( ( c = *str++ ) != 0 ) {
    uart_putc( c );
  }
}
 
int main( void )
{
  uart_init();
  uart_puts( "Hello uart\r\n" );
  while( 1 ) {
    char c = uart_getc();
    uart_putc( c );
  }
  return 0;
}

 

Cкачать исходники под avr-gcc (WinAvr) можно тут.

 

 

Запись опубликована в рубрике Микроконтроллеры avr с метками , , . Добавьте в закладки постоянную ссылку.

42 комментария: AVR: настройка uart/usart

  1. foton6 говорит:

    Спасибо за статью. Все предельно понятно.
    Сначала написал свою программу, выдает околесицу.
    Думаю возможно я криворук, забил вашу… Тоже не понятные символы.
    Дело ясное скорость обмена не совпадает. Естественно я перещитал опираясь на свою мегу. Стоит кварц на 5мГц, но фьюзы стоят по умолчанию, значит мега работает от внутреннего генератора с частотой 1мГц.
    значит (1000 000 / (9600 * 16) )-1 = 5,5 брал 5 и 6 для UBRRL/ Ни одно не подходит. Схема исправна, так как уже забивал прогу для UART, работала норм. В коде расчет UBRRL был «автоматический» под 9600, а что вместо частоты МК там бралось не разобрался, прогу найти не могу…
    Подскажите в чем дело может быть?(может быть частота МК всеже не 1мГц?, можноли както выяснить?)

  2. Артём Двинин говорит:

    foton6, какой у вас микроконтроллер?
    Что бы проверить частоту можно через заданный промежуток времени изменять состояние какого-нибудь вывода и осциллографом посмотреть. Ну или в крайнем случае светодиодом мигать раз в секунду и посмотреть сколько раз он мигнет за минуту.

  3. foton6 говорит:

    Atmega 8A-PU да как раз сегодня почитаю вашу статейку на счет таймеров и выясню частоту. На крайний случай поиграю с фьюзами и выставлю частоту.

  4. Артём Двинин говорит:

    Что-бы сделать задержку не обязательно использовать таймеры, можно использовать функцию _delay_ms.

    Кстати вспомнил, я когда-то тоже делал обмен по uart с тактированием от внутреннего RC-генератора. У меня тоже выводились крокозябры. Эту проблему тогда решил используя регистр подстройки частоты OSCCAL.

    Сделал цикл в котором изменял этот регистр от 0 до 255 и выводил это значение в uart.

    unsigned short i;
    for( i=0; i <= 255; i++ ) {
       OSCCAL = i;
       _delay_ms( 100 );
       printf( "OSCCAL=%urn", i);
       _delay_ms( 1000 );
    }

    В каком-то диапазоне значений OSCCAL текст стал выводиться нормально. Взял значение из середины этого диапазона и записывал его в регистр OSCCAL при старте программы. После этого все заработало.
    Но лучше все-таки тактировать от кварца, причём подбирать его так что-бы не надо было округлять значение, записываемое в UBRR, например кварц 11.0592 или 7.3728 МГц.

  5. foton6 говорит:

    Не стал морочится с высчитыванием точной частоты ))
    Просто фьюзы под внешний кварц выставил, работает отлично.
    В статье очень порадовали таблицы с битами, а то в большенстве статей(которые я читал) люди пишут куда ставить биты, а зачем, а уж темболее что будет если иначе поставить, не пишут.
    Конечно все есть в дата шите(по крайней мере по утверждениям) , а с ин-язами дела обстоят очень плохо. Я конечно летом пойду на курсы английского(сейчас физически не получается: учеба, работа.). Благодаря подобным статьям лишь удается пока двигаться )))))
    СПАСИБО !

  6. Артём Двинин говорит:

    Пожалуйста! Будут еще вопросы — пишите, буду рад помочь :)

  7. Артем говорит:

    Спасибо за подробную статью. Но есть один вопрос, который лично я никак не могу реализовать — это прием строки байт через UART в МК. Пробовал все возможные способы с буфером, но обрабатывать передачу МК так и не захотел.

  8. Артём Двинин говорит:

    Если Вам нужен приём текстовой строки, то попробуйте следующий код :

    #define BUF_SIZE 128
    char buf[BUF_SIZE];
    unsigned char buf_cnt = 0;
     
    while( 1 ) {
        char c = uart_getc();
        if(  (c == 'n') || ( c == 'r' ) ) {
            buf[buf_cnt] = 0;
            buf_cnt = 0;
            //вывод принятой строки
            uart_puts( buf );      
        }
        else {   
            //проверка на переполнение буфера
            if( buf_cnt < BUF_SIZE ) {     
              buf[buf_cnt] = c;
              buf_cnt++;
            }
        }
    }
  9. vital говорит:

    доброе время суток, у мя вопрос: есть N устройств хочется их объединить в сеть много прочитал, везде видится логика 1-master N-slave. теории и букав много, а вот схем реальных рабочих устройств нет т.е. я пытаюсь понять логику работы учитывая что у всех устройств может быть разная частота работа разные кварцевые генераторы внешние или внутренние, скорость uart по логике как то уравновешивает эти вопросы приводя работу по обмену между master любым slave по примерно одной частоте, но вот все равно присутствует ощущение вероятности сбоя/потери чего то. есть подозрения что МК между собой кроме TX и RX объединены чем то еще чтото типа синхронизации. Огромная просьба разъяснить эти моменты с приведенными ссылками на какие то схемы/проекты/статьи. Спасибо вам Артем за рожденный энтузиазм и интерес

  10. Артём Двинин говорит:

    Доброго времени суток, Vital.
    Вы всё правильно поняли, при обмене по сети есть один мастер и несколько ведомых.
    Тактовая частота у них может быть разная, но частота обмена должна быть одинаковая, при этом если используется внутренний RC генератор, то его лучше откалибровать, что бы было меньше ошибок.

    При этом обычно используется RS485. Дополнительной синхронизации нет. Сбои и потери отслеживаются при помощи таймаутов(timeout) и контрольной суммы.

    Алгоритм работы такой:
    Мастер отправляет пакет, в котором содержится адрес устройства
    Все ведомые этот пакет получают, но отвечает только то устройство адрес которого совпал.
    Мастер принимает ответ от ведомого и переходит к опросу следующего устройства.
    Если ответа от ведомого нет в течении некоторого времени (таймаут), то мастер повторяет попытку опроса ведомого.

    Пакет обычно имеет следующий вид:
    | адрес | длина данных | данные | контрольная сумма |

    Можно взять за основу modbus.

    Если есть ещё вопросы — пишите, постараюсь ответить.

    P/S Если не секрет, что за сеть из устройств?

  11. vital говорит:

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

    как только познаю мастерство хоть по минимуму начну практику, и обязательно поделюсь всеми бредовыми идеями) на самом деле вопрос разных много и по разным тематикам, если интересно передать навыки или помочь найти решения могу мучить вопросами)

  12. Артём Двинин говорит:

    Готовые проекты поищу, что-то было.
    А пока готов к мучению вопросами :)
    Пишите — постараюсь помочь.

  13. Vital говорит:

    Доброе время суток) вопрос у мя прозаичный, может подскажите простой uart терминал как сделать/где взять? т.е. не хочется к ПК прикручивать а смотреть о чем думает микросхема хотелось бы
    спасибо

  14. Артём Двинин говорит:

    А почему к ПК не хотите подключать?
    можно взять готовый uart-usb переходник, на мой взгляд, это самое простое решение

  15. Alexsander говорит:

    у меня проблем )))) помогите мне пожалуйста))) буду очень благодарен))))) я прошил atmega8A-PU на usart-эхо на кварце 16Mhz на скорости 115200 бод (что я подаю то и принимаю в терминале)……… atmega8A-PU на кварце 16Mhz подключен к COM-USB адаптер к ноутбук и через терминал подаю символы на atmega8A-PU а принимаю другие символы (например подаю «6» а получаю в ответ «3») вот в этом проблема))) помогите , объясните в чем причина

  16. Артём Двинин говорит:

    Мне кажется, всё дело в кварце. При расчете значения которое надо записать в UBRR получается не целое число. В результате частота обмена не 115200 а немного другая. Попробуйте поставить другой кварц например на 11059200 Гц, 7372800 Гц, или 14745600 Гц.

  17. Alexsander говорит:

    все получилось ))))дело было в купленном мной usb-uart ))) я купил FT232RL собрал плату на нем , подключил к нему мою atmega8 и все получилось ))) кварц поставил на 7372800 Гц))) ура )))
    дело за малым
    Создал программку на делфи для управление через uart )))запрограмировал на кнопку посылается «1» на atmega но аtmega не зажигает светодиод))) а в терминале посылаю ,зажигается свето диод )))вопрос вчем причина )))

  18. andrey tsykin говорит:

    Здравствуйте Артем! У меня есть один вопрос, прочитал вашу статью про работу с UART, почему то когда отправляю данные через преобразователь USB-RS232 на комп, в терминалке выходят непонятные символы, можете что нибуть посоветовать?

  19. Артём Двинин говорит:

    Здравствуйте!
    Посоветовал бы проверить, совпадает ли скорость обмена, настроенная на контроллере и компьютере.
    А еще убедиться, что выставлены нужные fuse-bit’ы. Если расчет велся для кварцевого резонатора, то и fuse-bit’ы должны выбирать работу от кварца.

  20. andrey tsykin говорит:

    Спосибо, проверил еще раз преобразователь USB-RS232 оказывается в нем проблема, драйвер для него установил не тот))

  21. Артём Двинин говорит:

    Пожалуйста! Удачи в разработке и безглючных программ ))

  22. Анатолий говорит:

    на сколько режимов можно настроить UART, если не менять скорость приема/передачи?

  23. Ваха говорит:

    Добрый вечер! Большое спасибо за вашу статью, все понятно и доступно изложено! У меня вопрос. У меня atmega8a.

    #define F_CPU  8000000UL
    #define BAUD 9600			
    #define MYUBRR (((F_CPU / (BAUD * 16UL))) - 1) // скорость 1200
    #define MYUBRR (((F_CPU / (BAUD * 64UL))) - 1) // скорость 4800
    void USART_Init( unsigned int ubrr)//Иниц модуля USART
    {
    	UBRRH = (unsigned char)(ubrr&gt;&gt;8);
            UBRRL = (unsigned char)ubrr;
            UCSRA |= (1&lt;&lt;U2X); 	// Удваеваем скорость что бы получить 9600
            UCSRB |= (1&lt;&lt;RXCIE)|(1&lt;&lt;RXEN)|( 1&lt;&lt;TXEN);
            UCSRC = (1&lt;&lt;URSEL)|(0&lt;&lt;USBS)|(3&lt;&lt;UCSZ0);
    }
    USART_Init(MYUBRR);

    Пробовал в протеусе — скорость правильная, как и должно по документации, а на практике совсем другая скорость. в чем дело?

  24. Артём Двинин говорит:

    Доброго времени суток, Ваха.
    Мне кажется, у Вас ошибка в формуле:

    MYUBRR (((F_CPU / (BAUD * 64UL))) - 1)

    Должно быть:

    MYUBRR (((F_CPU / (BAUD * 8UL))) - 1)

    И два дефайна MYUBRR не нужны, Вы же задаете скорость при помощи макроса BAUD.

    Если и это не поможет, проверьте fuse биты, может быть микроконтроллер работает от внутреннего RC генератора, а не от внешнего кварца.

  25. Ваха говорит:

    «И два дефайна MYUBRR не нужны, …» — это я вам работающие варианты хотел показать, в том числе и удвоение скорости. а вот с (BAUD * 8UL) не заработало ни на какой скорости

  26. Ваха говорит:

    Добрый день! Спасибо, что нашли время! МК работает от внутреннего генератора. Значения перебирал разные, остановился на этих (16 и 64), они только и работали. Просто не могу понять, почему не соответствуют формуле. Вроде у всех на сайте работает правильно, как и рассчитывали, а у меня методом тыка получается.

  27. Артём Двинин говорит:

    Если работает от внутреннего RC генератора, то проверьте на какую частоту он настроен fuse битами. По умолчанию настроено на 1МГц.
    В этом случае надо задавать другую частоту.

    #define F_CPU  1000000UL
  28. Ваха говорит:

    Установил fuse и все заработало правильно. Это не единственное где у меня были проблемы с этой частотой, но теперь то все понятно. Большое спасибо! Всем удачи!

  29. Артём Двинин говорит:

    Пожалуйста! Заходите ещё :)

  30. Сергей говорит:

    Поясните пожалуйста строчку
    uart_puts( «Hello uart\r\n» ); я принципе понимаю что мы тут отправляем значения Hello uart и переносим курсор на следующий строчку. Здесь опять же условие if( (c == ‘\n’) || ( c == ‘\r’ ) ) подскажите почему \n и почему \r есть ли еще такие обозначения .. и как правильно принять строчку с С#

  31. Артём Двинин говорит:

    Сергей,
    вы правы, uart_puts( «Hello uart\r\n» ) передает в UART строку «Hello uart» а затем символ перевода коретки ‘\r’ и символ перевода строки ‘\n’.
    Если принимать эту строку в терминале, то при приеме символа ‘\r’ текущая позиция переместится на начало строки, а при приеме символа ‘\n’ перейдем на новую строку.
    Кроме этих символов еще часто используются ‘\t’ — символ табуляции, ‘\0′ — нулевой символ (конец строки).

    При вводе строки с терминала ввод заканчивается нажатием на ENTER, и в зависимости от настроек терминала он отправляет либо ‘\r\n’ либо ‘\n\r’ или ‘\n’. По этому признаку и заканчиваем принимать строку и переходим к её обработке.

    Я не специалист по C# но могу подсказать что в среде Net2.0 для работы с COM портом можно использовать класс SerialPort и для чтения строки вызывать метод ReadLine.

  32. Atabek говорит:

    Максимальная скорость в битах в секунду или (kb/s), как установвит Atmega8
    один пример Напишите пожалуйста

  33. Артём Двинин говорит:

    Максимально возможную скорость можно рассчитать по простой формуле — (тактовая частота микроконтроллера)/8.
    Чтобы UART стал работать на этой скорости надо установить регистр UBRR = 0 и выставить бит U2X в регистре UCSRA.

    UBBR = 0;
    UCSRA |= ( 1 << U2X );
  34. Роман говорит:

    Вы можете помочь написать программу для UART на проект вольтамперметр на atmega8a.
    ниже бросаю то я уже сделал
    если кто-то поможет буду очень благодарен
    схема прибора https://yadi.sk/i/w3PaJNftgC2fv

    // Измерение постоянного тока с помощью AVR
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
     
     
    unsigned int voltage, current, adc_counter;
    volatile unsigned long voltage_value, current_value;
     
    // Функции работы с LCD
    #define RS PD0
    #define EN PD2
     
    // Функция передачи команды
    void lcd_com(unsigned char p)
    {
    	PORTD &amp;= ~(1 &lt;&lt; RS); // RS = 0 (запись команд)
    	PORTD |= (1 &lt;&lt; EN); // EN = 1 (начало записи команды в LCD)
    	PORTD &amp;= 0x0F; PORTD |= (p &amp; 0xF0); // старший нибл
    	_delay_us(100);
    	PORTD &amp;= ~(1 &lt;&lt; EN); // EN = 0 (конец записи команды в LCD)
    	_delay_us(100);
    	PORTD |= (1 &lt;&lt; EN); // EN = 1 (начало записи команды в LCD)
    	PORTD &amp;= 0x0F; PORTD |= (p &lt;&lt; 4); // младший нибл
    	_delay_us(100);
    	PORTD &amp;= ~(1 &lt;&lt; EN); // EN = 0 (конец записи команды в LCD)
    	_delay_us(100);
    }
    // Функция передачи данных
    void lcd_data(unsigned char p)
    {
    	PORTD |= (1 &lt;&lt; RS)|(1 &lt;&lt; EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
    	PORTD &amp;= 0x0F; PORTD |= (p &amp; 0xF0); // старший нибл
    	_delay_us(100);
    	PORTD &amp;= ~(1 &lt;&lt; EN); // EN = 0 (конец записи команды в LCD)
    	_delay_us(100);
    	PORTD |= (1 &lt;&lt; EN); // EN = 1 (начало записи команды в LCD)
    	PORTD &amp;= 0x0F; PORTD |= (p &lt;&lt; 4); // младший нибл
    	_delay_us(100);
    	PORTD &amp;= ~(1 &lt;&lt; EN); // EN = 0 (конец записи команды в LCD)
    	_delay_us(100);
    }
     
    // Функция вывода строки на LCD
    void lcd_string(unsigned char command, char *string)
    {
    	lcd_com(0x0C);
    	lcd_com(command);
    	while(*string != "")
    	{
    		lcd_data(*string);
    		string++;
    	}
    }
     
    // Функция вывода переменной
    void lcd_num_to_str(unsigned int value, unsigned char nDigit)
    {
    	switch(nDigit)
    	{
    		case 4: lcd_data((value/1000)+"0" );
    		case 3: lcd_data(((value/100)%10)+"0" );
    		case 2: lcd_data(((value/10)%10)+"0" );
    		case 1: lcd_data((value%10)+"0" );
    	}
    }
     
    // Функция инициализации LCD
    void lcd_init(void)
    {
    	DDRD = 0xFF;
    	PORTD = 0x00;
     
    	_delay_ms(50); // Ожидание готовности ЖК-модуля
     
    	// Конфигурирование четырехразрядного режима
    	PORTD |= (1 &lt;&lt; PD5);
    	PORTD &amp;= ~(1 &lt;&lt; PD4);
     
    	// Активизация четырехразрядного режима
    	PORTD |= (1 &lt;&lt; EN);
    	PORTD &amp;= ~(1 &lt;&lt; EN);
    	_delay_ms(5);
     
    	lcd_com(0x28); // шина 4 бит, LCD - 2 строки
    	lcd_com(0x08); // полное выключение дисплея
    	lcd_com(0x01); // очистка дисплея
    	_delay_us(100);
    	lcd_com(0x06); // сдвиг курсора вправо
    	lcd_com(0x0C); // включение дисплея, курсор не видим
    }
     
    // Обработчик прерывания от АЦП
    ISR(ADC_vect)
    {
    	ADCSRA = 0; // Выключаем АЦП
    	if((ADMUX &amp; 0x0F)==1) // Если был выбран канал ADC1
    	{
    		voltage_value = voltage_value + ADC; // Суммируем измеренные значения напряжения и помещаем в буфер
    		ADMUX = (ADMUX &amp; 0xF0) | 0; // Выбираем канал ADC0
    	}
    	else
    	{
    		current_value = current_value + ADC; // Суммируем измеренные значения тока и помещаем в буфер
    		ADMUX = (ADMUX &amp; 0xF0) | 1; // Выбираем канал ADC1
    		adc_counter++; // Увеличиваем счетчик выборок АЦП на 1
    	}
    	// Включаем АЦП
    	ADCSRA |= (1 &lt;&lt; ADEN)|(1 &lt;&lt; ADSC)|(1 &lt;&lt; ADPS2)|(1 &lt;&lt; ADPS1)|(1 &lt;&lt; ADPS0)|(1 &lt;&lt; ADIE);
    }
     
    int main(void)
    {
    	ADMUX |= (1 &lt;&lt; REFS1)|(1 &lt;&lt; REFS0); // Внутренний ИОН 2,56V
    	ADMUX |= (1 &lt;&lt; MUX0); // Подключаем канал ADC1
     
    	ADCSRA |= (1 &lt;&lt; ADEN) // разрешение АЦП
    	|(1 &lt;&lt; ADSC) // запуск преобразования
    	|(1 &lt;&lt; ADPS2)|(1 &lt;&lt; ADPS1)|(1 &lt;&lt; ADPS0) // предделитель на 128
    	|(1 &lt; 400)
    		{
    			ADCSRA = 0; // Выключаем АЦП
    			// преабразуем данные в реальное значение напряжения
    			voltage = (voltage_value/adc_counter) * 11/4;
    			// преабразуем данные в реальное значение тока
    			current = (current_value/adc_counter) * 10/4;
     
    			adc_counter = 0; // Обнуляем счетчик выборок АЦП
    			voltage_value = 0; // Обнуляем буфер значений напряжения
    			current_value = 0; // Обнуляем буфер значений тока
     
    			// Выводим данные на LCD
    			lcd_com(0xC0);
    			lcd_num_to_str(voltage/100, 2);
    			lcd_com(0xC3);
    			lcd_num_to_str(voltage, 2);
     
    			lcd_com(0xC9);
    			lcd_num_to_str(current/1000, 1);
    			lcd_com(0xCB);
    			lcd_num_to_str(current, 3);
     
    			// Включаем АЦП
    			ADCSRA |= (1 << ADEN)|(1 &lt;&lt; ADSC)|(1 &lt;&lt; ADPS2)|(1 &lt;&lt; ADPS1)|(1 &lt;&lt; ADPS0)|(1 &lt;&lt; ADIE);
    		}
    		_delay_ms(1);
    	}
    }
  35. Артём Двинин говорит:

    Роман, доброго времени суток!
    Чем смогу — помогу :)
    Есть пара замечаний по схеме.
    1) Ножка PD0 используюется для UART как RXD, а сейчас она управляет RS для LCD. Ндо перекинуть RS на другую ногу, например PD3
    2) Контроллер работает от внутреннего RC генератора, для обмена по UART лучше использовать внешний кварцевый резонатор, например на 11059200 Гц или 7372800 Гц или 14745600

  36. Anatoliy говорит:

    Для передачи большого количества байт, которые могут содержать все значения от 0x00 до 0xff, самым оптимальным будет применение логического протокола WAKE или MODBUS

  37. Вячеслав говорит:

    Здравствуйте, помогите пожалуйста, если сможете, в общем вот такая просьба: Привести алгоритм реализации UART на двух битах порта МК. Это нужно для курсовой работы.

  38. Atabek говорит:

    Здравствуйте я хотел отправить (например 4 байта) дата Атмега32 (16mhz) на Атмега8(1mhz) и у них частота разные как отправит и принимат дата можете написат маленькую программу.

  39. Atabek говорит:

    Здравствуйте я хотел отправить (например 1 байта) дата Атмега32 (16mhz) на Атмега8(1mhz) и у них частота разные как отправит и принимат дата можете написат маленькую программу. можно исползовать max485. заранее спасибо

  40. Qamdam говорит:

    Артем, спасибо за статью. Очень познавательно, особенно для новичков. Я только пробую, и затык произошел в самом ненужном месте :( Работаю с ATMega8A-PU. Работает все, кроме приема сигналов МК. Hello, uart получаю на выходе, но вот вывести хоть что нибудь на МК не получается. Молчит, как партизан. Помогите, в чем может быть проблема?

  41. Qamdam говорит:

    Да, забыл сказать, что я работаю с RS485, в полудуплексном режиме. Может все дело в этом, где-то нужно подрыгать ногами (XCK), чтобы переключиться от передачи к приему? Помогите примером, пожалуйста.

  42. Сергей говорит:

    Добрый день
    У меня вопрос по поводу связи 2 ATmega8 по uart, везде где я находил связь устроена так: первый МК передает данные на второй МК по uart, а второй передает дальше(обычно на LCD)…
    А как сделать чтобы один из них и принимал данные и отвечал…?

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

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>