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 połączyć rejestry ADCL i ADCH w ATMega16 dla 10-bitowego ADC?

Top Gun 13 Maj 2007 18:15 2650 20
REKLAMA
  • #1 3880948
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Moze zadam glupie pytanie wg niektorych, ale chce sie upewnic. Otoz przetwornik ADC jest 10-cio bitowy, czyli ze trzeba odczytac wartosci dwoch rejestrow z tematu. Czy polaczenie w celu otrzymania jednego wyniku polega na odczytaniu tych rejestrow do zmiennych unsigned char i dodanie ich do siebie czy jakos inaczej trzeba to zrobic? Pozdrawiam.
  • REKLAMA
  • #2 3880960
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Można odczytać ADCW
  • REKLAMA
  • #3 3881005
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    ADCW...?
    W gcc przepisujesz do zmiennej 16-bitowej ADC reszta cię nie interesuje...
  • #4 3884486
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Ja zrobilem to tak:
    
    unsigned char tekstA[8];
    
    int read_adc(void)
    {
    	ADMUX=0x41;		//VRef = AVCC - ADC1 jako input
    	ADCSRA = 0x87; // zezwolenie na konwersje, preskaler 128
    	ADCSRA |= 1<<ADSC; //start pojedynczej konwersji 
    	while(bit_is_set(ADCSRA,ADSC)){}; 
    	return ADCW; 
    }
    
    int main(void)		// program główny
    {
    	DDRA = 0xFD;
                unsigned int napiecie;
    	napiecie = read_adc();
    	itoa(napiecie, tekstA, 10);
    	lcd_xy(0,10);
    	write_text(tekstA);
    }
    

    I tak: moj zakres napiec to od 0-3V, to z rezystora PT100, nozke AREF podpialem pod AVCC i kondensator 220n do masy. No i kurcze ... mam ciagle jedne wyniki w okolicach 860 na wyswietlaczu niezaleznie jakie jest napiecie wejsciowe. Moze dodam, ze na porcie A rowniez mam podpiety LCD - to podobno ma jakies znaczenie ale nie jestem tego pewien. Nie bardzo wiem gdzie mam blad? Nie chodzi mi na razie o oscylacje wyniku, tylko, zeby sie zmienial wraz z napieciem, i tak jestem w trakcie robienia nowego schematu polaczen i nowej plytki. Przeniesc od razu LCD na inny port, np. C czy moze pozostac?
  • #5 3884743
    johny_w
    Poziom 24  
    Posty: 674
    Pomógł: 80
    Ocena: 64
    ADCW znaczy tyle samo co ADC - wystarczy zajrzeć do "iom16.h"

    Jak najbardziej odepnij LCD od przetwornika. To nie to samo co multipleksowanie na przykład LCD i przycisków. Tutaj tkwi źródło problemów.

    Sam program jest ok. Oczywiście pod warunkiem że to tylko fragment i dodatkowo dajesz inicjalizację LCD, itp. No i przenosząc LCD na port C wyłącz JTAG.
  • #6 3885334
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    No to jade z plytka :) Ale musze czekac na B327 bo chlorek jest do bani ;/ Za 2 dni powinienem jednak go dostac to wtedy bede cos wiecej dzialal :)
  • #7 3907879
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    No i plytka nowa zrobiona, mniej wiecej dziala tak jak potrzeba. Okazalo sie, ze zakres napiec jest do 2V z malutkim hakiem, wiec zostawilem napiecie odniesienia wewnetrzne, stabilnie mi mierzy. jednak jest pewno ale. Nie wiem dlaczego, ale sfiksowal mi albo kompilator albo cos innego. Otoz program w procesorze mimo, ze dziala to jednak wywala sie na warunkach, tzn. nie ma bledow kompilacji ale podczas pracy atmega tak jakby nie byl w stanie sprawdzac warunkow if gdyz nic sie nie dzieje. Od czego to moze byc? I jeszcze jedno.
    
    double temperatura=0.0, temp=0.0;
    unsigned char tekst[16], tekstA[8], tekstB[8], tekstC[8],  buf_zlicz[2], zliczanie=0;//, temp=0;
    	napiecie = read_adc();
    	temp = napiecie/800;
    	temperatura = temp * 300;
    	utoa(temperatura, tekstA, 10);
    	itoa(napiecie, tekstB, 10);
    	lcd_xy(0,2);
    	write_text(tekstA);
    	lcd_xy(1,0);
    	write_text(tekstB);
    

    jak widac w programie dodane sa liczby double. Gdzies na forum czytalem, ze przy pomocy funkcji utoa() jak sie zamienia liczby double to obetnie czesc ulamkowa i zostanie tylko czesc calkowita. Jednak cos to nie bardzo dziala i albo pokazuja sie krzaczki szlaczki albo po sekundzie znikaja napisy na wyswietlaczu. Gdzie jest blad? Bo ogolnie to ma dzialac tak, ze jak zmierzy napiecie to ma podzielic przez max. by wyszedl procent i ten procent ma byc zastosowany do temperatury 300 stopni, gdzie pokaze wlasciwa temperature. A moze znacie jakies inne sposoby jak to wyliczac? Pozdrawiam.
  • #8 3908368
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    O ile dobrze rozumiem to sygnałem wejściowym jest napięcie, a wyświetlić chcesz temperaturę do niego proporcjonalną...?
    Jeśli się nie mylę to zapisz to jako funkcję a zmienne daj typu unsigned int wtedy pominie ci część ułamkową...
  • #9 3925377
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Po dlugich bojach z nowym komputerem znowu wracam do pracy :) Ponizej jest kod ktory zastosowalem lecz niestety wyswietla mi tylko wartosc z przetwornika a wartosc temperatury dalej 0 :( Co nie gra? :(
    
    unsigned short int sekunda=0, liczba=0;
    double temperatura=0.0, temp=0.0;
    unsigned int napiecie, temperat;
    
    unsigned int zamien(unsigned int wejscie)
    {
    	temp = wejscie/800;
    	temperatura = temp * 300;
    	return temperatura;
    }
    
    ISR(TIMER2_OVF_vect)
    { 
    	TCNT2 = 0x63;
    	sekunda++;
    	if(sekunda==100)
    	{
    		Timer2_stop;
    		sekunda=0;
    		napiecie = read_adc();
    		temperat = zamien(napiecie);
    		itoa(temperat, tekstA, 10);
    		itoa(napiecie, tekstB, 10);
    		lcd_xy(0,2);
    		write_text(tekstA);
    		lcd_xy(1,0);
    		write_text(tekstB);
    		Timer2_start;
    	}
    }
    
  • #10 3925588
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    Funkcja read_adc() zwraca 10-bitową wartość z przetwornika czy faktycznie wartość przeliczoną na napięcie...?
  • REKLAMA
  • #11 3925634
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    wartosc z przetwornika - jest ta funkcja na listingu w pierwszych postach.
  • #12 3925697
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    No to sprawa jest dość prosta...
    Jeżeli do zmiennej temp która jest typu int czyli całkowitego podstawisz wynik z dzielenia liczby mniejszej niż 800 to dostaniesz zero to chyba logiczne...
    Dlaczego z uporem godnym lepszej sprawy liczysz tą temperaturę w jakiś zakręcony sposób zamiast prostą funkcją gdzie dajesz jako stały współczynnik liczbę typu float (tak najczęściej wychodzi) i wtedy to ma sens bo po wyliczeniu i wstawieniu do zmiennej całkowitej dostajesz wynik bez ułamka ale sensowny...
  • #13 3925787
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Nie no nie doczytales :) Zmienna temp jest typu double :) Nie bardzo rozumiem to co mowisz z tym wspolczynnikiem. Moglbys podac przyklad? :)
  • #14 3926015
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    No tak nie wiedzieć czemu wydawało mi się, że double to zmienna całkowita... nvm...
    Spróbuj zadeklarować wszystkie zmienne które wykorzystujesz w przerwaniu włącznie z tymi z których korzysta funkcja wywołana w przerwaniu (swoją drogą nie fajny pomysł), jako volatile...
    Co do współczynnika to chyba nie obca ci jest funkcja liniowa...?
    Może wpadł ci w oko taki wzór y=ax+b...
    Jak wiesz jaka temperatura jest dla 0V i dla 3V, bo taki masz zdaje się zakres to resztę łatwo policzyć...
    W zasadzie nie trzeba by wcześniej nawet na napięcie przeliczać...
  • #15 3926476
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Poszedlem twoja rada i tak zrobilem. Dziala to acz okazalo sie, ze nie jest to tak liniowe jakbym chcial aby bylo wiec wzor musze jakis kombinowany zrobic :) No ale najwazniejsze na razie, ze dziala :) Mam tylko mala przypadlosc. Na LCD jak sie pokaze wynik 3-cyfrowy jest fajnie, ale jak wynik spadnie na 2 cyfry to juz nie jest fajnie bo pozostaje na LCD ostatnia cyfra z 3-cyfrowego. I teraz jak zrobic, by wyczyscila sie ta ostatnia cyfra? Sprawdzac wartosc wyniku i w razie potrzeby wyslac spacje w miejsce trzeciej cyfry czy inaczej?
  • REKLAMA
  • #16 3926541
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    Zaraz za zmienna wysyłaj kilka spacji, bez żadnych warunków.
  • #17 3926643
    johny_w
    Poziom 24  
    Posty: 674
    Pomógł: 80
    Ocena: 64
    Albo użyć "sprintf" z biblioteki "stdio.h".

    sprintf (sbuf, "%0a.bf", temperatura); write_text (sbuf);


    W ciągu %... zamiast 'b' podstaw liczbę cyfr wyświetlanych po przecinku, a zamiast 'a' podstaw łączną liczbę znaków formatowanego ciągu łącznie z przecinkiem (kropką). Np. pisząc "%05.2f" dla temperatury równej "23.5" otrzymasz "23.50", a dla temperatury "5.12" dostaniesz "05.12".

    Zawsze otrzymasz równej długości tekst dzięki czemu nie trzeba dodawać spacji, czy "sprawdzać wartość wyniku".

    "Sprintf" domyślnie działa na liczbach całkowitych. Aby zadziałało na liczbach zmiennoprzecinkowych trzeba w makefile dołączyć odpowiednie polecenie dla linkera:
    "-Wl,-u,vfprintf -lprintf_flt -lm"


    Niestety takie rozwiązanie zwiększa rozmiar kodu wynikowego o ok. 5kb i nie jest zbyt szybkie...

    pozdrawiam, JnS
  • #18 3926683
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    No tak trzeba wziąść pod uwagę, że nawet jeżeli po przecinku jest wartość bliska jedności to zostaje obcięta...
    itoa przy wstawianiu wartości do tablicy zawsze przesuwa w stronę pierwszego ("0") pola dlatego jeżeli wysyłasz zawsze całą tablicę to przed itoa do wszystkich pól tablicy wstaw spację...
  • #19 3953479
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    No wiec sprobowalem sposobu pubusia:
    
    ISR(TIMER2_OVF_vect)
    { 
    	char y;
    	TCNT2 = 0x63;
    	sekunda++;
    	if(sekunda==50)
    	{
    		Timer2_stop;
    		sekunda=0;
    		napiecie = read_adc();
    		itoa(napiecie, tekstB, 10);
    		temperatura = 0.38*napiecie+2.2;
    		for(y=0; y<3; y++) tekstA[y] = " ";
    		for(y=0; y<3; y++) tekstB[y] = " ";
    		itoa(temperatura, tekstA, 10);
    		lcd_xy(0,2);
    		write_text(tekstA);
    		lcd_xy(1,0);
    		write_text(tekstB);
    		Timer2_start;
    	}
    }
    

    i niestety nie bardzo chce to dzialac. Chyba, ze cos zle robie? Co dziwne - tekstA wyswietla mi temperature jak zawsze a znowu tekstB na pierwszych 3-ch pozycjach wyswietla mi ``` i dopiero na 4-tej jak bedzie wiekszy zakres to mam cyferke. Dlaczego? Te zawilosci czasami mnie dobijaja ;/ Pozdrawiam.
  • #20 3953672
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Top Gun napisał:
    ... Co dziwne - tekstA wyswietla mi temperature jak zawsze a znowu tekstB na pierwszych 3-ch pozycjach wyswietla mi ``` i dopiero na 4-tej jak bedzie wiekszy zakres to mam cyferke. Dlaczego? Te zawilosci czasami mnie dobijaja ;/ Pozdrawiam.

    Powiedz mi przyjacielu , czy dla Ciebie double i int to ten sam typ zmiennej :?:
    Deklarujesz ...
    double temperatura ; 
    ... , a następnie ...
    itoa(temperatura, tekstA, 10);
    W efekcie , Ty "wciskasz" kompilatorowi , że zmienna temperatura jest typu int , a ON Ci "wciska" , że ``` , to cyfry ;) Jeśli chcesz "zamienić" double na string , to użyj właściwej funkcji , np. dtostrf(double,...).

    Piotrek
  • #21 3953717
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    zumek, dziekuje Ci za uwage, ale u mnie zmienna temperatura jest typu int :) Wynik jest prawidlowy tyle, ze bez czesci ulamkowej i o to mi chodzi :) A z tymi spacjami poradizlem sobie w ponizszy sposob:
    
    		if(temperatura>99) write_text(tekstA);
    		if(temperatura>9 & temperatura <100)
    		{
    			write_text(tekstA);
    			lcd_xy(0,4);
    			write_char(' ');
    		}
    		if(temperatura<10)
    		{
    			write_text(tekstA);
    			lcd_xy(0,3);
    			write_char(' ');
    			lcd_xy(0,4);
    			write_char(' ');
    		}
    

    moze i sa lepsze i prostsze sposoby jak rowniez "ladniejsze" ale na chwile obecna nie udalo mi sie z innym a ten dziala jak powinien :)

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy prawidłowego odczytu 10-bitowego przetwornika ADC w mikrokontrolerze ATMega16 poprzez połączenie rejestrów ADCL i ADCH. Zalecane jest użycie rejestru ADCW (lub ADC), który łączy oba rejestry w jedną 16-bitową wartość, co upraszcza odczyt bez konieczności ręcznego łączenia bajtów. Przykładowa funkcja read_adc() zwraca wartość ADCW po zakończeniu konwersji. Problemy z odczytem mogą wynikać z podłączenia wyświetlacza LCD do tego samego portu co ADC lub z braku odpowiedniej inicjalizacji. Wątpliwości pojawiły się także przy konwersji wartości ADC na temperaturę, gdzie ważne jest stosowanie odpowiednich typów zmiennych (np. double) oraz właściwych funkcji do konwersji liczb zmiennoprzecinkowych na tekst (np. dtostrf zamiast itoa). Wskazano, że dzielenie liczb całkowitych może prowadzić do utraty części ułamkowej, dlatego warto stosować mnożniki typu float i zmienne volatile w przerwaniach. W kwestii wyświetlania wyników na LCD, aby uniknąć pozostałości po poprzednich wartościach (np. nadmiarowe cyfry), proponowano nadpisywanie spacji lub użycie funkcji sprintf z formatowaniem, choć wiąże się to z większym rozmiarem kodu. Ostatecznie autor zastosował prostą metodę warunkowego nadpisywania spacji na LCD, co poprawiło czytelność wyświetlanych danych.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA