Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[ATmega32][ADC] Dziwne zachowanie przetwornika

quantor 19 Lis 2009 08:59 1556 5
  • #1 19 Lis 2009 08:59
    quantor
    Poziom 10  

    Witam.

    Problem który mi się nadarzył był już poruszony na forum (czyli nie jest to odosobniony przypadek), niemniej pozostał bez zadowalającej odpowiedzi.

    Mianowicie, w ramach przetestowania przetwornika podaję mu napięcie z potencjometru. Przy małych wartościach napięcia (nieco wyższych od zera) przetwornik bardzo zawyża (nawet do pełnego zakresu). Przy nieco wyższych napięciach wszystko jest jak należy, aż do pełnego zakresu. Co jest grane?...

    Pomiar wykonuję w przerwaniu co 10ms (100Hz) poprzez single conversion, zaś wynik oczytuję w kolejnym przerwaniu generowanym przez ADC.


    Poniżej fragmenty kodu:

    Inicjacja przetwornika:

    Code:

    void adc_init(void)
    {
       ADMUX = (1 << 7) | (1 << 6) | (0 << 5) | (0x00 << 0);
       ADCSRA = (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0x07 << 0);
    }


    void adc_select_channel(char channel)
    {
       channel   &= 0x0F;
       ADMUX   &= 0xF0;
       ADMUX   |= channel;
    }



    Inicjacja Timera:
    Code:

    void timer1_init(void)
    {
       TCCR1A = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0);
       TCCR1B = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | (0 << 0);

       TCNT1 = 0x00;   // Timer
       OCR1A = 2499;   // Capture
       
       TCCR1B |= 3;   // Prescaler = 64 -> start licznika
    }




    Inicjacja przerwań:
    Code:

    void irq_init()
    {
       TIMSK   = (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2);
       ADCSRA |=  1 << 3;      // INT z ADC

       EnableInterrupts();
    }



    Przerwanie z timera:
    Code:

    ISR(TIMER1_COMPA_vect, ISR_NOBLOCK)
    {
       static unsigned int counter = 0;

       adc_select_channel(7);
       adc_start();   // Start konwersji. Jak się skończy, wywoła się przerwanie. od ADC.

       if (counter == 0)
       {
          PORTD &= ~(1 << PORT4);      // Mrugnięcie diodą co 1s
       }
       else
       {
          if (counter == 10)
          {
          PORTD |= (1 << PORT4);
          }
       }


       counter++;

       if (counter >= 100)
       {
          counter = 0;
       }

    }




    Przerwanie z przetwornika:
    Code:

    ISR(ADC_vect, ISR_BLOCK)
    {
       char napis[16] = "ADC =  ";
       unsigned int ADCresult;

       ADCresult = 0;
       ADCresult |= (unsigned int) ADCL << 0;
       ADCresult |= (unsigned int) ADCH << 8;
       ADCresult &= 0x3FFF;

       (void) sprintf(napis+6, "%04u", ADCresult);

       write_command(HOME);      // LCD
       write_text(napis);
    }



    Z góry bardzo dziękuję za wszelkie podpowiedzi.

    --
    Pozdrawiam

    0 5
  • #2 19 Lis 2009 09:18
    __Maciek__
    Poziom 19  

    Tak na szybko :

    1. Przerwania powinny być tak krótkie jak to tylko możliwe. np. ustawienie flagi że trzeba coś zrobić ..

    Code:
     (void) sprintf(napis+6, "%04u", ADCresult);
    

       write_command(HOME);      // LCD
       write_text(napis);


    2. Co to jest ??

    ADCresult &= 0x3FFF;

    Raczej 0x3FF ;

    Aż dziw bierze że kompilator nie wyrzucił żadnego warninga, albo błędu.

    0
  • #3 19 Lis 2009 09:46
    tmf
    Moderator Mikrokontrolery Projektowanie

    Co znaczy zawyza nawet do pelnego zakresu? Moze masz jakies zaklocenia, ktore sie nakladaja na mierzony sygnal?

    Maciek: A dlaczego mialby wyrzucac ostrzezenie? ADCResult jest zadeklarowana jako unsigned int, wiec podana maska jest jak najbardzie poprawna. A, ze ADC daje tylko 10 bitow to inna sprawa.
    I BTW, nie wiem dlaczego ludzie lubia sobie utrudniac zycie i wprowadzac zrodla dodatkowych bledow. Zamiast:

    Code:
       ADCresult = 0;
    
       ADCresult |= (unsigned int) ADCL << 0;
       ADCresult |= (unsigned int) ADCH << 8;


    Nie prosciej zrobic:
    Code:
    ADCResult=ADC;

    0
  • #4 19 Lis 2009 10:01
    __Maciek__
    Poziom 19  

    Faktycznie .. rozdwoiły mi się w oczach F-y ...
    Ale mimo wszystko proponuję nie omijać linii ograniczającej wynik do 10 bitów.

    Co więcej gdy używałem atmegi32 nie miałem takich problemów. Może to faktycznie wina otoczenia.

    0
  • #5 19 Lis 2009 11:04
    Ch.M.
    Poziom 27  

    Witaj,
    Po 1: ubogo w komentarze :)

    Po 2: nie piszesz jak szybko taktujesz procesor, a tylko piszesz o prescalerze ADC (/2) więc domyślam się, że zegar jest poniżej 0,4MHz (By default, the successive approximation circuitry requires an input clock frequency between 50kHz and 200 kHz to get maximum resolution)
    [chyba, że źle rozumiem zapis ADCSRA = (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0x07 << 0) który ustawia tylko (2dwa razy?) bit 7 rejestru ADCSRA]

    Po 3: z tego co się zorientowałem to pracujesz w trybie pojedyńczej konwersji, w związku z tym powinieneś odrzucać pierwszy pomiar (robić dwa oddzielone od siebie o czas propagacji)

    Po 4: przydałby się schemat elektryczny...

    Piszesz o błędach w odczycie... nie chcę się rozpisywać i odpowiem tak: ich zachowanie zależy od tego co masz podłączone do portu ADC. Jeśli nie jest to urządzenie bateryjne to puść pomiar ciągły i odczytuj sobie co te 10ms wartość z przetwornika. Każdorazowa inicjalizacja wejścia ADC zajmuje czas i wprowadza niedokładność pierwszego pomiaru. Jeśli mam coś zasugerować to zostawiaj cały port z ADC niepodłączony, a tylko wejścia pomiarowe powinny się na nim znaleźć.

    Pozdrawiam

    0
  • #6 19 Lis 2009 22:26
    quantor
    Poziom 10  

    tmf napisał:
    Co znaczy zawyza nawet do pelnego zakresu? Moze masz jakies zaklocenia, ktore sie nakladaja na mierzony sygnal?


    Raczej na pewno nie. Sygnał udzie z baterii poprzez potencjometr. Mierzę miernikiem to napięcie, jest bardzo niskie...


    Cytat:
    Nie prosciej zrobic:
    Code:
    ADCResult=ADC;


    Racja! ;) Zapomniałem że w C można potraktować ADC H/L jako jeden rejestr. :-)

    Ale nie tu tkwi problem...

    Dodano po 30 [minuty]:

    Ch.M. napisał:
    Po 2: nie piszesz jak szybko taktujesz procesor, a tylko piszesz o prescalerze ADC (/2) więc domyślam się, że zegar jest poniżej 0,4MHz (By default, the successive approximation circuitry requires an input clock frequency between 50kHz and 200 kHz to get maximum resolution)


    Taktowanie proca jest na 16MHz. Prescaler jest ustawiony na 128 (w komentarzu jest błąd ;)), więc ADC taktowany jest 125 kHz. Poza tym katowanie ADC powyżej 200kHz ujawni się na najmłodszych bitach. Jeśli interesuje nas wynik 8-bit, można z tą częstotliwością przegiąć (co pisze w datasheecie).

    Cytat:
    [chyba, że źle rozumiem zapis ADCSRA = (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0x07 << 0) który ustawia tylko (2dwa razy?) bit 7 rejestru ADCSRA]


    Bit 7 rejestru ADCSRA to nie prescaler, tylko ADC ENABLE. Bit 6 to start konwersji, ale inicjując ADC tylko go włączam, ale nie uruchamiam procesu. Ustawienia prescallera to część "| (0x007 << 0), czyli trzy jedynki na trzech najmłodszych bitach.

    Cytat:
    Po 3: z tego co się zorientowałem to pracujesz w trybie pojedyńczej konwersji, w związku z tym powinieneś odrzucać pierwszy pomiar (robić dwa oddzielone od siebie o czas propagacji)


    Też o tym myślałem. Tylko w jakiejś nocie napisali, że w nowszych ATmegach. w trybie pojedynczej konwersji przetwornik robi dwa pomiary i pierwszy ignoruje. Nie ma o tym w datasheecie. Ale spróbuję, co mi szkodzi. ;) Albo puszczęgo we Free Running Mode, tylko wówczas przedłuży mi się to przerwanie 100-hertzowe o zawartość tego z ADC. Ale powinienem się "wyrobić". :-)





    Cytat:
    Piszesz o błędach w odczycie... nie chcę się rozpisywać i odpowiem tak: ich zachowanie zależy od tego co masz podłączone do portu ADC. Jeśli nie jest to urządzenie bateryjne to puść pomiar ciągły i odczytuj sobie co te 10ms wartość z przetwornika.


    Zaraz puszczę go we Free Running mode.


    Cytat:
    Każdorazowa inicjalizacja wejścia ADC zajmuje czas i wprowadza niedokładność pierwszego pomiaru.


    Inicjowany jest raz. Tylko konwersja jest włączana co 10 ms (100 Hz).

    Cytat:
    Jeśli mam coś zasugerować to zostawiaj cały port z ADC niepodłączony, a tylko wejścia pomiarowe powinny się na nim znaleźć.


    I tak też jest.


    Dobra, spróbuję z tym Free Running. Dzięki za wszelkie sugestie! :-)

    --
    Pozdrawiam

    Dodano po 5 [godziny] 25 [minuty]:

    OK, poradziłem sobie. Sprawa była banalna a ja szukałem za daleko. Tak, jakbym szukał przy użyciu lornetki muchy, która siedzi mi na nosie. ;-)

    Mianowicie, był walnięty potencjometr którym zadawałem wielkość. Miał blisko zera małą strefę, która stanowiła przerwę. A jak nóżka pomiarowa wisi, to odczyt wariuje. ;-)


    Acha! Tak jak pisałem, pierwszy odczyt od razu po włączeniu bloku ADC może być losowy (ale nie musi), potem nawet w trybie pojedynczego pomiaru każdy kolejny jest OK (chyba, że wyłączymy blok ADC, a potem włączymy).


    Niemniej dziękuję wszystkim za pomoc i sugestie. A wątek myślę do zamknięcia.

    --
    Pozdrawiam serdecznie.

    0