Перейти к содержанию
    

Переключение между каналами АЦП

Всем привет.

Как можно переключаться между каналами АЦП? К примеру, мне необходимо:

сделать преобразование, прочитать один канал (Vbg), затем опять сделать преобразование и прочитать канал ADC0.

 

Выкладываю код, переключения между каналами не происходит.

 

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

#define F_CPU 16000000

int i, flag, t;

void main(void) 
{

DDRA |= (1 << 1)|(1 << 2)|(1 << 6)|(1 << 7);         // Инициализация светодиодов

ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Установка частоты преобразования 125KHz

ADCSRA |= (1 << ADEN);              // Вкл. АЦП

ADCSRA |= (1 << ADIE);              // Разрешение прерывания от компаратора
sei();


    for(;;)
    {
        
    flag = 1;
    ADMUX |= (1 << 1)|(1 << 2)|(1 << 3)|(1 << 4);     // Выбор входного канала АЦП - Vbg
                ADCSRA |= (1 << ADSC);        // Запуск преобразования
        

                                flag = 0;
        ADMUX |= 0;             // Выбор входного канала АЦП - ADC0
        ADCSRA |= (1 << ADSC);        // Запуск преобразования
    }

}

//********************************************************************************//

ISR(ADC_vect) 
{
        
         if (flag == 1)
            {
             i = ADCL;        // Чтение младших 8 битов первыми
             i += (int)ADCH << 8;    // Чтение старших 2 битов, умножение их на 256 и сложение с мл. б.
            
            if (i < 255)
            {
            // Питание контроллера падает
            _delay_ms(200);

            PORTA |= (1 << 6);
        
            _delay_ms(200);
        
            PORTA &= ~(1 << 6); 
            }
            }

    
    if (flag == 0)
    {
        
    t = ADCL;        // Чтение младших 8 битов первыми
    t += (int)ADCH << 8;    // Чтение старших 2 битов, умножение их на 256 и сложение с мл. б.
        
        if (t < 830)
        {
        _delay_ms(200);

        PORTA |= (1 << 1);
        
        _delay_ms(200);
        
        PORTA &= ~(1 << 1); 
        }
            
            else
            {
                 
                _delay_ms(200);

                PORTA |= (1 << 2);
        
                _delay_ms(200);
        
                PORTA &= ~(1 << 2); 
            }

    }


}

 

Не знаю, необходим ли reti из прерывания?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не забывайте делать задержку после включения ADC битом ADEN на время затухания переходных процессов, задержку после переключения канала, необходимую для разряда Sample-Hold-ёмкости, чтобы избежать взаимовлияния каналов. Если же у Вас выходное сопротивление источника сигнала низкое то по-любому результаты первого после переключения канала АЦП лучше выбросить

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Выкладываю код, переключения между каналами не происходит.

 

Я конечно не специалист в С, но

1. По-моему у Вас в основном цикле (тот что for) не происходит ожидания результата преобразования (прерывание не успевает вызваться), а сразу присваивается 0 переменной flag и далее следует переключение канала. К сожалению не скажу на память, что происходит если переключить канал не дожидась окончания преобразования, но уверен, что результат преобразования точно будет некорректным. Таким образом, при вызове прерывания в первый раз, скорее всего флаг уже равен 0, (далее распределение флага случайно) и канал уже переключен.

2. Использование больших задержек в прерывании (как минимум 2х200 мс) - это очень плохо.

3. Зачем соблюдать порядок чтения результата АЦП при написании программ на С? Как правило компилятор сам будет соблюдать порядок. Еслия не ошибаюсь прочитать результат можно так i=ADCW

4. По-моему (см. первую строку моего поста) компилятор сам заботится о корректном выходе из процедуры обработки прерывания, так что ставить reti самостоятельно, наверное, не стоит.

Изменено пользователем smac

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

0) F_CPU следует определить в makefile, или до включения delay.h;

1) Здесь прерывание следует использовать лишь в том случае, если между запуском АЦ-преобразования и собстно его завершением вы занимаетесь какой-то другой полезной работой;

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

 

Примерчик простой реализация преобразования без прерываний:

/* ATMega103 (128 with M103C programmed */

#define BIT(x) (1 << (x))

WORD DoADC(BYTE _ADMUX)
{
WORD value;

ADMUX = _ADMUX;

_delay_ms(250); //  Wait till signals are stabilized	

ADCSR |= BIT(ADSC); // Start conversion
while(ADCSR & BIT(ADSC)); // Wait till conversion done

*((BYTE *) &value) = ADCL; // Read lower byte
*(((BYTE *) &value) + 1) = ADCH; // Read higher byte	

return value;
}

void GetVoltages(void)
{
ADCSR = BIT(ADEN) | BIT(ADSC) | BIT(ADPS2) | BIT(ADPS1) |  BIT(ADPS0); // Enable ADC, start dummy conversion to init ADC
while(ADCSR & BIT(ADSC)); // Wait till dummy conversion done	

voltage1 = DoADC(0x00);						// AREF, single ended input - PORTF0
voltage2 = DoADC(BIT(MUX0));				// AREF, single ended input - PORTF1
voltage3 = DoADC(BIT(MUX1));				// AREF, single ended input - PORTF2
voltage4 = DoADC(BIT(MUX1) | BIT(MUX0));	// AREF, single ended input - PORTF3

ADCSR = 0; // disable ADC
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я не силен в СИ, но после каждого ночала преобразования я бы "засыпал" -

ADC Noise Reduction - и преобр. точнее и наверняка.

И еще. Первое преобразование нужно делать "холостое".

 

Каналы АЦП можно переключать сразу после начала предыдущего преобр.

включится он по окончании предыдущего преобр.

Изменено пользователем domowoj

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всем привет.

Как можно переключаться между каналами АЦП? К примеру, мне необходимо:

сделать преобразование, прочитать один канал (Vbg), затем опять сделать преобразование и прочитать канал ADC0.

 

Выкладываю код, переключения между каналами не происходит.

 

#include <util/delay.h>

#include <avr/io.h>

#include <avr/interrupt.h>

 

#define F_CPU 16000000

 

int i, flag, t;

 

void main(void)

{

 

.......

 

flag = 0;

[b]ADMUX |= 0; // Выбор входного канала АЦП - ADC0

ADCSRA |= (1 << ADSC); // Запуск преобразования

}

 

}

 

