Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: проблема с АЦП
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
azure
Помогите разобратся с АЦП.

Пишу программу для считывания уровня напряжения с переменного резистора (от 0 до 5 В).
Микроконтроллер: ATmega16 работает на частоте 16МГц. Вход A0. Разрядность АЦП: 10бит. Считывание одинарное (но вызывается регулярно)

Программа работает, но при считывании уровня 0В выдает в ADCW не 0, а 15. При этом с верхним уровнем никаких проблем нет (1023).

Код программы ниже. Написано в WinAVR последней версии (на нижних версиях результат тот же).
Бьюсь неделю, не могу понять. Пробовал прошивать другой контроллер (8535) - результат тот же 07.gif

Код
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
    #define  F_CPU 16000000UL
#include <util/delay.h>

volatile unsigned int ADC_w = 0;

int
segment_decoder(int num, int point)
{
int result = 0x3F;
    switch(num)
    {
        case 0: result = 0x3F;
        break;
        case 1: result = 0x06;
        break;
        case 2: result = 0x5B;
        break;
        case 3: result = 0x4F;
        break;
        case 4: result = 0x66;
        break;
        case 5: result = 0x6D;
        break;
        case 6: result = 0x7D;
        break;
        case 7: result = 0x07;
        break;
        case 8: result = 0x7F;
        break;
        case 9: result = 0x6F;
        break;
        default: result = 0x3F;
        break;
    }

if(point!=0)
    {
    result = (result | 0x80);
    }

  return result;
}

ISR(ADC_vect)
{
    ADC_w = ADCW;//ADCL;
}


int main(void)
{
_delay_ms(2);

DDRA = 0x00;
DDRB = 0xFF;
DDRC = 0xFF;
DDRD = 0xFF;

PORTA= 0xFF;
PORTB= 0x00;
PORTC= 0x00;
PORTD= 0x00;


SREG    |= _BV(7);

   ADCSRA |= (1<<ADPS0);
   ADCSRA |= (1<<ADPS1);
   ADCSRA |= (1<<ADPS2);
    
ADMUX  = 0x40;
    
   ADCSRA |= (1 << ADATE);
   ADCSRA |= (1<<ADIE);
   ADCSRA |= (1<<ADEN);

sei();
   while(1)
    {
    ADCSRA |= (1<<ADSC);
    while(ADCSRA & 0x40)
    {
            PORTB = segment_decoder( (ADC_w/1000)%10, 0);
        PORTD = 2;
    _delay_us(10);
        PORTD = 0;
            PORTB = segment_decoder( (ADC_w/100)%10, 0);
        PORTD = 4;
    _delay_us(10);
        PORTD = 0;
            PORTB = segment_decoder( (ADC_w/10)%10, 1);
        PORTD = 8;
    _delay_us(10);
        PORTD = 0;
            PORTB = segment_decoder( ADC_w%10, 0);
        PORTD = 16;
    _delay_us(10);
    }
    };

return 0;
}
GDI
там 0 может и не быть, потому как шумы, эффекты квантования и т.п., а вы уверены, что это не ошибка в отображении значения? для проверки можно поставить светодиод на какой нибудь вывод порта и зажигать его если значение АЦП будет равно константе (15 в вашем случае).
azure
Я результат преобразования вывожу на семисегментные индикаторы, ручку переменоого резистора кручю - вижу колеблится или нет (внешних помех никаких нет, результат стоит стабильно).

Общался я с человеком, который подобное писал на Асемблере для МК 8535 - говорит, что у него начинает с 0 до 1023. Свою программу я прогнал и в 8535 - у меня с 15 до 1023.

Что интересно, при моделировании в VMLab - показывает от 0 до 1023. Вот и думаю что не так: или код, или программатор, или ??

Не могу никак добится результата sad.gif
MrYuran
Цитата(azure @ Mar 20 2008, 17:28) *
Я результат преобразования вывожу на семисегментные индикаторы, ручку переменоого резистора кручю - вижу колеблится или нет (внешних помех никаких нет, результат стоит стабильно).

Общался я с человеком, который подобное писал на Асемблере для МК 8535 - говорит, что у него начинает с 0 до 1023. Свою программу я прогнал и в 8535 - у меня с 15 до 1023.

