Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ADC в ATtiny461
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
ivainc1789
Только что закончил небольшшой заказ в 10 устройств на ATtiny461. В каждом устройстве необходимо было мерить как униполярные, так
и дифф сигналы. Так как прослеживалась тенденция к повторным заказам, решил приложить определенные усилия к написанию программы
калибровки внутреннего ADC. Попытки сделать это чуть не сорвали сроки проекта - оказалось, что ADC ATtiny461 имеют существенные
разбросы по параметрам от экз к экз и др. тонкости:
1. Обычное униполярное измерение в 4 экземплярах из 10 показало ошибку в 20 LSB при напряжениях на входе около 2.2 вольт при
опоре 2.56 вольт. Вроде требования даташита выполнены - для униполярных сигналов все напряжения в доп пределах. Измерения низких
напряжений (в районе 100 mV) были практически идеальны. Насколько я понимаю, ADC в этих 4 экз. имеют слишком большой INL. Как с
этим бороться просто не представляю. А если бы устройств было не 10, а 100? Что, делать стенд для отбраковки кристаллов?
2. ADC в AVR по скорости работы и так аутсайдер, но определенные тонкости выявились и при переключениях каналов. Особенно когда
есть и униполярные и дифф. (у меня было по 2 сигнала каждого типа). Засада в том, что униполярные мерялись стабильно, но дифф
колбасило конкретно, сэмплы отличались почти в два раза. Сначала подумал, что PCB неправильно разведена, но вроде не первый раз
развожу, решил искать причину дальше. По-моему, из AVR аппликух была в свое время выдрана некая функция AdcStable():
Код
void AdcStable(void){
       signed int Vmax,Vmin,V[4];
       unsigned char i;
       //Loop until the ADC value is stable. (Vmax <= (Vmin+1))
       for (Vmax=10,Vmin=0;Vmax > (Vmin+1);){    
         V[3] = V[2];V[2] = V[1];V[1] = V[0];// сдвиг в буфере
         SETBIT(ADCSRA,ADSC);while(CHKBIT(ADCSRA,ADSC));// получить результат
         V[0] = ADC;
         Vmin = V[0];  // Vmin is the lower VOLTAGE
         Vmax = V[0];  // Vmax is the higher VOLTAGE
         for (i=0;i<=3;i++){//Save the max and min voltage
           if (V[i] > Vmax) Vmax=V[i];
           if (V[i] < Vmin) Vmin=V[i];    
         }          
       }        
     }

C ней дифф сигналы стали измеряться нормально, но косяк проявился в том, что компилятор IAR v5.50 при оптимизации по скорости
сделал так, что цикл в этой функции становился вечным и я не смог разобраться в чем дело. При оптимизации по коду все работало
нормально. Однако это наблюдение покоя так и не дало, поэтому данную функцию пришлось выкинуть. В даташите на ATmega324 написано,
что при измер дифф сигналов схеме нужно время чтобы прошли переходные процессы переключения и это время указано - 125us. Как
только поставил паузу 200us после смены мультиплексора (измерения шли группой, но single conversions) - сразу все начало измеряться нормально. Но горький осадок остался: ADC и так не быстр, скорость выборок падает с увеличением кол-ва сигналов, дак еще и переходные задерживают... Что же делать? Кто как выкручивается?
Мне особенно интересно, как все это хозяйство калибруется в серию? Это же все серьезный риск получить приключений на собственное то самое место...
sigmaN
А на каком именно цикле он зацикливается?
Может сделать
Код
volatile signed int Vmax, Vmin, V[4];
volatile unsigned char i;
?
А если на этом виснет
Код
while(CHKBIT(ADCSRA,ADSC));// получить результат
то это немного странно, но тоже проверять объявления CHKBIT ADCSRA ADSC добавить volatile куда надо(хотя какраз таки тут он должен быть уже по дефолту)...

А ещё может быть при оптимизации по скорости у вас вообще ADC не инициализируется, а цикл ожидания не может дождаться результатов.
ivainc1789
Цитата
А если на этом виснет
Код
while(CHKBIT(ADCSRA,ADSC));// получить результат
то это немного странно, но тоже проверять объявления CHKBIT ADCSRA ADSC добавить volatile куда надо(хотя какраз таки тут он должен быть уже по дефолту)...
А ещё может быть при оптимизации по скорости у вас вообще ADC не инициализируется, а цикл ожидания не может дождаться результатов.

