Приветствую читателей данного блога — I’m Still Alive 🙂 посему и avr-start тоже. Последние полгода моей жизни были весьма насыщенными разного рода событиями, поэтому пришлось отодвинуть на некоторое время выпуск статей. Наконец, можно снова заняться более привычными вещами, для разгрузки предлагаю простой пример использования UART.

Для тех кому интересны перспективы сайта. В последнее время мне подвернулась возможность заниматься разработкой в НИИ. Ничего необычного не было, но некоторые тонкости прояснили многое. Подглядел для себя множество интересных схемотехнических решений. Была возможность изучить микроконтроллеры семейства PIC16 и посмотреть на задачи решаемые серьезными программистами C++. Заодно, прокачал скилл владения solidworks и altium designer.

В целом — текущие знания позволяют выполнять поставленные цели, но все же я решил поставить планку повыше, ибо еще есть куда расти и к чему стремиться. Перспективным на текущий момент, я вижу изучение ПЛИС и микроконтроллеров STM, но это только планы. Действовать буду по обстоятельствам 🙂

После пиковского MPLAB я решил отказаться от любимого CodeVision, как от среды разработки в пользу Atmel studio. Собственно я не отказываюсь от CAVR как генератора кода. С конвертацией проектов я проблем не заметил, если вы понимаете что пишите, то разницы никакой.

Перейдем к практической части. Задача — напечатать на компьютере текст, передать его на микроконтроллер и вывести на дисплей. Для вывода текста на дисплей я заюзал библиотеку найденную на сайте chipenable. Код для юарта сгенерировал в CAVR 🙂

#define F_CPU 8000000
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "lcd_lib.h"
 
 
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)
 
// USART Receiver buffer
#define RX_BUFFER_SIZE 16
char rx_buffer[RX_BUFFER_SIZE];
 
unsigned char rx_wr_index,rx_rd_index,rx_counter;
 
 
// This flag is set on USART Receiver buffer overflow
char rx_buffer_overflow;
char read_enable = 0;
volatile char lcd_out = 0;
 
ISR(USART_RXC_vect )
{
	char status,data;
	status=UCSRA;
	data=UDR;
	if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
	{
		if (data =='*')
		{
			rx_wr_index=0;
			read_enable = 1;
 
		}
 
		if((data == '$')&&(read_enable == 1))
		{
				read_enable = 0;
				lcd_out = 1;				
		}
 
		if (read_enable == 1)
		{
			rx_buffer[rx_wr_index++]=data;
			if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
 
			if (++rx_counter == RX_BUFFER_SIZE)
			{
				rx_counter=0;
				rx_buffer_overflow=1;
			}
 
		}
 
	}
}
 
int main(){
 
	DDRD= 0x00;
	PORTD= 0x00;
 
	// USART initialization
	// Communication Parameters: 8 Data, 1 Stop, No Parity
	// USART Receiver: On
	// USART Transmitter: On
	// USART Mode: Asynchronous
	// USART Baud Rate: 9600
	UCSRA=0x00;
	UCSRB=0x98;
	UCSRC=0x86;
	UBRRH=0x00;
	UBRRL=0x33;
 
	sei();
	LCD_Init();
	LCD_Goto(0,0);
	LCD_WriteData('h');
	LCD_WriteData('i');
 
	while (1)
	{
 
    	if (lcd_out == 1)
		{	
			LCD_Clear();
			LCD_Goto(0,1);		
			_delay_ms(5);
			LCD_SendString(&rx_buffer[1]);
			lcd_out = 0;
			rx_wr_index= 0;
			rx_counter = 0;			
		}
	}
}

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

#define DATA_REGISTER_EMPTY (1<<UDRE)

Флаг относится к передаче данных микроконтроллера, т.е. прежде чем передать следующий байт, нужно подождать пока уйдет предыдущий байт, данный флаг показывает пуст ли буфер, т.е. ушел ли байт или нет. Хоть здесь передачу с микроконтроллера мы не используем, но пояснить стоит.

