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

[ATMEGA8][C] Woltomierz - rosnący błąd pomiarowy

Faber33 12 Lip 2010 13:47 5538 31
  • #1 12 Lip 2010 13:47
    Faber33
    Poziom 16  

    Witam. Chciałbym zapytać Was co morze być powodem że w przedstawionym poniżej mierniku na Atmega8 przy pomiarze napięcia pomiar wyświetlony na LCD różni się od tego co wskazuje multimetr (Unit UT54)???. Dodam że miernik ten jest podłączony do zasilacza regulowanego 0-35V. Największy problem mam z tym że błąd między wskazaniem miernika na Atmege8 a multimetrem nie jest stały, np. gdy ustawię 3V na zasilaczu multimetr wskazuje mniej więcej 3.00 wskazanie na LCD też jest w granicach błędu (tysięczne części po przecinku), ale gdy np. ustawię 10V (multimetr pokazuje 10V) to na LCD pomiar różni się już o ok. "20 setek". Czy wyższe napięcie ten gorzej :/ Przy 30V pomiar jest "rozjechany" już o ponad 0.5V. Próbowałem skalibrować dzielnikiem przy atmedze (dzielnik 1:13.33) (Rezystory to 100k i 7k5) ale to nie eliminuje rosnącego błędu. Zmieniłem już nawet napięcie referencyjne na zewnętrzne 5V (wyeliminowało to "skakanie" setnych i tysięcznych części po przecinku).

    Może pomiar jest źle przeliczany...ciężko mi się domyślić co może być nie tak bo jestem początkującym elektronikiem :) :/ Lub może ten zasilacz "nie chce" dobrze współgrać z miernikiem:

    https://www.elektroda.pl/rtvforum/topic1560889-0.html

    Oto schemat miernika:

    [ATMEGA8][C] Woltomierz - rosnący błąd pomiarowy

    Część kodu programu:

    Zdefiniowane wartości:

    Code:
    #define Uref       5.02      // Napięcie referencyjne
    
    #define Rpom       0.1              // Rezystor pomiarowy
    #define R1         100000           // Dzielnik - R1 100k ohm
    #define R2         7500         // Dzielnik - R2 7k5  ohm


    Code:
       // pomiar wartosci ADC0 - napiecie
    
             for(i = 0 ; i < 20 ; i++)
          {
             ADCSRA|= _BV(ADSC);          //start pojedynczej konwersji
                while(bit_is_set(ADCSRA,ADSC)){};   
                ADC_L = ADCL;
                ADC_H = ADCH;
             ADC_temp  = ADC_H*256+ADC_L;
             ADC_temp_sr = ADC_temp_sr + ADC_temp;   
             if(i==19)         
             {
                ADC_temp = ADC_temp_sr/20;
                Uin = (Uref/1024)*ADC_temp;
                napiecie = ((R1+R2)/R2)*Uin;
                
                napiecie_int = napiecie;
                napiecie_float = (napiecie*100)-(napiecie_int*100);
                   
             }
            }

    0 29
  • #2 12 Lip 2010 13:53
    flapo213
    Poziom 21  

    Witaj,

    No tak niestety każdy przetownik najlepiej mierzy w 3/4 zakresu pomiaroweg (najdokładnieszy pomiar). Jeśli chcesz większą dokładność to niestety pasuje wprowadzić jakiś multpilekser zewnętrzny z innym podzielnikiem na wejście ewentualnie wykorzystać kilka wejść ADC z różnymi dzielnikami i oprogramować to odpowiednio. Jakoś pomiaru zależy również a powiedziałbym przedwszystkim od jakości źródła referencyjnego im dokładniejsze tym lepije znaczy większ dokładność pomiarów. Sprawdź również oscyloskopem jakoś tego napięcia tzn. czy nie ma tętnień szpilek i takich tam.

    PS.

    Refrencję masz trochę źle połączoną spójrz do datasheet ATM8 tam jest opisane jak to powinno być zrobione i rysunek też jest. To ten obrazek z dławikiem i kondensatorkami.

    Pozdrawiam i życzę sukcesów

    0
  • #3 12 Lip 2010 13:57
    Faber33
    Poziom 16  

    Niestety niema dojścia do oscyloskopu :/. Zastanawiałem się nad zastosowaniem jako napięcia referencyjnego diody Zenera TL431. Dało by to jakiś konkretny zauważalny efekt????

    PS. Zapomniałem wspomnieć że zastosowałem tak jak zalecają w nocie dławik 10uH

    0
  • #4 12 Lip 2010 14:32
    flapo213
    Poziom 21  

    Witaj,

    ciężko powiedzieć, gdyż TL431 będziesz również zasilał z tego samego źródła zasilania więc np. tętnienia zasilania się pewnie przeniosą ale uniezależnisz się od tempratury i takich tam. TL431 na pewno będzie lepszym rozwiązaniem niż ATm8-we źródło napięcia ref. lub tymbardziej napięcie zasilania. Spróbuj najpierw napięcie Aref podłączyć do Avcc i ustaw w rejestrach.

    PS. Softwarowo używasz zmiennych typu float jak mniemam.

    Pozdrawiam

    0
  • #5 12 Lip 2010 14:38
    Faber33
    Poziom 16  

    Mam to już tak podpięte (Aref do AVcc) ale to dało tylko taki efekt jak pisałem powyżej że setne i tysięczne przestały przeskakiwać. Spróbuje zastosować ten TL431 może to da jakiś efekt...ale czy to może wyeliminować ten niestały błąd???

    PS. Nie znam się zbyt dobrze na programowaniu ale z tego co się orientuję to i zmiennych float i int

    Tutaj są wszystkie zdefiniowane zmienne:

    Code:
    int i, ADC_H, ADC_L;
    
       float prad, napiecie, temperatura, ogran, pradogr;
       long ADC_temp, ADC_temp_sr;
       float Uin;
       int napiecie_int , napiecie_float;
       int prad_int , prad_float;
       int pradogr_int , pradogr_float;
       char menu = 0;
       char setup;

    0
  • #6 12 Lip 2010 15:06
    zerolinux
    Poziom 10  

    Witam.

    Ja bym na Twoim miejscu sprawdził poprawność wskazań tego UT.

    Miałem już taki przypadek, że miernik pokazywał niewłaściwe napięcie. Im napięcie mierzone było wyższe tym błąd był większy.

    Rafał

    0
  • #7 12 Lip 2010 15:10
    tmf
    Moderator Mikrokontrolery Projektowanie

    Przy 30V sama rozdzielczość ADC ogranicza cię już do 30mV. Do tego dodaj błędy przetwarzania i nieliniowości ADC i już 0,5V nie jest niczym niezwykłym. Jeśli interesuje cię dokładniejszy pomiar to obawiam się, że musisz skorzystać z innego ADC. Nie bez powodu przetworniki ADC do tanich nie należą.

    0
  • #8 12 Lip 2010 15:13
    Faber33
    Poziom 16  

    Aha....no niezbyt ciekawie to wygląda teraz :/ Chodzi Ci zastosowanie zewnętrznego ADC????Jeśli tak to co byś doradził zrobić ??(szkoda bo ostatnio zrobiłem nową płytkę do tego miernika z modyfikacjami :/).

    PS. Może jest jakiś inny sposób aby to rozwiązać...bo ceny aż odstraszają :/

    0
  • #9 12 Lip 2010 15:48
    tmf
    Moderator Mikrokontrolery Projektowanie

    Pytanie czy potrzebujesz aż taką dokładność? Dla 30V błąd 0,5V jest IMHO bez znaczenia. Możesz też zrobić programową korekcje - albo w postaci funkcji, która ci zlinearyzuje przetwornik, albo look up table. Jeśli pomiar masz stabilny to w ten sposób możesz dowolnie zminimalizować błąd.

    0
  • #10 12 Lip 2010 15:55
    Faber33
    Poziom 16  

    Aha. Próbowałem kombinować z wartością Uref i dzielnikiem napięcia ale jakoś nie moge dobrze trafić odpowiednich wartości. A mógłbyś napisać jak taka funkcja powinna wyglądać????

    0
  • #11 12 Lip 2010 16:11
    tmf
    Moderator Mikrokontrolery Projektowanie

    Sprzęt zostawiasz tak jak masz. Po prostu na wynik obliczonego napięcia nakładasz funkcję, która go skoryguje. Zrób sobie w excelu prosta tabelkę, w jednej kolumnie napięcie jakie jakie faktycznie zmierzyłeś, w drugiej napięcie jakie powinno być. Następnie scatter plot, na otrzymany wykres dodaj linię trendu (wybierz dopasowanie, być może liniowe wystarczy), każ wyświetlić równanie funkcji. To równanie będzie funkcją, która skoryguje ci odczyty. Wrzucasz w twój program i zapominasz o problemie.

    1
  • #12 12 Lip 2010 17:22
    Faber33
    Poziom 16  

    Nie wiem co się dzieje bo coraz gorzej jest...pomiar napięcia mi się rozjeżdża...zmiana wartości dzielnika nawet o kilka set Ohm nie daje efektu :(...Albo jest pomiar zaniżony gdzieś o 1.2 (przy pomiarze wartości ok. 18V - czy wyżej tym gorzej...na dole chociaż jest to ok. 20 setek) albo zawyżony o 40 setek (przy pomiarze tej samej wartości ok. 18V). Wygląda tak jakby była "dziura" w ustawianiu dzielnika.

    Niemam już bladego pojęcia co się z tym dzieje:/

    Dodatkowo mam mierzone ograniczenie a ADC4 i zmiana napięcia wpływa na wyświetlany wynik ograniczenia :/

    Przełożyłem nawet do drugiego miernika atmegę i to samo się dzieje...wcześniej takich wariacji to nie było...nie było takiej dziury w dzielniku :/

    0
  • #13 12 Lip 2010 17:33
    tmf
    Moderator Mikrokontrolery Projektowanie

    Z tego co widzę twój dzielnik dzieli napięcie 14 razy. Czyli z 30V na wyjściu ci robi ok. 2V. Jako VRef masz 5V, czyli wykorzystujesz mniej niż połowę zakresu ADC. Czyli rozdzielczość pomiaru wynosi ok. 70mV. Druga rzecz - nigdzie nie widzę, żebyć ustawiał rejestr multiplexera, a wykorzystujesz kilka kanałów ADC. Po zmianie wejścia ADC jak długo czekasz przed kolejnym pomiarem?

    0
  • #14 12 Lip 2010 17:56
    Faber33
    Poziom 16  

    Ten kod poniżej to część kodu w której wykonywane są wszystkie pomiary...z tego co widzę to odrazu są chyba wykonywane kolejne pomiary

    Code:
       while(1)
    
       {

          prad = 0;
          napiecie = 0;
          temperatura = 0;            
          ADC_temp = 0;
          ADC_temp_sr = 0;
          Uin = 0;
            pradogr = 0;
          ogran = 0;

             // pomiar wartosci ADC0 - napiecie
             for(i = 0 ; i < 20 ; i++)
          {
             ADCSRA|= _BV(ADSC);          //start pojedynczej konwersji
                while(bit_is_set(ADCSRA,ADSC)){};   
                ADC_L = ADCL;
                ADC_H = ADCH;
             ADC_temp  = ADC_H*256+ADC_L;
             ADC_temp_sr = ADC_temp_sr + ADC_temp;   
             if(i==19)         
             {
                ADC_temp = ADC_temp_sr/20;
                Uin = (Uref/1024)*ADC_temp;
                napiecie = ((R1+R2)/R2)*Uin;
                
                napiecie_int = napiecie;
                napiecie_float = (napiecie*100)-(napiecie_int*100);
                   
             }
            }

          temperatura = 0;            
          ADC_temp = 0;
          ADC_temp_sr = 0;
          Uin = 0;
          //prad = 0;

             // pomiar wartosci ADC1
             ADMUX |= _BV(PC0);          
             for(i = 0 ; i < 20 ; i++)
          {
             ADCSRA|= _BV(ADSC);          //start pojedynczej konwersji
                while(bit_is_set(ADCSRA,ADSC)){};   
                ADC_L = ADCL;
                ADC_H = ADCH;
             ADC_temp  = ADC_H*256+ADC_L;
             ADC_temp_sr = ADC_temp_sr + ADC_temp;   
             if(i==19)         
             {
                ADC_temp = ADC_temp_sr/20;
                Uin = (Uref/1024)*ADC_temp;
                prad = Uin/Rpom;
                
                prad_int = prad;
                prad_float = (prad*1000)-(prad_int*1000);
             }
            }


          temperatura = 0;            
          ADC_temp = 0;
          ADC_temp_sr = 0;
          Uin = 0;

            ADMUX &= ~_BV(PC0);

             // pomiar wartosci ADC2
             ADMUX |= _BV(PC1);          
             for(i = 0 ; i < 20 ; i++)
          {
             ADCSRA|= _BV(ADSC);          //start pojedynczej konwersji
                while(bit_is_set(ADCSRA,ADSC)){};   
                ADC_L = ADCL;
                ADC_H = ADCH;
             ADC_temp  = ADC_H*256+ADC_L;
             ADC_temp_sr = ADC_temp_sr + ADC_temp;   
             if(i==19)         
             {
                ADC_temp = ADC_temp_sr/20;
                Uin = (Uref/1024)*ADC_temp;
                temperatura = Uin*90;
             }
            }
          ADMUX &= ~_BV(PC1);
     
           
            ADC_temp = 0;
            ADC_temp_sr = 0;
            Uin = 0;
            //prad = 0;

               // pomiar wartosci ADC4
               ADMUX |= _BV(PC2);           
               for(i = 0 ; i < 20 ; i++)
            {
                ADCSRA|= _BV(ADSC);             //start pojedynczej konwersji
                   while(bit_is_set(ADCSRA,ADSC)){};   
                   ADC_L = ADCL;
                   ADC_H = ADCH;
                ADC_temp  = ADC_H*256+ADC_L;
                ADC_temp_sr = ADC_temp_sr + ADC_temp;   
                if(i==19)           
                {
                    ADC_temp = ADC_temp_sr/20;
                    pradogr = (Imax/1024)*ADC_temp;
                    ogran = pradogr;
                   
                    pradogr_int = ogran;
                    pradogr_float = (ogran*1000)-(pradogr_int*1000);
                }
             }

          ADMUX &= ~_BV(PC2);


          if(temperatura >= Temp_wl)
          {
             PORTB|= _BV(PB1);
          }

          if(temperatura <= Temp_wy)
          {
             PORTB &= ~_BV(PB1);
          }

            if(temperatura >= Temp_max)
          {
              PORTD &= ~_BV(PD0);
            }

          if(temperatura <= Temp_min)
          {
             PORTD|= _BV(PD0);
            }
                
          if(bit_is_clear(PINB,PB2))
          {
             
    //      while(bit_is_clear(PINB,PB2)){}

                Waitms(100);
                menu=menu+1;
                if(menu > 3)
                   {
                   menu=0;
                   }
          }

       
          
          if(menu==0) // prad napiecie
             {
             lcd_U_I();               
             }

          if(menu==1) /// moc rezystancja
             {
             lcd_P_R();      
             }

          if(menu==2) // temp fan
             {
             lcd_T_FAN();
             }
                   
          if(menu==3) // temp on off
             {
             lcd_TON_TOFF();
             }
          
                
       //   if(bit_is_set(PINB,PB1))
       //   {
       //   czysc();
       //   write_text("Fan ON  ");
       //   }

       }
       return 0;

    }


    mam taki plik przy tym wsadzie o nazwie waitms.c ale z tego co zmieniałem tam to chyba chodzi w nim o szybkość z jaką pojawiają się zmiany na LCD

    0
  • #15 13 Lip 2010 07:40
    arturt134
    Poziom 26  

    Masz szeregowy rezystor 100k. Jest on wielokrotnie większy niż impedancja przetwornika. Jeżeli chcesz mieć jakąkolwiek dokładność, to daj między dzielnik a ADC wzmacniacz operacyjny. Nie musi to być jakiś orzeł, wystarczy jakiś za 1zł.
    Zastosuj kalibrację - wartości rezystorów nie są idealne. Sprawdź liniowość, jeżeli układ nadal będzie mocno nieliniowy, to zastosuj kalibrację wielopunktową.
    Zajrzyj też do mojej dzisiejszej odpowiedzi w wątku:
    https://www.elektroda.pl/rtvforum/topic1715418.html

    0
  • #16 13 Lip 2010 11:43
    Faber33
    Poziom 16  

    Aha...spróbuje coś zdobyć...ograniczenie prądowe mam mierzone na ADC4 i tu pojawia się błąd...nawet jak zewrę ADC do masy to się coś pokazuje a nie powinno...:/ Mam tam wpięty potencjometr suwakiem do ADC a dwie pozostałe nóżki między Vcc a masę. I nie obracając tym potencjometrem a zmieniając innym napięcie w zasilaczu zmienia się na LCD wartość ograniczenia ( do zasilacza nie mam podpiętego obciążenia). Jakim cudem tam coś jest jak nie ruszam tego??? ;/

    0
  • #17 13 Lip 2010 12:18
    arturt134
    Poziom 26  

    Jaką masz wartość gdy zewrzesz nóżkę ADC do GND?
    Czy masę masz solidną, czy jakąś cienką ścieżkę?
    Gdzie dołączasz ADC4 do masy? Czy do nóżki AGND?

    Na pewno przyda ci się filtr na AVCC. Daj kondensator 10u, rezystor szeregowy 33R i następny kondensator 10u. Jeżeli nie masz pod ręką 10u, to 1u pewnie też będzie dobry.
    Potencjometr na ADC4 podepnij między AGND i AVCC. Dodatkowo daj na wejściu jakiś mały kondensator ceramiczny, np. 100n.
    Napisz czy coś się poprawiło.

    1
  • #18 13 Lip 2010 13:36
    Faber33
    Poziom 16  

    Ścieżka od masy jest takiej samej prawie grubości jak pozostałe ścieżki i cyfrowa się z nią łączy zaraz pod podstawką od Atmegi. Filtr nie dał efektu.

    teraz to kolejne dziwy :/ teraz to się tylko zero pokazuje(niezależnie od położenia potencjometru). Jest podpięty przez kondensator. Wcześniej jakieś liczby były a teraz zero. Dodam że liczby wyskakiwały tylko wtedy jak był włączony zasilacz!

    Edit: teraz niewiarygodnym cudem na ADC5 działa chociaż nic w programie nie zmieniałem!!! na ADC4 nie działa:/. Jak jest teraz na max potencjometr to pogarsza się jasność LCD:/

    0
  • #19 13 Lip 2010 13:42
    arturt134
    Poziom 26  

    Jak to przez kondensator? Kondensator powinien być równolegle, między suwakiem potencjometru a AGND. Mam nadzieję, że nie dałeś go szeregowo...
    Filtr na AVCC jeżeli nie pomógł, to i nie zaszkodzi. Lepiej go zostaw.
    Jeżeli woltomierzem stwierdzisz obecność napięcia na ADC4, to i przetwornik powinien odczytać tam liczbę różną od zera.

    Aha, mam nadzieję, że odłączyłeś swoje szeregowe rezystory. Niech do ADC będzie dołączony tylko potencjometr i to niezbyt duży (<=10k).

    0
  • #20 13 Lip 2010 14:05
    Faber33
    Poziom 16  

    nie dałem szeregowo, jest między masą a ADC( suwak potencjometru) . Proszę o wyjaśnienie jakim cudem bez zmieniania czegokolwiek w programie zmieniło się wejście pomiaru ograniczenia prądu z ADC4 na ADC5 .

    0
  • #21 13 Lip 2010 14:12
    arturt134
    Poziom 26  

    To OK.
    No nie wiem, to twój program. Może coś pomyliłeś?

    0
  • #22 13 Lip 2010 14:46
    Faber33
    Poziom 16  

    Albo są źle te ADMUXy adresowane...nie moge znaleźć nigdzie żeby pisało konkretnie jak to robić żeby był adresowany konkretny port...bo może się mu kicha :/

    0
  • #24 14 Lip 2010 12:39
    Faber33
    Poziom 16  

    czyli wystarczy np. ADMUX | = _BV(0001) I przypisze go do ADC1 ???Tak
    ??

    0
  • #25 14 Lip 2010 12:52
    szelus
    Specjalista - Mikrokontrolery

    Nie, bo wynik będzie zależał od tego, co już jest wpisane do rejestru ADMUX. Poczytaj o operacjach bitowych w C.
    Najlepiej zrobić tak:

    Code:

    ADMUX = (ADMUX & 0xf0) | nr_kanalu;

    Pierwsza część równania, aby nie zmieniać ustawień napięcia odniesienia. Prawidłowo, zamiast 0xf0 należałoby skonstruować maskę z odpowiednich definicji bitów, ale nie chce mi się teraz kombinować.

    0
  • #26 14 Lip 2010 13:07
    Faber33
    Poziom 16  

    teraz to jest tak, np. ADMUX |= _BV(PC1) więc nie idzie się w tym rozpoznać :/ dlatego chciałem to inaczej zapisać żeby wiedział jak to właściwie działa

    0
  • #27 14 Lip 2010 13:20
    arturt134
    Poziom 26  

    Najlepiej wybierając kanał wpisuj wartość nie zastanawiając się jakie bity są ustawione, a jakie nie.
    Czyli dla REF-a wewnętrznego i wyrównania do prawej wybór kanałów będzie wyglądał tak:
    dla ADC0: ADMUX = 0;
    dla ADC1: ADMUX = (1<<MUX0);
    dla ADC2: ADMUX = (1<<MUX1);

    0
  • #28 14 Lip 2010 13:33
    szelus
    Specjalista - Mikrokontrolery

    Faber, jak to, nie idzie? Proszę, takie trudne?
    http://www.google.com/#hl=pl&source=hp&q=kurs+programowania+w+C
    Powiedz, co to ma być? Zakładasz kolejne wątki dla problemów ze swoim układem. Podajesz informacje lakonicznie, bez żadnych szczegółów, wszystko trzeba z Ciebie wyciągać... Chociaż byś linka dał do poprzednich wątków, gdzie już jakieś szczegóły wypłynęły, nie mówiąc, abyś raz się zebrał i podał komplet informacji - kompletny schemat i program, skoro nie masz bladego pojęcia, co robisz. Myślisz, że każdy ma czas czytać wszystkie Twoje posty i zgadywać co masz na myśli?

    Dodano po 13 [minuty]:

    arturt134, chyba zamieszałeś. Dla wewnętrznego napięcia odniesienia bity REFSx muszą być oba na 1. Poza tym, kombinacja bitów MUXx bezpośrednio daje numer wejścia. Jakbyś chociaż podał przykład dla ADC3:

    Code:

    ADMUX = _BV(MUX0) | _BV(MUX1) | _BV(REFS0) | _BV(REFS1);

    Mam wrażenie, że kolega Faber w ogóle tego nie łapie.

    0
  • #29 14 Lip 2010 13:40
    arturt134
    Poziom 26  

    Sorki, pomyliłem się.
    Powinno być tak:
    dla ADC0: ADMUX = (1<<REFS0) | (1<<REFS1);
    dla ADC1: ADMUX = (1<<REFS0) | (1<<REFS1) | (1<<MUX0);
    dla ADC2: ADMUX = (1<<REFS0) | (1<<REFS1) | (1<<MUX1);

    PS. Ja też mam wrażenie, że kolega Faber ma z tym pewien problem.

    0
  • #30 14 Lip 2010 13:44
    Faber33
    Poziom 16  

    hmm:P staram się załapać:P tylko takie pytanie mam czy trzeba przy każdym ADMUXie dodawać wartość związaną z napięciem odniesienia skoro w jednym z plików mam:

    Code:

    #include <avr/io.h>
    #include "naglowkowy.h"

    /**************************************************************/

    void ADC_init(void)
    {
       
    // Wybranie wewnętrznego żródła napięcia odniesienia   
       ADMUX |= _BV(REFS0);      
        ADMUX |= _BV(REFS1);

       //ADMUX |= _BV(ADLAR);       
    // Zezwolenie na konwersję   
       ADCSRA |= _BV(ADEN);      

       ADCSRA |= _BV(ADPS0);   
       ADCSRA |= _BV(ADPS1);      
       
    }

    0