нет, тут точно не виснет. Инициализация ADC происходит в начале программы один раз, кроме того с оптимизацией по коду все работает нормально.
Цитата
А на каком именно цикле он зацикливается?
Может сделать
Код
volatile  signed int Vmax, Vmin, V[4];
volatile unsigned char i;
?

да, наверно стоит попробовать... хотя в аппнотах вроде не было объявлений volatile. Хорошо, что проблема решилась простой задержкой 200us после переключения мультиплексора. Хотя сейчас я задумался, а не лучше ли вместо задержки просто запускать холостое преобразование? В том же даташите на atmega324 проскочила фраза, что при переключении дифф каналов это время необходимо в частности для инициализации схемы компенсации смещения на входах... Но все же ясно, что время холостого преобразования зависит от частоты тактирования ADC, и на высоких частотах одного холостого будет снова не хватать... Потому считаю, что метод введения задержки все же лучше.
Разобрался частично с калибровкой. Большая часть ошибки при N=900...1023 состояла в том, что я опрометчиво посчитал опору как 2560mV (типовое значение), а по факту оказалось 2500mV. Измеренное смещение ADC составило +1LSB, и после всего этого стало ясно что и gain error укладывается в +2LSB. В общем, тяжело, но разобрался. Если бы тока в AVR делали опору поточнее, было бы совсем здорово, а то не от чего оттолкнуться...
Duhas
вообще для поточнее и побыстрее ставится внешний АЦП )

когда все в одном кристалле ессно будут вылазить разные неудобства, но все они решаемы в той или иной мере, как вы и сами нам рассказали )
defunct
Цитата
По-моему, из AVR аппликух была в свое время выдрана некая функция AdcStable()

такого говнокода не могло быть в AVR аппликухах. Программистов такого уровня в Atmel не держат.

Цитата
Как только поставил паузу 200us после смены мультиплексора

Многократно обсуждалось...
При смене канала просто делайте два измерения. Первое выбрасывайте, второе оставляйте.
singlskv
Цитата(defunct @ Jul 10 2010, 02:27) *
Многократно обсуждалось...
При смене канала просто делайте два измерения. Первое выбрасывайте, второе оставляйте.

Это Вы ерунду "сморозили"...
при смене на дифф. каналах нужна просто пауза на 125мс:
Special care should be taken when changing differential channels. Once a differential channel
has been selected, the gain stage may take as much as 125 μs to stabilize to the new value.
Thus conversions should not be started within the first 125 μs after selecting a new differential
channel. Alternatively, conversion results obtained within this period should be discarded.
AHTOXA
Цитата(defunct @ Jul 10 2010, 04:27) *
такого говнокода не могло быть в AVR аппликухах. Программистов такого уровня в Atmel не держат.

Высокого же вы мнения о программистах атмеляsmile.gif. Однако этот код именно из их аппноты ( avr450 smile.gif )
defunct
Цитата(AHTOXA @ Jul 10 2010, 02:47) *
Высокого же вы мнения о программистах атмеляsmile.gif.

Да, высокого, вот оригинал по ссылке:

Код
void Stable_ADC(void)                     // loop until you have a stable value
{
  int V[4];
  char i;
  int Vmax, Vmin;
  
    //Loop until the ADC value is stable. (Vmax <= (Vmin+1))
    for (Vmax=10,Vmin= 0;Vmax > (Vmin+1);)
    {
        V[3] = V[2];
        V[2] = V[1];
        V[1] = V[0];
        ADCSR |= 0x40;                      // Start a new A/D conversion
        while (!(ADCSR & (1<<ADIF)))        // wait until ADC is ready      
;
        V[0] = ADC;
        Vmin = V[0];                          // Vmin is the lower VOLTAGE
        Vmax = V[0];                          // Vmax is the higher VOLTAGE  
        /*Save the max and min voltage*/
        for (i=0;i<=3;i++)
        {
            if (V[i] > Vmax)
                Vmax=V[i];
            if (V[i] < Vmin)
              Vmin=V[i];  
        }
    }
}

функция написана и отформатирована читаемо по крайней мере.

Цитата(singlskv @ Jul 10 2010, 02:44) *
Это Вы ерунду "сморозили"...
при смене на дифф. каналах нужна просто пауза на 125мкс:
Alternatively, conversion results obtained within this period should be discarded.

