АЦП - выбор лапки

Обсуждаем контроллеры компании Atmel.
kobzar
Грызет канифоль
Сообщения: 278
Зарегистрирован: Вт дек 03, 2013 11:04:04
Откуда: Киев
Контактная информация:

Re: АЦП - выбор лапки

Сообщение kobzar »

Так а и сказал что благодарю за мысль - по аналогии я уже накручу то надо!!!
Влодение рускай арфаграфией - это как владение кунг-фу: настаящие мастира не преминяют ево бес ниабхадимости
Реклама
Аватара пользователя
zero648
Вымогатель припоя
Сообщения: 650
Зарегистрирован: Пн июн 18, 2012 12:01:04
Откуда: Челябинская область, Копейск

Re: АЦП - выбор лапки

Сообщение zero648 »

pyzhman писал(а): , что не есть трувэй. :))
А у нас что, биты мультиплексора теперь раскиданы по всему регистру, что-ли? Каким местом здесь не тру?
Можно, конечно, и так, что тоже самое
(0<<MUX0) //ADC0
(1<<MUX0) //ADC1
(2<<MUX0) //ADC2
Реклама
Аватара пользователя
pyzhman
Друг Кота
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск
Контактная информация:

Re: АЦП - выбор лапки

Сообщение pyzhman »

Это не по поводу вашего кода. Это по поводу:
kobzar писал(а):Ето не трувей которым пользуются только КВАВР-щеки! :)
:))
Docendo discimus
Аватара пользователя
zero648
Вымогатель припоя
Сообщения: 650
Зарегистрирован: Пн июн 18, 2012 12:01:04
Откуда: Челябинская область, Копейск

Re: АЦП - выбор лапки

Сообщение zero648 »

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

Re: АЦП - выбор лапки

Сообщение pyzhman »

Согласен. Вообще говоря, интереснее с машиной говорить на ее языке, а не на абракадабрском десятичной системы. Равно как и с людьми. Впрочем, это уже офтоп; не обижусь, если удалят.
Docendo discimus
Реклама
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

Коты, помогите. Мне нужно опрашивать три АЦП лапы, но при опросе, все лапы при опросе имеют значения 1023 1022 1021 (эти значения меняются на 1 или 2). Пользуюсь ATMega 328P 20мГц. Прочитал справочную литературу, сделал сначала программу по опросу одной лапки, все работает замечательно. Если программа опрашивает один из портов и не опрашивает другие, значения получаются правильные. Если же я отправляю опрос трех лапок в цикле, то получаются неправильные значения. Я выложу кусок кода из программы, если вам не тяжело, покажите мою ошибку.

init_adc.h

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

#ifndef INIT_ADC_H_
#define INIT_ADC_H_

volatile unsigned int vuntADCdata00;
volatile unsigned int vuntADCdata01;
volatile unsigned int vuntADCdata02;
volatile unsigned char uchChangeADC;

void init_adc (void);			//Функция инициализации АЦП
void change_adc(void);			//Функция изменения АЦП

#endif /* INIT_ADC_H_ */
init_adc.c

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

#include <avr/io.h>
#include "init_adc.h"
#include <avr/interrupt.h>

ISR (ADC_vect)
{
	if (uchChangeADC == 0)
		vuntADCdata00 = (ADCL | ADCH << 8);	// Считываем ADC PC0
	else
		if (uchChangeADC == 1)
			vuntADCdata01 = (ADCL | ADCH << 8);	// Считываем ADC PC1
		else
			vuntADCdata02 = (ADCL | ADCH << 8);	// Считываем ADC PC2
}

void init_adc (void)			//Функция инициализации АЦП
{
	ADMUX 	|=(0 << REFS1)	| (1 << REFS0)	// Напряжение питания AVcc
			| (0 << MUX3) 	| (0 << MUX2)	| (0 << MUX1)	| (0 << MUX0);	//вход PC0

	ADCSRA	|= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);//20000мГц

	ADCSRA 	|= (1 << ADEN)		//Включение АЦП
			| (1 << ADSC)		//запуск преобразования
			| (0 << ADATE)		// 0 - Одиночное преобразование, 1 - настраеваемое преобразование (регистр ADCSRB (биты ADTS0-ADTS2))
			| (1 << ADIE);		//Разрешение прерывания от компаратора
	uchChangeADC = 0;
}

void change_adc(void)//Функция изменения АЦП
{
	if (uchChangeADC == 0)
	{
		ADMUX 	|=(0 << REFS1)	| (1 << REFS0)	// Напряжение питания AVcc
				|(0 << MUX3) 	| (0 << MUX2)	| (0 << MUX1)	| (1 << MUX0);	//вход PC1
		uchChangeADC = 1;
	}
	else
	{
		if (uchChangeADC == 1)
		{
			ADMUX 	|=(0 << REFS1)	| (1 << REFS0)	// Напряжение питания AVcc
					| (0 << MUX3) 	| (0 << MUX2)	| (1 << MUX1)	| (0 << MUX0);	//вход PC2
			uchChangeADC = 2;
		}
		else
		{
			ADMUX 	|=(0 << REFS1)	| (1 << REFS0)	// Напряжение питания AVcc
					| (0 << MUX3) 	| (0 << MUX2)	| (0 << MUX1)	| (0 << MUX0);	//вход PC0
			uchChangeADC = 0;
		}
	}
	ADCSRA |=(1 << ADSC);		//запуск преобразования
}
main.c

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

void main (void)
{
init_adc();//инициализация ацп
while(1)
{
change_adc();//Функция должна менять лапку АЦП а я потом считываю значения из переменных vuntADCdata00 vuntADCdata01 и vuntADCdata02
}
}

Но в итоге переменные vuntADCdata00 vuntADCdata01 и vuntADCdata02 содержат ересь в виде 1023 1021 1022, помогите!
Кот должен прожить жизнь без сожаления.
Реклама
Аватара пользователя
zero648
Вымогатель припоя
Сообщения: 650
Зарегистрирован: Пн июн 18, 2012 12:01:04
Откуда: Челябинская область, Копейск

Re: АЦП - выбор лапки

Сообщение zero648 »

А если функцию так сделать:

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

void change_adc(void)//Функция изменения АЦП
{
   uchChangeADC++;
   if (uchChangeADC == 3) {uchChangeADC=0;}
   ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0);   // Напряжение питания AVcc
   ADCSRA |=(1 << ADSC);      //запуск преобразования
}
kobzar
Грызет канифоль
Сообщения: 278
Зарегистрирован: Вт дек 03, 2013 11:04:04
Откуда: Киев
Контактная информация:

Re: АЦП - выбор лапки

Сообщение kobzar »

Вобщем и целом, благодаря вашим советам получилось сделать выбор канала через свиг регистра!
Другими словами - правильно и без всяких там разных систем счисления! Я не стану утверждать что мой метод более или менее правильный чем предложенные ранее, просто я педант - и в процессе изучения Си для АВР-ок! Поставленная перед самим собой задача, была сделать и понять так как "Правильно" а не "Будет работать". Конечно я понимаю что со временем обьем полученных знаний умноженный на уровень лени приведет меня к тому что я буду на уровне подсознания оперировать двиочными и десятичными числам автоматом переводя их в названия выставленных битов ... но пока что - я вижу точно что я выставил и зачем! Кроме того меня спрашивали зачем заморачиваться с циклом? Ответ банален - сделав оди раз цыкл - мне больше не надо дописывать строки кода если я допустим захочу использовать 3,5,7 входов ацп! Мне достаточно будет просто изменить длину цикла :)
Примитивная функция для опроса трех ног выглядит так (конечно при использовании более чем первые три входа функцию нужно будет модифицировать но ето выходит за рамки моей задачи)

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

int readADC(int ch)
{
	unsigned char set_admux = ADMUX;
	set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
	if (ch != 0)
	{
		set_admux |= (1<<ch-1);
	}
	ADMUX = set_admux;
	char buffer[12];
	_delay_ms(20);
	ADCSRA |= (1 << ADSC); // Запускаем преобразование
	while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования
	int val = ADC;
	itoa(val, buffer,10);
	return  buffer; // Возвращаем значение АЦП
}
И ее использование для вывода данных например на ЛЦД 3310 может иметь примитивный вид

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

LcdClear();
			for (int i=0;i<=2;i++)
			{
				LcdGotoXYFont(1,i+1);
				LcdStr(FONT_1X,readADC(i));
			}
			LcdUpdate();
				
			_delay_ms(1000);
Ну а дальше добавим несколько замеров и вывод средне арифметического и так далее - но это уже совершенно другая сказка ....
Влодение рускай арфаграфией - это как владение кунг-фу: настаящие мастира не преминяют ево бес ниабхадимости
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

zero648 писал(а):А если функцию так сделать:

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

void change_adc(void)//Функция изменения АЦП
{
   uchChangeADC++;
   if (uchChangeADC == 3) {uchChangeADC=0;}
   ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0);   // Напряжение питания AVcc
   ADCSRA |=(1 << ADSC);      //запуск преобразования
}
Мысль понятна. Я подобную манипуляцию с ADMUX не пробовал делать. Через 7 часов я доберусь до МК и программатора и обязательно попробую и отпишусь о результатах. Спасибо за помощь!
Кот должен прожить жизнь без сожаления.
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

DruidCat писал(а):
zero648 писал(а):А если функцию так сделать:

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

void change_adc(void)//Функция изменения АЦП
{
   uchChangeADC++;
   if (uchChangeADC == 3) {uchChangeADC=0;}
   ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0);   // Напряжение питания AVcc
   ADCSRA |=(1 << ADSC);      //запуск преобразования
}
Мысль понятна. Я подобную манипуляцию с ADMUX не пробовал делать. Через 7 часов я доберусь до МК и программатора и обязательно попробую и отпишусь о результатах. Спасибо за помощь!
Не, не работает такой способ записи. :(

А вот так заработало.

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

#include <avr/io.h>
#include "init_adc.h"
#include <avr/interrupt.h>

ISR (ADC_vect)
{
   if (uchChangeADC == 0)
      vuntADCdata00 = (ADCL | ADCH << 8);   // Считываем ADC PC0
   else
      if (uchChangeADC == 1)
         vuntADCdata01 = (ADCL | ADCH << 8);   // Считываем ADC PC1
      else
         vuntADCdata02 = (ADCL | ADCH << 8);   // Считываем ADC PC2
}

void init_adc (void)         //Функция инициализации АЦП
{
 //  ADMUX    |=(0 << REFS1)   | (1 << REFS0)   // Напряжение питания AVcc (Нет необходимости в этой записи)
 //       | (0 << MUX3)    | (0 << MUX2)   | (0 << MUX1)   | (0 << MUX0);   //вход PC0 (Нет необходимости в этой записи)
   ADCSRA   |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);//20000мГц

   ADCSRA    |= (1 << ADEN)      //Включение АЦП
 //     | (1 << ADSC)      //запуск преобразования (Нет необходимости в этой записи)
         | (0 << ADATE)      // 0 - Одиночное преобразование, 1 - настраевоемое преобразование (регистр ADCSRB (биты ADTS0-ADTS2))
         | (1 << ADIE);      //Разрешение прерывания от компаратора
   uchChangeADC = 0;
}

void change_adc(void)//Функция изменения АЦП
{
   if (uchChangeADC == 0)
   {
      ADMUX = 0х41;   //вход PC1
      uchChangeADC = 1;
   }
   else
   {
      if (uchChangeADC == 1)
      {
         ADMUX = 0x42;   //вход PC2
         uchChangeADC = 2;
      }
      else
      {
         ADMUX = 0x40;   //вход PC0
         uchChangeADC = 0;
      }
   }
  [b] _delay_ms(1);[/b]//Без паузы не работает, у меня программа большая, поэтому 1 мс достаточно
   ADCSRA |=(1 << ADSC);      //запуск преобразования
}
Но вот возникла другая проблема, снятие показаний с канала не совпадают, с логикой. Получается, если я выбираю канал РС0, то данные снимаются с РС2, если РС1, то данные с РС0 и если РС2 то данные снимаются с РС1. Коты, не подскажете почету так? И я не понял, почему заработала запись активизации канала только такой записью ADMUX = 0x40? Еще заработал выбор АЦП, с записью регистра ADMUX предложенный kobzar. По чему так, я не могу понять
Кот должен прожить жизнь без сожаления.
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

kobzar писал(а):Вобщем и целом, благодаря вашим советам получилось сделать выбор канала через свиг регистра!
Другими словами - правильно и без всяких там разных систем счисления! Я не стану утверждать что мой метод более или менее правильный чем предложенные ранее, просто я педант - и в процессе изучения Си для АВР-ок! Поставленная перед самим собой задача, была сделать и понять так как "Правильно" а не "Будет работать". Конечно я понимаю что со временем обьем полученных знаний умноженный на уровень лени приведет меня к тому что я буду на уровне подсознания оперировать двиочными и десятичными числам автоматом переводя их в названия выставленных битов ... но пока что - я вижу точно что я выставил и зачем! Кроме того меня спрашивали зачем заморачиваться с циклом? Ответ банален - сделав оди раз цыкл - мне больше не надо дописывать строки кода если я допустим захочу использовать 3,5,7 входов ацп! Мне достаточно будет просто изменить длину цикла :)
Примитивная функция для опроса трех ног выглядит так (конечно при использовании более чем первые три входа функцию нужно будет модифицировать но ето выходит за рамки моей задачи)

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

int readADC(int ch)
{
	unsigned char set_admux = ADMUX;
	set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
	if (ch != 0)
	{
		set_admux |= (1<<ch-1);
	}
	ADMUX = set_admux;
	char buffer[12];
	_delay_ms(20);
	ADCSRA |= (1 << ADSC); // Запускаем преобразование
	while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования
	int val = ADC;
	itoa(val, buffer,10);
	return  buffer; // Возвращаем значение АЦП
}
И ее использование для вывода данных например на ЛЦД 3310 может иметь примитивный вид

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

LcdClear();
			for (int i=0;i<=2;i++)
			{
				LcdGotoXYFont(1,i+1);
				LcdStr(FONT_1X,readADC(i));
			}
			LcdUpdate();
				
			_delay_ms(1000);
Ну а дальше добавим несколько замеров и вывод средне арифметического и так далее - но это уже совершенно другая сказка ....
А если вот так тебе сделать?

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

unsigned int readADC(int ch)
{
	unsigned char set_admux = ADMUX;
	set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
	if (ch != 0)
	{
		set_admux |= (1<<ch-1);
	}
	ADMUX = set_admux;

	_delay_ms(20);
	ADCSRA |= (1 << ADSC); // Запускаем преобразование
	while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования

	return (ADCL | ADCH << 8); // Возвращаем значение АЦП
}
АЦП 10 битный, сделай возврат данных функции unsigned int (16 бит от 0 )
И зачем городить с itoa? Я так и не понял, возвращай сразу 10 битное значение АЦП функции.
Кот должен прожить жизнь без сожаления.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

Re: АЦП - выбор лапки

Сообщение Alexeyslav »

Предыдущее значение получаешь потому что мультиплексор фиксируется не в тот момент когда начинается преобразование - чтобы этого не было, после окончания преобразования АЦП нужно выдержать некоторое время(1-2 такта на частоте АЦП) после переключения канала, потом стартовать преобразование, тогда будет измерен правильный канал.

В догонку, посоветую "правильный" способ переключения каналов в цикле - заносишь в массив констант номера нужных каналов, и в цикле по индексу с массива присваиваешь значение, не забывая OR-ить нужные настройки источника опорного напряжения. Таким образом у тебя имеется единственное место определяющее номера каналов и последовательность их выбора - массив с номерами каналов. Размер массива можно использовать как параметр цикла...
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

Большое спасибо за помощь! По второму пункту ответа все предельно ясно. А вот по первому я слегка запутался. У меня программа работает по такой логике:
1) Выбор канала.
2) Пауза.
3) Запуск преобразования.
4) Прерываение.
5) Съем показаний.

Пожалуйста, покажите мне, где нужно сделать паузу в 2 такта, чтоб съем показаний был корректный. И если не затруднит, подскажите как лучше эту паузу сделать. _delay_ms - это бяка, каких свет не видывал.
Кот должен прожить жизнь без сожаления.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

Re: АЦП - выбор лапки

Сообщение Alexeyslav »

А попробуйте запустить преобразование вручную, т.е. константой задать номер канала, измерить и так несколько каналов. Может, просто с циклом что-то не то и номер канала подставляется не тот?

Вот странно, посмотрел у себя(mega48PA) - устанавливаю номер канала и сразу же за ним команда начала преобразования, измерение происходит ровно на заданном канале.
В даташите читал какие-то предостережения на счет переключения канала при измерении, но помню что в одном проекте на mega8 тоже была такая фигня, получал измерение предыдущего канала. Не помню уже как проблему разрешил...
Тут походу с кондачка не решится, и надо таки покурить даташит и программу. Что-то мне кажется, проблема в программе.

Сделай тестовую функцию без прерываний - задаешь канал, получаешь результат. И запусти подряд несколько измерений, потом по UART передать на терминал в протеусе будет наглядно видно.
Аватара пользователя
DruidCat
Встал на лапы
Сообщения: 116
Зарегистрирован: Чт май 03, 2012 06:27:23
Откуда: Челябинск

Re: АЦП - выбор лапки

Сообщение DruidCat »

Alexeyslav писал(а): Сделай тестовую функцию без прерываний - задаешь канал, получаешь результат. И запусти подряд несколько измерений, потом по UART передать на терминал в протеусе будет наглядно видно.
Сделал, работает нормально. Задаю канал, делаю паузу на установку канала. Потом жду завершения преобразования. Снимаю показания. У меня глупый вопрос: если происходит прерывание, это ведь означает окончание преобразования? В справочнике написанно что Да.
Одним словом я написал драйвер АЦП работающий на другом принципе. В виде функции, которая получает номер канала и возвращает результат. Ее особенность в том, что она не использует пауз, таймеров и прерываний. И работает онлайн, код этой функции можно встроить в большую программу, и она не будет тормозить основную программу. Сейчас я ее красиво оформлю и выложу на форуме, вдруг кому пригодится. :)
Кот должен прожить жизнь без сожаления.
Alexeyslav
Друг Кота
Сообщения: 4550
Зарегистрирован: Чт май 05, 2011 21:26:34
Откуда: Украина, Славутич
Контактная информация:

Re: АЦП - выбор лапки

Сообщение Alexeyslav »

Погоди ка, а как эта функция вообще работает? Ведь чтобы вернуть результат надо дождаться конца измерения - а значит внутри функции нужно ЖДАТЬ. Либо это вовсе не функция, а просто синоним команды запуска преобразования(куда там больше?) которая ничего не возвращает, а результат ложится и обрабатывается совсем в другом месте.
Ответить

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