logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak wyświetlić wynik z przetwornika ADC na LCD w Atmega16?

Elektricon 10 Sie 2007 20:28 2150 21
REKLAMA
  • #1 4165717
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    chodzi mi o wynik z przetwornika, nie moge dojsc do tego jak to wyswietlic na lcd...
    rozumiem ze wynik jest na 10 bitach z rejestrow ADCL i ADCLH
    jezeli przetwornik ma 1024 "progow" to rozumiem ze wynik zawiera rowniez czesc ulamkowa prawda...
    wiec mam osobno wyliczyc calkowita wartosci i ulamkowa i przeliczac wedlug wzorow (wartosc*1024)/5 V ?
    na jakich bitach jest czesc ulamkowa?
    i w jaki sposob wyswietlic to na LCD,czyli jak przeliczyc na ASCII ?
    bede wdzieczny za nakierowanie
    bo jestem zielony w temacie
    pozdrawiam
  • REKLAMA
  • #2 4165950
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Przepisujesz do zmiennej 16-bit (unsigned int) z ADC (nie trzeba rozbijać odczytu na L i H bo tym się zajmie kompilator)...
    Napięcie w mV wyliczasz ze wzoru (który jest w dokumentacji) Vin = (ADC * Vref) / 1024...
    Co do wyświetlania...
    
    unsigned char volt[4], i=0;
    
    i=4;
    do
    {
     volt[i] = (Vin%10) + 0x30;
     Vin/=10;
     i--;
    }while(i);
    

    W ten oto sposób w tablicy masz już liczbę w postaci kodów ASCII...
  • #3 4166105
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    przy okazji..
    co robie zle ? :/
    pubus ,wielkie dzieki..:D
    jeszcze nie uwzglednilem ponizej twojego kodu..wiec sie nie przejmuj ;)

    SIGNAL(SIG_ADC)  //obsługa przerwania od A/C 
    { 
    double adc;
    unsigned char wynik;
     adc = ADCW; //pod zmienną adc podstawiona jest wartość odczytana z rejestrów ADCH i ADCL 
     //adc=(adc*1024)/5;
     itoa(adc, wynik, 10); 
     instrukcja((0*0x40+0)|0x80);
     tekst("W: ");
     tekst(wynik);
    
    
    } 
    w mainie .....
    sei();
    
     DDRA=0x00;
     PORTA=0x00;
    
    ADMUX=0x41; //ADC1, VCC, ADCL - 0-7  ADCH 8-7 (0,1)
    
    
    
    ADCSRA|=_BV(ADIE);
    ADCSRA|=_BV(ADEN);
    ADCSRA|=_BV(ADSC);
    ADCSRA|=_BV(ADATE);
    
    ADCSRA|=_BV(ADPS2);
    ADCSRA|=_BV(ADPS1);
    ADCSRA&=~_BV(ADPS0);
  • #4 4166707
    dturczak
    Poziom 19  
    Posty: 416
    Pomógł: 9
    Ocena: 5
    a ja mam takie pytanie..
    czy da sie pogodzic taką rzecz,ze wyprowadzenie LCD tzn DB7 mam na pinie A.7 ? (chociaz nie uzywam go jako wejscia przetwornika mam krzaki na wyswietlaczu :(
  • #5 4167000
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Elektricon napisał:
    przy okazji..
    co robie zle ? :/ ...

    Sprawdź w pliku nagłówkowym stdlib.h , jak wygląda prototyp funkcji itoa().

    Piotrek
  • #6 4167149
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    zostosowalem sie do pomyslu Pubus'a...
    ale niestety nie dziala...
    
    SIGNAL(SIG_ADC)                        // przerwanie z przetwornika ADC
    {
       unsigned int adc; 
       adc = ADC; 
       adc=(adc*5)/1024; 
       unsigned char pom, i=0; 
       instrukcja((0*0x40+0)|0x80); 
       tekst("W: ");
       i=4; 
          do 
            { 
               pom= (adc % 10) + 48; 
               adc=adc/10; 
               dana(pom);
               i--; 
            }while(i); 
    
    
    
    
    
    
    }
    

    o ile pierwsza liczbe przetwarza to na pozostalych miejscach sa zawsze zera...
    czemu trzeba stosowac unsigned int a nie mozna double?
    moze to w tym problem ?
    ale z kolei jak zamienie na double to operacji % 10 kompilator nie chce przepuscic...
  • REKLAMA
  • #7 4167157
    jedrek_1981
    Poziom 12  
    Posty: 85
    Pomógł: 3
    Ocena: 1
    dturczak napisał:

    czy da sie pogodzic taką rzecz,ze wyprowadzenie LCD tzn DB7 mam na pinie A.7 ? (chociaz nie uzywam go jako wejscia przetwornika mam krzaki na wyswietlaczu :(

    Ocyzwicie, ze tak.
    Napewno nie jest to powodem krzakow na wyswietlaczu.
  • #8 4167450
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Ja tu kombinuje co może być nie tak i nawet nie zauważyłem od razu tego "kwiatuszka"...
    Napięcie referencyjne masz rozumiem 5mV...? Tak...?
    Jeśli masz jednak 5V to wypadało by jako referencyjne przy obliczeniach dać 5000mV, nieprawdaż...?
    Zmienna adc może być unsigned long int...
  • REKLAMA
  • #9 4167493
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    to ja juz nie wiem o co chodzi:
    
    SIGNAL(SIG_ADC)                        // przerwanie z przetwornika ADC
    {
     unsigned long int adc; 
     adc=(ADC*5000)/1024; 
     unsigned char pom, i=0; 
      instrukcja((0*0x40+0)|0x80); 
     tekst("W: ");
    i=4; 
    do 
    { 
    pom = (adc % 10) + 48; 
    adc=adc/10; 
    dana(pom);
    i--; 
    }while(i); 
    
    }
    


    rzeczywiscie z tym napiecie ref... niedopatrzenie,nie zauwazylbym wogole
    .tylko ze teraz jeszcze dziwniej sie to zachowuje ;)
    po pierwsze zmieniaja sie tylko 2 pierwsze pozycje...
    i tak np. dla napiecia 2.307 mam na lcd W: 5400 ,dla 2,424 - 4300
    z czego moze to wynikac ? :/

    do Aref 100nF, do Avcc napiecie przez filtr(dlawik+C)....gnd do gnd ..
    ADMUX=0x41;

    ADCSRA|=_BV(ADPS2);
    ADCSRA|=_BV(ADPS1);
    ADCSRA&=~_BV(ADPS0);

    ADCSRA|=_BV(ADIE);

    ADCSRA|=_BV(ADEN);

    w petli while co jakis czas ADCSRA|=_BV(ADSC);
  • #10 4167776
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Wypróbuj ten kod...
    
    #include <avr/interrupt.h> 
    #include <inttypes.h> 
    #include <stdlib.h> 
    #include <math.h>
    #include <avr/io.h>
    
    #define F_CPU   8000000 //20000000 //18432000 //3686400
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) 
    
    /****** Zmienne globalne ****************************************************************************/
    volatile unsigned char koniec=0;
    volatile unsigned long int adc;
    /**************************************************************************************************/
    
    SIGNAL (ADC_vect)
    {
     adc = ADC;
     koniec=1;
    }
    
    int main(void)
    {
     unsigned int pom=0;
     unsigned char i=0;
    
     ADMUX |= _BV(REFS0) | _BV( MUX2); 
     ADCSRA |= _BV(ADEN) | _BV(ADSC) | _BV( ADIE) | _BV(ADPS2) | _BV( ADPS1) | _BV(ADFR);
    
     for(;;)
     {
      if(koniec)
      {
       koniec=0;
       i=4;
       pom = (adc*5000) / 1024;
       do
       {
         dana((pom%10)+48); //dana zakładam, że jest funkcją wyświetlającą na LCD znak
         pom/=10;
         i--;
       }while(i);
      }
    
     }
     return 0;
    }
    
  • #11 4167898
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    do wejscia adc1 mam dolaczony potencjometr..
    normalny" program w bascomie smiga..
    tutaj nie chce :/
    ja juz nie wiem,te wartosci z kosmosu u mnie sie biora chyba..
    nawet z twoim programem..
    o ile jest dobry...
    ADFR to chyba nie atmega16 (ADATA),i zalaczylem sei i wybralem adc1..
    reszte nie zmienialem wogole i lipa.. :/

    eh...jeszcze raz.... zrobilem nawet bez przerwan
    //wejscie ADC1, do AREF podlaczony 100nF,jako napiecie odniesienia AVCC...
    ADMUX=0x41;

    //preskaler 64
    ADCSRA|=_BV(ADPS2);
    ADCSRA|=_BV(ADPS1);
    ADCSRA&=~_BV(ADPS0);
    //wlaczenie przetwornika
    ADCSRA|=_BV(ADEN);

    while(1)
    {
    ADCSRA|=_BV(ADSC); //pomiar
    while(bit_is_set(ADCSRA,ADSC)){}; //oczekiwanie na koniec pomiaru
    pom = (ADC*5000) / 1024; //przeliczenie wartosci
    instrukcja((0*0x40+0)|0x80); //kursor lcd na 0,0
    i=0;
    do
    {
    pom/=10;
    dana((pom%10)+48); //wyswietlenie kolejnej cyfry

    i++;
    }while(i<4);
    }
    }

    przeciez to jest niemozliwe zeby nie dzialalo :(
  • REKLAMA
  • #12 4168072
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    No tak fakt w atmega16 inaczej się tryb free run odpala...
    Ok to może inaczej...
    Wypróbuj ten kod...
    
    #include <avr/interrupt.h> 
    #include <inttypes.h> 
    #include <stdlib.h> 
    #include <math.h>
    #include <avr/io.h>
    
    #define F_CPU   8000000 //20000000 //18432000 //3686400
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) 
    
    int main(void)
    {
     unsigned long int adc=0;
     unsigned int pom=0;
     unsigned char i=0;
    
     for(;;)
     {
       ADMUX |= _BV(REFS0) | _BV(MUX2); 
       ADCSRA |= _BV(ADEN) | _BV(ADSC) | _BV( ADIE) | _BV(ADPS2) | _BV( ADPS1);
       
       do{}while(!(ADCSRA&0x10));
       adc = ADC;
       ADCSRA |= _BV(ADIF);
      
       pom = (adc*5000) / 1024;
       i=4;
       do
       {
         dana((pom%10)+48); 
         pom/=10;
         i--;
       }while(i);
     }
    
     return 0;
    }
    
  • #13 4168239
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    no nie wiem..
    przedziwne zachowanie..
    ale po stronie sprzetowej jestem pewien ze wszystko jest ok.
    zamieszczam pelny kod ktory wykorzystuje..
    jakby ktos to mogl wrzucic i sprawdzic u siebie bylbym wdzieczny...
    wejscie na PA1,piny lcd ustawic na poczatku+zmienic DDR'y do nich w mainie

    glowny kod (pubusa wiekszosc (dzieki za wlozony wysilek.. ):
    
    int main(void) //program główny
    {
    DDRA&=~_BV(1);
    PORTA&=~_BV(1);
     PORT_DB6|=_BV(PIN_DB6);
     PORT_DB7|=_BV(PIN_DB7);
    
     DDRC|=_BV(PIN_RS);
     DDRC|=_BV(PIN_E);	
     DDRC|=_BV(PIN_DB4);
     DDRC|=_BV(PIN_DB5);
     DDRD|=_BV(PIN_DB6);
     DDRA|=_BV(PIN_DB7);
    
     ini_lcd();
    
         instrukcja(0x0c); //0f
    	 instrukcja(0x01);
    
     unsigned long int adc=0; 
     unsigned int pom=0; 
     unsigned char i=0; 
    
     for(;;) 
     { 
       ADMUX |= _BV(REFS0) | _BV(MUX0); 
       ADCSRA |= _BV(ADEN) | _BV(ADSC) | _BV( ADIE) | _BV(ADPS2) | _BV( ADPS1); 
        
       do{}while(!(ADCSRA&0x10)); 
       adc = ADC; 
       ADCSRA |= _BV(ADIF); 
      
       pom = (adc*5000) / 1024; 
       i=4; 
         instrukcja((0*0x40+0)|0x80);
       do 
       { 
         dana((pom%10)+48); 
         pom/=10; 
         i--; 
       }while(i); 
    delay_us(1000000);
     } 
    
     return 0; 
    } 

    reszta w zalaczniku..
    zaczynam sie poddawac
    Załączniki:
    • adc.txt (4.82 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #14 4168441
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Jak możesz to zamieść schemat podłączenia LCD...
  • #15 4168471
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    lcd jest sprawny..
    adc pod bascomem smiga z lcd
    a kazdy pin mozna przypisac...
    
    #define PORT_E   PORTC
    #define PIN_E   3
    
    #define PORT_RS  PORTC
    #define PIN_RS  2
    
    #define PORT_DB4 PORTC
    #define PIN_DB4 4
    
    #define PORT_DB5 PORTC
    #define PIN_DB5 5
    
    #define PORT_DB6 PORTD
    #define PIN_DB6 7
    
    #define PORT_DB7 PORTA
    #define PIN_DB7 7
    

    tylko ddry w mainie trzeba pozmieniac..
    to napewno nie problem z lcd..
  • #16 4168769
    dturczak
    Poziom 19  
    Posty: 416
    Pomógł: 9
    Ocena: 5
    ladnie sie tutaj widze meczycie ;)
    przede wszystkim w odwrotnej kolejnosci wyswietlacie na lcd.. stad moze te kosmiczne liczby.., przy metodzie "wylawiania cyfr z pozycji" jedziemy przeciez od prawej do lewej..
    wiecej nie zauwazylem,ale moze ktos odkryje..
    pozdrawiam..
  • #17 4169323
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    no dzieki,to troszke wyjasnia,
    heh ze wczesniej na to nie wpadlem...
    oczywiste to jest..
    tylko ze nadal w czyms tkwi blad :|
  • Pomocny post
    #18 4169336
    dturczak
    Poziom 19  
    Posty: 416
    Pomógł: 9
    Ocena: 5
    przede wszystkim wynik jezeli ma byc w int musi byc formie unsigned long int.... przy zwyklym unsigned int bedziesz mial to co obserwujesz czyli 2 pozycje ci obetnie..
    
     ADMUX |= _BV(REFS0) | _BV(MUX0); 
       ADCSRA |= _BV(ADEN) | _BV(ADSC) | _BV( ADIE) | _BV(ADPS2) | _BV( ADPS1) | _BV(ADATE) ; 
     sei();
    
    SIGNAL(SIG_ADC)    
    {
       unsigned long int adc,pom;
       unsigned char i;
       adc = ADC; 
       pom = (adc*5000) / 1024; 
       i=4; 
         instrukcja((0*0x40+0)|0x80); 
       do 
       { 
         dana((pom%10)+48); 
         pom/=10; 
         i--; 
       }while(i); 
    
    }
    

    ze to dziala ,prawie jest pewne ;)
    oczywiscie wynik w odwrotnej kolejnosci...
    pozdrawiam :)
  • #19 4169345
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    dzieki wielkie ,dziala cos :)
    a moze ktos mi powiedziec jak wyeliminowac to ze ostatnie 2 cyfry strasznie mi skacza.... wina zaklocen? ,co sie z tym przewaznie robi ?
  • #20 4169375
    dturczak
    Poziom 19  
    Posty: 416
    Pomógł: 9
    Ocena: 5
    mozna dac filtr dlawik+cewka na AVCC..
    w signal mozna wyswietlac wynik ostatnich 10 pomiarow i robic srednia w ten sposob :
    
    volatile unsigned long int adc; 
    unsigned char ile_adc;
    unsigned long int ad[11];
    /**************************************************************************************************/ 
    SIGNAL(SIG_ADC)    
    {
    ile_adc++;
    ad[ile_adc]=ADC;
    
    	if (ile_adc==10)
    	{
    	   ile_adc=0;	
    	   unsigned long int adc,pom;
    	   unsigned char i;
    	   unsigned char nap[5];
           adc=0;
    	   for(i=1;i<11;i++)  adc=adc+ad[i]; 
    
    	   adc = adc/10.0; 
    	   pom = (adc*5000) / 1024; 
    
    	     instrukcja((0*0x40+0)|0x80); 
    	  for(i=1;i<5;i++)
    	  {
    	     nap[i]=(pom%10)+48; 
    	     pom/=10; 
    	  }
    	  dana(nap[4]);
    	  tekst(".");
    	  dana(nap[3]);
    	  dana(nap[2]);
    	  dana(nap[1]);
    	}
    
    }
    
  • #21 4169383
    Elektricon
    Poziom 12  
    Posty: 97
    Ocena: 2
    wow..super :)
    caly dzien myslenia,a ty mi wszystko powiedziales w pare chwil..
    stokrotne dzieki...
    a takie pytanie bardziej teoretyczne..
    wynik z miernika rozni mi sie od przetwornika o jakies 30mV...
    to normalny margines bledu ?
    w zaleznosci ktory kanal zastostuje moge miec rozne bledy?
    w jaki sposob wprowadzic poprawke?
    widzialem jakies wzory w datashetcie do atmegi..
    prosze o naprawdzenie na koniec :)

    co to jest to GAIN ?
    ADC=(VPOS – VNEG)⋅GAIN⋅512/VREF

    jest tez jakis przyklad:
    
    ADMUX = 0xED (ADC3 - ADC2, 10x gain, 2.56V reference, left adjusted result)
    Voltage on ADC3 is 300 mV, voltage on ADC2 is 500 mV.
    ADCR = 512 * 10 * (300 - 500) / 2560 = -400 = 0x270
    ADCL will thus read 0x00, and ADCH will read 0x9C. Writing zero to ADLAR right
    adjusts the result: ADCL = 0x70, ADCH = 0x02.

    nie za bardzo mam pojecia o co chodzi...
  • #22 4169458
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Z tą kolejnością to oczywiście racja...
    Trochę się zasugerowałem tym wyświetlaniem od razu z obliczeń...
    Dlatego pierwszy przykład był z tablicą...
    Przyczepie się tylko do tych zmiennych...
    Wiem, że zmienna na której przeliczam z 10-bit na napięcie musi być typu long ale ta do której podstawiam nie...
    Nie teoretyzuje bo zawsze tak robię i nie ma problemu...
    Ba nawet jako wartość zwracana przez funkcję...
    
    volatile unsigned long int adc;
    ...
    unsigned int volt(void)
    {
     ...
     return((adc*5000)/1024);
    }
    


    Więc z kąd ta rozbieżność...?