Что интересно, при моделировании в VMLab - показывает от 0 до 1023. Вот и думаю что не так: или код, или программатор, или ??

Не могу никак добится результата sad.gif

А если закоротить вход на землю?
Скорее всего просто нету нуля у этого резистора
azure
smile.gif я это уже пробовал, он там есть. Мерял тестером - 0.
konstan
Я при работе с АЦП использую только 8 старших бита, так как если посмотреть в даташит, то оговаривается, что ошибка может быть в младших 2 битах.
MrYuran
А если так попробовать - ADC_w=0;
и пустить разок

Если покажет 0, то надо померить напряжение между входом и аналоговой землёй, только нормальным цифровым вольтметром

Блин, это ж AVR...
Вообще по правилам на время преобразования рекомендуется гасить ядро, а по прерыванию от АЦП просыпаться и обрабатывать результат.
Хотя 15 это не 1-2 разряда, а все 4. Вряд ли в этом дело.
galjoen
Цитата(MrYuran @ Mar 20 2008, 17:42) *
А если закоротить вход на землю?
Скорее всего просто нету нуля у этого резистора

+1
Только нуля нет именно в момент измерения. Я с таким сталкивался, правда не на АВР. АЦП в момент измерения ток вытекающий начинает генерировать. А когда не мереет ничо из него не течёт. А переменный резистор с большим сопротивлением?

Нет не в этом дело. У вас в программе ошибка. Ну остаток от деления на 1000 вы на индикатор вывели. А при выводе след. разряда надо из значения АЦП предыдущий * 1000 ( 100, 10) вычетесть, а уж потом остаток считать. Как же у вас вообще всё это работало? Там ведь начиная со следующего после 1го ненулевого разряда ошибка получалась! Попробуйте под отладчиком таким способом 222 (десятичное) вывести - 200 на индикаторе получите.
Или я что-то там с вашей программой не понял?
GDI
Если результат стабильно 15, то скорее всего что то с программой, попробуйте сделать как я говорил со светодиодом или как выше предложили, попробуйте просто в вашу переменную ADC_w записывать "0" - что будет на экране?
azure
8 бит мне мало, нудно мерять с точностью 0.1%

при штучном ADC_w=0, ноль показывается, ща буду мерять напряжения

Цитата(galjoen @ Mar 20 2008, 17:14) *
Или я что-то там с вашей программой не понял?


Что-то не понялм smile.gif
Там такие опреации:

(ADC_w/100)%10
ADC_w = 222 следует: ADC_w/100 = 22.2 делее %10 получаем то что после точки 2
ADC_w = 321 следует: ADC_w/100 = 32.1 делее %10 получаем то что после точки 1

Ранее использовал floor()
domowoj
Нужно "засыпать" на время преобразования.
Можно попробовать измерить внутренний 0-(MUX=1111).
Что-то с землями.
azure
Внутренней землей я задавался - был 0 (меряло верно) - что странно smile.gif.

Нашол я AVR120: Characterization and Calibration of the ADC on AVR, где рассказывается о возможных искажениях (ошибках) измерений.

То-есть: АЦП меряет от 15 до 1023. Делаю смещени (математикой): получаю от 0 до 1008. Умножаю на коэффициент 1024/1009: получаю прямую от 0 до 1023.

При практике: идеальное измерение значений подаваемых на вход. Подавалось на вход напряжение 5В, 2.5В. 0.25В и 0В - проверка мультиметром показало АЦП с этой корекцией меряет верно.
Эксперементировал на 2 ATmega16 и одной 8535.

Может это и не верное, но пока решение.

Очень и искрене благодарен всем за отклик на помощь. СПС!
domowoj
Может дело в данном образце контроллера?
SasaVitebsk
Кстати. А что за контроллер?

В новых контроллерах вы можете прочитать 0 и Vcc питание. Это позволяет найти смещение и смасштабировать.
Lexdaw
На Меге16 у меня работают сразу три АЦП без выключения ядра 10 бит и все нормально от 0 до 1023.Единственное что подается сигнал с ОУ.
azure
Цитата(Lexdaw @ Mar 21 2008, 09:05) *
На Меге16 у меня работают сразу три АЦП без выключения ядра 10 бит и все нормально от 0 до 1023.Единственное что подается сигнал с ОУ.