Отнюдь не ерунду, а то самое "alternatively" из ДШ.
Только не уточнил что АЦП не разгонять выше 8KSPS (125 мкс --> 8Khz).
Вот например как оно делается у меня для дифф каналов:

Код
#pragma vector=TIMER1_COMPA_vect
__interrupt void T1OCAISRHandler(void)
{
    // 1ms timer event (Fd = 1kHz)
    if (!adcContext.lock)
    {
        adcContext.CurrentChan = 0;
        adcContext.DummyCycle = TRUE;   // notify to discard this result as mux channel changes
        adcContext.SampleSequence += 1; // inc sample id
        adcStartConversion( adc_GetMuxIndex( adcContext.CurrentChan ) );
        adcContext.lock = 1;
    }
    else
    {
        // overload.... skip current sample on all channels.
        adcContext.nSampleOffsetErrors += 1;
    }
}


#pragma vector=ADC_vect
__interrupt void AdcISRHandler(void)
{
    U16 Val = ADC;
    if (adcContext.DummyCycle)
    { // restart current channel
        adcStartConversion( adc_GetMuxIndex( adcContext.CurrentChan ) );
        adcContext.DummyCycle = FALSE;
    }
    else
    {

        // handle current conversion result
        adcUpdateChannel( &adcContext.Channels[ adcContext.CurrentChan ], Val );

        // switch to the next channel and launch it
        adcContext.CurrentChan++;
        if (adcContext.CurrentChan > MUX_LAST_INDEX)
        {
            adcContext.lock = 0;
            adcContext.CurrentChan = 0;
        }
        else
        {
            adcStartConversion( adc_GetMuxIndex( adcContext.CurrentChan ) );
            adcContext.DummyCycle = TRUE; // mux changes - need to discard the next result
        }

    }
}

Куда приятнее в прерывании от АЦП просто забирать 100% верный результат, чем натыкивать delay_us() в основном цикле программы убивая при этом ее портируемость и способность к расширению.
AHTOXA
Цитата(defunct @ Jul 10 2010, 06:44) *
Да, высокого, вот оригинал по ссылке:
функция написана и отформатирована читаемо по крайней мере.

biggrin.gif То есть, по-вашему, отформатированный говнокод перестаёт быть говнокодом?
xemul
Цитата(ivainc1789 @ Jul 2 2010, 23:24) *
... По-моему, из AVR аппликух была в свое время выдрана некая функция AdcStable():
...
C ней дифф сигналы стали измеряться нормально ... поэтому данную функцию пришлось выкинуть.

По поводу осмысленности кода - целиком согласен с defunct. Даже индийские программеры уже вроде бы запомнили, что нельзя читать неинициализированные автоматические переменные.
Измеряться правильно стало потому, что в функции делается минимум два измерения. Максимальное число измерений в функции зависит от начального содержимого signed int V[4].
Ещё правильнее то, что Вы эту функцию выкинули.
Цитата
В даташите на ATmega324 написано, что при измер дифф сигналов схеме нужно время чтобы прошли переходные процессы переключения и это время указано - 125us. Как только поставил паузу 200us после смены мультиплексора (измерения шли группой, но single conversions) - сразу все начало измеряться нормально. Но горький осадок остался: ADC и так не быстр, скорость выборок падает с увеличением кол-ва сигналов, дак еще и переходные задерживают... Что же делать? Кто как выкручивается?

Дык других вариантов и нет - после переключения канала/режима измерения нужно немного подождать. Как ждать - через delay_us(), по таймеру или скипать одно измерение после переключения - решать Вам.
Цитата
Мне особенно интересно, как все это хозяйство калибруется в серию?

У АЦП АВР очень больное место одно - точность встроенной опоры. имхо, если исключить прочие неразумности, ничего кроме опоры калибровать не требуется. Правда, это тянет за собой деление (зачастую ненужное где-либо ещё в проге). Еще одно "но" - входные сигналы не должны превышать _минимальное_ даташитное Vref.
Естественный вариант - использовать внешнюю опору или внешний(-ие) АЦП.
defunct
Цитата(AHTOXA @ Jul 10 2010, 11:55) *
То есть, по-вашему, отформатированный говнокод перестаёт быть говнокодом?

критериями которые дают повод назвать код говнокодом в т.ч. есть и форматирование.
Когда функция хорошо читается в ней легче разглядеть потенциальную ошибку.
AHTOXA
Цитата(defunct @ Jul 11 2010, 01:45) *
критериями которые дают повод назвать код говнокодом в т.ч. есть и форматирование.
Когда функция хорошо читается в ней легче разглядеть потенциальную ошибку.

Вы не ответили на мой вопрос. Вы считаете, что если говнокод отформатировать, то он перестанет быть говнокодом?
Или даже спрошу ещё более конкретно: считаете ли вы приведённый вами кусок кода от атмеля - говнокодом?
defunct
Цитата(AHTOXA @ Jul 10 2010, 23:27) *
Вы не ответили на мой вопрос. Вы считаете, что если говнокод отформатировать, то он перестанет быть говнокодом?

Я считаю, что любой нормальный код можно превратить в говнокод одним лишь форматированием. Например просто - разместить всё в одной строке.

Цитата
Или даже спрошу ещё более конкретно: считаете ли вы приведённый вами кусок кода от атмеля - говнокодом?

Не считаю, потому что код оформлен читаемо - алгоритм читается, править/отладить легко, потенциальный баг видно.
Другой вопрос - дубовая (не расширяемая) реализация алгоритма - это сделано специально, т.к. основная задача любого наглядного пособия - донести суть/принцип работы, а не чтобы его можно было взять и бездумно вставить в свой проект.

В первом же варианте код не читается, править/отлаживать нереально, и баги не видно - это и делает его говнокодом.
singlskv
Цитата(defunct @ Jul 10 2010, 04:44) *
Отнюдь не ерунду, а то самое "alternatively" из ДШ.
Только не уточнил что АЦП не разгонять выше 8KSPS (125 мкс --> 8Khz).
Вот например как оно делается у меня для дифф каналов:
Такой способ конечно возможен, но у него есть и недостатки.
- Вы почти в 2 раза уменьшаете итоговый sample rate АЦП
- окончание Sаmple & Hold(для "правильного" преобразования) заметно сдвигается во времени
от начала этого пребразования(это надо учитывать при необходимости)
Цитата
Куда приятнее в прерывании от АЦП просто забирать 100% верный результат, чем натыкивать delay_us() в основном цикле программы убивая при этом ее портируемость и способность к расширению.
delay_us() на 125мкс в основном цикле это уже какой-то ненормальный вариант,
таймеры еще никто не отменял...

defunct
Цитата(singlskv @ Jul 11 2010, 00:42) *
- Вы почти в 2 раза уменьшаете итоговый sample rate АЦП
- окончание Sаmple & Hold(для "правильного" преобразования) заметно сдвигается во времени
от начала этого пребразования(это надо учитывать при необходимости)

1. На одну четверть (макс АЦП sample rate - 15kHz, а 125us полюбому надо пропустить).
2. Относительно предыдущего "правильного" преобразования для того же канала там будет всегда одинаковый интервал определяемый sample таймером плюс +/- jitter вносимый более приоритетными обработчиками прерываний.
AHTOXA
Цитата(defunct @ Jul 11 2010, 03:26) *
В первом же варианте код не читается, править/отлаживать нереально, и баги не видно - это и делает его говнокодом.

У вас какое-то нестандартное понятие о говнокоде. Обычно под словом "говнокод" понимается всё же содержание, а не форматирование... Причём там (в том куске, который вы назвали "говнокодом") форматирование не экстремально плохое (типа всё в одну строчку), а просто несколько ужатое по сравнению с оригиналом.
Короче, я считаю, что вы промахнулись в своём первом утверждении, что "атмель такого не пишет", а дальше просто выкручивались.
sigmaN
Ееее. Моя любимая тема - говнокод! bb-offtopic.gif

Хоть практика программирования у меня относительно не велика, но уверяю Вас, говнокод может быть прекрасно отформатирован и даже снабжен комментариями, но если это говнокод - он,ИМХО, всё равно будет говнокодом smile.gif
defunct
Цитата(AHTOXA @ Jul 11 2010, 01:02) *
Обычно под словом "говнокод" понимается всё же содержание, а не форматирование...

Всякий бывает: http://govnokod.ru/c

Цитата
У вас какое-то нестандартное понятие о говнокоде

Возможно. Если бы мы вместе работали, мы бы друг-друга поняли, особенно если бы сделали друг-другу review. smile.gif
Филисофия простая - "кто ясно мыслит, тот ясно излагает".
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.