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

[attiny2313][C] problem z TIMER0 w trybie FAST PWM

Electix 17 Lis 2010 16:18 3382 19
  • #1 8755257
    Electix
    Poziom 21  
    Witam serdecznie kolegów.
    Mam taki oto problem. Do portu PB2(OC0A) mam podłączoną diodę LED czerwoną poprzez rezystor 470Ω. Mikrokontroler pracuje na wewnętrznym generatorze na 8MHz. Konfiguruję TIMER/COUNTER0 do pracy w trybie FAST PWM. A moim celem jest regulacja jasności świecenia diodą od 0 do max. I tu pojawia się problem bo tak jak mogę nastawić max jasności, tak przy nastawieniu wartości 0 w rejestrze OC0A dioda dalej świeci (słabo ale dalej świeci), niezależnie od nastawionej wartości preskalera dla częstotliwości taktowania PWM. Dopiero wyłączenie PWM, czyli nastawienie wartości 000 na bitach konfiguracji preskalera w rejestrze TCCR0B, powoduje zgaśnięcie diody. Nie rozumiem takiego zachowania. :|

    1. Czy coś źle robię, może gdzieś coś źle nastawiłem w rejestrach?
    2. Czy da się tę diodę zgasić przy działającym PWM dla wartości wypełnienia 0% i jeśli tak, to jak?
    3. Czy ewentualnie powinienem to robić w innym trybie pracy PWM np Phase Correct?

    Nadmienię jeszcze że na początku ten układ wyglądał tak, że do portu mikrokontrolera była podłączona bramka tranzystora BS170 poprzez który sterowałem diodę i efekt był ten sam. Brak możliwości całkowitego wygaszenia diody przy pracującym PWM.

    Załączam też kod programu:

    
    #include<avr/io.h>
    #include<util/delay.h>
    
    void pwmset(void); //funkcja konfigurująca timery
    void pinset(void); //funkcja konfigurująca porty
    void lightn(void); //funkcja płynnie właczająca światło
    void lightf(void); //funkcja płynnie wyłączająca światło
    
    
    int main(void)
    {
    	pinset();
    	pwmset();
    	_delay_ms(500);
    	lightn();
    	_delay_ms(500);
    	lightf();
    	while(1);
    		
    }
    //---------------funkcje-----------
    void lightn(void)
    	{
    		unsigned char i;
    		for(i=0; i<255; i++)
    		{
    			OCR0A=i;
    			_delay_ms(25);
    		}
    	}
    
    void lightf(void)
    	{
    		unsigned char i;
    		for(i=255; i>0; i--)
    		{
    			OCR0A=i;
    			_delay_ms(25);
    		}
    	}
    
    void pinset(void)
    {
    	DDRA=0B00000000;
    	PORTA=0B00000110;
    	DDRB=0B00011100;
    	PORTB=0B00000000;
    	DDRD=0B00100000;
    	PORTD=0B00000000;
    }
    
    void pwmset(void)
    {
    	TCCR0A=0xA3; //konfiguracja fast pwm timera 0
    	TCCR0B=0x00; //pwm stop
    	OCR0A= 0x00; //wspolczynnik 50%
    	OCR0B= 0x00; //wspolczynnik 100%
    	TIMSK= 0x00; //brak przerwan
    	TIFR=  0x00; //brak przerwan
    	TCCR0B=0x03; //prescaler /64 start pwm
    }
    


    Za podpowiedzi z góry dziękuję! :)
  • #2 8755350
    tadzik85
    Poziom 38  
    Ostatnio ktoś miał podobny problem. Rozwiązaniem jest zanegowanie PWMa.
  • #3 8755400
    Electix
    Poziom 21  
    Właśnie zapomniałem o tym wspomnieć wcześniej, że tego zanegowania też próbowałem i efekt jest ten sam. :|
  • #4 8755785
    mieczotronix
    Poziom 16  
    Też ostatnio robiłem ledy na PWM-ie. I jest tak jak mówisz, bo PWM generuje szpilkę o długości 1/256 cyklu i LED świeci z 1/256 jasności. Pewnie nawet jest to opisane w datasheecie, nie chciało mi się szukać. Stwierdziłem, że skoro tak, to po prostu dam jednego ifa i będę wyłączał LED-a na siłę, np. przez wyłączenie PWMa. Ja akurat robiłem PWM dla 8-miu LEDów, więc wyłączać muszę inaczej.
  • #5 8755787
    Konto nie istnieje
    Poziom 1  
  • #6 8756509
    Andrzej__S
    Poziom 28  
    Atmel napisał:

    The extreme values for the OCR0A Register represents special cases when generating
    a PWM waveform output in the fast PWM mode. If the OCR0A is set equal to BOTTOM,
    the output will be a narrow spike for each MAX+1 timer clock cycle. Setting the OCR0A
    equal to MAX will result in a constantly high or low output (depending on the polarity of
    the output set by the COM0A1:0 bits.)

    Zgodnie z tym przy ustawieniu bitów COM0A1:0=3 i wpisaniu do OCR0A wartości 255, na wyjściu powinno być cały czas zero logiczne.
    Spróbuj może zmienić funkcję 'pwmset()':
    
    void pwmset(void) 
    { 
       TCCR0A=0xF3; //konfiguracja fast pwm timera 0 
       TCCR0B=0x00; //pwm stop 
       OCR0A= 0x00;
       OCR0B= 0x00;
       TIMSK= 0x00; //brak przerwan 
       TIFR=  0x00; //brak przerwan 
       TCCR0B=0x03; //prescaler /64 start pwm 
    }
    

    Wyrzuć z pętli głównej te funkcje 'lightn()' i 'lightf()' - poprzestań tylko na tym:
    
    int main(void) 
    { 
       pinset(); 
       pwmset();
       _delay_ms(500);
       OCR0A=0xFF;
       while(1); 
           
    }
    

    i sprawdź, co się stanie. Dioda powinna zapalić się całkowicie i po 0,5 sekundy zgasnąć całkowicie.

    P.S. Zakładam, że diodę masz podłączoną od portu przez rezystor do masy. Tego nie napisałeś, ale tak wynikało z Twojego programu.

    EDIT:
    Electix napisał:

    3. Czy ewentualnie powinienem to robić w innym trybie pracy PWM np Phase Correct?

    To też powinno rozwiązać problem, tym bardziej, że chyba nie potrzebujesz dużej częstotliwości PWM (w trybie Phase Correct PWM częstotliwość jest o połowę mniejsza niż w trybie Fast PWM).
  • #7 8757757
    Electix
    Poziom 21  
    Po dostosowaniu się do powyższych wskazówek, tak się stało jak napisał kolega w powyższym poście. Dioda zapaliła się na max, a potem całkowicie zgasła. No to zacząłem eksperymentować i zaproponowany przez kolegę Andrzej__s'a kod pętli głównej, zastąpiłem dawnym kodem z pętlą - najpierw rozjaśniającą diodę, a potem ściemniającą. I problem znów wrócił, bo po zaciemnieniu diody funkcją:
    
    void lightf(void)
       {
          unsigned char i;
          for(i=0x00; i<0xFF0; i++)
          {
             OCR0A=i;
             _delay_ms(25);
          }
       } 
    

    LED nadal się tliła. Czego kompletnie nie mogę zrozumieć :|

    Dopiero modyfikacja powyższej funkcji do takiej postaci:
    
    void lightf(void)
       {
          unsigned char i;
          for(i=0x00; i<0xFF0; i++)
          {
             OCR0A=i;
             _delay_ms(25);
          }
          OCR0A=0xFF;
       }
    

    pozwoliła mi osiągnąć upragniony efekt wygaszenia diody do zera. :)

    Spróbowałem też trybu PHASE CORRECT PWM i okazuje się, że nie usunęło to problemu niedogasania LED. I też ten problem rozwiązałem dorzucając do funkcji ściemniającej i rozjaśniającej linijkę ładującą wartość min (max) do rejestru OCR0A. Muszę chyba odpalić ten program w symulatorze i zobaczyć jak te funkcje są realizowane i skąd się bierze takie, a nie inne działanie. Może to wynika ze sposobu optymalizacji kodu przez GCC?

    Ma ktoś z Was Szanowni Koledzy pomysł dlaczego tak jest? :)
  • #8 8757999
    Konto nie istnieje
    Poziom 1  
  • #9 8758237
    Andrzej__S
    Poziom 28  
    No tak, ale w ogóle zmienna 'i' w pętli jest unsigned char, a wartość 0xFF0, z którą jest porównywana jest większa od 255. To właściwie ile razy zostanie wykonana pętla?
  • #10 8758267
    Konto nie istnieje
    Poziom 1  
  • #11 8758395
    Andrzej__S
    Poziom 28  
    atom1477 napisał:

    Chm, nie zauważyłem. No to nie wiem. Powinno się wykonywać nieskończoną ilośc razy.

    ...albo będzie porównywało z ostatnim bajtem, czyli z 0xF0, więc skończy na 240.
    Niemniej gdyby było 0xFF, to pętla wykona się 255 razy, a nie 254, jako że masz tam 'i++', więc warunek jest sprawdzany (tak mi się wydaje) przed inkrementacją.
  • #12 8758477
    Konto nie istnieje
    Poziom 1  
  • #13 8758527
    Electix
    Poziom 21  
    Przepraszam Panowie w przykładach pętli w argumentach pętli for jest warunek i<0xFF przepisując kod na forum popełniłem literówkę :P Ale zrobiłem przy okazji taki teścik że wpisałem wartość 0xFF0 do warunku i ku mojemu zaskoczeniu kompilator nie ostrzegł nawet że argument warunku przekracza zakres zmiennej z którą jest porównywany.
    Sprawdziłem też sugestię
    Cytat:
    Niemniej gdyby było 0xFF, to pętla wykona się 255 razy, a nie 254, jako że masz tam 'i++', więc warunek jest sprawdzany (tak mi się wydaje) przed inkrementacją.
    Z powyższego postu i zmieniłem i++ na ++i i zabieg ten w efekcie nic nie dał. Muszę chyba zajrzeć do porządnego podręcznika języka C i prześledzić kolejność sprawdzania warunków w pętlach. Być może tu jest pies pogrzebany.
    Puki co tematu jeszcze nie zamykam :)
  • #14 8758587
    Andrzej__S
    Poziom 28  
    Jeżeli i=254, sprawdzany jest warunek i<255. Ponieważ 254<255 wykonywana jest inkrementacja, czyli przy ostatnim wykonaniu pętli i=255.
    Gdyby zamiast i++ było ++i to najpierw wykonywana byłaby inkrementacja, a następnie sprawdzany warunek i wtedy działanie pętli skończyłoby się na 254.
    Gdyby było '<=' pętla nigdy by się nie zakończyła, bo zmienna typu unsigned char jest zawsze mniejsza lub równa 255.
    Czyżby mnie źle nauczyli?
  • #15 8758761
    Konto nie istnieje
    Poziom 1  
  • #16 8758810
    Konto nie istnieje
    Konto nie istnieje  
  • #17 8759438
    Electix
    Poziom 21  
    Jeszcze raz napiszę dla jasności, że jeżeli chodzi o tryby pracy PWM i tą tlącą się diodę LED przy skrajnej wartości wpisanej do rejestru OCR0A, już zrobiłem test we wszystkich dwóch trybach pracy PWM i dla obu reakcji na porównanie dla normalnej i zanegowanej, czego wynikiem było, że efekt nie wyłączającej się całkowicie diody LED istniał tylko dla FAST PWM w trybie zerowania wyjścia przy porównaniu. Tak jak pisali koledzy wyżej, po prostu pojawiał się na wyjściu OC0A pik o długości trwania 1/256 i to było na tyle zauważalne że dioda nie gasła całkowicie, a tliła się delikatnym światłem. Odwrócenie reakcji wyjścia na porównanie w trybie FAST PWM, czyli ustawianie wyjścia przy porównaniu, załatwiło problem. Przy minimalnym wypełnieniu dioda gasła, przy maksymalnym świeciła na max. W trybie CORRECT PHASE PWM, niezależnie od nastawienia reakcji wyjścia na porównanie, tego złośliwego efektu nie było. Wszystko działało ok.
    Zacząłem więc bawić się pętlami, które płynnie miały rozjaśniać i ściemniać diodę i wyszło że pętle te nie doliczały do wartości granicznych, co objawiało się powróceniem problemu tlącej się LEDy. Problem ten załatwia dodanie zaraz za pętlą ściemniającą instrukcji ładującej do rejestru OCR0A wartości skrajnej (albo 0xFF albo 0x00 w zależności od rodzaju pracy wyjścia), lub problem rozwiąże inny rodzaj pętli napisany tak, aby ta pracowała poprawnie w całym zakresie pojemności licznika. Nad czym teraz pracuję :)
  • Pomocny post
    #18 8759624
    Konto nie istnieje
    Poziom 1  
  • Pomocny post
    #19 8759896
    Andrzej__S
    Poziom 28  
    atom1477 napisał:

    Inkrementacja jest wykonywana po obiegu pętli.

    Racja, chyba mi się mózg zapętlił :)
  • #20 8760178
    Electix
    Poziom 21  
    Hm debata bardzo ciekawa i dała mi wiele inspiracji. Dziękuję wszystkim serdecznie za pomoc. Myślę że temat czas zamknąć :)
REKLAMA