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

atmega8+ADC+brak odczytu

mateusz140m 26 Lut 2009 00:13 3149 16
  • #1 6205546
    mateusz140m
    Poziom 11  
    Witam serdecznie bo to moj 1 post na forum.
    AVcc zwarte z Vcc Aref do amsy przez 100nF
    PB1 - dioda sterowana PWM
    PC0 - wejscie z czujnika CNY70
    Układ ma sciemniac i rozjasniac diode w zaleznosci od napiecia podawanego na ADC0. Woltomierz poakzuje 1.5-3.5V
    Całosc zasilana z 3 baterii AA ("paluszkow") Sam PWM działa. W połaczeniu z ADC juz nie.

    Moge prosić mądrzejszych Kolegów o rade?
    Pozdrawiam


    #include <avr/io.h>               // dost�p do rejestr�w
    #include <avr/interrupt.h>        // funkcje sei(), cli()definicje SIGNAL, INTERRUPT
    #include <stdlib.h> 
    #define cbi(sfr, bit)   (_SFR_BYTE(sfr) &= ~_BV(bit)) 
    #define sbi(sfr, bit)   (_SFR_BYTE(sfr) |= _BV(bit))
    #define tbi(PORT,BIT)   PORT^=_BV(BIT)  
    
    /********** ZMIENNE***********/
    volatile uint16_t pwmA=0x09; 
    volatile int counter=0;
    volatile uint16_t x;
    /*************PWM************/
    
    void PWM_Init (void) 
    { 
    	sbi(DDRB,PB1);// OC1A jako wyjscie
     	sbi(DDRB,PB2); // OC1B jako wyjscie
    	TCCR1A = (1 << COM1A1)|(1 << COM1A0) | (1 << COM1B1)|(1 << COM1B0)|(1 << WGM11);  // COM1A1 COM1A1 wypelnij +
    	TCCR1B = (1<<WGM13) | (1<<WGM12) | (0<<CS12) |(0<<CS11)|(1<<CS10); //wgm11:3 f \ast pwm CS10:3 prescale 1024
    	ICR1H = 0x03; 
    	ICR1L = 0xFF ; 
    	OCR1A =pwmA;
    	TIMSK |= (1 << TOIE1); //emable timer1 interrupt
     	sei(); 
    } 
    
    SIGNAL (SIG_OVERFLOW1) 
    { 
    
     OCR1A = pwmA; 
    }
    
    
    /*************ADC************/
    
    void ADC_Init (void) 
    {
    
    sei();
    ADMUX=(0<<REFS1)|(1<<REFS0); //ustawienie Vref=AVcc
    ADMUX=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0); //ADC0
    ADCSRA=(1<<ADEN)|(1<<ADIE)|(1<<ADFR)|(1<<ADSC)|(0<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
    //ADEN w�aczenie przztwoernika
    //ADIE interrupt enable
    //ADFR free running
    //ADPS preskaler 8
    //ADSCrozpoczecie konwersji
    }
    
    SIGNAL (SIG_ADC)
    {
    	counter++;
    	if(counter==50)
    	{
    		pwmA=(ADCL|(ADCH<<8));
    		counter=0;
    	}
    	
    	
    }
    
    
    int main (void) 
    { 
     
     
    PWM_Init(); 	//inicjalizajca PWM
     ADC_Init();
    
     for(;;)  
     {
    
     } 
     return (0);      
    } 
    
    
    


    Proszę zwracać uwagę, na poprawną pisownię - regulamin p.15
    [zumek]
  • #2 6205680
    gbr3
    Poziom 15  
    Nie zagłębiałem się w kod ale AREF powinno być zwarte do VCC(ew. jakiś mały rezystor po drodze).
    Polecam lekturę tego http://avr.elektroda.eu/?q=node/30. Ja nie mając pojęcia o adc po lekturze tego mniej więcej wiedziałem o co chodzi :)
    Pozdrawiam :D
  • #3 6205761
    mateusz140m
    Poziom 11  
    gbr3 napisał:
    Nie zagłębiałem się w kod ale AREF powinno być zwarte do VCC(ew. jakiś mały rezystor po drodze).
    Polecam lekturę tego http://avr.elektroda.eu/?q=node/30. Ja nie mając pojęcia o adc po lekturze tego mniej więcej wiedziałem o co chodzi :)
    Pozdrawiam :D

    Czytałem, i budowałem program na podstawie tej storny i 10 innych i pomimo tego ze wydaje mi sie z epojałem o co w tym wszystkim chodzi nadal nie działa.
    Aref zwarte do Vcc powinno byc jesli ustawione by było
    ADMUX=(0<<REFS1)|(o<<REFS0);
    Natomiast ustawienie
    ADMUX=(0<<REFS1)|(1<<REFS0);
    to Vref=AVcc a na Vref powinien byc kondensator filtrujacy do masy(zasilania?) No własnie gdzie?

    Jesli sie mysle poprawcie...
  • #4 6205908
    Freddie Chopin
    Specjalista - Mikrokontrolery
    i nie widzisz tu zadnego problemu?

    Cytat:

    ADMUX=(0<<REFS1)|(1<<REFS0);
    to Vref=AVcc a na Vref powinien byc kondensator filtrujacy do masy(zasilania?)

    zgadza sie, jednoczesnie kasujesz wszystkie pozostale bity w tym rejestrze.

    Cytat:

    ADMUX=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0); //ADC0

    a tutaj pracowicie kasujesz WSZYSTKIE bity w calym rejestrze, wlacznie z tymi ustawionymi przed chwila...

    nie rozumiem niektorych osob... po co wpisujesz do rejestru same zera na te miejsca (pomijam to, ze kasujesz przy okazji caly rejestr), skoro... tam juz sa same zera? odrobina myslenia nie zaszkodzi...

    4\/3!!
  • #5 6206948
    mateusz140m
    Poziom 11  
    Freddie Chopin
    Ja też nie rozumiem niektorych osob...
    A co do rady to dziekuje bardzo dobra! Nie zauważyłem że rejestr jest adresowany bajtowo a nie bitowo. Stawiam piwko:)
    ps. a co do
    ADMUX=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0); //ADC0

    to napisałem to sobie zeby łatwiej było mi połapac sie w zawartosci rejestru i jak widac to był strzł do własnej bramki:cry:
    Jeszcze raz dzieki wszystko działa jak trzeba, pozdrawiam.
  • #6 6219613
    Włodar
    Poziom 10  
    Witam!

    Mam bardzo podobny problem. Mój program słuzy tylko testowaniu przetwornika A/C w mikrokontrolerze ATmega8 - do wejścia ADC0 poprzez rezystor 10 k połączyłem się z potencjometrem włączonym pomiedzy ACC i GND. Do portu D podłaczone są dwa dekodery z bin do bcd a następnie dwa 7-segmentowe wyświetlacze led. Obsługa portu działa, bo jeśli wpisze się na sztywno jakąś liczbę do kodu (np. PORTD = 20; ) to dana liczba pokaże się na wyświetlaczu. Niestety wartość z przetwornika A/C zawsze jest 0. Może ktos wie dlaczego? Oto porgram:

    define F_CPU 1000000L
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h> 
    
    
    unsigned char bin8toBCD (unsigned char a)
    {
    unsigned char dziesiatki;
    unsigned char jednosci;
    unsigned char wynik;
    
    	jednosci = a%10; //jednosci sa reszta z dzielenia liczby a przez 10
    	dziesiatki = a/10; //dzisiatki sa wynikiem dzielenia przez 10
    
    		dziesiatki = dziesiatki << 4;
    		wynik = jednosci | dziesiatki;
    
    return (wynik);
    }
    
    
    
    
    SIGNAL(SIG_ADC)       // przerwanie z przetwornika ADC
    {
    unsigned char liczba;
      liczba = ADCL;
    PORTD = bin8toBCD (liczba);   // czytaj wartość z przetwornika ADC (mniej znaczacy bajt)
    
    }
    
    int main(void)                        // program główny
    {
    DDRD=0xff; //wyjscia
    PORTD=0xff; //normalnie zera
      DDRC = 0xFF;                         // wszystkie linie PORTB jako wyjścia
      ADMUX = 0;                // wybierz kanał 0 przetwornika ADC
      ADCSR = _BV(ADEN)|_BV(ADIE)|_BV(ADPS0)|_BV(ADPS1);
                              // włącz przetwornik i uruchom generowanie przerwań
                              // częstotilwość taktowania F_ADC=F_CPU/8
    
      sei();                // włącz obsługę przerwań
    
    
    
    	while(1)
    	{
    			_delay_ms(100);
    	}
    }
    


    Na funkcję "bin8toBCD" możecie nie zwracać uwagi, bo działa dobrze. Chodzi o sam przetwornik A/C

    Pozdrawiam
  • #8 6221017
    Włodar
    Poziom 10  
    Cytat:
    twoj kod zaklada, ze do VREF podpiete jest ZEWNETRZNE napiecie odniesienia. czy tak jest aby na pewno?


    Tak. Do VREF podłączyłem Vcc poprzez rezystor 1k.
  • #9 6221093
    Freddie Chopin
    Specjalista - Mikrokontrolery
    kolejna porcja

    1. rejestr sterujacy ADC zwie sie ADCSRA

    2. zapewne chcesz dzialac w trybie ciaglym przetwornika, ale aby to osiagnac musisz go najpierw odpalic - nigdzie tego nie robisz, nigdy nie wykonana zostanie konwersja, nigdy nie bedzie przerwania - musisz wlaczyc tez bity ADSC i ADFR.

    3. aktualnie wynik masz wyrownany do prawej - ignorujesz wiec dwa najstarsze bity. moze lepiej byloby ignorowac dwa najmlodsze - wystarczy wynik wyrownywac do lewej (ADLAR) oraz odczytywac rejestr ADCH.

    4\/3!!
  • #10 6221949
    Włodar
    Poziom 10  
    Hmmm niestety wyświetlacz nadal pokzauje "00"

    Cytat:
    2. zapewne chcesz dzialac w trybie ciaglym przetwornika, ale aby to osiagnac musisz go najpierw odpalic - nigdzie tego nie robisz, nigdy nie wykonana zostanie konwersja, nigdy nie bedzie przerwania - musisz wlaczyc tez bity ADSC i ADFR.

    Racja! W ogóle o tym zapomniałem - przyznaję bez bicia :P Dziękuję.

    Cytat:
    1. rejestr sterujacy ADC zwie sie ADCSRA

    Teoretycznie masz rację - ten rejestr na prawdę zwie się ADCSRA. Jednak dziwi mnie, że:
    1. na stonce: http://avr.elektroda.eu/?q=node/30 w obydwu przykładach jest ADCSR
    2. kompilator przyjmuje zarówno nazwę z "A" jak i bez "A" nie zwracając błędu.
    Może w AVR-GCC przyjęto obie te nazwy? Dziwne w każdym razie... Zwróciłeś moją uwagę na ciekawą rzecz.

    Cytat:
    3. aktualnie wynik masz wyrownany do prawej - ignorujesz wiec dwa najstarsze bity. moze lepiej byloby ignorowac dwa najmlodsze - wystarczy wynik wyrownywac do lewej (ADLAR) oraz odczytywac rejestr ADCH.

    Racja ale na razie tym się nie będę zajmował. Kręcę potencjometrem w lewo i w prawo a na wyświetlaczu są nadal zera... Na razie dokładnością się nie przejmujmy o tym pomyśli się później, gdy coś zacznie się dziać.

    Dokonałem poprawek w kodzie związanych z inicjalizacją przetwornika - rejestr ADCSRA. Sprawdziłem też po raz dugi (przy świetle dziennym) czy nie popełniłem błedu w połaczeniach, bo robię ten układ na płytce stykowej i mam niezłą plontaninę przewodów związanych z wyświetlaczem. "Niestety" przewody są połączone dobrze...

    Oto kod programu po poprawkach:

    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    
    unsigned char bin8toBCD (unsigned char a)
    {
    unsigned char dziesiatki;
    unsigned char jednosci;
    unsigned char wynik;
    
    	jednosci = a%10; //jednosci sa reszta z dzielenia liczby a przez 10
    	dziesiatki = a/10; //dzisiatki sa wynikiem dzielenia przez 10
    
    		dziesiatki = dziesiatki << 4;
    		wynik = jednosci | dziesiatki;
    
    return (wynik);
    }
    
    
    
    
    SIGNAL(SIG_ADC)       // przerwanie z przetwornika ADC
    {
    unsigned char liczba;
      liczba = ADCL;	// czytaj wartość z przetwornika ADC (mniej znaczacy bajt)
    PORTD = bin8toBCD (liczba);   // konwesja do BCD i wystawienie wartosci na port
    
    }
    
    int main(void)                        // program główny
    {
    DDRD=0xff; //wyjscia
    PORTD=0xff; //normalnie zera
      DDRC = 0xFF;       // wszystkie linie PORTB jako wyjścia
      ADMUX = 0;               // wybierz kanał 0 przetwornika ADC
      ADCSR = _BV(ADEN)|_BV(ADIE)|_BV(ADFR)|_BV(ADSC)|_BV(ADPS0)|_BV(ADPS1);
                              // włącz przetwornik i uruchom generowanie przerwań
                              // częstotilwość taktowania F_ADC=F_CPU/8
    
      sei();                // włącz obsługę przerwań
    
    
    
    	while(1)
    	{
    			
    		_delay_ms(100);
    	}
    }
  • #11 6222053
    Freddie Chopin
    Specjalista - Mikrokontrolery
    wg mnie teraz powinno dzialac, bo mam podobny kod (podobna inicjalizacje przetwornika) i chodzi jak nalezy

    proponuje jeszcze - w ramach zmiany kodu na zgodny z nowsza konwencja - zmiane przerwania z SIGNAL(costam) na

    ISR(ADC_vect)
    {
    ...
    }

    sprobowalbym jeszcze na twoim miejscu odczytac tak czy siak rejestr ADCH - w datasheecie pisze, ze mechanizm odczytow tych dwoch rejestrow jest troche zakrecony - moze odczytujac jedynie nizszy costam blokujesz i dlatego nie dziala. dodaj wiec na przyklad:

    volatile uint8_t liczba;

    liczba=ADCH;
    liczba=ADCL;
    ...

    4\/3!!
  • #12 6222071
    krzemowy
    Poziom 19  
    To powinno działać, posprzątałem Ci trochę w kodzie, poczytaj sobie komentarze i zajrzyj do avr-libc czy wektor "ADC_vect" jest właściwy dla Atmega8.


    #define F_CPU 1000000UL
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    
    unsigned char bin8toBCD (unsigned char a)
    {
       unsigned char dziesiatki;
       unsigned char jednosci;
       unsigned char wynik;
       jednosci = a%10; //jednosci sa reszta z dzielenia liczby a przez 10
       dziesiatki = a/10; //dzisiatki sa wynikiem dzielenia przez 10
       dziesiatki = dziesiatki << 4;
       wynik = jednosci | dziesiatki;
       return (wynik);
    }
    
    ISR(ADC_vect)       //nie stosuje się "SIGNAL" - patrz avr-libc
    {
       unsigned char liczba;
       liczba = ADCL;	// czytaj wartość z przetwornika ADC (mniej znaczacy bajt)
       PORTD = bin8toBCD (liczba);   // konwesja do BCD i wystawienie wartosci na port
    }
    
    int main(void)                        // program główny
    {
       DDRD=0xff; //wyjscia
       PORTD=0xff; //normalnie zera
       //DDRC = 0xFF;       // wszystkie linie PORTB jako wyjścia - jaki PORTB??? zwierałeś sobie tą instrukcją wejścia ADC do masy, stąd przetwornik mierzył ciągle zero
       ADMUX = 0;               // wybierz kanał 0 przetwornika ADC
       ADCSR = _BV(ADEN)|_BV(ADIE)|_BV(ADFR)|_BV(ADSC)|_BV(ADPS0)|_BV(ADPS1);
                              // włącz przetwornik i uruchom generowanie przerwań
                              // częstotilwość taktowania F_ADC=F_CPU/8
    
       sei();                // włącz obsługę przerwań
       while(1)
       {
          _delay_ms(100);
       }
    }
  • #14 6222245
    Włodar
    Poziom 10  
    Udało się! ADC ożył!

    Mieliście rację - "SIGNAL" jest przestarzały, gdy zamieniłem na "ISR" to coś się zaczęło dziać. Poszukam lepszego potencjometru w swoich zbiorach. Jednak widzę, że jakaś liczba się wyświetla - różna od zera i zalezna od ustawienia potencjometru.

    Warto jednak zwrócić uwagę na kilka ciekawych rzeczy:

    1.
    Cytat:
    sprobowalbym jeszcze na twoim miejscu odczytac tak czy siak rejestr ADCH - w datasheecie pisze, ze mechanizm odczytow tych dwoch rejestrow jest troche zakrecony - moze odczytujac jedynie nizszy costam blokujesz i dlatego nie dziala

    To fakt! Gdy odczytuje najpierw jeden rejestr a póxniej drugi to wszystko gra. Natomiast, gdy odczytam tylko ADCL to na wyświetlaczu pojawia się liczba ale krecenie potencjometrem nic nie daje - trzeba zresetować procesor i dopiero wtedy pojawia się ina wartość

    2. Niekiedy coś jakby się "zacina". Jest pewne opóźnienie pomiędzy tym jak kręcę potencjometrem a tym jak zmienia się wartość na wyświetlaczu. Warto dodać, że to opóźnienie ma zmienną wartość - czasem nie ma go wcale a czaem to wręcz kilkanaście sekund....

    Krzemowy - dziękuję bardzo za uporządkowanie kodu. Ja mam z tym problemy - zawsze piszę w taki dziwny i bałaganiarki sposób. Coś usunę, cos wkleję.. wszystko się porozjerzdża a ja piszę sobie spokojnie dalej :D

    Chopin - wielkie dzięki. Na prawdę bardzo dużo mi pomogłeś.

    Teraz poszukam bardziej precyzyjnego potencjometru i zobaczę co będzie się działo, bo ten mój jest jakiś lipny i różnie może być....

    Dziwny jest ten przetwornik A/C w ATmedze... Niedawno eksperymentowałem z jakimś mikrokontrolerem Siemens opartym na 8051 z kilkoma bajerami -w tym przetwornik A/C. Muszę przyznać, że tamten przetwornik był o wiele prostrzy w obsłudze.

    Cytat:
    damn... przez caly czas myslalem ze ADC jest na porcie A [; fakt - jest na porcie C.

    Haha znam ten ból :) Takie duperele potrafią wykończyć czlowieka... Kiedy zacząłem eksperymenotwać z wyświetlaczem LED to nic mi nie działało - jedna cyfra pozostawa stale nie wyświetlona... Kombinowałem z tym porgramem, męczyłem się, ciągle coś zmieniałem i NIC, NIC, NIC... W końcu coś mnie tknęło... wziąłem rezystor od wspólnej anody dla tej cyfry, zmierzyłem go i okazalo się, że jest 10k ... Po prostu cyfra się nie świeciła, bo był za mały prąd... Myślałem, że to rezystor 500, pomyliłem i przez to się katowałem pół dnia pisząc programy a już pierwszy był dobry jak się później okazało.

    Dodano po 1 [godziny] 14 [minuty]:

    Wydaje mi się, że wszystko jest w porządku.
    Najlepiej czytać jednak całą wartość rejestru ADCW, ponieważ czytanie ADCH lub ADCL daje nienajlepsze rezultaty - nie wiem dlaczego ale wówczas wynik "wariuje" i są te dziwne przestoje o których już pisałem. Ja zastosowałem ten sam program co powyżej ale wykonałem następującą obsługę przerwania:

    ISR(ADC_vect)       // obsługa przerwania od ADC
    {
       unsigned char liczba;
    	liczba = ADCW;
    	liczba = liczba >> 2; //usuwam dwa najmłodsze bity
       	PORTD = bin8toBCD (liczba);   // konwesja do BCD i wystawienie wartosci na port
    }


    Teraz wszystko działa płynnie i bardzo ładnie.
  • #15 6222660
    Freddie Chopin
    Specjalista - Mikrokontrolery
    zamiast tak kombinowac, to wlacz wyrownanie wyniku do lewej (ADLAR) i czytaj tylko ADCH - dziala, sprawdzone.

    tak jak robisz teraz jest blednie, bo tracisz zarowno 2 najmlodsze, jak i 2 najstarsze bity (no chyba ze kompilator cudem to poprawil) - skoro zmienna liczba jest 8-bitowa, to nic ci nie da przypisanie do niej wartosci 16-bitowej (ADCW), bo najstarsze 8-bitow i tak idzie w kosmos.

    4\/3!!
  • #16 6222688
    Włodar
    Poziom 10  
    Cytat:
    zamiast tak kombinowac, to wlacz wyrownanie wyniku do lewej (ADLAR) i czytaj tylko ADCH - dziala, sprawdzone.

    dzięki, zaraz zobaczę.

    Dodano po 10 [minuty]:

    Racja. Teraz działa jeszcze lepiej.
    Dziękuję :) Jestem początkującym programistą mikrokontrolerów :)
  • #17 6289728
    Cineman
    Poziom 11  
    Witam.

    Mam bardzo podobny problem dlatego nie chce zaczynać nowego wątku. Podobnie jak kolega Mateusz140m łącze PWM z ADC, jednak do generowania PWM wykorzystuje timer2. Dokumentacje myślę zrozumiałem dosyć dobrze. ADC działa, bo był testowany. Sam PWM także, jednak nie mogę połączyć jednego i drugiego.

    Vref jest podłączony do AVCC, natomiast do wejscia przetwornika jest podłączony potencjometr 1k. Napięcie jest więc z zakresu od 0 do 5V. Odczytuje tylko ADCH.

    Najprawdopodobniej jest coś z przerwaniami, jednak nie wiem co.

    
    
    #define F_CPU 8000000UL  /* 8 MHz Internal Oscillator */
    
    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    #define cbi(sfr, bit)   (_SFR_BYTE(sfr) &= ~_BV(bit))
    #define sbi(sfr, bit)	(_SFR_BYTE(sfr) |= _BV(bit))
    
    
    void PWM_init(void){
    	
    	sbi(DDRB,PB3);		//PB3 jako wyjscie 
    
    	OCR2=0x5f;			// inicjacja OCR2
    
    	TCCR2 |= ((1<<WGM20)|(1<<WGM21)); 	// tryb fast PWM
    	sbi(TCCR2,COM21);			// tryb nie owdracający PWM
    	sbi(TCCR2,CS22);		// prescaler na 64
    	sbi(TIMSK,TOIE2);	// odblokowanie przerwan timera2 od przepelnienia
    	sei();	//odblokowanie globalne przerwań
    }
    
    void ADC_init(void){		//inicjacja ADC
    	
    	sbi(ADMUX,REFS0);		// Vref ma potencjał AVCC=VCC~5V
    	sbi(ADMUX,ADLAR);		//wynik * bitowy w ADCH
    	sbi(ADCSRA,ADEN);		// ADC odblokowany
    	sbi(ADCSRA,ADFR);		// tryb free running
    
    	ADCSRA |= ((1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)); // prescaler na 128
    
    	sbi(ADCSRA,ADSC);	// wlączenie ADC
    
    }
    
    ISR(TIMER2_OVF_vect){
    	
    	OCR2=ADCH;
    }	
    
    int main (void)
    {
    
    	ADC_init();
    	PWM_init();	
    
    	for(;;){}
    }
    
REKLAMA