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

ATmega8: работа на Си с USART/UART

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

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

Так же как и с таймерами, для работы c USART имеется набор прерываний:

и регистров:

В книге "Практическое программирование МК Atmel AVR" Ю.Ревич очень не советует использовать прерывания для работы c USART, потому-что, к примеру, чтение из буфера USART подразумевает ожидание когда в этом буфере что-то появится. Если это делать из прерывания, то другие прерывания повиснут в ожидании пока медленый USART не получит свои данные. В то время как цикл ожидания в главном теле программы вполне нормальная вещь и не мешает работе периферии.

Инициализация

Примеры инициализации USART приведены в оффициальном руководстве к ATmega8:

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

скорость=частота_кварца/(16*битрейт -1);

.т.е. для 16МГц кварца и битрейта 9600 будет: 16000000/(16*9600)-1 = 103.1(6)

т.е. 103 если округлить до целого. Скорость также можно посмотреть в таблицах официального руководства:

После уcтановки скорости, следут разрешить прием и передачу по USART установкой флагов TXEN и RXEN в регистре UCSRB:

Последнее, что нужно сделать, это указать режим работы 8N1 установкой флагов UCSZ0, UCSZ1, URSEL в регистре UCSRC:


Простейшая программа для работы с USART:


/* uart.c sends 'H' symbol every second via UART for AVR ATmega8

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

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

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

        for (;;)
        {
		_delay_ms(1000);
		UDR='H';
        }


        return 0;
}

Здесь каждую секунду, через USART, микроконтроллер посылается символ H.

Этот пример шлет уже строку:

/* send.c sends string  every second via UART for AVR ATmega8

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

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>

register unsigned char i asm("r28");

int writeSerial(char* str)
{
	for(i=0;i<strlen(str); i++)		//fixed см. коментарии (25.07.2020)
	{
		while(!(UCSRA&(1<<UDRE))){}; // wait ready of port
		UDR = str[i];
	}
	return 0;
}


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

        for (;;)
        {
		_delay_ms(1000);
		writeSerial("Hello World!\n");

        }
        return 0;
}

Последний пример сначала читает из USART строку с символом двоеточия на конце, и затем посылает ее обратно уже без двоеточия:

/* serial.c read string via UART with symbol ':' as End-Of-Line
and write this string in UART backward

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

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

const unsigned char MAX_STRING=32;

register unsigned char i asm("r28");

int writeSerial(char* str)
{
	for(i=0;(i<MAX_STRING && str[i] != ':'); i++)
	{
		while(!(UCSRA&(1<<UDRE))){};
		UDR = str[i];
	}
	return 0;
}


int readSerial(char* str)
{
	i=0;
	do {
		while(!(UCSRA&(1<<RXC))) {};
		str[i]=UDR;
		i++;
	} while (str[i-1] != ':' && i <MAX_STRING);

	return 0;
}


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

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

        for (;;)
        {
		char data[MAX_STRING];
		_delay_ms(1000);
		readSerial(data);
		PORTB ^= (1<<PB5); // blink

		_delay_ms(1000);
		writeSerial(data);

		PORTB ^= (1<<PB5); // blink for visual control

		uint8_t k; // clear array
		for(k=0; k < MAX_STRING; k++) data[k]=0;
        }


        return 0;
}

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

Пример работы последней программы: