реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> ADC в ATtiny461, калибровка
ivainc1789
сообщение Jul 2 2010, 19:24
Сообщение #1


Профессионал
*****

Группа: Свой
Сообщений: 1 175
Регистрация: 5-01-05
Пользователь №: 1 807



Только что закончил небольшшой заказ в 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 и так не быстр, скорость выборок падает с увеличением кол-ва сигналов, дак еще и переходные задерживают... Что же делать? Кто как выкручивается?
Мне особенно интересно, как все это хозяйство калибруется в серию? Это же все серьезный риск получить приключений на собственное то самое место...
Go to the top of the page
 
+Quote Post
sigmaN
сообщение Jul 3 2010, 07:57
Сообщение #2


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



А на каком именно цикле он зацикливается?
Может сделать
Код
volatile signed int Vmax, Vmin, V[4];
volatile unsigned char i;
?
А если на этом виснет
Код
while(CHKBIT(ADCSRA,ADSC));// получить результат
то это немного странно, но тоже проверять объявления CHKBIT ADCSRA ADSC добавить volatile куда надо(хотя какраз таки тут он должен быть уже по дефолту)...

А ещё может быть при оптимизации по скорости у вас вообще ADC не инициализируется, а цикл ожидания не может дождаться результатов.


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
ivainc1789
сообщение Jul 3 2010, 18:23
Сообщение #3


Профессионал
*****

Группа: Свой
Сообщений: 1 175
Регистрация: 5-01-05
Пользователь №: 1 807



Цитата
А если на этом виснет
Код
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 делали опору поточнее, было бы совсем здорово, а то не от чего оттолкнуться...
Go to the top of the page
 
+Quote Post
Duhas
сообщение Jul 4 2010, 09:48
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 227
Регистрация: 13-04-07
Пользователь №: 27 018



вообще для поточнее и побыстрее ставится внешний АЦП )

когда все в одном кристалле ессно будут вылазить разные неудобства, но все они решаемы в той или иной мере, как вы и сами нам рассказали )
Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 9 2010, 22:27
Сообщение #5


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



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

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

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

Многократно обсуждалось...
При смене канала просто делайте два измерения. Первое выбрасывайте, второе оставляйте.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jul 9 2010, 23:44
Сообщение #6


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



Цитата(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.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jul 9 2010, 23:47
Сообщение #7


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(defunct @ Jul 10 2010, 04:27) *
такого говнокода не могло быть в AVR аппликухах. Программистов такого уровня в Atmel не держат.

Высокого же вы мнения о программистах атмеляsmile.gif. Однако этот код именно из их аппноты ( avr450 smile.gif )


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 10 2010, 00:44
Сообщение #8


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(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() в основном цикле программы убивая при этом ее портируемость и способность к расширению.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jul 10 2010, 08:55
Сообщение #9


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(defunct @ Jul 10 2010, 06:44) *
Да, высокого, вот оригинал по ссылке:
функция написана и отформатирована читаемо по крайней мере.

biggrin.gif То есть, по-вашему, отформатированный говнокод перестаёт быть говнокодом?


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
xemul
сообщение Jul 10 2010, 09:57
Сообщение #10



*****

Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731



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

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

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

У АЦП АВР очень больное место одно - точность встроенной опоры. имхо, если исключить прочие неразумности, ничего кроме опоры калибровать не требуется. Правда, это тянет за собой деление (зачастую ненужное где-либо ещё в проге). Еще одно "но" - входные сигналы не должны превышать _минимальное_ даташитное Vref.
Естественный вариант - использовать внешнюю опору или внешний(-ие) АЦП.
Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 10 2010, 19:45
Сообщение #11


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



Цитата(AHTOXA @ Jul 10 2010, 11:55) *
То есть, по-вашему, отформатированный говнокод перестаёт быть говнокодом?

критериями которые дают повод назвать код говнокодом в т.ч. есть и форматирование.
Когда функция хорошо читается в ней легче разглядеть потенциальную ошибку.
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Jul 10 2010, 20:27
Сообщение #12


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



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

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


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 10 2010, 21:26
Сообщение #13


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



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

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

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

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

В первом же варианте код не читается, править/отлаживать нереально, и баги не видно - это и делает его говнокодом.
Go to the top of the page
 
+Quote Post
singlskv
сообщение Jul 10 2010, 21:42
Сообщение #14


дятел
*****

Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065



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

Go to the top of the page
 
+Quote Post
defunct
сообщение Jul 10 2010, 21:57
Сообщение #15


кекс
******

Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326



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

1. На одну четверть (макс АЦП sample rate - 15kHz, а 125us полюбому надо пропустить).
2. Относительно предыдущего "правильного" преобразования для того же канала там будет всегда одинаковый интервал определяемый sample таймером плюс +/- jitter вносимый более приоритетными обработчиками прерываний.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 13:21
Рейтинг@Mail.ru


Страница сгенерированна за 0.01542 секунд с 7
ELECTRONIX ©2004-2016