Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: АЦП и ATmega48
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Дмитрий_Мигачев
Вообщем не могу понять как работать с несколькими каналами АЦП. Юзаю VMLab, он меня всем удовлетворяет, сейчас пишу тут одну вещь и встал...Дело в том что есть какой то пульт управления, и результаты с рычагов должны оцифровываться 50 раз в секунду, вообщем как я думал, по переполнению таймера счетчика, мы уходим в прерывание, где тама, тама производим необходимую настройку ацп, далее разрешаем прерывание по завершению АЦП и так с двумя каналами. Я думал что бы работать с двумя каналами необходимо завести переменную, к примеру в начеле она равна нулю, после первого АЦП она инкрементируется, и далее производиться обработка второго канала, но компилятор выдает ошибком, вообщем мне кажется что я выбрал не правильный путь, подскажите как сделать правильней
Для понятливости того, что я написал приведу пример своего кода:
*************************
*************************
unsigned char adc_chan, b;
unsigned short kanal1, kanal2;
SIGNAL(SIG_OVERFLOW1){
asm("nop");
TCNT1=0xFFEB;
if (adc_chan==0) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS0);
ADMUX=0; //ADC0
ADCSRA|=_BV(ADSC);
asm("sei");
if (adc_chan==1) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS0);
ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);//ADC7
ADCSRA|=_BV(ADSC);
asm("sei");
}
}
SIGNAL(SIG_ADC){
if (adc_chan==0){
b=ADCL;
kanal1=(ADCH<<8)+b;
adc_chan++;
}
if (adc_chan==1){
b=ADCL;
kanal2=(ADCH<<8)+b;
adc_chan=0;
}
}
void idle_init(void) {
SMCR=_BV(SE);
asm("sei");
}
void timer1_init(void) {
TCCR1B=_BV(CS12)+_BV(CS10);//CLK/1024
TCNT1=0xFFEB;
TIMSK1=_BV(TOIE1);
}
// ***********************************************************
// Main program
//
int main(void) {
idle_init();
timer1_init();
adc_chan=0;
while(1) {
asm("sleep");
}

}
но он матюгается на этот код, а как сделать правильней?
Дмитрий_Мигачев
Вопрос конешно я задал довольно не понятно, но извините у нас уже просто почти 6 утра)
defunct
пишем функцию

Код
#define MUX_STATE (0 << REFS1)|(0 << REFS0)
#define ADC_STATE (1 << ADEN)|(0 << ADPS2)|(1 << ADPS1)|(1 <<ADPS0)

int ReadAdc(unsigned char ChannelNum)
{
   ADMUX = ChannelNum | MUX_STATE;
   ADCSRA = (1 << ADSC) |  ADC_STATE;
   while ( (ADCSRA & ( 1 << ADIF))==0);
   return ADCW;
}


и подставляем в нее номер канала как параметр.

Channel0 = ReadAdc( 0 );
Channel2 = ReadAdc( 2 );
Laksus
Я не вникал в суть кода, но, по моему после первой
if() в SIGNAL(SIG_OVERFLOW1) не хватает }.

____________
Александр
2006 05 28
Laksus
И еще парочка мыслей по "косметике".

1- asm("sei"); в конце обработчика прерывания, по моему, совсем не нужны.
Прерывния и так установятся.

2- для того, чтобы считать 16-битное значения в С,
излишне выражение
b=ADCL;
kanal1=(ADCH<<8)+b;
компилятор только запутывается.
Достаточно
kanal1=ADC ;
а компилятор сам разберется с верхними/нижними байтами и сделает в лучшем виде.

_____________
Александр
2006 05 28
Дмитрий_Мигачев
Всем спасибо за ответы, более или менее разобрался, было много недочетов в том коде, поскольку писал на сонную голову, вчера на работе переделывал, но код не рациональный, хотелось бы узнать, как сделать более правильный код:
int My_global;
unsigned char adc_chan, b;
unsigned short kanal1, kanal2;
SIGNAL(SIG_OVERFLOW1){
TCNT1=0xFFEB
if (adc_chan==0) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);
ADMUX=0;
ADCSRA|=_BV(ADSC);
}
if (adc_chan==1) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);
ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);
ADCSRA|=_BV(ADSC);
}
}
SIGNAL(SIG_ADC){
if (adc_chan==0){
b=ADCL;
kanal1=(ADCH<<8)+b;
adc_chan++;
ADCSRA&=~_BV(3);
return;
}
if (adc_chan==1){
b=ADCL;
kanal2=(ADCH<<8)+b;
adc_chan=0;
ADCSRA&=~_BV(3);
}
}
void idle_init(void) {
SMCR=_BV(SE);
asm("sei");
}
void timer1_init(void) {
TCCR1B=_BV(CS12)+_BV(CS10);
TCNT1=0xFFEB;
TIMSK1=_BV(TOIE1);
}
int main(void) {
idle_init();
timer1_init();
adc_chan=0;
while(1) {
asm("sleep");
}

}
haker_fox
Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
Код
int My_global;
unsigned char adc_chan;
unsigned short kanal1, kanal2;

