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

Przetwornik C/A z wykorzystaniem timera

^Rachel 27 Sty 2011 16:22 2184 9
  • #1 9064897
    ^Rachel
    Poziom 21  
    Witam.

    Myślę nad wykonaniem zasilacza laboratoryjnego sterowanego uC. Napisałem program w BASCOMIE bez większych kłopotów, jednak w wersji demo, przez ogranicznie długości kodu program był niekompletny. Postanowiłem porzucić Bascoma i nauczyć się C. Na początek sam PWM. Oto co udało mi się napisać :

    Cytat:

    #include <avr\io.h>

    void pwm_init(void) //procedura wlaczajaca pwm
    {
    DDRB=0b00000010;
    TCCR1A = 0b10000010;
    TCCR1B = 0b00011001;
    ICR1 = 1000; //okres PWM
    OCR1A = 0; //szerokosc impulsu max - 1023 - 10bit PWM - poczatkowa wartosc = 0
    }

    void pwm(unsigned short int wypelnienie) // zmiana wypelnienia
    {
    if (wypelnienie>=1023) wypelnienie=1023;
    else
    OCR1A = wypelnienie;
    }

    void portinit(void);


    int main(void) {
    unsigned short int wypelnienie=0;
    pwm_init();
    DDRD=0x00;
    PORTD=0xFF;
    while(1)
    {
    pwm(wypelnienie);
    if (!(PIND & 0x01)) wypelnienie++;
    if (!(PIND & 0X02)) wypelnienie--;

    }
    }


    Jednak układ nie działa tak jak bym chciał. Wszystko symuluję, przy rozdzielczości 10-bit, mam 1024 kroki regulacji. Więc przy 5V jakies 4,88mV.
    Jednak tak pięknie nie jest. Gdy ustawię OCR1A = 512 to napięcie spada do 0V.

    Wystarczyła by mi rozdzielczość 8-bit :
    Cytat:

    TCCR1A = 0b10000001;
    TCCR1B = 0b00010001;


    Jednak wtedy to dopiero dzieją się cuda. Przez cały czas na wyjściu mam stan wysoki.

    Co robić ? Może mam źle ustawiony rejestr TCCR1 - ustawiałem go zgodnie z datasheet.

    Dodano po 47 [minuty]:

    Skorzystam z Timer 0. Wyczytałem z datasheet jak z niego skorzystać i urodziłem taki kod :

    Cytat:

    #include <avr\io.h>
    int main(void)
    {
    DDRD=0xFF;
    TCCR0|=0b01011001; //licznik T0 tryb pWM , prescaler = 0
    TCNT0=0; //wartosc poczatkowa rejestru zliczania
    while(0)
    {
    if (TIMSK & 0x01) PORTD = 0XFF; //jezeli zostanie ustawiona flaga TOV0 - Timer 0 overflow to ustaw portd
    }
    }


    Wszystko niby dobrze ustawione, a nadal zero efektów, tak jakby timer wogole nie wsytartowal.
  • #2 9065291
    janbernat
    Poziom 38  
    Włącz zezwolenie globalne na obsługę przerwań sei().
  • #3 9065483
    ^Rachel
    Poziom 21  
    Zmieniłem kod, włączyłem obsługę przerwań, ustawiłem bit :
    TOIE0: Timer/Counter0 Overflow Interrupt Enable
    czyli wlaczenie przerwania przepelnienia.

    Później sprawdzam flage TOV0 w rejestrze TIFR, czy licznik jest przepelniony, jesli tak ustawiam wszystkie bity na PORTD.

    Cytat:

    #include <avr\io.h>
    #include <avr\interrupt.h>
    int main(void)
    {
    DDRD=0xFF;
    TCCR0|=0b01011001; //licznik T0 tryb pWM , prescaler
    TIMSK = 0x01;
    sei();
    while(0)
    {
    if (TIFR & 0x01) PORTD = 0xFF;
    }
    }




    Nadal nic. Nawet w trybie pracy krokowej na nozkach procesora nie pojawia sie stan wysoki :(
  • #4 9066131
    janbernat
    Poziom 38  
    Podaj typ procesora.
    I nie pisz takich potworków:
    "TCCR0|=0b01011001; //licznik T0 tryb pWM , prescaler "
    Bo to wymaga sięgania do dokumentacji danego procesora i studiowania co autor zrobił.
    Zastosuj makro _BV albo przesunięcie bitowe.
    Każdy bit w takich rejestrach ma swoją nazwę.
    Poza tym flaga w TIFR sama się wykasuje tylko wtedy gdy jest obsłużone przerwania.
    A jak nie to trzeba ją kasować ręcznie.
    Wpisując 1- pomimo tego że stan tej flagi był 1.
  • #5 9066212
    Andrzej__S
    Poziom 28  
    ^Rachel napisał:

    
    TIMSK = 0x01; 
    sei(); 
    while(0) 
    { 
    if (TIFR & 0x01) PORTD = 0xFF; 
    }
    


    Jak zrobisz w ten sposób, to warunek if (TIFR & 0x01) nigdy nie będzie spełniony. Nie masz procedury obsługi przerwania, więc wywołane zostanie przerwanie "__vector_default", które nic nie robi, poza tym, że zeruje flagę, którą chcesz sprawdzić. Nic dziwnego, że ten kod nie działa.

    W celu wygenerowania PWM nie jest konieczne używanie przerwań, więc proponowałbym porzucić tę drogę i wrócić do pierwszego sposobu.

    Cytat:

    Wszystko symuluję, przy rozdzielczości 10-bit, mam 1024 kroki regulacji. Więc przy 5V jakies 4,88mV. Jednak tak pięknie nie jest. Gdy ustawię OCR1A = 512 to napięcie spada do 0V.

    A jak dokładnie jest? Co się dzieje, jak ustawisz inną wartość OCR1A?
    Pierwszy przedstawiony kod teoretycznie powinien działać, tylko że rozdzielczość w tym przypadku jest taka, jak wartość ICR1 + 1, czyli 1001 a nie 1023.

    Ogólnie to trudno się jednoznacznie ustosunkować do tego kodu, ponieważ nie podałeś ani typu procesora, ani częstotliwości taktowania, ani czym to kompilujesz, ani jak sprawdzasz poprawność działania (oscyloskop czy pomiar napięcia).
    Kod też pozostawia wiele do życzenia. Powinien być w znacznikach code, a nie quote. Dobrze byłoby też umieszczać więcej komentarzy, stosować wcięcia w kodzie, używać nazw bitów, przykładowo zamiast:
    
    TCCR1A = 0b10000010;
    TCCR1B = 0b00011001;
    

    czytelniej będzie:
    
       // ustawienie timera w tryb fast PWM - WGM13:0=14,
       // prescaler = 1 CS12:0=1, wyjście PWM OC1A w trybie
       // non-inverting COM1A1:0=2
       TCCR1A = 1<<COM1A1 | 1<<WGM11;
       TCCR1B = 1<<WGM13 | 1<<WGM12 | 1<<CS10;
    

    Jak napiszesz byle jak, to jest dużo mniejsze prawdopodobieństwo, że komuś będzie się chciało Tobie pomagać.
  • #6 9066833
    ^Rachel
    Poziom 21  
    Ok, to teraz od początku:

    uC - ATMega8
    taktowanie - wewnętrzny oscylator 1MHz
    przetwornik - układ całkujący, rezystor 10kΩ kondensator 10µ, do tego wtórnik na WO.

    Piszę to w WinAVR, a tam kompilator to chyba avr-gcc jest.
    Pomiar napięcia na wyjściu WO za pomocą woltomierza.

    Andrzej__S napisał:

    W celu wygenerowania PWM nie jest konieczne używanie przerwań, więc proponowałbym porzucić tę drogę i wrócić do pierwszego sposobu.


    Tak jak pan radzi, zastosuję Timer1.
    Więc na początek muszę poustawiać rejestry TCCR1A i TCCR1B, zgodnie z tabelą :
    Przetwornik C/A z wykorzystaniem timera

    i tą :

    Przetwornik C/A z wykorzystaniem timera

    tabelka z ustawieniami prescalera :

    Przetwornik C/A z wykorzystaniem timera

    Rejestry:
    Przetwornik C/A z wykorzystaniem timera

    Przetwornik C/A z wykorzystaniem timera

    Zgodnie z powyższym, zastosuję 8-bitowy szybki PWM w trybie non-inverting ( nie odwracającym), prescaler 64 :

    
    TCCR1A |= (1<<COM1A1)|(1<<WGM10);
    TCCR1B |= (1<<WGM12)|(1<<CS11)|(1<<CS10);
    


    Rozumiem, że rejestr OCR1A decyduje o wypełnieniu.

    Program :

    
    #include <avr\io.h>
    
    void init_pwm(void)
    {
    TCCR1A |= (1<<COM1A1)|(1<<WGM10);  //ustawiam bity w rejestrach
    TCCR1B |= (1<<WGM12)|(1<<CS11); 
    DDRB |= (1<<1); // port OC1A - PB1 jako wyjście
    }
    
    void pwm(unsigned char wypelnienie) //typ unsigned char - zakres 0 - 255
    {
    OCR1A = wypelnienie;   
    }
    
    void init_port(void)
    {
    DDRD=0x00; 
    PIND=0x03;
    }
    
    int main(void)
    {
    unsigned char wypelnienie;
    init_pwm();
    init_port();
    for(;;)
    {
    if (!(PIND & 0x01)) wypelnienie++;  //jezeli 0 na PIND to wypelnienie zwiekszone o 1
    if (!(PIND & 0x02)) wypelnienie--;
    pwm(wypelnienie);
    }
    }
    
    



    Symulacja :

    Przetwornik C/A z wykorzystaniem timera

    Ilość przyciśnięcia klawisza podłączonego do PD0 - odpowiedzialnego za dekrementację rejestru OCR1A, w zależności od napięcia wyjściowego :

    0 - 0.019 V
    1 - 0.64 V
    2 - 1.26 V
    3 - 1.9 V
    4 - 2.5 V
    5 - 3.1V
    6 - 3.7V
    7 - 4.4V
    8 - 0.019V
    9 - 0.64V

    itd. wynika z tego, że krok jest co 0,6V , żadna to regulacja.

    Co może być przyczyną takiego zachowania się układu ? Regulacja powinna być co 5/256 = 0.019 V.
  • Pomocny post
    #7 9066990
    janbernat
    Poziom 38  
    Następna zaraza to jest debouncing od przycisków.
    Następna to jest sprawdź:
    "if(bit_is_clear(PINx,y))" zamiast Twojego.
    I chyba "wypelnienie" powinno być volatile poza funkcjami jako globalne.
  • Pomocny post
    #8 9067042
    Andrzej__S
    Poziom 28  
    Cytat:

    Co może być przyczyną takiego zachowania się układu ? Regulacja powinna być co 5/256 = 0.019 V.

    Spróbowałbym zacząć od debouncing'u. Styki mają przecież jakieś drgania. Zamiast tego możesz na próbę pominąć sprawdzanie styków, a zamiast tego w pętli for zmieniać wartość OCR1A z jakimś dużym opóźnieniem, przykładowo:
    
    //na początku dołącz
    #include <util/delay.h>
    //......
    for(;;) 
    { 
       pwm(wypelnienie++);
       _delay_ms(500); // tak żeby przyrząd zdążył zmierzyć nową wartość
    }
    


    @janbernat: Widzę, że się spóźniłem z tym debouncing'iem. Co do volatile to w tym przypadku zbędne. Zmienna nie jest używana jednocześnie w przerwaniu i w pętli głównej, tylko przekazywana jako parametr do funkcji pwm().
  • #9 9067109
    ^Rachel
    Poziom 21  
    Zrobiłem tak jak Pan kazał :

    
    #include <avr\io.h>
    #include <avr\sfr_defs.h>
    void init_pwm(void)
    {
    TCCR1A |= (1<<COM1A1)|(1<<WGM10);  //ustawiam bity w rejestrach
    TCCR1B |= (1<<WGM12)|(1<<CS11); 
    DDRB |= (1<<1); // port OC1A - PB1 jako wyjście
    }
    
    void init_port(void)
    {
    DDRD=0x00; 
    PIND=0x03;
    }
    
    int main(void)
    {
    unsigned char wypelnienie=0;
    init_pwm();
    init_port();
    for(;;)
    {
    if (bit_is_clear(PIND,1)) wypelnienie++;  //jezeli 0 na PIND to wypelnienie zwiekszone o 1
    if (bit_is_clear(PIND,2)) wypelnienie--;
    OCR1A = wypelnienie;
    }
    }
    


    teraz napięcie na wyjściu jest równe 2 V i nie zmienia się.

    janbernat napisał:
    Następna zaraza to jest debouncing od przycisków.


    Ale w symulacji nie ma czegoś takiego jak drgania styków, tutaj są elementy idealne, dopiero w układzie rzeczywistym ten efekt występuje.

    janbernat napisał:

    Następna to jest sprawdź:
    "if(bit_is_clear(PINx,y))" zamiast Twojego.


    Na jedno wyjdzie :) ale sprawdziłem.

    Dodano po 2 [minuty]:

    Spróbuję jeszcze tak jak Pan "Andrzej__S" każe.

    Dodano po 8 [minuty]:

    Nie no, nie wierzę...

    Myślałem, że w symulacji nie ma drgania styków, a jednak.
    A tak długo nad tym siedziałem :) dzięki wielkie !

    Nie chce za bardzo was męczyć, ale mam jeszcze jedno pytanie :
    czy komenda _delay_ms(); nadaje się do eliminacji drgań styków ?
    tzn, czy nie wstrzyma ona pracy uC na te 25ms ?

    Dodano po 2 [minuty]:

    Teraz dopiero zauważyłem, że we właściwościach przycisku można zmienić switching time :)

    Dodano po 22 [minuty]:

    
    if (bit_is_clear(PIND,0))
    { 
    wypelnienie++;
    _delay_ms(25);
    loop_until_bit_is_clear(PIND,0);
    _delay_ms(25);
    }
    if (bit_is_clear(PIND,1)) 
    {
    wypelnienie--;
    _delay_ms(25);
    loop_until_bit_is_clear(PIND,1);
    _delay_ms(25);
    }
    


    i tym oto sposobem pozbyłem się niechcianych drgań, przez które straciłem cały dzisiejszy dzien.
  • #10 9067348
    janbernat
    Poziom 38  
    No pozbyłes się- ale to nie jest dobra metoda.
    _delay() to jest łom- a nie klucz.
    Poczytaj o timerach, przerwaniach itp.
    Wpisz w google debouncing C- a zobaczysz ile jest sposobów na pominięcie niepotrzebnych pustych petli procesora.
REKLAMA