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

atmega8+ADC+brak odczytu

26 Lut 2009 00:13 2936 16
  • 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


    Code:
    #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]
  • Multimetr FlukeMultimetr Fluke
  • 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
  • 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...
  • Multimetr FlukeMultimetr Fluke
  • 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!!
  • 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
    Code:
    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.
  • 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:

    Code:
    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
  • Specjalista - Mikrokontrolery
    twoj kod zaklada, ze do VREF podpiete jest ZEWNETRZNE napiecie odniesienia. czy tak jest aby na pewno?

    4\/3!!
  • 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.
  • 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!!
  • 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:

    Code:
    #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);
       }
    }
  • 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!!
  • Poziom 18  
    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.


    Code:
    #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);
       }
    }
  • Specjalista - Mikrokontrolery
    damn... przez caly czas myslalem ze ADC jest na porcie A [; fakt - jest na porcie C.

    4\/3!!
  • 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.[/b]

    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:

    Code:
    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.
  • 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!!
  • 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 :)
  • Poziom 10  
    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.

    Code:


    #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(;;){}
    }