Может в этом и ничего страшного нет в Вашем случае, но в глаза бросилось.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

по ДШ вроде 125 мкС мин. паузу рекомендуют, а напрактике чато делают "холостое" преобраование.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не забывайте делать задержку после включения ADC битом ADEN на время затухания переходных процессов,
Странно, а я думал что эта задержка уже включена в первое преобразование.

задержку после переключения канала, необходимую для разряда Sample-Hold-ёмкости, чтобы избежать взаимовлияния каналов.
Не подскажите в какой момент времени после переключения канала в ADMUX,

соответствующий пин подключаеться к Sample-Hold ?

Если же у Вас выходное сопротивление источника сигнала низкое то по-любому результаты первого после переключения канала АЦП лучше выбросить
А если выходное сопротивление ИСТОЧНИКА очень высокое, то первый результат верный ?

 

 

АЦП можно переключать сразу после начала предыдущего преобр.

включится он по окончании предыдущего преобр.

А как Вы определяете начало очередного преобразования ?

 

по ДШ вроде 125 мкС мин. паузу рекомендуют

125 мкС это требование при переключении только дифференциальных каналов.

 

2 B_Sergey_N

1.flag используется и в прерывании и в основной проге, исчем по форуму слово volatile

2. задержки по много мс в прерывании не есть хорошо

3. ADMUX |= 0; просто не меняет ничего

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

УВХ начинает работать вместе с АЦП, на то оно и УВХ (или аналоговое запоминающее устройство)

и ему до лампочки что запоминать, а аналоговый MUX уже подал на вход сигнал (это по определению).

К тому же АЦП последовательного приближения начинает преобразование со старших разрядов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

УВХ начинает работать вместе с АЦП, на то оно и УВХ (или аналоговое запоминающее устройство)

и ему до лампочки что запоминать, а аналоговый MUX уже подал на вход сигнал (это по определению).

Не очень понял что Вы этим хотели сказать ? Что УВХ всегда подключен к какому-то пину ?

Или что после занесения значения в ADMUX соответствующий пин подключается к УВХ ?

Или что после ADCSRA |= (1 << ADSC); уже началось преобразование и можно

менять ADMUX ?

 

Разъясните что Вы имели в виду...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На конденцаторе хранения УВХ напряж.запоминается в момент ADCSRA |= (1 << ADSC)

при этом ADMUX уже переключен на нужный канал.

Если же мультиплексор переключить после ADCSRA |= (1 << ADSC), то он пеключится после окончания

текущего преобразования.

 

И не нужно заботиться об времени установления входного аналогового сигнала при переключении мультиплексора, в любом случае времени для запоминания в УВХ хватит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На конденцаторе хранения УВХ напряж.запоминается в момент ADCSRA |= (1 << ADSC)

при этом ADMUX уже переключен на нужный канал.

Если же мультиплексор переключить после ADCSRA |= (1 << ADSC), то он пеключится после окончания текущего преобразования.

Это не совсем так, реальный запуск преобразования начинается не в момент

подачи ADCSRA |= (1 << ADSC), а через некоторое время после этого.

Это время зависит от выбранного прескейлера и от времени начального включения АЦП битом ADEN.

Те если по-простому, АЦП запустится через 0 - (N-1) тактов проца после ADCSRA |= (1 << ADSC),

где N - это прескайлер АЦП. То есть переключение каналов безопасно делать только

после реального начала преобразования.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Большое спасибо всем за ответы!!! Особенно спасибо SysRq, я воспользовался его предложением.

Все-таки на мой взгляд без прерываний жить легче =). Вечно у меня с ними какие-то проблемы! ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что-то я не понимаю, вы про какой проц говорите? Если записать новое значение в мультиплексор в процессе преобразования, оно запомнится, но реально занесется в регистр мультиплексора только на последнем такте. И нельзя переписывать мультиплексор сразу после запуска, нужно, чтобы прошел хотя-бы один такт АЦП.

Там есть только одно противное место. Если для измерения на разных каналах используются разные источники Uref, то при переключении их следует дожидаться перезаряда емкости фильтра Uref, иначе получите ерунду.

Изменено пользователем rudy_b

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что-то я не понимаю, вы про какой проц говорите? Если записать новое значение в мультиплексор в процессе преобразования, оно запомнится, но реально занесется в регистр мультиплексора только на последнем такте. И нельзя переписывать мультиплексор сразу после запуска, нужно, чтобы прошел хотя-бы один такт АЦП.

Там есть только одно противное место. Если для измерения на разных каналах используются разные источники Uref, то при переключении их следует дожидаться перезаряда емкости фильтра Uref, иначе получите ерунду.

Мысль высказанная про отсутствие УВХ в AVR была свежей... :)

Но Вы быстро исправились, а в остальном логика работы примерно как Вы и описали.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...