Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Переключение между каналами АЦП
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
B_Sergey_N
Всем привет.
Как можно переключаться между каналами АЦП? К примеру, мне необходимо:
сделать преобразование, прочитать один канал (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-ёмкости, чтобы избежать взаимовлияния каналов. Если же у Вас выходное сопротивление источника сигнала низкое то по-любому результаты первого после переключения канала АЦП лучше выбросить
smac
Цитата(B_Sergey_N @ Sep 28 2008, 22:41) *
...
Выкладываю код, переключения между каналами не происходит.


Я конечно не специалист в С, но
1. По-моему у Вас в основном цикле (тот что for) не происходит ожидания результата преобразования (прерывание не успевает вызваться), а сразу присваивается 0 переменной flag и далее следует переключение канала. К сожалению не скажу на память, что происходит если переключить канал не дожидась окончания преобразования, но уверен, что результат преобразования точно будет некорректным. Таким образом, при вызове прерывания в первый раз, скорее всего флаг уже равен 0, (далее распределение флага случайно) и канал уже переключен.
2. Использование больших задержек в прерывании (как минимум 2х200 мс) - это очень плохо.
3. Зачем соблюдать порядок чтения результата АЦП при написании программ на С? Как правило компилятор сам будет соблюдать порядок. Еслия не ошибаюсь прочитать результат можно так i=ADCW
4. По-моему (см. первую строку моего поста) компилятор сам заботится о корректном выходе из процедуры обработки прерывания, так что ставить reti самостоятельно, наверное, не стоит.
SysRq
0) F_CPU следует определить в makefile, или до включения delay.h;
1) Здесь прерывание следует использовать лишь в том случае, если между запуском АЦ-преобразования и собстно его завершением вы занимаетесь какой-то другой полезной работой;
2) Прерывание будет срабатывать не по вашей задумке, т.к., как уже подметил smac, или необходимо ждать завершения текущего преобразования перед сменой флага и канала (просто ждать нельзя, см пункт 2 -- теряется целесообразность вообще прерывания), либо исключить ситуацию что флаг меняется до срабатывания прерывания (например, АЦ-преобразование по второму каналу пусть запускается из прерывания, после оконцания работы с 1-м каналом). Регистры АЦП-блока буферизуются (если идет АЦ-преобразование, то запись в регистры не повлияет на текущее преобразование), и таким образом у вас преобразование выполнится для одного канала, а результаты воспримутся как для другого...

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

/* 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
}
domowoj
Я не силен в СИ, но после каждого ночала преобразования я бы "засыпал" -
ADC Noise Reduction - и преобр. точнее и наверняка.
И еще. Первое преобразование нужно делать "холостое".

Каналы АЦП можно переключать сразу после начала предыдущего преобр.
включится он по окончании предыдущего преобр.
VladimirYU
Цитата(B_Sergey_N @ Sep 28 2008, 22:41) *
Всем привет.
Как можно переключаться между каналами АЦП? К примеру, мне необходимо:
сделать преобразование, прочитать один канал (Vbg), затем опять сделать преобразование и прочитать канал ADC0.

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

[code]
#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); // Запуск преобразования
}

}


Может в этом и ничего страшного нет в Вашем случае, но в глаза бросилось.
hainiken
по ДШ вроде 125 мкС мин. паузу рекомендуют, а напрактике чато делают "холостое" преобраование.
singlskv
Цитата(Николай Иванович Приходько @ Sep 28 2008, 23:02) *
Не забывайте делать задержку после включения ADC битом ADEN на время затухания переходных процессов,
Странно, а я думал что эта задержка уже включена в первое преобразование.
Цитата
задержку после переключения канала, необходимую для разряда Sample-Hold-ёмкости, чтобы избежать взаимовлияния каналов.
Не подскажите в какой момент времени после переключения канала в ADMUX,
соответствующий пин подключаеться к Sample-Hold ?
Цитата
Если же у Вас выходное сопротивление источника сигнала низкое то по-любому результаты первого после переключения канала АЦП лучше выбросить
А если выходное сопротивление ИСТОЧНИКА очень высокое, то первый результат верный ?


Цитата(domowoj @ Sep 29 2008, 06:09) *
АЦП можно переключать сразу после начала предыдущего преобр.
включится он по окончании предыдущего преобр.
А как Вы определяете начало очередного преобразования ?

Цитата(hainiken @ Oct 5 2008, 00:15) *
по ДШ вроде 125 мкС мин. паузу рекомендуют

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

2 B_Sergey_N
1.flag используется и в прерывании и в основной проге, исчем по форуму слово volatile
2. задержки по много мс в прерывании не есть хорошо
3. ADMUX |= 0; просто не меняет ничего
domowoj
УВХ начинает работать вместе с АЦП, на то оно и УВХ (или аналоговое запоминающее устройство)
и ему до лампочки что запоминать, а аналоговый MUX уже подал на вход сигнал (это по определению).
К тому же АЦП последовательного приближения начинает преобразование со старших разрядов.
singlskv
Цитата(domowoj @ Oct 5 2008, 18:22) *
УВХ начинает работать вместе с АЦП, на то оно и УВХ (или аналоговое запоминающее устройство)
и ему до лампочки что запоминать, а аналоговый MUX уже подал на вход сигнал (это по определению).
Не очень понял что Вы этим хотели сказать ? Что УВХ всегда подключен к какому-то пину ?
Или что после занесения значения в ADMUX соответствующий пин подключается к УВХ ?
Или что после ADCSRA |= (1 << ADSC); уже началось преобразование и можно
менять ADMUX ?

Разъясните что Вы имели в виду...
domowoj
На конденцаторе хранения УВХ напряж.запоминается в момент ADCSRA |= (1 << ADSC)
при этом ADMUX уже переключен на нужный канал.
Если же мультиплексор переключить после ADCSRA |= (1 << ADSC), то он пеключится после окончания
текущего преобразования.

И не нужно заботиться об времени установления входного аналогового сигнала при переключении мультиплексора, в любом случае времени для запоминания в УВХ хватит.
singlskv
Цитата(domowoj @ Oct 5 2008, 20:21) *
На конденцаторе хранения УВХ напряж.запоминается в момент ADCSRA |= (1 << ADSC)
при этом ADMUX уже переключен на нужный канал.
Если же мультиплексор переключить после ADCSRA |= (1 << ADSC), то он пеключится после окончания текущего преобразования.
Это не совсем так, реальный запуск преобразования начинается не в момент
подачи ADCSRA |= (1 << ADSC), а через некоторое время после этого.
Это время зависит от выбранного прескейлера и от времени начального включения АЦП битом ADEN.
Те если по-простому, АЦП запустится через 0 - (N-1) тактов проца после ADCSRA |= (1 << ADSC),
где N - это прескайлер АЦП. То есть переключение каналов безопасно делать только
после реального начала преобразования.
B_Sergey_N
Большое спасибо всем за ответы!!! Особенно спасибо SysRq, я воспользовался его предложением.
Все-таки на мой взгляд без прерываний жить легче =). Вечно у меня с ними какие-то проблемы! ))
rudy_b
Что-то я не понимаю, вы про какой проц говорите? Если записать новое значение в мультиплексор в процессе преобразования, оно запомнится, но реально занесется в регистр мультиплексора только на последнем такте. И нельзя переписывать мультиплексор сразу после запуска, нужно, чтобы прошел хотя-бы один такт АЦП.
Там есть только одно противное место. Если для измерения на разных каналах используются разные источники Uref, то при переключении их следует дожидаться перезаряда емкости фильтра Uref, иначе получите ерунду.
singlskv
Цитата(rudy_b @ Oct 5 2008, 22:23) *
Что-то я не понимаю, вы про какой проц говорите? Если записать новое значение в мультиплексор в процессе преобразования, оно запомнится, но реально занесется в регистр мультиплексора только на последнем такте. И нельзя переписывать мультиплексор сразу после запуска, нужно, чтобы прошел хотя-бы один такт АЦП.
Там есть только одно противное место. Если для измерения на разных каналах используются разные источники Uref, то при переключении их следует дожидаться перезаряда емкости фильтра Uref, иначе получите ерунду.
Мысль высказанная про отсутствие УВХ в AVR была свежей... smile.gif
Но Вы быстро исправились, а в остальном логика работы примерно как Вы и описали.
domowoj
Вывод!
Делай все как надо - и не будет никаких казусов!
Николай Иванович Приходько
Цитата(singlskv @ Oct 5 2008, 00:38) *
Странно, а я думал что эта задержка уже включена в первое преобразование.

Нет. Первое холостое преобразование нужно делать, чтобы инициализировать модуль АЦП, поэтому его нужно делать даже в том случае, если питание на модуль АЦП (установкой бита ADEN) подали несколько часов назад. Тем более что время первого преобразования зависит от выбранной частоты тактирования модуля АЦП, и оно может меняться в весьма широких пределах. Тогда как длительность переходных процессов при подаче питания на модуль АЦП фиксирована и не зависит от частоты его тактирования.

Цитата(singlskv @ Oct 5 2008, 00:38) *
Не подскажите в какой момент времени после переключения канала в ADMUX,соответствующий пин подключаеться к Sample-Hold ?

Меня тоже интересует этот вопрос smile.gif Но ответа на него я не нахожу.

Цитата(singlskv @ Oct 5 2008, 00:38) *
А если выходное сопротивление ИСТОЧНИКА очень высокое, то первый результат верный ?

Простите. ОписАлся. Разумеется всё наоборот. smile.gif
VladimirYU
Цитата(Николай Иванович Приходько @ Oct 7 2008, 13:03) *
Нет. Первое холостое преобразование нужно делать, чтобы инициализировать модуль АЦП, поэтому его нужно делать даже в том случае, если питание на модуль АЦП (установкой бита ADEN) подали несколько часов назад. Тем более что время первого преобразования зависит от выбранной частоты тактирования модуля АЦП, и оно может меняться в весьма широких пределах. Тогда как длительность переходных процессов при подаче питания на модуль АЦП фиксирована и не зависит от частоты его тактирования.
Меня тоже интересует этот вопрос smile.gif Но ответа на него я не нахожу.


Простите. ОписАлся. Разумеется всё наоборот. smile.gif


А можно ссылочку необходимоть холостого преобразования. Никогда им не пользовался и проблем небыло. Часто о нем слышал ткните пожалуйста (а то фантом какой то получается). Насчет выборки, как раз все расписано. См. DS для меги128 рис 110, 111, 112.
domowoj
Вот на русском, почти даташиит(хотя и не без ошибок)
Микроконтроллеры AVR семейств Tiny и Mega фирмы Atmel
http://lord-n.narod.ru/walla.html

Первое преобразование на один такт больше.
singlskv
Цитата(Николай Иванович Приходько @ Oct 7 2008, 13:03) *
Нет. Первое холостое преобразование нужно делать, чтобы инициализировать модуль АЦП, поэтому его нужно делать даже в том случае, если питание на модуль АЦП (установкой бита ADEN) подали несколько часов назад. Тем более что время первого преобразования зависит от выбранной частоты тактирования модуля АЦП, и оно может меняться в весьма широких пределах. Тогда как длительность переходных процессов при подаче питания на модуль АЦП фиксирована и не зависит от частоты его тактирования.
Первое преобразование не холостое и даташит на этот счет говорит однозначно:
A normal conversion takes 13 ADC clock cycles. The first conversion after the ADC is switched
on (ADEN in ADCSRA is set) takes 25 ADC clock cycles in order to initialize the analog circuitry.

То есть 12 лишних ADC циклов для настройки аналога, и уж поверьте что эти 12
циклов рассчитанны для худшего случая, те fADC=200Кгц, так что даже первое преобразование
уже правильное.
Единственный вариант когда нужно делать "холостое" преобразование, когда
опрос АЦП должен быть регулярно, а 25 ADC циклов слишком много, тогда при инициализации АЦП
делаем "холостое" преобразование.
Ну и конечно если выходное сопротивление источника велико, но в этом случае и одного доп.
преобразования может быть мало и при этом теоритических +-0,5LSB никогда не достичь.
Цитата
Меня тоже интересует этот вопрос smile.gif Но ответа на него я не нахожу.
Дык в даташите есть ответ:
The actual sample-and-hold takes place 1.5 ADC clock cycles after the start of a normal conversion
and 13.5 ADC clock cycles after the start of an first conversion.

То есть всего 1,5 ADC цикла, и начинаются они не после ADCSRA |= (1 << ADSC),
а после реального старта преобразования, те когда после выдачи ADCSRA |= (1 << ADSC)
подоспеет очередной фронт fADC.
Николай Иванович Приходько
Цитата(domowoj @ Oct 7 2008, 18:29) *
Первое преобразование на один такт больше.

Не на один, а на 12. Курите датушит.
A normal conversion takes 13 ADC clock cycles. The first conversion after the ADC is
switched on (ADEN in ADCSRA is set) takes 25 ADC clock cycles in order to initialize
the analog circuitry.


Цитата(singlskv @ Oct 7 2008, 22:22) *
для худшего случая, те fADC=200Кгц,

200кГц - это не худший случай, а номинальный. Худший - это Fadc=1МГц и большое выходное сопротивление источника сигнала
singlskv
Цитата(Николай Иванович Приходько @ Oct 7 2008, 22:35) *
200кГц - это не худший случай, а номинальный. Худший - это Fadc=1МГц и большое выходное сопротивление источника сигнала
для 200кГц 10бит номинально
для 1МГц 8бит,
так что примерно один фиг, доп. 12 тактов fADC хватит в любом варианте...
А большое выходное сопротивление источника сигнала...,
дык даташит его однозначно регламенирует:
The ADC is optimized for analog signals with an output impedance of approximately 10 kΩ or
less.

Так что если у Вас больше, это Ваши сложности...
Николай Иванович Приходько
Цитата(singlskv @ Oct 7 2008, 22:46) *
для 200кГц 10бит номинально
для 1МГц 8бит,
так что примерно один фиг, доп. 12 тактов fADC хватит в любом варианте...

12 тактов при Fadc=1МГц длятся по времени в 5 раз меньше чем при Fadc=200кГц.
А Вы говорите "один фиг".

А если брать в расчёт нижнюю границу "нормы" 50 кГц... 200кГц, то и в 20 раз
singlskv
Цитата(Николай Иванович Приходько @ Oct 7 2008, 23:01) *
12 тактов при Fadc=1МГц длятся по времени в 5 раз меньше чем при Fadc=200кГц.
А Вы говорите "один фиг".

а 10бит к 8битам как соотносятся ?
domowoj
Цитата(Николай Иванович Приходько @ Oct 8 2008, 01:35) *
Не на один, а на 12. Курите датушит.
A normal conversion takes 13 ADC clock cycles. The first conversion after the ADC is
switched on (ADEN in ADCSRA is set) takes 25 ADC clock cycles in order to initialize
the analog circuitry.

Звиняйте Дядьку.
"И на старуху бывает проруха"
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.