Страница 1 из 2

Помогите разобратся с работой UART в atmege8

Добавлено: Чт май 02, 2013 09:57:21
Dmti
По случаю достался релейный блок от газоанализатора "Хоббит" с убитым камнем
(Atmega8L). Камень перепаял на новый и теперь надо в него что-нибудь залить. В середине
90х программировал на турбо си то этому решил что-нибудь с ваять сам. Скачал AVR Studio 5 и
после копирования и объединения нескольких примеров для начинающих получилось вот
это.

Код: Выделить всё

//#include <avr/delay.h>	// добавил библиотеку выполнения пауз
//В будущем сделать реальные часы и реализовать недельный таймер
#include <avr/io.h>			// для работы с портами
#include <stdio.h>			// для включении UART
#include <avr/eeprom.h>		// для сохранении в пзу
#include <avr/interrupt.h>	// и прерывание будем использовать

uint8_t EEMEM ADDR;			//Сетевой адрес устройства
uint8_t EEMEM STATC;		//Состояние реле порта С
uint8_t EEMEM STATD;		//Состояние реле порта D

int Tmp[5];					//Временный буфер
volatile  char ready = 0;	//Получена ли строка? 
volatile  int bufsize = 0;	//Длина строки

int Data[5];
#define Adres	Data[0]		//адрес опрашиваемого устройства		
#define Comanda Data[1]		//выполняемая команда	
#define DataA	Data[2]		//Вспомогательная переменная 
#define DataC	Data[3]		//Значение порта С 
#define DataD	Data[4]		//Значение порта D 


void uart_init( void )
{
	//настройка скорости обмена
	//инициализируем USART
	UBRRH = 0;
	UCSRA = 0;					//Кварц 4мГц		
	UBRRL = 0x19;				//Скорость на скорость 9600
	
	//8 бит данных, 1 стоп бит, без контроля четности
	UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
	//разрешить прием, передачу данных и прерывание по приёму байта
	UCSRB = ( 1 << TXEN ) | ( 1 << RXEN ) | (1 << RXCIE );
	
	//Настройка внешних портов
	DDRC = 0x3f;				// назначаем 0-5 линии порта C на  
выход
	DDRD= 0xf8;					// назначаем 4-7 линии  
порта D на выход
	DataC = STATC;				// считали значение порта С из  
EEPROM
	PORTC = DataC;				// и загнали в порт С
	DataD = STATD;				// считали значение порта D из  
EEPROM
	PORTD = DataD;				// и загнали в порт D
	return;
}


//Сообщить состояние портов
void ReadData(void )
{
	for (int i = 0;  i < 4; i++)
  {
	UDR = Data[i];  
  } 
  return;
}

//***********************************Настраиваем проц.*****************************************
//Изменить состояние портов
void WriteData( void )
{  
 	PORTC = DataC & 0x3f;		// и устанавливаем в нужное состояние
	PORTD = DataD & 0xf8;		// и устанавливаем в нужное состояние
  return;
}

//*************************************блок функций********************************************
//Изменить адрес устройства в сети
void RenADDR ( void )
{
	ADDR = Adres;				//Адрес сохраняем сразу 	
	return;
}

//Сохранить состояние портов
void SaveData (void)
{
	STATC = DataC;	//Сохраняем состояние порта С в ЕРОМ
	STATD = DataD;	//Сохраняем состояние порта D в ЕРОМ	
}

//Копируем из одного массива Tmp в другой рабочий Data
void CopyD (void)
{
 for (int i = 0;  i < bufsize; i++)
  {
	Data[i] = Tmp[i];
	Tmp[i] = 0;  
  }
  bufsize = 0;
  ready = 0;	
}
//************************************Обрабатываем события**************************************
//Обработчик прерывания чтение из порта
ISR (USART_RXC_vect)
{ 
	if ((bufsize < 5)	&&  (bufsize < sizeof(Tmp)-1))	Tmp[bufsize++] = UDR;		 
//Сохраняем следующее сообщение	
	if (bufsize == 4 && ADDR == Tmp[3]) ready = 1; bufsize = 0;				 
		//Строка получена 5 значений
	 	
}

/*/Обработчик прерывания чтение в порт
ISR (USART_TXC_vect)
{ 
	if ((bufsize < 5)	&&  (bufsize < sizeof(Tmp)-1))	UDR = Tmp[bufsize++];		 
//Читаем следующее сообщение	
	if (bufsize = 4) ready = 1;							 
						//Строка значений отправлена
	 	
}	//*/

//обработчик прерывания моргаем светодиодом
ISR(TIMER1_COMPA_vect)
{
	PORTD ^=0x08;
//Посылаем слово для проверки порта ПОТОМ УДАЛИТЬ!
	unsigned char  dt = ('O');		UDR = dt;
			dt = ('k');		UDR = dt;
			dt = ('!');		UDR = dt;
			dt = (0x0d);		UDR = dt;
			dt = (0x0a);		UDR = dt;
}
//**************************************Основная программа**************************************
int main(void)
{
//Инициализируем порт
		uart_init();

//Запускаем таймер
		TCCR1A=0x40;					//при совпадении  
уровень OC1A меняется на противоположный
		TCCR1B=0x05;					//CLK/1024
		TIMSK = 0x10;					//разрешаем  
прерывание по совпадению
		sei();							 
//разрешаем прерывания глобально

//бесконечный цикл								
	while(1)									
	{
		if(ready) {							//Если  
строка получена то выполняем тело программы
			CopyD();						 
//Сначала копируем временный массив в постоянный
			//Команды управлением процессом
			if(Comanda == 1) ReadData();	//Сообщить состояние портов
			if(Comanda == 2) WriteData();	//Изменить состояние портов
			if(Comanda == 3) RenADDR();		//Меняем адрес
			if(Comanda == 4) SaveData();	//Сохраняем предустановки портов
			//Надо доделать в будущем		//Корректируем  
время
										 
	//Запускаем таймер
			}
	}
}
Суть в неработающем UART. Специально дописал в прерывании моргании лампочки вот такой
код

Код: Выделить всё

//Посылаем слово для проверки порта ПОТОМ УДАЛИТЬ!
	unsigned char dt = ('O');		UDR = dt;
	unsigned char dt = ('k');		UDR = dt;
	unsigned char dt = ('!');		UDR = dt;
	unsigned char dt = (0x0d);		UDR = dt;
	unsigned char dt = (0x0a);		UDR = dt;
На ногах pin30, pin31, pin32 смотрю осциллографом но не чего не вижу такое ощущение что
выходы в подвешенном состоянии и где собака порылась не пойму. Если не трудно ткните
носом чтоб заработало.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Чт май 02, 2013 11:39:43
Vov123
После каждого

Код: Выделить всё

UDR = dt;
введи

Код: Выделить всё

loop_until_bit_is_clear(UCSRA, 5);
ожидание того,что буфер пуст и готов к приёму новых данных.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пт май 03, 2013 15:02:17
Dmti
Вы хотите сказать, что я должен сделать вот так

Код: Выделить всё

unsigned char	dt = ('O');		UDR = dt;	loop_until_bit_is_clear(UCSRA, 5);
					dt = ('k');		UDR = dt;	loop_until_bit_is_clear(UCSRA, 5);
					dt = ('!');		UDR = dt;	loop_until_bit_is_clear(UCSRA, 5);
					dt = (0x0d);	UDR = dt;	loop_until_bit_is_clear(UCSRA, 5);
					dt = (0x0a);	UDR = dt;	loop_until_bit_is_clear(UCSRA, 5);
Нигде не встречал в примерах подобную конструкцию, если можно поподробней (цикл пока бит не удалён), а что означает 5?. Пятый бит регистра UCSRA не установится? Это, наверно, надо писать в условии. После изменения на такую конструкцию даже лампочка шмыргать перестала.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пт май 03, 2013 15:09:06
da-nie
А почему нельзя просто писать UDR='A'; и т.д.?
цикл пока бит не удалён
Это ожидание завершения передачи. По окончании передачи сбрасывается флаг.
Только лучше делать наоборот. Сначала ждать, пока линия освободится, а затем уже передавать.
В Win AVR это делается как:

Код: Выделить всё

while (!(UCSRA&(1<<UDRE)));
UDR='A';

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пт май 03, 2013 15:46:51
Dmti
Изменил вот на это:

Код: Выделить всё

                                        if (!(UCSRA&(1<<UDRE))) UDR='o';
					if (!(UCSRA&(1<<UDRE))) UDR='k';
					if (!(UCSRA&(1<<UDRE))) UDR='!';
					if (!(UCSRA&(1<<UDRE))) UDR=(0x0d);
					if (!(UCSRA&(1<<UDRE))) UDR=(0x0a);
Лампочка с ново моргать стала, на ноге проца SP30 появился логический ноль, и всЁ.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пт май 03, 2013 15:59:19
Dmti
С такой конструкцией тоже самое

Код: Выделить всё

while (!(UCSRA&(1<<UDRE)));	UDR='o';
					while (!(UCSRA&(1<<UDRE)));	UDR='k';
					while (!(UCSRA&(1<<UDRE)));	UDR='!';
					while (!(UCSRA&(1<<UDRE)));	UDR=(0x0d);
					while (!(UCSRA&(1<<UDRE)));	UDR==(0x0a);

Лампочка моргает на ногах 30, 31, 32 соответственно 0,1.0 и не меняется.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пт май 03, 2013 17:41:37
da-nie
А у вас прерывание сделано? Тогда не будет работать цикл ожидания.
Запрещайте все прерывания и из главной функции просто посылайте байты вот тем вот циклом. Не из прерывания таймера.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 05:51:42
Dmti

Код: Выделить всё

 
А у вас прерывание сделано? 
Мне казалось, что сделал

Код: Выделить всё

void uart_init( void )
{
	//настройка скорости обмена
	//инициализируем USART
	UBRRH = 0;
	UCSRA = 0;					//Кварц 4мГц		
	UBRRL = 0x19;				//Скорость на скорость 9600
	
	//8 бит данных, 1 стоп бит, без контроля четности
	UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );
	//разрешить прием, передачу данных и прерывание по приёму байта
	UCSRB = ( 1 << TXEN ) | ( 1 << RXEN ) | (1 << RXCIE );
	
	//Настройка внешних портов
	DDRC = 0x1f;				// назначаем 0-5 линии порта C на выход
	DDRD= 0xfC;					// назначаем 3-7 линии порта D на выход
	DataC = STATC;				// считали значение порта С из EEPROM
	PORTC = DataC;				// и загнали в порт С
	DataD = STATD;				// считали значение порта D из EEPROM
	PORTD = DataD;				// и загнали в порт D
	
	//Запускаем таймер
		TCCR1A=0x40;			//при совпадении уровень OC1A меняется на противоположный
		TCCR1B=0x05;			//CLK/1024
		TIMSK = 0x10;			//разрешаем прерывание по совпадению
		sei();					//разрешаем прерывания глобально
	return;
Или вы имеете какие-то ещё прерывания?

Код: Выделить всё

Тогда не будет работать цикл ожидания.
Вы имеете ввиду while (!(UCSRA&(1<<UDRE)));
или какой цикл?

Код: Выделить всё

Запрещайте все прерывания и из главной функции просто посылайте байты вот тем вот циклом.
То есть отсюда

Код: Выделить всё

