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

[atmega32][c/avr studio] problem z interpretacja adc

watchcat 02 Sty 2011 23:07 2479 12
REKLAMA
  • #1 8949882
    watchcat
    Poziom 10  
    Witam,
    wiem ze temat był przerabiany wielokrotnie ale po przeczytaniu postów dalej nie mogę znaleźć błędu u siebie.

    Pod pin 8 portu A mam podpięty czujnik temperatury LM35. Napięcie jakie mierze na samym pinie jest poprawne np. ok 230mV czyli mniej więcej 23C.

    Chciałam napisać programik wyświetlający wartość binarną z wejścia analogowego. Ponieważ mam ok. 5.06V zasilanie AVCC to przy 230mV powinnam mieć wynik ok .46 (230/5060*1024) i to powinno pokazać się na diodach (również portA). Tak na początek.

    Niestety nie pokazuje sie.. pojawia mi sie raz (?!) wartość 99 (01100011) lub czasem 96 (01100000). Nie wiem skąd sie biorą. Już nawet testowałam sobie diody czy zwarcia gdzieś nie mam ale wszystko wydaje się ok. Nie ma ustawianych nowych wartości w ADCL i ADCH..

    Wertowałam datasheet i wydaje się że wszystke bity mam ustawione tak jak powinny.. Nie mam dodanego kondensatora na aref ale to jest tylko dla eliminacji szumów tak?

    A jak przedstawiana jest liczba w ADCL i ADCH z przecinkiem? bo tego nie umiem znaleźć? jest zaokrąglana czy odcinana?

    jesli ktoś by miał chwilę, byłabym wdzięczna za rzucenie okiem na mój kod. Może tam jest jakiś błąd?

    
    unsigned char adcresultH=0;
    unsigned char adcresultL=0;
    
    ISR(ADC_vect)								//ADC converter Interrupt
    {
     adcresultL=ADCL; 
     adcresultH=ADCH;
     return;
    }
    
    int main  (void)
    {
    
        DDRA=0b01111111; //outputy '1' diodki swiecace + input '0' z czujnika na wejsciu analog A7 (ADC7);
    	SFIOR=0;
    	SFIOR|=(1<<PUD); //ADTS2:0 =0 na free running mode; dodatkowo wyl. podciąganie pull-up PUD
    	ADMUX|= (1<<REFS0)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0); // //REFS1=0 & REFS0=1 AVCC with external capacitor at AREF pin | MUX (ADC7 single ended) ok
    	SREG|=(1<<SREG_I);
    	ADCSRA|=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE); 
    			//ADCSRA
    			//Bit 7 ADEN in  ADC Enable bit podnieść na 1; 
    			//Bit 6 ADCS Start Conversion bit -  podnieść na wysoki 1, aby zacząć konwersję, samo opada po wykonaniu
    			//Bit 5 ADATE = 1 auto trigger;
    			//Bit 4 ADIF is set after writing ADCH&L 
    			//Bit 3 ADC Interrupt Enable & I-bit in SREG =ADC Conversion Complete Interrupt
    			//Bit 2 ADPS2:0: ADC Prescaler Select Bits 0
    
    
    	PORTA=0b10000000; // diody zaswiecaja sie dla zaznaczenia poczatku
    	_delay_ms(30);
    	PORTA=0xff; //gasna
    
    		while (1)
    		{
    		
    		    ADCSRA |=  (1<<ADSC) | (1<<ADIF); nastepny cykl
                while ((1<<ADIF)!=(ADCSRA&(1<<ADIF)));   // czekaj na koniec konwersji
    
    		  		
    		    PORTA=~adcresultL; // zaswiecic diody odpowiadajace bitom w ADCL 
    		    _delay_ms (50);
    			PORTA=~adcresultH;
    		    _delay_ms (50);
    			PORTA=0b10000000;
    			adcresultH=adcresultL=0;
    		  
        
    		}
    }
    

    byłabym wdzięczna za sugestie, pozdrawiam
  • REKLAMA
  • Pomocny post
    #2 8950134
    sulfur
    Poziom 24  
    Pobieżnie przeglądając nasuwają się dwa wnioski (w sumie trzy ale od razu zakładam, że koleżanka do portu PA7 nie podłączyła diody LED).

    1. Wcale nie musi to być 99 albo 96. Do wyniku być może dodać jeszcze 128.
    2. Z uporem maniaka włącza koleżanka podciąganie na pinie, do którego podpięty jest czujnik temperatury. Nie wiem co prawda czy jest możliwe załączenie podciągania podczas gdy wejście jest obsługiwane przez ADC, ale traktuję to jako błąd.

    W ADCL i ADHL nie ma takiego pojęcia, jak liczba z przecinkiem. Zapoznać się z zagadnieniem kwantyzacji i ponownie zadać pytanie.

    adcresultH=adcresultL=0;
    Odradzam stosowania takich konstrukcji.

    Regulamin nakłada obowiązek korzystania ze znaczników code. Proszę o tym pamiętać.
  • #3 8950369
    gdL
    Poziom 27  
    Koleżanko, w programowaniu, z reguły sprawdza się "małe klocki", bo czym więcej napiszesz na raz, tym trudniej znaleźć błąd. W tym przypadku polecam odpiąć na początek czujnik temp, a wejście ADC podłączyć do potencjometru i pokręcić, żeby zobaczyć, czy cokolwiek się zmienia i jak.

    Oprócz tego przeczytaj w nocie katalogowej jak przechowywane są dane konwersji, Tu może się przydać przesunięcie bitowe w zależności od ADLAR, do finalnego sformatowania wyniku, w zależności od rozdzielczości z jaką chcesz dokonać pomiaru.
  • #4 8953398
    watchcat
    Poziom 10  
    dziekuje za odpowiedz.


    Sulfur: poprawilam juz tekst - rzeczywiscie zdecydowanie wyglada schludniej.
    1. podaje to co pojawia sie na ledach jak podlaczam czujnik. to znaczy w ACDL jest 96 (01100000) a ACDH jest 0.. czemu sugerujesz ze tam jeszcze może byc 128 dodane?
    2. przetestowałam nieswiadomie wczesniej - tak, da się podlaczyc podciaganie na pinie ktory jest zdefiniowany jako acd :) ale ja to ogolnie wylaczam PUDem w SFIORze dlatego potem po prostu stosuje przepisanie na PORTA. Mierzyłam wtedy od razu wartość: przy podciaganiu była rzeczywiscie 2.6V, a potem poprawne 230mV. Nie jest to moze zbyt eleganckie, ale pamietam o tym. Bardzo dziękuje za czujnosc w tym wypadku.
    3. kwantyzacje sobie przegladne - moze tu cos jeszcze mam nie dopiete..
    4. czemu nie stosowac cytowanych przez ciebie przypisan zera w jednej linii?



    gdL: wydawalo mi sie ze to sa małe klocki - czunikiem zmieniam sobie tak samo napięcie po prostu stawiając go blisko lampki i wtedy już mam np. 320mV.. dlatego stwierdziłam ze tak zrobie.. ale może podepne jeszcze potencjometr jak radzisz.. z tym ze mi to wygladalo jak bardziej bład konfiguracji rejestrów.. bo na samej 'nóżce' mierze napięcie i jest tyle ile powinno..

    z tego co zrozumialam z noty to wynik jest przechowywany w 10 bitowym rejestrze podzielonym na L i H, jutowanym w lewo lub w prawo. Czyli ze przy malych liczbach sugeruja czytanie np tylko H, który powinien wystarczac do podania wartosci.
  • REKLAMA
  • Pomocny post
    #5 8953571
    tmf
    VIP Zasłużony dla elektroda
    Problemem są twoje zmienne adcresult. Jak zwykle to bywa zapomniałaś dodać magicznego modyfikatora volatile - bez tego optymalizator pzyjmuje te zmienne za stałe i pod tym kątem optymalizuje pętlę w main. No i robi się kaszana. Poza tym diodami świecisz po 50ms, niezły cyborg z ciebie, skoro to ci wystarcza :) W pętli nie ma też sensu inicjować za każdym razem ADC skoro puszczasz go w trybie free running. Niespecjalnie ma także sens zerowanie zmiennej adcresult. W dodatku dostęp do niej masz źle zrobiony, bo powinien być atomowy, a nie jest. U ciebie sytuację ratuje czekanie na flagę zakończenia konwersji, tyle, że w trybie free running konwersja trwa na około - to nie single conversionn mode. Dodatkowo zwolnij także zegar ADC, bo jak sądzę przy odczycie temperatury zależy ci na dokładności a nie prędkości. Oversampling też byłby na miejscu.
    Co do pull upów - włączenie ADC nie powoduje przejęcia funkcji pinu i przypisany mu bit rejestrów IO działa normalnie, to znaczy, że można wymusić stan wejścia ADC programowo, można włączyć pull upa itd. I ma to przy LM zastosowanie - ja w ten sposób wykrywam brak podłączenia termometru. Jak nie go nie podłącze to przy włączonym pull upie mam na ADC Vcc, jak jest LM35 to wymusza potencjał zależny od temperatury. Ot taka sztuczka :)
  • REKLAMA
  • Pomocny post
    #6 8953836
    sulfur
    Poziom 24  
    Ad1. Co do dodawania 128. Wystawiasz na port 8-bitową liczbę, ale najstarszy bit na tym porcie pracuje jako wejście (ADC). Nie ma więc tam diody, a nawet jeśli jest, to nigdy się nie zaświeci.

    Ad4. Dlatego
    #include<avr/io.h>
    #include<util/delay.h>
    #include<avr/interrupt.h>
    
    volatile char razem1, razem2;
    volatile char osobno1, osobno2;
    
    int
    main()
    {
      razem1=razem2=0;
      osobno1=0;
      osobno2=0;
    
      return 0;
    }
    int
    main()
    {
      razem1=razem2=0;
      48:	10 92 61 00 	sts	0x0061, r1
      4c:	80 91 61 00 	lds	r24, 0x0061
      50:	80 93 62 00 	sts	0x0062, r24
      osobno1=0;
      54:	10 92 63 00 	sts	0x0063, r1
      osobno2=0;
      58:	10 92 60 00 	sts	0x0060, r1
  • REKLAMA
  • #7 8954497
    janbernat
    Poziom 38  
    Włączasz ADATE.
    Ustawiasz SFIOR=0;
    Co daje taki efekt:
    "Switching to Free Running
    mode (ADTS[2:0]=0) will not cause a trigger event, even if the ADC Interrupt Flag is set."
    Wyrzuć ADATE, nic nie ustawiaj w SFIOR.
    Ustaw prescaler do ADC- to jest zupełnie inny prescaler niż do Timerów.
    W zależności od zegara- ADPS1, ADPS2, ADPS3.
    W ogóle nic nie ustawiaj w SFIOR- ani PUD- pull up disable- ani nic innego.
    I na początku:
    volatile uint16_t odczyt_ADC;
    A potem w przerwaniu:
    odczyt_ADC=ADC;
    Jak ustwisz ADLAR to wynik będzie dosunięty do lewej w 16 bitowej zmiennej.
    Można wtedy wyświetlić na porcie górne 8 bitów.
    Ale nie na porcie A.
    I wolniej- bo bedzie mrugać.
  • Pomocny post
    #8 8954653
    tmf
    VIP Zasłużony dla elektroda
    janbernat - zapomniałeś, że ustawia też ADSC, co powoduje wyzwolenie pierwszej konwersji, reszta leci już automatycznie jak to w trybi free running. Tu jest wszystko ok.
  • #9 8967499
    watchcat
    Poziom 10  
    tmf napisał:
    Problemem są twoje zmienne adcresult. Jak zwykle to bywa zapomniałaś dodać magicznego modyfikatora volatile

    :) oczywiscie!!! wiedziałam ze coś takiego musiałam właśnie zrobić, dzięki! juz poprawiłam zapętla się ładnie odświeżając

    tmf napisał:
    Poza tym diodami świecisz po 50ms, niezły cyborg z ciebie, skoro to ci wystarcza :)

    wychodzi na to ze cyborgiem jestem bo starcza ;] a tak serio - mam jeszcze coś poknocone bo teoretycznie mam 16MHz włożone i tak zdefiniowane ale jakoś inaczej mi działa, ale to inny temat który musze sobie wyprostować ;) [/quote]

    tmf napisał:
    W pętli nie ma też sensu inicjować za każdym razem ADC skoro puszczasz go w trybie free running.

    pozostałości po próbach - rzeczywiscie miałam to usunąć w trybie free mode

    tmf napisał:

    Niespecjalnie ma także sens zerowanie zmiennej adcresult. W dodatku dostęp do niej masz źle zrobiony, bo powinien być atomowy, a nie jest.

    moja cyborgowatość nie ma funkcji niwelacji nieoznaczoności heisenberga i coraz cześciej trudno mi manipulowac atomami.. :) a tak serio nie do końca zrozumiałam..


    tmf napisał:
    Dodatkowo zwolnij także zegar ADC, bo jak sądzę przy odczycie temperatury zależy ci na dokładności a nie prędkości. Oversampling też byłby na miejscu.


    czyli zeby ADPS2:0 ustawic na przyklad na 0b111 co spowoduje zmniejszenie częstotliwości /128 ?

    tmf napisał:
    Co do pull upów - włączenie ADC nie powoduje przejęcia funkcji pinu i przypisany mu bit rejestrów IO działa normalnie, to znaczy, że można wymusić stan wejścia ADC programowo, można włączyć pull upa itd. I ma to przy LM zastosowanie - ja w ten sposób wykrywam brak podłączenia termometru. Jak nie go nie podłącze to przy włączonym pull upie mam na ADC Vcc, jak jest LM35 to wymusza potencjał zależny od temperatury. Ot taka sztuczka :)


    jesli robie coś takiego
     
    PORTA=~adcresultL; 
    


    to jeśli nie mam wyłączonych globalnie pull-upów w moim przypadku (diody na tym samym porcie co ADC) się podciąganie na pinie (bo ADC daje mu wartość ale nie pełną i on ją uzupełnia) - co powoduje że stale mam 2.5V; jeśli mam nie miec wyłączonego pull-up to musze robić tak:

     
    PORTA=(~adcresultL)&0b01111111; //A7 zawsze na zero aby wyl pull-up; reszta - diody do zaswiecenia się 
    


    przetestowałam - wiec wydaje mi sie ze dobrze mysle..

    ale sztuczkę zapamiętam - w innym układzie rzeczywiscie moze byc bardzo przydatne i sprytne

    Dodano po 8 [minuty]:

    sulfur:
    Ad1.
    masz rację - potem zrozumiałam o co ci chodzi. Ale teoretycznie z datasheet wynika ze przy 23 mV przy napieciu 5V powinna mi wyjsc liczba 47 albo cos kolo tego więc ten bit powinien być i tak zero

    Ad4.
    super odpowiedź - od razu wiem o co chodzi :) dodatkowa linia kodu po kompilacji - niepotrzebny spam :) dzięki!

    Dodano po 18 [minuty]:

    janbernat napisał:

    Włączasz ADATE.
    Ustawiasz SFIOR=0;

    niezupełnie - ja najpierw ustawiam SFIOR na zero (choć i tak ini jest zerem) a potem ustawiam ADATE. Musze go ustawić bo
    "Auto Triggering is enabled by setting the ADC Auto Trigger Enable bit, ADATE in ADCSRA."

    janbernat napisał:

    Co daje taki efekt:
    "Switching to Free Running mode (ADTS[2:0]=0) will not cause a trigger event, even if the ADC Interrupt Flag is set."

    chodzi o switching czyli jesli mam inny tryb i zmieniam na frm.. dobrze myślę?


    janbernat napisał:

    Wyrzuć ADATE, nic nie ustawiaj w SFIOR.

    moge nic nie ustawiać SFIOR ale PUD to nie ma wpływu na sam ADC


    janbernat napisał:

    Ustaw prescaler do ADC- to jest zupełnie inny prescaler niż do Timerów.
    W zależności od zegara- ADPS1, ADPS2, ADPS3.

    zmieniłam ADPS2:0 na jedynki zmiejszając częstotliwość

    janbernat napisał:

    Jak ustwisz ADLAR to wynik będzie dosunięty do lewej w 16 bitowej zmiennej.
    Można wtedy wyświetlić na porcie górne 8 bitów.

    wiem ale jeśli mam do prawej to moge sobie wyświetlić tylko dolne 8 bitów i wyjdzie na to samo..

    janbernat napisał:

    Ale nie na porcie A.

    tylko tam mam diodtki na mojej 'testowce' wiec nie mam wyjścia :)


    ok. na koniec mój kodzik oczyszczony wygląda tak:
    
    
    
    #include <io.h>							//Input/Output streaming 
    #include <interrupt.h>					//interrupts handlers
    
    #define F_CPU 16000000UL //16 Mz
    #include <util\delay.h>		
    
    volatile unsigned char adcresultH=0;
    volatile unsigned char adcresultL=0;
    
    
    
    ISR(ADC_vect)								//ADC converter Interrupt
    {
     adcresultL=ADCL; //mounting two 8-bit values 'ADCH.ADCL' to one 16-bit unsigned int value 'adcresult'
     adcresultH=ADCH;
     return;
    }
    
    int main  (void)
    {
    
        DDRA=0b01111111; //outputy '1' diodki swiecace + input '0' z czujnika na wejsciu A7 (ADC7);
    	ADMUX|= (1<<REFS0)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0); // (1<<ADLAR)| MUX (ADC7 single ended) 
    	SREG|=(1<<SREG_I);
    	ADCSRA|=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); //
    
    	PORTA=0b00000000; // zaswiecaja sie dla zaznaczenia
    	_delay_ms(200);
    	PORTA=0b01111111; //gasna
    
    		while (1)
    		{
    		  PORTA=(~adcresultL)&0b01111111; //A7 zawsze na zero aby wyl pull-up; reszta - diody do zaswiecenia się 
    		  _delay_ms (300);
    		  PORTA=(~adcresultH)&0b01111111; //A7 zawsze na zero aby wyl pull-up; reszta - diody do zaswiecenia się 
    		  _delay_ms (300);
    
    		}
        
    
    }
    
    
    
    


    efekt:
    przy ADCH widze ze jest zupełnie pusty - diody wygaszone zupełnie,
    ADCL ma wygląda tak (-)-**---- lub tak (-)-*-***- gdzie gwiazdki to diody świecące. Ja to interpretuje zdodnie z datasheetem przy ADLAR=0 ze osiem bitów ADCL są ustawione od prawej do lewej czyli wartosci 47 lub 48 co miało wyjść. Dziekuje Wam za pomoc - problem rozwiązany.
    z moich prób wynika ze musze mieć co najmniej "division factor" = 8 (czyli ADPS1 i ADPS0 na 1) żeby działało poprawnie.. czy ktoś moze pomóc mi zastanowić się czemu to jest konieczne.. wychodzi ze zegar ADC muszę mieć nie szybszy niż 16MHz/8 = 2MHz ...
  • Pomocny post
    #10 8968064
    tmf
    VIP Zasłużony dla elektroda
    Raczej częstotliwość musi być niższa, żeby działało poprawnie - preskaler obniża częstotliwość na nie podwyższa. I efekt jest zgodny z notą - ADC działa jako tako do 1MHz, aczkolwiek rosną szumy. Widać twój egzemplarz działa aż do 2MHz, ale pewnie szumi wtedy okropnie.
    Przy okazjiu, usuń to return z ISR bo jest niepotrzebne. Rejestr ADC możesz czytać jako całość (znaczy po prostu ADC) a nie osobno jego połówki.
  • #11 8968158
    watchcat
    Poziom 10  
    tmf napisał:
    Raczej częstotliwość musi być niższa, żeby działało poprawnie - preskaler obniża częstotliwość na nie podwyższa.

    tak - literówka zmniejsza a nie zwiększa..

    tmf napisał:
    I efekt jest zgodny z notą - ADC działa jako tako do 1MHz, aczkolwiek rosną szumy. Widać twój egzemplarz działa aż do 2MHz, ale pewnie szumi wtedy okropnie.

    nie umiem tego znaleźć. mam rozdział 22.7 ADC Noice Canceler ale widze tam nic o 1MHz. Przy prescalerze jest napisane ze
    " The ADC module contains a prescaler, which generates an acceptable ADC clock frequency from any CPU frequency above 100 kHz. " czyli że generuje częstotliwości akceptowalne dla zegara częstotliwości ADC z jakiegokolwiek CPU powyżej 100kHz..

    tmf napisał:

    Przy okazjiu, usuń to return z ISR bo jest niepotrzebne.

    ok

    tmf napisał:

    Rejestr ADC możesz czytać jako całość (znaczy po prostu ADC) a nie osobno jego połówki.

    korzystałam tylko z noty i nie wiedziałam. Tam pisali żeby najpierw L potem H czytać jeśli coś.. ale dzięki. Akurat chyba w tym kodku lepiej było czytać tylko L bo od razu mogłam rzucić na diody bo mi się rozmiar zgadzał.
  • Pomocny post
    #12 8968254
    tmf
    VIP Zasłużony dla elektroda
    Nota procesora, sekcja Electrical Characteristics/ADC Characteristics - Clock Frequency 50-1000kHz.
  • #13 8968296
    watchcat
    Poziom 10  
    ok -rzeczywiście tam nie spojrzałam. Dzieki
REKLAMA