SIGNAL(SIG_OVERFLOW1)
  {
    TCNT1=0xFFEB;
    switch(adc_chan)
      }  
         case 0:
         ADMUX=0;
         break;

         case 1:
         ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);
         break;
       }
    ADCSRA|=_BV(ADSC);
  }


SIGNAL(SIG_ADC)
  {
    switch(adc_chan)
      {
        case 0:
        kanal1=ADC;
        adc_chan++;
        break;

        case 1:
        kanal2=ADC;
        adc_chan=0;
        break;
      }
    ADCSRA&=~_BV(3);
  }


void idle_init(void)
  {
    SMCR=_BV(SE);
  }

void timer1_init(void)
  {
    TCCR1B=_BV(CS12)+_BV(CS10);
    TCNT1=0xFFEB;
    TIMSK1=_BV(TOIE1);
  }

int main(void)
  {
        ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);  
    idle_init();
    timer1_init();
    adc_chan=0;
    while(1)
      {
        asm("sleep");
      }
  }


Ну примерно бы так я офоромил программу, правда еще надо комментарии дописать. Ну и на всякий случай на симуляторе прогнать: нет ли где ошибок)))
Дмитрий_Мигачев
Цитата(haker_fox @ May 29 2006, 10:55) *
Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
...

Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?
haker_fox
Цитата(Дмитрий_Мигачев @ May 29 2006, 13:19) *
Цитата(haker_fox @ May 29 2006, 10:55) *

Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
...

Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?


Нет предела совершенству, конечно можно найти еще множество путей. Наличие переменной adc_chan не обязательно, можно использовать сразу регистр ADMUX - ведь в нем хранится номер канала.
SasaVitebsk
Цитата(Дмитрий_Мигачев @ May 29 2006, 07:19) *
Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?


Необязательно ожидать завершения операции от АЦП. Особенно в Вашем случае (50 раз в секунду). smile.gif

Алгоритм работы такой:
1) АЦП запускается в "автоматическом" режиме без генерации прерываний.
2) Организуется прерывание по таймеру.
3) В данном прерывании:
a) Читается текущее значение
б) Пререключается слудующий канал.
ВНИМАНИЕ! Не наоборот!
Дмитрий_Мигачев
Вообще я хотел сделать так, чтобы оцифровка каналов, начиналась каждые 50 секунд, мне так не обходимо.

Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов
defunct
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:26) *
Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов


Просто в прерывании таймера запускайте одиночное преобразование АЦП и считывайте показания.
Не думаю, что для решения вашей задачи нужно вообще шаманить с прерываниями от АЦП.
Дмитрий_Мигачев
Вообще это часть моей задачи. Я хочу сделать радиоуправляемую модель, с 2 пропорциональными и 3 дискретными каналами. Приемопередающий модуль DP1203. Вот и изучаю, как правильней с этим работать
defunct
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:48) *
Вот и изучаю, как правильней с этим работать

тут много путей в равной степени правильных.
Однозначно сказать, что такой-то путь самый правильный нельзя..

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

Вопрос только в том, нужно ли усложнять жизнь если производительности МК хватает для реализации даже самого простого из правильных путей?
SasaVitebsk
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:26) *
Вообще я хотел сделать так, чтобы оцифровка каналов, начиналась каждые 50 секунд, мне так не обходимо.

Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов


Перечитайте ещё раз мой пост выше. Мой алгоритм эффективней т.к не требует ожидания от ацп!
Поясню детальнее. Напимер у Вас 10 каналов. Инициализируем таймер на 50/10=5сек. В прерывании алгоритм такой:
1) Считываем значение предыдущего канала
2) Изменяем канал
3) выходим
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.