Приветствую читателей данного блога — 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

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

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

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

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

  • здравствуйте УЧИТЕЛ…….еще вопрос есть

    телефон отправляет множество команд. Например, OK, RING, ERROR…
    Иногда нужно, чтобы при получении команды контроллер смог опознать её и выполнить какое-то действие. Например, получен входящий звонок. Модуль при этом отправляет в контроллер:

    RING

    RING

    RING

    В зависимости от настроек модуля, может отправлять ещё и номер того, кто звонит. Пока нет никакой программы, контроллер ничего с этим сделать не сможет (в лучшем случае) или (в худшем) сделает что не то, а то и вовсе зависнет (не сможет выйти из прерывания).

    Требования к коду обработки:
    1. Минимальное количество времени на сохранение полученных команд. Никаких задержек в программе прерывания быть не должно. Потом уже с полученным массивом будем делать что угодно.

    2. Сохранение всех полученных команд в одном буфере. Для разделения отдельных будем использовать символ $.

    3. Распознавание распространенных команд в числовые коды. Например, OK будет 1, ERROR — 4, RING — 2.

  • if((mass[0] == ‘R’)&&(mass[1]== ‘I’)&&(mass[2] == ‘N’)&&(mass[3] == ‘G’))
    {
    PORTC = 0xFF;
    delay_ms(500);
    i++;
    printf(«ATH\r»);
    }
    через терминал кп работайет но йесли падключайу тел неработайет

  • Учител умне йест готови пройект gsm control sms control но я хачу сама зделит этово..
    Памагите этово распазнават……

  • В вашем случае нужно разобраться с тем как передаются данные, UART такая штука что он шлет байты один за другим. Т.е. вы можете прочитать 4 символа, но могут быть mass[0] == ‘I’)&&(mass[1]== ‘N’)&&(mass[2] == ‘G’)&&(mass[3] == ‘R’) или (mass[0] == ‘N’)&&(mass[1]== ‘G’)&&(mass[2] == ‘R’)&&(mass[3] == ‘I’). Байт это символ ASCII. Признак окончания строки тоже байт. Поэтому нужно анализировать то что приходит. Определяющим может быть таймаут, или специальный символ. Например, если мне приходит байт ‘$’, то я уже знаю что после него пойдут нужные мне символы, которые нужно будет записать в массив, в противном случае я в массив ничего не записываю и ничего не делаю. Собственно про это статья выше. В конце концов когда в разберетесь с этим, вам нужно будет применить к своей прошивке, т.е. перед символом ‘R’ полюбому приходит определяющий символ. И скорее всего символ окончания строки ‘\r’ является признаком окончания массива.

  • УЧИТЕЛ УМНЕ не получайетса галава тармазилса…этово смс и atmega8

  • Научитесь передавать сообщения с ПК на AVR любой длины. Если статья не понятна, то пишите что не понятно, чтобы я мог исправить. И плиз не пишите учител, тут все равны.

  • через пк avr работайет…RING передавал все норме но если падключаю тел неработайет.
    умне симнис с35. я падумил…tx наприжение 2.8 ето хватит нуштоби работила avr

  • OYBEK, подключите к компу, телефон и посмотрите что приходит, сразу станет понятно что с телефона приходит не просто 4 байта RING

  • ПРОВЕРИЛ подключил кп тел и пасматрел
    ASCCII дайот RING а
    HEX вот это

    0D 0A 0A 52 49 4E 47 0D 0A 0A я понйил это ring но не нашол(0D 0A 0A)
    R I N G

  • 52 49 4E 47
    R I N G

    0D-\n =новая сторка
    0A-\r = возрат каретки
    а патом что делею

  • Блин))) у вас определяющий символ 0x0A
    if(getchar()==0x0A && getchar() == 0x0A) //если по юарту пришло 2 возврата каретки, читаем массив
    {
    gets(mass,4);
    }
    if(mass[0]=’R’ && mass[1]=’I’ …..)
    {
    PORTC=0xFF;
    }

  • непалучилса

  • как я получу все одном буферу

  • #define BUF_SIZE 128 //Исходящий буфер
    #define BUF_MASK (BUF_SIZE-1)
    #define IN_BUF_SIZE 64 //Входящий буфер
    #define IN_BUF_MASK (IN_BUF_SIZE-1)

    volatile char buffer[BUF_SIZE]=»»;
    volatile char inbuf[IN_BUF_SIZE]=»$»; //inner buffer of USART
    volatile uint8_t ind_in=0, ind_out=0, rxind_out=0, rxind_in=0, mess = 0;
    volatile uint8_t com_detect=0; //сюда будет записана обнаруженная команда

    #define TIMEOUT 100 //на случай если команда так и не принята

    Пишем обработчик прерывания приёма данных:

    //recieving Data from RS232
    ISR (USART_RXC_vect)
    {
    uint8_t tmp;
    tmp = UDR;
    if (tmp == 0x0D) //получен конец команды —
    {
    mess++; //one more message
    inbuf[rxind_in++] = ‘$’; //вставляем разделитель в буфер
    rxind_in &= IN_BUF_MASK;
    }
    else
    {
    if (tmp != 0x0A) //очистка непонятного символа с модуля
    {
    inbuf[rxind_in++] = tmp; //записываем в буфер
    rxind_in &= IN_BUF_MASK;
    }
    }
    sei ();
    }
    как я можна зделую code vision

  • OYBEK, возьмите готовую прошивку, если не хочется разбираться и не парьтесь. Если нужно изучить, начните с самого начала.

  • здравствуйте
    как можно очистит буфер scanf();
    первойе читалка scanf() работайет а втрарой нет

    char hex[60];

    scanf(«%s»,hex);
    printf(«%s»,hex);

    первы
    if((hex[56] == 0x36)&&(hex[57] == 0x37)&&(hex[58] == 0x30)&&(hex[59] == 0x43))
    {

    PORTD.2 = 1;
    delay_ms(500);
    for(;on<1;on++)
    {

    printf("AT+CMGD=1\n\r\r");

    on1 = 0;

    }

    };

    втарой
    if((hex[56] == 0x41)&&(hex[57] == 0x37)&&(hex[58] == 0x30)&&(hex[59] == 0x43))
    {

    PORTD.2 = 0;
    delay_ms(500);
    for(;on1<1;on1++)
    {

    printf("AT+CMGD=1\n\r\r");
    on = 0;
    }

  • Scanf считывает строку, вот и подумайте, какие условия поставлены и почему они не могут быть выполнены.

  • здравствуйте
    Я нашол причина

    for(;;)
    {

    }; вот здес я не паставил ето (;)

  • Всех с наступившем Новым годом!
    Вот решил переделать под CodeVisionAVR приветствие ОК выводится а дальше пустой экран .
    Где ошибка не пойму вроде всё правильно .

    #include
    #include
    #define F_CPU 8000000

    //#include
    // Alphanumeric LCD Module functions
    #asm
    .equ __lcd_port=0x18 ;PORTD
    #endasm
    #include

    #define RXB8 1
    #define TXB8 0
    #define UPE 2
    #define OVR 3
    #define FE 4
    #define UDRE 5
    #define RXC 7

    #define FRAMING_ERROR (1<<FE)
    #define PARITY_ERROR (1<<UPE)
    #define DATA_OVERRUN (1<<OVR)
    #define DATA_REGISTER_EMPTY (1<<UDRE)
    #define RX_COMPLETE (1<<RXC)
    char lcd_buffer[33]; //массив с данными для экрана
    // USART Receiver buffer
    #define RX_BUFFER_SIZE 8
    char rx_buffer[RX_BUFFER_SIZE];

    char read_enable = 0;
    volatile char lcd_out = 0;

    #if RX_BUFFER_SIZE<256
    unsigned char rx_wr_index,rx_rd_index,rx_counter;
    #else
    unsigned int rx_wr_index,rx_rd_index,rx_counter;
    #endif

    // This flag is set on USART Receiver buffer overflow
    bit rx_buffer_overflow;

    // USART Receiver interrupt service routine
    interrupt [USART_RXC] void usart_rx_isr(void)
    {
    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;
    }}}}

    #ifndef _DEBUG_TERMINAL_IO_
    // Get a character from the USART Receiver buffer
    #define _ALTERNATE_GETCHAR_
    #pragma used+
    char getchar(void)
    {
    char data;
    while (rx_counter==0);
    data=rx_buffer[rx_rd_index];
    if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
    #asm("cli")
    —rx_counter;
    #asm("sei")
    return data;
    }
    #pragma used-
    #endif

    // Standard Input/Output functions
    #include

    #asm(«sei»)

    int main(){

    DDRD= 0x00;
    PORTD= 0x00;

    UCSRA=0x00;
    UCSRB=0x98;
    UCSRC=0x86;
    UBRRH=0x00;
    UBRRL=0x33;

    #asm(«sei»)

    lcd_init(16);
    lcd_gotoxy(0,1);
    lcd_putsf(«OK»);

    while (1)
    {
    if (lcd_out == 1)
    {
    lcd_clear();
    lcd_gotoxy(0,0);
    delay_ms(5);
    sprintf(&rx_buffer[1]);
    lcd_out = 0;
    rx_wr_index= 0;
    rx_counter = 0;

    }
    }
    }

  • Собственно не вижу вывода текста

  • вот поменял после ОК выводится температура но показания не полные нет точки ноля и градусов цельсия.ГДЕ ЕЩЁ КОСЯК СИДИТ?

    while (1)
    {
    if (lcd_out == 1)
    {
    lcd_clear();
    lcd_gotoxy(0,0);
    delay_ms(5);
    lcd_puts(&rx_buffer[1]);
    lcd_out = 0;
    rx_wr_index= 0;
    rx_counter = 0;
    }
    }
    }

  • Ввожу *123456$ всё окей выводится,а *1234567$ после 7 дальше выводятся кракозябры какие-то почему я не могу целую строку заполнить???

  • У вас размер буфера 8 байт, а вы пытаетесь вывести больше

  • Спасибо разобрался.У меня проблема была в передающем контроллере.В протеусе всё работает БУДЕТ ЛИ РАБОТАТЬ В ЖЕЛЕЗЕ???????

  • Это от вас зависит

  • Подскажите пожалуйста, у меня вот буфер настроен КодомВизардом, а как из такого буфера брать строку и помещать ее в переменную? Или точнее как передать по UART к примеру число 2123483 и записать это число в переменную?

    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
    {
    rx_buffer[rx_wr_index++]=data;
    #if RX_BUFFER_SIZE == 256
    // special case for receiver buffer size=256
    if (++rx_counter == 0) rx_buffer_overflow=1;
    #else
    if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
    if (++rx_counter == RX_BUFFER_SIZE)
    {
    rx_counter=0;
    rx_buffer_overflow=1;
    }
    #endif
    }

  • сначала читать http://avr-start.ru/?p=4557 потом почитать в гугле про принцип fifo, после внимательно посмотреть на rx_buffer

  • а как программно реализовать все оставшиеся «ноги» в 9-ти контактном КОМ порте: RST, DTR….?

  • Это точно рабочий код? Не все переменные которые в прерывании изменяются volatile, WinAVR такие переменные выкидывает( . И в main, как выводится всё слово, если используются данные по адресу первой ячейки массива, т.е только буква e появится на экране?

  • посмотрите реализацию LCD_SendString и поймете. по поводу того как оптимизируется код в winavr не могу сказать ибо в нем не работал

  • Я понял, тут она не описана поэтому вопрос возник. А библиотека откуда взята на вывод? Я просто не использую библиотеки, сам для себя делаю.

  • скачайте проект, там все сырцы есть, можно посмотреть

  • у меня вопрос учител. как подсоеденить контроллер (вывод UART) к компьютеру и какую терминальную программу лучше использовать.И просьба не обижайтесь когда вас называют учител.этот человек со средней Азии.я сам не русский на половину.Говорю вам спасибо за ваши уроки они мне помогают.А пока сам не проэксперементируеш.с проектами .ничего не поймёш

  • я не обижаюсь, просто никого не учу, весь материал это мое видение процесса, кому надо тот находит для себя полезное. поэтому не являюсь учителем, а когда вы пишете такое, то звучит как стеб. http://avr-start.ru/?p=509

  • Здравствуйте. Буду очень благодарен за любую помощь. Пытаюсь по UART получить заведомо известную строку и по этой строке выполнить определенное действие. Написал такой Код в CodeVision

    volatile char led_ON[7]=»Led_ON»; //верная строка для включения светодиода
    volatile char led_OFF[7]=»Led_OF»;// верная строка для выключения светодиода
    volatile char buff[6]; // буфер для размещения пришедших символов

    while (1)
    {

    // gets(buff,6);

    if(gets(buff,6)) // если пришедшие символы размещены в буфере
    { // сравним их с заведомо известными строками

    if ( strcmp(led_ON, buff) == 0) // если строки в массивах led_ON и buff совпадают
    { // включим светодиод
    PORTB.0=1;
    }

    if ( strcmp(led_OFF, buff) == 0) // если строки в массивах led_OFF и buff совпадают
    { // выключим светодиод
    PORTB.0=0;
    }

    printf(«%s»,buff,»\r\n»);// выведем то что есть в буфере
    }

    }

    вообщем как только я отсылаю по UART строку «Led_ON» светодиод загорается, отправляю «Led_OF» светодиод потухает. И так можно делать много раз и все работает.

    Но стоит отправить что-то другой, например «123» и после этого команды «Led_ON» и «Led_OF» не работают(.

    И по UART ф-цией printf() я получаю уже, что-то вроде _ONLed, d_ONLe… те же символы, но в разном порядке. Как с этим бороться?) Заранее большое спасибо.

  • раз у вас строка всегда одной длины, то проще всего при каждом принятом байте сравнивать содержимое последних n байт, со своими строками. так как вы хотите не получится. у вас функция ожидает 6 байт, вы толкаете их в разнобой, но всегда начинаете их складывать с нулевого. вот и результат.

  • По этой статье все стало намного светлее в моеи голове,спасибо за статью.
    Учитель значит господин,слово господин на русский переводиться как учитель ))

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

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

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