Podsumowanie tematu

✨ Dyskusja dotyczy problemów z wyświetlaniem wyniku pomiaru z 10-bitowego przetwornika ADC mikrokontrolera Atmega16 na wyświetlaczu LCD. Użytkownik próbuje przeliczyć wartość ADC odczytaną z rejestrów ADCL i ADCH na napięcie w mV, stosując wzór Vin = (ADC * Vref) / 1024, gdzie Vref to napięcie odniesienia (zwykle 5V lub 5000mV). Poruszono kwestie konwersji wartości liczbowej na format ASCII do wyświetlenia na LCD, z wykorzystaniem operacji dzielenia modulo 10 i dodawania 0x30 do uzyskania kodów ASCII cyfr. Zwrócono uwagę, że do przechowywania wyniku pomiaru należy używać zmiennej typu unsigned long int, aby uniknąć obcinania wartości i błędów w wyświetlaniu. Omówiono także problem drgań i zakłóceń na ostatnich cyfrach wyniku, sugerując zastosowanie filtrów (dławik, kondensator) na linii AVCC oraz uśrednianie wyników z kilku pomiarów w przerwaniu ADC. Wskazano na poprawne ustawienia rejestrów ADMUX i ADCSRA dla Atmega16, w tym wybór kanału ADC1, napięcia odniesienia AVCC z kondensatorem 100nF na AREF, preskalera i trybu pracy. Poruszono również problem przypisania pinów LCD, w tym nietypowego podłączenia DB7 do pinu PA7, oraz konieczność odpowiedniej konfiguracji DDR portów. W dyskusji pojawiły się przykładowe fragmenty kodu w C z obsługą przerwania ADC, konwersją wyniku na napięcie i wyświetlaniem na LCD. Na koniec omówiono zagadnienia kalibracji i błędów pomiarowych, w tym wpływ napięcia odniesienia, kanałów ADC oraz pojęcie wzmocnienia (GAIN) w kontekście różnicowego pomiaru napięć, z odniesieniem do dokumentacji Atmegi16.
Wygenerowane przez model językowy.
REKLAMA