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

Опрос 6 каналов АЦП ATmega8

Добавлено: Чт апр 06, 2017 23:11:20
Tol19
Здравствуйте!Есть проблема. Есть программа двух канального вольтметра с индикацией на WH1602, которая работает и моделируется в протеусе:

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "lcd_lib.h"//библиотека для работы с дисплеем
#include <stdio.h>

unsigned int voltage[4], voltage_1,   adc_counter;
volatile unsigned long voltage_value[4], voltage_value_1;

uint8_t res;
 //Функция вывода переменной
void lcd_num_to_str(unsigned int value, unsigned char nDigit)
{
switch(nDigit)
{
case 4: lcd_dat((value/1000)+'0');
case 3: lcd_dat(((value/100)%10)+'0');
case 2: lcd_dat(((value/10)%10)+'0');
case 1: lcd_dat((value%10)+'0');
}
}

// Обработчик прерывания от АЦП
ISR(ADC_vect)
{
ADCSRA = 0; // Выключаем АЦП 


if ((ADMUX & 0x0F)==1)
	{
		voltage_value = voltage_value + ADC; // Суммируем измеренные значения тока и помещаем в буфер
		ADMUX = (ADMUX & 0xF0) | 0; // Выбираем канал ADC0
		 // Увеличиваем счетчик выборок АЦП на 1
	}	
	else{
		voltage_value_1 = voltage_value_1 + ADC; // Суммируем измеренные значения тока и помещаем в буфер
		ADMUX = (ADMUX & 0xF0) | 1;
		adc_counter++;
	}


// Включаем АЦП
ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE)|(0<<REFS1)|(0<<REFS0);
}

int main(void) 
{
ADMUX |= (0 << REFS1)|(0 << REFS0); // Внутренний ИОН 2,56V
ADMUX |= (1 << MUX0); // Подключаем канал ADC1

ADCSRA |= (1 << ADEN) // разрешение АЦП
	|(1 << ADSC)     |(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0) // предделитель на 128
	|(1 << ADIE); // разрешение прерывания от АЦП

sei(); // Глобально разрешаем прерывания

lcd_init(); // Инициализация LCD
_delay_ms(25);

while(1) 
{
// вычисляем среднее значение АЦП
if (adc_counter > 40)
{
ADCSRA = 0; // Выключаем АЦП 
// преобразуем данные в реальное значение напряжения
voltage[0] = (voltage_value[0]/adc_counter)* 1.075;
voltage[1] = (voltage_value[1]/adc_counter)* 1.075;
voltage[2] = (voltage_value[2]/adc_counter)* 1.075;
voltage[3] = (voltage_value[3]/adc_counter)* 1.075;

adc_counter = 0; // Обнуляем счетчик выборок АЦП
voltage_value[0] = 0; // Обнуляем буфер значений напряжения
voltage_value[1] = 0; // Обнуляем буфер значений напряжения
voltage_value[2] = 0; // Обнуляем буфер значений напряжения
voltage_value[3] = 0; // Обнуляем буфер значений напряжения

lcd_gotoxy(0,1);
lcd_num_to_str(voltage[0]/100, 2);
lcd_string(".",1);
lcd_num_to_str(voltage[0], 2);

// Включаем АЦП
ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE)|(0<<REFS1)|(0<<REFS0) ;
}   
_delay_ms(1);
}
}
Однако, когда я пытаюсь опросить таким способом хотя бы 4 канала, возникают проблемы и протеус показывает пустой экран. Пробовал так:

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

switch (ADMUX & 0x0F)
 {
 case 0:voltage_value0+=ADC;break;
 case 1:voltage_value1+=ADC;break;
 case 2:voltage_value2+=ADC;break;
 case 3:voltage_value3+=ADC;break;
 }
ADMUX=(ADMUX & 0xF0)+((ADMUX & 0x0F)++ % 4);

И так:

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

voltage_value[ADMUX & 0x0F]+=ADC;
ADMUX=(ADMUX & 0xF0)+(((ADMUX & 0x0F)+1) % 4);
Бесполезно. В чем может быть проблема?

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Пт апр 07, 2017 08:08:02
ARV
Вообще-то для опроса канала АЦП разумно сделать отдельную функцию:

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

#define ADC_REF  _BV(REFS1) | _BV(REFS0)

int get_adc(uint8_t channel){
   ADMUX = ADC_REF | (channel & 0x07);
   ADCSRA |= _BV(ADSC); 
   while(ADCSRA & _BV(ADSC));
   // тут можно провести какие-то преобразования результата, если надо
   return ADC; 
}
и потом работать с результатом этой функции, передавая ей номер канала АЦП - усреднять результаты, выводить на экран и т.п.

и никаких прерываний не надо (пока). до первого вызова функции следует настроить АЦП правильными битами ADCSRA.

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Пт апр 07, 2017 08:16:51
ozonn
для начала попробуйте напрямую записывать записывать в ADMUX, без извращений, типа ADMUX = 0b11110000, ADMUX = 0b11110001 и т.д.
Это проще, с какой стороны не глянь. Я в своем вольтамперметре опрашиваю таким образом три входа и работает безо всякиз проблем :)

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Пт апр 07, 2017 21:18:18
Tol19
Даже так не выходит. Почему?

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

if ((ADMUX & 0x0F)==0)
    {
        voltage_value[0] = voltage_value[0] + ADC; // Суммируем измеренные значения тока и помещаем в буфер
        ADMUX = (ADMUX & 0xF0) | 1; // Выбираем канал ADC0
         // Увеличиваем счетчик выборок АЦП на 1
    ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE)|(0<<REFS1)|(0<<REFS0);
    }   
    if ((ADMUX & 0x0F)==1)
    {
        voltage_value[1] = voltage_value[1] + ADC; // Суммируем измеренные значения тока и помещаем в буфер
        ADMUX = (ADMUX & 0xF0) | 2; // Выбираем канал ADC0
        // Увеличиваем счетчик выборок АЦП на 1
        
        ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE)|(0<<REFS1)|(0<<REFS0);
    }
    if ((ADMUX & 0x0F)==2)
    {
        voltage_value[2] = voltage_value[2] + ADC; // Суммируем измеренные значения тока и помещаем в буфер
        ADMUX = (ADMUX & 0xF0) | 0; // Выбираем канал ADC0
        adc_counter++;// Увеличиваем счетчик выборок АЦП на 1
        ADCSRA |= (1 << ADEN)|(1 << ADSC)|(1 << ADPS2)|(1 << ADPS1)|(1 << ADPS0)|(1 << ADIE)|(0<<REFS1)|(0<<REFS0);
    }

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Сб апр 08, 2017 06:37:28
ARV
Tol19 писал(а):не выходит. Почему?
потому что делаете через анус. делайте хотя бы приблизительно правильно, и результат не заставит себя ждать.

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Сб апр 08, 2017 06:45:02
Tol19
Если бы я знал, как правильно, я бы не стал задавать вопросы, не так ли? :idea:

Re: Опрос 6 каналов АЦП ATmega8

Добавлено: Сб апр 08, 2017 09:02:33
COKPOWEHEU
в коде Tol19 отрабатывают все условия подряд, а не какое-то одно, но это неважно потому что неверен сам подход. Более правильный указал ARV - сначала заставить работать функцию и уже потом разбираться с прерываниями.