Помогите чайнику с АЦП на avr

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Ребятки, хочу сделать цифровой вольтметр со статической индикацией, на atmega8.
порты D b B использую для вывода на 7сегментники значение целой и дробной части напряжения, мне этого достаточно.
Но вот что то я делаю нетак, потому что рисовать он не хочет, показывает нули.
Видимо что то не правильно в функции обработки ацп, потому что если в программу с момента расчета напряжения присвоить константу, т.е. переменной u, то считает и показывает все правильно.

Вот собственно код программы:

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

#include <avr/io.h>
#include <util/delay.h>
	DDRB=0b11111111;
	DDRD=0b11111111;
	char mas[10]={63,6,91,79,102,109,125,7,127,111};// массив кодов соответствующих цифрам на 7-ми сегментном индикаторе
	unsigned int u;
	float napr;
	unsigned char seg1;
	unsigned char seg2;
	
void ADC_init()
	{
		ADMUX = 0b11100000;//Использование внутреннего ИОНа, ADLAR=1(8 старших битов пишутся в ADCH)
		ADCSRA=0b10001011;//АЦП включен,прерывание ацп разрешены, тактовая частота -/8
	}
unsigned int ADC_result(void)
	{
		_delay_us(10);//задержка для стабилизации входного напряжения
		ADCSRA|=0x40;//начинаем преобразование (ADSC = 1)
		while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
		ADCSRA|=0x10;//устанавливаем ADIF
		return ADCH;// ADCH содержит значение старших 8 битов значения АЦП
	}
void main (void)
	{
	while (1)
	{
			u=ADC_result;  
			napr=u*0,01;// Напряжение вычисляется из кода АЦП в его реальное значение
			seg1=napr; //т.к. seg1- пренадлежит к типу char - отбрасывается дробная часть
			seg2=((napr-seg1)*10);// Считается значение десятых долей напряжения
		PORTB=mas[seg1];	//вывод целой части значения напряжения
		PORTD=mas[seg2];	//вывод значения дробной части	
	}
}
Реклама
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

Приведите в порядок типы переменных.
Docendo discimus
Контактная информация:
Реклама
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

pyzhman писал(а):Приведите в порядок типы переменных.
а что собственно с ними нетак?
Модератор
Аватара пользователя
Сообщения: 13490
Зарегистрирован: Ср ноя 26, 2008 16:34:25
Откуда: Тамбовская обл.

Сообщение ploop »

В упор не вижу, где у вас вызывается ADC_init()
И прекратите использовать магические числа, типа ADMUX = 0b11100000. Все биты именованы, и, как правило, их имена совпадают с даташитовскими.
Пример:

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

ADCSRA =
  _BV(ADEN) |         // Включить АЦП
  _BV(ADIE) |         // Разрешить прерывание
  _BV(ADSC) |         // Запустить первое преобразование
  _BV(ADATE);         // Автопреобразование
И без комментариев код читается, если чтец хоть раз в даташит заглядывал.
Реклама
Эиком - электронные компоненты и радиодетали
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

ploop писал(а):В упор не вижу, где у вас вызывается ADC_init()
точно, а где её нужно вызывать, в начале функции main?
Реклама
Модератор
Аватара пользователя
Сообщения: 13490
Зарегистрирован: Ср ноя 26, 2008 16:34:25
Откуда: Тамбовская обл.

Сообщение ploop »

Там, где нужно инициализировать АЦП :)
У вас - перед бесконечным циклом.

Кстати, u=ADC_result; не скомпилится. Надо u=ADC_result(); :)
Реклама
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Вот, постарался учесть все замечания, все равно - не пашет((

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

#include <avr/io.h>
#include <util/delay.h>
	char mas[10]={63,6,91,79,102,109,125,7,127,111};// массив кодов соответствующих цифрам на 7-ми сегментном индикаторе
	unsigned int u;
	float napr;
	unsigned char seg1;
	unsigned char seg2;
	
void ADC_init()
	{
		ADMUX|=(0<<MUX0)|(0<<MUX1)|(0<<MUX2)|(0<<MUX3)|(1<<REFS1)|(1<<REFS0)|(1<<ADLAR);//Использование внутреннего ИОНа, ADLAR=1(8 старших битов пишутся в ADCH)
		ADCSRA|=(1<<ADEN)|(1<<ADIE)|(3<<ADPS0);//АЦП включен,прерывание ацп разрешены, тактовая частота -/8
	}
unsigned int ADC_result(void)
	{
		_delay_us(10);//задержка для стабилизации входного напряжения
		ADCSRA|=0x40;//начинаем преобразование (ADSC = 1)
		while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
		ADCSRA|=0x10;//устанавливаем ADIF
		return ADCH;// ADCH содержит значение старших 8 битов
	}
void main (void)
	{
			DDRB=0b11111111;
			DDRD=0b11111111;
			ADC_init();

	while (1)
	{
			u=ADC_result();  
			napr=u*0,01;// Напряжение вычисляется из кода АЦП в его реальное значение
			seg1=napr; //т.к. seg1- пренадлежит к типу char - отбрасывается дробная часть
			seg2=((napr-seg1)*10);// Считается значение десятых долей напряжения
		PORTB=mas[seg1];	//вывод целой части значения напряжения
		PORTD=mas[seg2];	//вывод значения дробной части	
	}
}
И вот так код выглядит значительно лучше.
ploop
Модератор
Аватара пользователя
Сообщения: 13490
Зарегистрирован: Ср ноя 26, 2008 16:34:25
Откуда: Тамбовская обл.

Сообщение ploop »

Проверьте ADCSRA: (пишу по памяти)
ADIE - включает прерывания. У вас не используются. Не нужно значит
АЦП у вас включается, но не запускается преобразование. Как минимум его нужно запустить, установив бит ADSC, а чтобы сам на автомате крутился - ещё ADATE.
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Бит ADSC устанавливается в функции ADC_result
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Эхххх...
Решил я воспользоваться генератором начального кода в Codevisionavr.

вот что получилось:

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

char mas[10]={63,6,91,79,102,109,125,7,127,111};
	unsigned int u;
	float napr;
	unsigned char seg1;
	unsigned char seg2;
unsigned char read_adc(void)
{
delay_us(10);
ADCSRA|=0x40;
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCH;
}
void main(void)
{
PORTB=0x00;
DDRB=0xFF;
PORTD=0x00;
DDRD=0xFF;
ADMUX=0xE0;
ADCSRA=0x83;
while (1)
      {
     u=read_adc();  
			napr=u*0.01;			
			seg1=napr;				
			seg2=((napr-seg1)*10);	
		PORTB=mas[seg1];			
		PORTD=mas[seg2];		
      }
}


но вот теперь я столкнулся с другой проблемой - ацп работает только когда напряжение меньше 0,5 вольта, а при значениях более 0,5 - показывает все равно 0,5
вот как это выглядит:
Изображение
Изображение
Изображение
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

Приведите переменные в порядок. По-русски говоря, используйте одного типа. Выкиньте вещественную - нафига она не нужна.
Docendo discimus
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Еще это похоже на зашкал. Для начала AVCC запитать, и ведь наверняка протеус на это ругается.
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Engineer_Keen писал(а):Еще это похоже на зашкал. Для начала AVCC запитать, и ведь наверняка протеус на это ругается.
А чем запитать AVCC? напряжением питания?

О великий!!!
Честь тебе и хвала!
Все заработало!
Последний раз редактировалось dictor Чт янв 23, 2014 16:33:22, всего редактировалось 1 раз.
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

pyzhman писал(а):Приведите переменные в порядок. По-русски говоря, используйте одного типа. Выкиньте вещественную - нафига она не нужна.
Сделал так:

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

while (1)
      { 
			napr=read_adc()*0.01;			// Напряжение вычисляется из кода АЦП в его реальное значение
			seg1=napr;				//т.к. seg1- пренадлежит к типу char - отбрасывается дробная часть
			seg2=((napr-seg1)*10);	// Считается значение десятых долей напряжения
		PORTB=mas[seg1];			//вывод целой части значения напряжения
		PORTD=mas[seg2];		
      }
}
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Для симулятора да, питание подвести +5V или VCC. В реальности можно между ними еще фильтр поставить.
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Engineer_Keen писал(а):Для симулятора да, питание подвести +5V или VCC. В реальности можно между ними еще фильтр поставить.
А в реальной схеме?

И ещё вопрос, если брать за опорное питание напряжение AVCC, то к AREF какой конд цеплять, и куда его второй конец вести?
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Как делать в реальной схеме написано в даташите (там простая LC-цепочка), но для простых показометров достаточно просто соединить VCC и AVCC как в симуляторе, а вместо фильтра просто усреднять значения программно.
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Спасибо всем за поддержку и помощь, без вас бы я ещё год над этиммяукал :)))
Родился
Сообщения: 13
Зарегистрирован: Сб мар 10, 2012 14:07:20
Откуда: Краматорск

Сообщение dictor »

Мдаа...
Собрал я схему в железе, а оно чего то гонит снова...
Цифры не отображаются на индикаторах вообще адекватно.
Просто разом мерцают все сегменты индикатора, то ярче, то тусклее, причем при подаче сигнала на ацп изменяется характер мерцаний, но они все равно незакономерны, горя все сегменты, просто с разной яркрстью...
Что бы это могло быть?
Встал на лапы
Сообщения: 100
Зарегистрирован: Пт авг 02, 2013 07:20:04

Сообщение titoFF »

Може данные слишком часто обновляются, цифры меняются часто, а глаз неуспевает...
Ответить

Вернуться в «AVR»