Зеркало сайта: vivacious-stockings-frog.cyclic.app

ATmega8: работа на Си с USART/UART через прерывание

разделы: AVR , UART , дата: 16 августа 2015г.


UART для передачи использует два
провода, один на прием, другой
на передачу. Поэтому протокол
является полнодуплексным. Однако,
регистр данных UDR один.

Работа с UART через прерывания мне показалась более эффективной нежели через циклы ожидания. Потому что, программа кроме того что принимает или передает что-либо через UART, она, как правило, занята еще какими-то задачами, будь то опрос датчиков или вывод информации на дисплей. И в таком случае возникает совсем не простая проблема эффективного переключения между задачами. И если, например, ожидание приема корректных данных затянется то микроконтроллер по сути подвиснет. Делать прерывание ожидания по таймауту, это лишний код которого можно избежать, если реализовать рабту с UART через прерывания. Ниже приведен исходник эхо-программы которая читает строку через UART и посылает ее обратно. Строка должна оканчиваться символом двоеточия. В программе используется USART_RXC вектор, который срабатывает когда на порт что-то поступило. Обработчик прерывания это "что-то" переносит в рабочий буфер. Если принятый байт является символом двоеточия, то устанавливается флаг done, по которому главная программа понимает что были приняты какие-то данные. Конечно, следовало бы принятую строку заносить в сетк, но в данном случае решил исходник не усложнять.


/* for ATMEGA-8 echo_serial.c read string via UART with symbol ':' as End-Of-Line
and write this string in UART backward
site: http://countzero.weebly.com

compile:
avr-gcc -mmcu=atmega8 -Wall -Os -o echo_serial.elf echo_serial.c
avr-objcopy -O ihex echo_serial.elf echo_serial.hex
*/

#define F_CPU 16000000UL
#define LEN 32
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

char buffer[LEN];

register unsigned char IT asm("r16");
volatile unsigned char done;
volatile unsigned char IDX;

inline void clearStr(char* str)
{
	for(IT=0;IT<LEN;IT++)
		str[IT]=0;
}

void writeSerial(char* str)
{
	IT=0; while (str[IT] != 0 && IT < LEN)
	{
                while(!(UCSRA&(1<<UDRE))){};
                UDR = str[IT];
		IT++;
        }
}

ISR(USART_RXC_vect)
{
	char bf= UDR;
	buffer[IDX]=bf;
	IDX++;

	if (bf == ':' || IDX >= LEN)
	{
		IDX=0;
		done=1;
	}

}

void blink13(uint8_t count)
{
	PORTB |= (1<<PB5);
	count =(count <<1);count--; //count=(count*2)-1;
	for (IT=0;IT<count;IT++)
	{
		_delay_ms(500);
		PORTB ^= (1<<PB5);
	};
};

int main(void)
{
	// USART init
        UBRRL=103;
        UCSRB=(1<<TXEN)|(1<<RXEN)|(1<<RXCIE);
        UCSRC=(1<<URSEL)|(3<<UCSZ0);

        DDRB |= (1<<PB5); //  pinMode(13,OUTPUT);

	blink13(3); //ready indication
	IDX=0;
	done=0;
	sei();

	for (;;)
        {
		if (done)
		{
			PORTB |= (1<<PB5);
			writeSerial(buffer);
			clearStr(buffer);
			PORTB &= ~(1<<PB5);
			done=0;
		}
	}
        return 0;
}

Здесь максимальная длина принимаемой строки 32 байта. blink13() это вспомогательная функция, мигает диодом заданное количество раз. Максимальное значение 127. При старте программы, происходит трекратное мигание, что сигнализирует о том, микроконроллер начал работу и готов к приему. Программа весит что-то около 330 байтов.