#define RX_COMPLETE (1<<RXC)

— Флаг показывает закончена прием данных или нет.

Следующие три флага показывают ошибку при приеме данных

#define FRAMING_ERROR (1<<FE)

— ошибка чтения стоп бита

#define PARITY_ERROR (1<<UPE)

— ошибка контроля четности

#define DATA_OVERRUN (1<<DOR)

— потеря данных при приеме

Общая идея такова: прием данных производится в прерывании, есть массив rx_buffer[] размером RX_BUFFER_SIZE, например 16. Если нет ошибок и первый символ ‘*’ — звездочка, то начинаем писать данные в rx_buffer[], при поступлении символа доллара ‘$’ считаем передачу оконченной. Выставляем флаг lcd_out и в основном цикле выводим текст на дисплей.

При тестировании всплыло несколько косяков — естественно, нельзя писать данные больше размера буфера, иначе они будут писаться по кругу; нужно учитывать кодировку символов дисплея; нельзя выводить символов больше чем это может себе позволить дисплей. Лучше написать обработчики подобных ситуаций, в данном случае это только пример :). При написании правильной управляющей программы на ПК, все это можно обойти.

Еще одно замечание, из сказанного выше, чтобы вывести русский шрифт на экран его нужно сконвертировать, например программой Lcd20, полученные кракозябы нужно передавать.

Ради теста покидал байтики через терминал
uart_ex12

Видосик запилил, качество плохое — любимая записывалка сломалась, поэтому что есть. А так, все работает.

Прошивка и схема

ЗЫ: Просьба в комментариях оставлять вопросы только по этой статье. Все остальные вопросы и обсуждения прошивок, исходников, проектов, можно обсудить на форуме или группе вк.