а код программы можете выложить?
я сделал смещение, с ним все отлично работает (совпадает с измерениями мультиметра), но совесть мучает smile.gif
Lexdaw
Да дело вряд ли в коде.Если сигнал прямо с переменника.,то надо учесть,что у него может не быть R=0,да еще если высокоомный,а у вас подтяжка внутренними резисторами.Вот здесь и может выскакивать.
Kovrov
15 LSB offset error - это жесть
а так получается что 15lsb *2.5 mv = 37.5 mV (если ион внутренний)
тут правильно советуют
попробуйте отсоединить ножку ADC0 (или какой у вас канал?) от схемы и припаять её к AGND, (только именно отсоединить а не ленится :-)
канал ADC менять не пробывали?
=GM=
Цитата(azure @ Mar 20 2008, 13:45) *
Помогите разобратся с АЦП.
Пишу программу для считывания уровня напряжения с переменного резистора (от 0 до 5 В).
Микроконтроллер: ATmega16 работает на частоте 16МГц. Вход A0. Разрядность АЦП: 10бит. Считывание одинарное (но вызывается регулярно). Программа работает, но при считывании уровня 0В выдает в ADCW не 0, а 15. При этом с верхним уровнем никаких проблем нет (1023)


Похоже у вас плохое опорное напряжение. Попробуйте внутреннее 2,56В для начала или подключите к ноге AREF внешний опорник. Не забудьте только биты REFS1-0 поправить.

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

1) Запустили ацп в режиме free run, но в главном цикле вы всё время запускаете начало преобразования. Если вы хотите single conversion, то надо дождаться конца преобразования.

2) Есть стробы записи в регистры 1, 2, 3. А вот в регистр 4 строба записи нет, надо добавить.

3) В главном цикле используется переменная ADC_w, но она может быть испорчена в любой момент в прерывании. Примите меры.

4) Непонятна роль оператора while(ADCSRA & 0x40). Почему что-то делается, когда преобразование не закончено?

5) Зачем стоит оператор return 0 в конце программы? У вас там что, операционка стоит?
VladimirYU
Цитата(Lexdaw @ Mar 21 2008, 10:05) *
Единственное что подается сигнал с ОУ.

+1. Для себя после принял за правило: с переменника прямо на вход АЦП сигнал не подавать. На ОУ собрать повторитель да еще и подфильтровать немного.
Kuzmi4
Повторитель - обязательно.
Только там уровень нуля подпрыгнет, надо будет подшаманить...
azure
Нашол я где была зарыта ошибка!
Скажу что все перепробывал, что советовали (про MUX4...MUX0 для контроля точек 0B 1.22B) - на них тоже выдавало ошибки, причем на 1.22 - непостежимого характера smile.gif. Только операционик не пробовал лепить, он сути б не менял.


И так ошибка была в коде (про непрерывный режим - тоже ошибка была, но в том коде была незаметна) и в самом начале:

Код
int main(void)
{
_delay_ms(2);

[b]DDRA = 0x00;[/b]
DDRB = 0xFF;
DDRC = 0xFF;
DDRD = 0xFF;

[b]PORTA= 0xFF;[/b]
PORTB= 0x00;
PORTC= 0x00;
PORTD= 0x00;


Изначально порт А устанавливался как входно на считывание. И при считывании 0В выдавал уровень. При удалении выделенных строк - все заработало как надо, меряет от 0...1023 smile.gif

Всем сноваже СПС! yeah.gif
Kibi
поделюсь тоже соображениями, нет защиты от дребезга, 50мс думаю не помешает, также очень интересно как подведена аналоговая линия, подтяжка к + или к земле, стоит ли защитный диод, насколько развязана плата по питанию....
azure
Цитата(Kibi @ Mar 24 2008, 16:19) *
поделюсь тоже соображениями, нет защиты от дребезга, 50мс думаю не помешает, также очень интересно как подведена аналоговая линия, подтяжка к + или к земле, стоит ли защитный диод, насколько развязана плата по питанию....


Дребезга тут нет, сигнал снимается с резистором в параллель которому конденсаторы.
Аналоговая земля с цифровой объеденены. Питания тоже, только питание к АЦП шло от основного через даташитовскую LC цепочку.

Для лучшей точности пробовал питать АЦП от другого источника с общей землей - полождительные результаты.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.