//бесконечный цикл								
	while(1)									
	{
		if(ready) {							//Если строка получена то выполняем тело программы
			CopyD();						//Сначала копируем временный массив в постоянный
			//Команды управление процессом
Лампочка без посылки в UART моргает 2 раза в секунду с посылкой 1 раз в 7 секунд, то есть, мне кажется, посыл идет и из прерывания, или я не прав?

Код: Выделить всё

Не из прерывания таймера.
То есть, вы думаете, что из прерывания вызов другого прерывания может создавать проблемы?

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 06:21:44
da-nie
Вы имеете ввиду while (!(UCSRA&(1<<UDRE)));
или какой цикл?
Да.

Чтобы не мучиться с вашей программой, я просто приведу простейшую работу с UART на atmega8:

Код: Выделить всё

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//библиотеки
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/iom8.h>

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//частота контроллера
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define F_CPU 1000000L
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//скорость передачи данных UART, бит/с
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define UART_SPEED 1200L

void main(void)
{
 //устанавливаем режим передачи данных
 UCSRB=(1<<RXEN)|(1<<TXEN);
 //RXCIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по завершению приёма по UART разрешено
 //TXCIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по завершению передачи по UART разрешено
 //UDRIE=1 и прерывания разрешены (бит I=1 в регистре SREG) : прерывание по опустошению регистра данных UART разрешено
 //RXEN=1 : активация приёмника, вывод D0 становится входом UART.
 //TXEN=1 : активация передатчика, вывод D1 становится выходом UART.
 //CHR9=1 : длина передаваемой посылки с становится равной 11 бит (9 бит данных + старт-стоповый бит + стоп-бит).
 //RXB8-расширенный стоп-бит
 //TXB8-расширенный стоп-бит
 //вычисляем значение регистра скорости передачи данных
 unsigned long speed=F_CPU/(16L*UART_SPEED)-1L;
 UBRRH=speed>>8;
 UBRRL=speed&0xFF;

 while(1)
 {
   while (!(UCSRA&(1<<UDRE)));
   UDR='A';
  _delay_ms(10);
 }
}
Вот это зашивайте, запускайте HyperTerminal с нужной скоростью (управления потоком НЕТ, скорость 1200, 8 бит, чётность не проверяется) и смотрите, что принимается. А лампочка это не информативно. :)
То есть, вы думаете, что из прерывания вызов другого прерывания может создавать проблемы?
Оно у вас просто не вызовется. Вы же не использовали вложенные прерывания. Поэтому пока вы в прерывании таймера, никакие другие прерывания не работают. Правда, в данном случае вы их и не ждёте в таймере. Так что это не критично.
Критично вот что. Я не очень помню, какие флаги сбрасываются в прерываниях UART. Это вполне может не давать работать циклу ожидания.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 07:18:23
Dmti
То есть разрешить

Код: Выделить всё

 //UDRIE=1 и прерывания разрешены (бит I=1 в регистре SREG) :
Сейчас попробую.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 07:40:39
Dmti
Изменил на

Код: Выделить всё

/разрешить прием, передачу данных и прерывание по приёму байта и опустошению буфера
	UCSRB = ( 1 << TXEN ) | ( 1 << RXEN ) | (1 << RXCIE ) | (1 << UDRIE);
но не чего ни изменилось, только лампочка моргать перестала, а изначально я делал по этому примеру
http://www.atmega8.ru/wiki/view/doc.17.html

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 08:03:06
Vov123
Прошу пардону,WinAvr не моя стезя,ошибся в макросах,конечно-же надо

Код: Выделить всё

loop_until_bit_is_set(UCSRA, 5);
Пример на mega16 в архиве.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 08:31:34
Dmti
Вообще мне не совсем понятно, откуда берётся эта функция

Код: Выделить всё

loop_until_bit_is_set(UCSRA, 5);
где она определена? Если можно расскажите поподробней.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 08:51:41
Vov123
Отсутствие "волшебных слов" отбивают желания помогать на форумах.
Читайте книгу Шпака,которая про C для AVR.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Сб май 04, 2013 16:54:45
da-nie
Изменил на
Не нужно изменять вашу программу. Вы сперва добейтесь обычной работы UART, а потом уже модифицируйте программу, как угодно. Нужен путь от простого к сложному, а не наоборот. Сначала откомпилируйте мой фрагмент и зашейте его в контроллер. Посмотрите, работает ли вообще UART. Потому как бывает и что преобразователь интерфейса типа MAX232 глючит (скажем, погорел). И только после того, как UART точно заработает потихоньку наращивайте проект проверяя на каждом этапе.

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Вс май 05, 2013 11:22:59
Dmti
Всё разобрался, INT0 надо принудительно перед записью в UDR устанавливать в 1, а после сбрасывать, обязательно. Vov123 правильно говорил про проверку но конструкции

Код: Выделить всё

while ( !(1<<UDRE) ) { UDR = Data[i] };
while ( !(1<<UDRE) );  UDR = Data[i];
по чему-то не работали, а так стало всё норм.

Код: Выделить всё

while ( !( 1<<UDRE) ){};
	UDR = Data[i];
и по железу толи сам убил, толи попалась глючная микруха SP2102. Завтра магазин откроется перепаяю. Главное осциллографом вижу пакеты импульсов и INT0 переключается

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Вс май 05, 2013 12:15:01
da-nie
INT0
Но ведь это внешнее прерывание. Разве вы его используете в вашей программе?
SP2102
Что это за микросхема?

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Вс май 05, 2013 16:06:41
Dmti
INT0 32 нога говорит микросхеме фирмы silabs SP2102 (переходник USB->UART) в какую сторону данные передавать.
Читайте книгу Шпака,которая про C для AVR.
Сегодня дочитал, но если честно, выражения
loop_until_bit_is_set(UCSRA, 5);
так и не нашел, но нашел в даташите

Код: Выделить всё

C Code Example(1)
void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) )
;
/* Put data into buffer, sends the data */
UDR = data;
}

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Вс май 05, 2013 16:44:13
Vov123
Чего ты читал?Чего честно не нашёл?Стр.337

Re: Помогите разобратся с работой UART в atmege8

Добавлено: Пн май 06, 2013 11:09:37
Dmti
Унас или версия книги не та, или я читал не внимательно, но не нашел.