80 комментариев: Пример приема данных по UART

  • Спасибо за статьи. Слежу и постоянно читаю. Долго не было ничего, уже начал переживать, что больше не будет 🙂 Всё как всегда понятно и доступно. Спасибо.

  • Поддерживаю вас с переходом на классический С. Владея им можно писать и под АВР и под ПИК и под остальное. КодВижен закрывает новому юзеру наглядность полного цикла программы! Больше работы с даташитами приведет к улучшению знания архитектуры и перефирии!
    Спасибо!
    Шикарный IDE Eclipse пожете попробовать. Плагин авр присутствует!

  • Здравствуйте.
    Напишите как принимать строку например от GPS приемника, декодировать ее и вывести данные на экран.

  • до gps еще руки не дошли

  • Здравствуйте!
    1.Есть ли возможность использовать, старт и стоп бит. Есть устройства, в которых нет возможности установки спец символов.
    2.Что значит вот это выражение (rx_counter-1).
    3. Изменил значения строки и столбца в строке LCD_Goto(5,1), а ему фиолетово — выводит данные на то же место. 😯
    Спасибо!

  • Так, с окончанием строки разобрался, а вот с началом приема — никак нет.
    Со вторым вопросом разобрался.

  • Не путайте, старт и стоп биты задействованы, ибо юарт тут аппаратный, вы всего лишь передаете байт означающий начало строки и байт означающий конец строки.
    Обновлено, была проблема с оптимизацией

  • Понял, тогда каким образом можно опознать байт означающий начало приема данных.
    Отправляю данные из МК на устройство, в ответ должен получить две строки, но на дисплей выводит только первую. Как вывести две строки

    AT+CLCC // выводит только её
    +CLCC: 1,1,4,0,0,»+ циферки»,145

    P.S. Работаю в студии 6.1, строку типа #define PARITY_ERROR (1<<UPE) не принял, заменил UPE на PE — проект закомпилировался.

  • В статье же описано — начало приема * окончание $

  • Артур, Я вашу статью изучил и опробовал, но я писал об устройстве (mobile), в котором я не могу задать условный бит,который означал бы старт передачи, (либо переход а новую строку). Но терминал же его видит. Конец строки нашел в интернете- ‘/r’. Как можно в начале переделать прием данных, чтобы он принимал все, но заканчивал прием при встречи символа ‘/r’.

  • Не вижу проблемы, меняй символ доллара на ‘\r’ с начала приема убираешь звездочку и все будет работать. Длина строки, которую передает твое устройство, скорее всего фиксированная, от этого и надо плясать, если длина неизвестна, то не помешает сделать таймауты.

  • Артур, Да я уже заменил, символ $ на ‘\r’. Все работает чудно. Но с принятием — проблема.
    Сделал так:
    if (data) вместо if (data==’*’)
    На дисплее выбивает один корявый символ не поддающийся описанию и на этом все. Длина не фиксированная, принимает все символы, только вот при отправке символы пропадали, добавил условие в функции отправки — while ( !( UCSRA & (1<<UDRE)) ); и теперь все пучком.

  • Сообщением выше, я написал, что принимает все символы при использовании спец бита.

  • Артур, как можно записать число типа Int, которое меняется!

  • Не совсем понятно что вы имеете ввиду

  • Значит, есть целочисленное или дробное число, которое приходит (например с АЦП). Т.е. число постоянно меняется. Как его вывести на дисплей?

  • урок 10 посмотрите, если в кратце — функцией sprintf преобразовываете свое число в массив символов, который уже выводится на дисплей

  • Благодарю. Я полагал, записать данные типа int через библиотеку этого проекта, но как я понял она выводит данные типа char. Поэтому я и спрашивал как вывести числа. Но к счастью студия обладает возможностью вывода как и в CVAVR. Ещё раз благодарю.

  • я так понял, в строке LCD_SendString(&rx_buffer[1]); вывод идет, начиная с первого символа сообщения…

  • Совершенно верно, первый символ «служебный», поэтому нет смысла его выводить

  • ISR(USART_RXC_vect )
    это обработка прерывания на языке atmel studio?

  • Да. В скобках указан «источник» прерывания, легко догадаться что это приемник UART

  • Здравствуйте!
    ASCII конвертирават HEX декодировать

  • Я нимок управлят atmega8 СМС УПРАВЛЕНИЕ но я через с35 симинис отправлю сабшени. как принимат смс и обративат atmega8 и чвото ключит выхадной

  • Поймали входящую SMS, вырезали хвост, само сообщение:
    ———————————————————-
    Итак имеем сообщение 30182C06 ( Текст «0001» )
    Это сообщение состоит из символов ASCII ‘3’ ‘0’ ‘1’ ‘8’ ‘2’ ‘C’ ‘0’ ‘6’
    Эти символы нужно преобразовать в HEX формат
    (или сразу в двичку) чтоб вывести в порт контроллера
    Преобразуем в HEX: 30h 18h 2Ch 06h ( ВОТ ЭТОГО И НЕ МОГУ !!! )
    Распишем эти значения в виде двичной последовательности и
    выстроим эту последовательность в такой ряд:
    06h 2Ch 18h 30h
    00000110 00101100 00011000 00110000
    Теперь из приведенной выше двоичной последовательности будем делать выборки
    по 7 бит. Выборки делаем начиная справа. Причем сделав очередную выборку
    добавим к ней 0 слева.
    Ниже показаны сделанные выборки с добавленными нулями
    Выборка 1: 00110000 — символ ‘0’ (ASCII)
    Выборка 2: 00110000 — символ ‘0’ (ASCII)
    Выборка 3: 00110000 — символ ‘0’ (ASCII)
    Выборка 4: 00110001 — символ ‘1’ (ASCII)

    ;——————————————-
    end

  • char sms[] = {’3′ ’0′ ’1′ ’8′ ’2′ ‘C’ ’0′ ’6′};
    char hex[];


    while(1)
    {
    hex[i] = (sms[i*2]-0x30)*10+(sms[i*2+1]-0x30);
    }

  • НИКАК не панимаю как паймат ето в смс коди мне придйот ето код в UDR вот

    AT+CMGD=1 OK
    AT+CMGR=1 +CMGR: 0,,23
    0791999810584088240C919998316230170000419022229363020430182C06
    OK
    char sms[] = {’3′ ’0′ ’1′ ’8′ ’2′ ‘C’ ’0′ ’6′};
    char hex[];

    rx = UDR; ВОТ получил сигнал от udr и дабавил rx .
    и как декодирават ето коду.0791999810584088240C91999831623017000041902222936 {3020430182C06} как аделнна я вазму ето коду ASCII конвертирават коду

    if(rx == ) // вот какой код я запису ето смс «0001» > 3020430182C06 или hex
    {
    PORTD = 0xFF;// ВКлючит
    }

  • AT+CMGD=1 OK
    AT+CMGR=1 +CMGR: 0,,23
    0791999810584088240C919998316230170000419022229363020430182C06
    OK
    как я аделлна вазьму ето коду на rx мне нужние цифри вот мне нужна ето коди 30 18 2С 06 ASCII 30 18 2С 06 это код от смс 0001.
    как я переобризаваю коди 30 18 2С 06 и поличу hex; одну пачку коду и ключит выханой порту
    if(rx == ) как я нимог записат коди ‘ ‘,или hex 0x..,
    {

    }пожалуйста памагите етову управление смс и atmega
    я многа учил вашиму сайту уроки. все прочитал добавляйте ето тож смс контрол

  • вот я знаю как отправит смс от atmega но трудна мне как получит смс atmega ,
    интернети нинашол смс управление от code vision
    вот я так отправлаю смс НО Я КАК ПОЛИЧУ СМС И КАК ОБРАТИВАЮ ВХАДНОЙ СМС
    if (PINB.0==0)
    {
    printf(«ATH\r»);
    delay_ms(500);
    printf(«at+cmgf=0\r»);
    delay_ms(500);
    printf(«at+cmgs=80\r»);
    delay_ms(500); printf(«079183609310000011000C918390239276
    320008AA42041A043B0438043C043004420438044704350441043A0430044
    F00200443044104420430043D043E0432043A04
    3000200432044B043A043B044E04470435043D0430\x1A»);
    delay_ms(7000);
    }
    printf(«ATH\r»); // Положить трубку
    delay_ms(500);
    }

  • ВОТ УЖЕ ТРИ ДНЯ Я СИДУ НА КАМПЮТЕРИ ПРОЧИТАЮ это смс управление пожалуйста памагите етову управление смс и atmega 😥

  • вам всего навсего нужно прочитать массив данных, я не знаю сколько байт должно придти, но оно должно быть всегда одинаковым, допустим 5
    char mass[5];
    while(1)
    {
    gets(mass,5);
    }

    Далее из этого массива нужно преобразовать 2 байта в один, нужно заранее знать какие они по номеру, допустим это 4 и 5 (т.е.3 и 4)

    while(1)
    {
    hex = (mass[3]-0×30)*10+(mass[4]-0×30);
    }

    Полученная переменная hex будет хранить в себе 1 байт, вам нужно сравнить 4 байта т.е. лучше сделать массив. В итоге еще добавится проверка
    if((hex[0]=='0') && (hex[1]=='0')&&(hex[2]=='0')&&(hex[3]=='1'))
    По факту можно 2 байта в 1 не переводить, раз последовательность известна, можете сразу анализировать приходящий массив

  • здравствуйте я немного понял и записал вотак и пасматрел proteyus signali видил

    ‘2C’ ASCII
    gets(mass,2);
    hex = (mass[0]-0x30)*10+(mass[1]-0x37);
    ВОТ Я ПОЛУЧИЛ hex 0x20 и астайотса йешо адин байит 0х0С
    0х20 и как прибавляу 0х0С и получу 0х2С.

    UDR = hex;
    if(hex == 0x20)
    {
    PORTC = 0xFF; // РАБОТИЛА НО НИ ПОНЛИСТА
    }

  • Не понял вопрос, строчка hex = (mass[0]-0×30)*10+(mass[1]-0×30); даст вам на выходе значение 0x2C, если в mass[0]=’2′ и mass[1]=’C’
    если вы хотите проверять то
    if(hex==0x2C)
    {
    PORTC=0xFF;
    }

  • здравствуйте
    gets(mass,2);
    hex = (mass[0]-0x30)*10+(mass[1]-0x37);
    UDR = hex;
    if(hex == 0x2C) // это не палучайетса а 0x20 работайет. пачему недайот 0x2С
    {
    PORTC = 0xFF;
    }
    Объясните, пожалуйста ВОТ mass[0] вычитайет 0x30 .mass[1] вычитайет 0x37 а *10+ это ни понйел

  • Чем отличается 0x00 т.е. ноль, от символа ‘0’ кодировки ASCII? тем что символ нуля имеет код 0x30. т.е. чтобы перевести из кодировки ASCII в hex нужно от кода ASCII отнять значение 0x30, почему вы вычитаете 0x37?
    Теперь разберемся с «*10». ваше значение состоит из двух «составляющих» 0x20 и 0x0C, которые если сложить получится 2C. Как получить 0x02 мы уже разобрались, нужно из символа ‘2’ в кодировке ASCII это 0x32 отнять 0x30, получим 0x02. Из 0x02 нужно получить 0x20, элементарно, для этого нужно умножить на 10.

  • gets(mass,2);
    hex = (mass[0]-0x30)*10+(mass[1]-0x30);
    UDR = hex;
    if(hex == 0x2C) // вот это ни даййот 0x2C а 0x27 ;чтоже делет
    {
    PORTC = 0xFF;
    }

  • я пасматрел протеюс сигнали вот все мой код#include
    #include
    #include
    // Standard Input/Output functions
    #include

    // Declare your global variables here

    char hex;

    char mass[2];

    void main(void)
    {
    PORTD = 0xFF;
    DDRD = 0x00;
    PORTC = 0x00;
    DDRC = 0xFF;
    // USART initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART Mode: Asynchronous
    // USART Baud Rate: 19200
    UCSRA=0x00;
    UCSRB=0x18;
    UCSRC=0x86;
    UBRRH=0x00;
    UBRRL=0x19;

    // Global enable interrupts
    #asm(«sei»)

    while (1)
    {

    gets(mass,2);
    hex = (mass[0]-0x30)*10+(mass[1]-0x30);
    UDR = hex;
    if(hex == 0x2C) // ýòî íå ïàëó÷àéåòñà à 0x27 ðàáîòàéåò
    {
    PORTC = 0xFF;
    }

    };
    }

  • OYBEK, я вам объясняю принцип, а вы хотите готовое решение, чтобы подставить и работало. Это же скучно и не интересно. Посмотри ASCII это же не сложно и поймете все ошибки.
    Нужно смотреть полученный байт в пределах 0x30-0x39 или 0x41-0x46 в зависимости от этого
    либо отнимать 0x30 либо 0x31 и умножать нужно конечно же на 0x10
    Вообще я не понимаю зачем эти переводы и пр. сделайте так:
    gets(mass,2);
    if((mass[0]==’2′)&&(mass[1]==’C’))
    {
    PORTC=0xFF;
    }

  • ООО СПАСИБА я поняла свои ошибки..ASCII это массив я думил ‘ 2 ‘,’ C ‘ вот вы правду гаварили зачем мне ASCII convertiravat hex/// ну у мне йест гатови ‘ 2.’ массив

  • СПАСИБА БАЛШОЙ ВЫ МОЙ УЧИТЕЛ

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика