АЦП: ADCH равно 0

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 13
Зарегистрирован: Вс янв 20, 2013 12:53:13

Сообщение maks_m4a1 »

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

#define F_CPU     12000000L

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

#define T_TIME    3//9765 // Prescaler 1024 ~ 0.1s

#define LED_DDR DDRB
#define LED_PT  PORTB
#define LED1	0
#define LED2	1
#define LED3	2
#define LED4	3
#define AM_ADC	10
#define AM_LEDS 4

unsigned char leds[4] = {0,1,2,3};
unsigned int LASTMV_C = 0;
unsigned int LAST_MAX = 0;
unsigned int TICK	  = 0;
unsigned int SUM	  = 0;

unsigned int AMPLITUDES[5];
unsigned int TEMP_A[5];
unsigned int LAST_MAX_V[AM_ADC];

ISR(TIM0_OVF_vect){
	LASTMV_C++; //int
	LAST_MAX_V[LASTMV_C] = LAST_MAX; // nat
	LAST_MAX = 0;
	if (LASTMV_C == AM_ADC){
		LASTMV_C = 0;
		SUM = 0;
		for(unsigned char i = 1; i<=AM_ADC; i++)
			SUM = SUM + LAST_MAX_V[i]; //nat
		SUM = SUM/AM_ADC; //nat
		for(unsigned char i = 1; i<=AM_LEDS; i++)
			TEMP_A[i] = AMPLITUDES[i] - SUM;
		unsigned char var = 1;
		for(unsigned char i = 2; i<=AM_LEDS; i++)
			if(TEMP_A[var]>TEMP_A[i])
				var = i;
		AMPLITUDES[var] = (AMPLITUDES[var]+SUM)/2;
	}
}

ISR(ADC_vect){
	char dflgdfgh = ADCL;
	LAST_MAX = ADCH;
	if(ADCH < AMPLITUDES[1])
		PORTB = 1 << LED1 | 0 << LED2 | 0 << LED3 | 0 << LED4;
	else
		if(ADCH < AMPLITUDES[2])
			PORTB = 1 << LED1 | 1 << LED2 | 0 << LED3 | 0 << LED4;
		else
			if(ADCH < AMPLITUDES[3])
				PORTB = 1 << LED1 | 1 << LED2 | 1 << LED3 | 0 << LED4;
			else
				PORTB = 1 << LED1 | 1 << LED2 | 1 << LED3 | 1 << LED4;
	ADCSRA |= 1 << ADSC;
}

int main(void){  	
	LASTMV_C = 1;
	LAST_MAX = 0;
	
 	for(unsigned char i = 1; i<= AM_LEDS; i++)
		LAST_MAX_V[i] = 0;
	AMPLITUDES[1] = 1;
	AMPLITUDES[2] = 82;
	AMPLITUDES[3] = 133;
	AMPLITUDES[4] = 184;

	TIMSK0 |= 1 << TOIE0; //TOIE0: Timer/Counter0 Overflow Interrupt Enable
	TCCR0B  = 1 << CS02 | 1<<CS00; // Prescaler 1024
	
	DDRB    = 0xFF;
	ADMUX   = 0 << REFS0 | 1 << ADLAR | 1 << MUX1 | 0 << MUX0;
	ADCSRA  = 1 <<ADEN  | 1<<ADSC | 1<<ADATE | 1<<ADIE | 1<<ADPS0 | 1<<ADPS1;
	//ADCSRB  = 0x00; 
	sei();
		
	while(1);

	return 0;
}
Оба прерывание работают нормально. Но вот почему-то ADCH всегда 0. Делаю под тини13а. Эмулирую в протеусе 7, пишу в студие 6 (так и не понял, есть ли там встроенный дебагер или нет). Прошу помощи, на меге8 вся срань работала нормально.
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

Первое, зачем запись

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

ADCSRA |= 1 << ADSC;
в прерывании АЦП?
Вы установили бит ADATE, биты ADTS0...2 у вас ноль, т.е. АЦП работает в режиме непрерывного преобразования. Перезапускать его нет надобности.

Второе, запись, к примеру

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

ADMUX   = 0 << REFS0 | 1 << ADLAR | 1 << MUX1 | 0 << MUX0;
Запись

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

0 << REFS0
ничего не даст. Поверьте бит не сбросится.
Если не уверены запишите так:

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

ADMUX = 0x00;
ADMUX   =  1 << ADLAR | 1 << MUX1;
А так вроде бы должно работать. Скиньте проект и протеус, посмотрю более детально.
Реклама
Родился
Сообщения: 13
Зарегистрирован: Вс янв 20, 2013 12:53:13

Сообщение maks_m4a1 »

На счет "0 << XX" я знаю, просто написал так, чтобы экспериментировать проще было. Бит в единицу выставлял для подстраховки, а то где-то у мене так не работало. Вот привожу код и проект. Буду очень благодарен, ибо в прошлый раз тоже с тинькой я не разобрался и писал под мегу.
Вложения
Attiny13A.7z
(34.99 КБ) 166 скачиваний
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

Недосмотрел сначала.
Первое. Проблема была в том, что весь порт настроен на выход. Канал АЦП надо настроить на вход.
Второе. АЦП работает без таймера 0 (его прерывания). Что вы делаете в прерывании я не понял, но это прерывание приводит к тому, что прерывания от АЦП перестают генерироваться. Я весь код в прерывании закомпилировал и сейчас работает. С таймером разберитесь или объясните что он должен делать.
Третье. Если вы хотите использовать весь диапазон АЦП, то от результата надо отнимать некую константу (значение младших 2-х разрядов).
Вложения
Attiny13A.rar
(66.17 КБ) 161 скачивание
Реклама
Эиком - электронные компоненты и радиодетали
Мудрый кот
Аватара пользователя
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

Сообщение Kavka »

When ADCL is read, the ADC Data Register is not updated until ADCH is read. Consequently, if
the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read
ADCH. Otherwise, ADCL must be read first, then ADCH.
"При чтении регистра ADCL регистр данных АЦП не обновляется пока не будет прочитан ADCH. ... "
А у вас возможно множественное чтение из ADCH (несколько раз ADCH встречается в условных операторах). Да ещё и режим непрерывного преобразования. Это, всего лишь, моё предположение, но при первом же чтении из ADCH АЦП уходит на новый цикл, и у вас из ADCH читаются нули.
Либо вычитывать младший-старший регистры в переменные, либо использовать определение порта ADC - будет считываться правильным образом 16-ти битное значение (unsigned short или uint16_t).
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

У него есть строчка

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

LAST_MAX = ADCH;
дальше в прерывании везде я заменил ADCH на LAST_MAX.
То, что в ADCH в какой-то момент (когда закончено новое преобразование) появится новое значение и так понятно. Я сделал так, что это значение считывается один раз при входе в прерывание.
Реклама
Родился
Сообщения: 13
Зарегистрирован: Вс янв 20, 2013 12:53:13

Сообщение maks_m4a1 »

Вот, переписал.
Спойлер

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

#define F_CPU     12000000L

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

#define T_TIME    3//9765 // Prescaler 1024 ~ 0.1s

#define LED_DDR DDRB
#define LED_PT  PORTB
#define LED1	0
#define LED2	1
#define LED3	2
#define LED4	3
#define AM_ADC	10
#define AM_LEDS 4

unsigned char leds[4] = {0,1,2,3};
unsigned int LASTMV_C = 0;
unsigned int LAST_MAX = 0;
unsigned int TICK	  = 0;
unsigned int SUM	  = 0;

unsigned int AMPLITUDES[5];
unsigned int TEMP_A[5];
unsigned int LAST_MAX_V[AM_ADC];

ISR(TIM0_OVF_vect){
	PORTB ^= 1 << 3;
	LASTMV_C++; //int
	LAST_MAX_V[LASTMV_C] = LAST_MAX; // nat
	LAST_MAX = 0;
	if (LASTMV_C == AM_ADC){
		LASTMV_C = 0;
		SUM = 0;
		for(unsigned char i = 1; i<=AM_ADC; i++)
			SUM = SUM + LAST_MAX_V[i]; //nat
		SUM = SUM/AM_ADC; //nat
		for(unsigned char i = 1; i<=AM_LEDS; i++)
			TEMP_A[i] = AMPLITUDES[i] - SUM;
		unsigned char var = 1;
		for(unsigned char i = 2; i<=AM_LEDS; i++)
			if(TEMP_A[var]>TEMP_A[i])
				var = i;
		AMPLITUDES[var] = (AMPLITUDES[var]+SUM)/2;
	}
}

ISR(ADC_vect){
	char dflgdfgh = ADCL;
	unsigned char _ADCH = ADCH;
	if(LAST_MAX < _ADCH)
		LAST_MAX = _ADCH;
	if(_ADCH < AMPLITUDES[1])
		PORTB = 1 << LED1 | 0 << LED2 | 0 << LED3 | 0 << LED4;
	else
		if(_ADCH < AMPLITUDES[2])
			PORTB = 1 << LED1 | 1 << LED2 | 0 << LED3 | 0 << LED4;
		else
			if(_ADCH < AMPLITUDES[3])
				PORTB = 1 << LED1 | 1 << LED2 | 1 << LED3 | 0 << LED4;
			else
				PORTB = 1 << LED1 | 1 << LED2 | 1 << LED3 | 1 << LED4;
	ADCSRA |= 1 << ADSC;
}

int main(void){  	
	LASTMV_C = 1;
	LAST_MAX = 0;
	
 	for(unsigned char i = 1; i<= AM_LEDS; i++)
		LAST_MAX_V[i] = 0;
	AMPLITUDES[1] = 1;
	AMPLITUDES[2] = 82;
	AMPLITUDES[3] = 133;
	AMPLITUDES[4] = 184;

	//TIMSK0 |= 1 << TOIE0; //TOIE0: Timer/Counter0 Overflow Interrupt Enable
	TCCR0B  = 1 << CS02 | 1<<CS00; // Prescaler 1024
	
	DDRB    = 1 << LED1  | 1 << LED2 | 1 << LED3 | 1 << LED4 | 0 << 4;
	ADMUX   = 1 << ADLAR | 1 << MUX1;
	ADCSRA  = 1 <<ADEN   | 1 << ADSC  | 0 << ADATE| 1 << ADIE | 1 << ADPS0 | 1 << ADPS1;
	//ADCSRB  = 0x00; 
	sei();
		
	while(1);

	return 0;
}
Это я попытался так цветомузыку реализовать (БПФ для меня пока страшны), разбивал все на потоки, потом хочу вторым микроконтроллером визуализацию сделать. АЦП замеряет напряжение, сохраняет максимальное значение амплитуды, выводит результат в соответствие со значением AMPLITUDES. Дальше, через некоторое время, постоянно запускается таймер. Он сначала делает 10 (я поставил неверное условие, потому 11, хотя это ничего сильно не меняет) записей LAST_MAX в массив LAST_MAX_V. Когда количество записей и инкремент совпадает, я начинаю искать среднее значение максимальной амплитуды, сравниваю его со значениями в AMPLITUDES и там, где ближе всего находится SUM, беру и опять высчитываю среднее значение. Обнуляю переменные и опять делаю 10 замеров. В результате получается так, что программа сама подбирает нужные значение для сравнивание конкретному диоду. До конца, естессно, я пока это не довел, но думаю, с этого можно будет сделать что-то интересное. Так что работа таймера для меня критична, а вот почему все тупит при его запуске - хз. На ATmega8A оно все хорошо работало.
П.С. АЦП поставил в ручной режим.
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

Из прерывания таймера убери строку

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

PORTB ^= 1 << 3;
она явно лишняя (я такое использую для определения срабатывает ли прерывание)

Как работает?
Родился
Сообщения: 13
Зарегистрирован: Вс янв 20, 2013 12:53:13

Сообщение maks_m4a1 »

Для этого же и использовал. Оно, походу, работало, но не т.к. я ожидал. Я потом ещё немного посидел над кодом и в протеусе более-менее нормально работает. Ещё вопрос: как выставить 20МГц на тиньке, а то мне кажется, что моргания в реальной сборке запаздывают немного?
Спойлер

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

#define F_CPU     12000000L

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

#define T_TIME    3//9765 // Prescaler 1024 ~ 0.1s

#define LED_DDR DDRB
#define LED_PT  PORTB
#define LED1	0
#define LED2	1
#define LED3	2
#define LED4	3
#define AM_LEDS 3

unsigned char leds[4] = {0,1,2,3};
unsigned int LAST_MIN = 255;
unsigned int LAST_MAX = 0;
unsigned int TICK	  = 0;
unsigned int SUM	  = 0;
unsigned int DIV	  = 0;
unsigned char AM_ADC  = 5;
unsigned char LASTMV_C= 0;

unsigned char AMPLITUDES[5];
unsigned int TEMP_A[5];
unsigned char LAST_MAX_V[10];
unsigned char LAST_MIN_V[10];

ISR(TIM0_OVF_vect){
	LASTMV_C++; //int
	LAST_MAX_V[LASTMV_C] = LAST_MAX; // nat
	LAST_MIN_V[LASTMV_C] = LAST_MIN; 
	LAST_MAX = 0;
	LAST_MIN = 255;
	if(LASTMV_C == AM_ADC){
		LASTMV_C = 0;
		SUM = 0;
		DIV = 0;
		for(unsigned char i = 1; i<=AM_ADC; i++)
			DIV = DIV + LAST_MAX_V[i]; //nat
		DIV = DIV/AM_ADC; //nat
		for(unsigned char i = 1; i<=5; i++)//{
			TEMP_A[i] = AMPLITUDES[i] - DIV;
		unsigned char var = 1;
		for(unsigned char i = 2; i<=5; i++)
			if(TEMP_A[var]>TEMP_A[i])
				var = i;
		AMPLITUDES[var] = (AMPLITUDES[var]+DIV)/2;

				
	}
}

ISR(ADC_vect){
	char dflgdfgh = ADCL;
	unsigned char _ADCH = ADCH;
	if(LAST_MAX < _ADCH)
		LAST_MAX = _ADCH;

	for(unsigned char i = 1; i<=AM_LEDS; i++)
		if(AMPLITUDES[i] < _ADCH)
			PORTB |= 1 << leds[i-1];
		else
			PORTB &= ~(1 << leds[i-1]);
	ADCSRA |= 1 << ADSC;
}

int main(void){  	
 	for(unsigned char i = 1; i<= AM_LEDS; i++)
		LAST_MAX_V[i] = 0;
	AMPLITUDES[1] = 10;
	AMPLITUDES[2] = 40;
	AMPLITUDES[3] = 90;

	TIMSK0 |= 1 << TOIE0; //TOIE0: Timer/Counter0 Overflow Interrupt Enable
	TCCR0B |= 1 << CS02; // Prescaler 256
	
	DDRB    = 1 << LED1  | 1 << LED2  | 1 << LED3 | 1 << LED4 | 0 << 4 | 1 << 5;
	ADMUX   = 1 << ADLAR | 1 << MUX1;
	ADCSRA  = 1 <<ADEN   | 1 << ADSC  | 0 << ADATE| 1 << ADIE | 1 << ADPS0 | 1 << ADPS1;

	sei();
		
	while(1);

	return 0;
}
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

Kavka писал(а):
When ADCL is read, the ADC Data Register is not updated until ADCH is read. Consequently, if
the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read
ADCH. Otherwise, ADCL must be read first, then ADCH.
"При чтении регистра ADCL регистр данных АЦП не обновляется пока не будет прочитан ADCH. ... "
А у вас возможно множественное чтение из ADCH (несколько раз ADCH встречается в условных операторах). Да ещё и режим непрерывного преобразования. Это, всего лишь, моё предположение, но при первом же чтении из ADCH АЦП уходит на новый цикл, и у вас из ADCH читаются нули.
Не знаю запускается или нет преобразование при этом, но точно достаточно считать старший регистр, чтобы можно было работать.
Тем более у нас используется левое выравнивание.О чем и гласят ваши процитированные строки.
if
the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read
ADCH
Если используется левое выравнивание и не нужна точность более, чем 8 бит, достаточно считать ADCH

Вряд ли оно нулями заполняется, скорее просто новым заменяется.
Вообще плохой тон тулить что то в прерываниях.
тем более когда запущено непрерывное преобразование.
Я пробовал- прерывания срабатывают так часто, что на остальное времени не остается. Лучше запускать его по таймеру, либо пусть оно молотит без прерывания - то бишь аппаратно и в нужном месте считывать всегда свежие показания.
Замечание насчет того, что многократное считывание регистра поддерживаю так как содержание могло поменяться
Всегда нужно считывать во временную, а потом с ней работать.
Тем более если несколько условий одну переменную опрашивают, а она между ними поменялась, в итоге могут наступить оба условия сразу либо вообще не наступить:)
Контактная информация:
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

Далеко не все кушают 20МГц. tiny13 только 10МГц. Это ее предел.
Мудрый кот
Аватара пользователя
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

Сообщение Kavka »

eess9, до 20 МГц могут работать и 13 и 13A. По спецификации. При внешнем тактировании.
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

Имеется ввиду внешний сигнал синхронизации? На ноге LCKI? Не доглядел тогда, sorry
Мудрый кот
Аватара пользователя
Сообщения: 1810
Зарегистрирован: Чт июн 10, 2010 08:55:35
Откуда: Сибирские Афины

Сообщение Kavka »

Да. PCINT3/CLKI/ADC3/PB3
Когда уже ничего не помогает - прочтите, наконец, инструкцию.
Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII)
Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Ответить

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