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 байтов.