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

Realizacja programowa PWM dla sterowania diody RGB [asm]

darkonel 06 Wrz 2010 21:20 3457 10
  • #1 06 Wrz 2010 21:20
    darkonel
    Poziom 19  

    Witam Kolegów Forumowiczów, mam taki dylemat: potrzebuję zrealizować programowy niezależny 3-kanałowy PWM sterujący pracą diody RGB. Napisałem program, który ładnie generuje PWM tylko jest problem - brakuje czasu na inne procedury.
    Z założenia program pisany jest w asemblerze na mikrokontroler AT89C2051, który ma sterować diodą mocy RGB, dodatkowo obsługiwać sterowanie z pilota RC5 oraz obsługiwać pamięć EEPROM po I²C. Zrealizowałem już 8-bitowy PWM, którego program działa tak, iż w cyklu występuje 256 przerwań, co jest zdecydowanie za często, bo aby uzyskać stosunkowo wysoką częstotliwość PWM (aby diody nie migotały na małym wypełnieniu) to procek nie ma na nic innego czasu (a przecież trzeba odbierać RC5 z pilota itp.). Program (obsługa przerwania) wygląda następująco:

    Code:

    Int_PWM:mov    TH0,#high(Time)   ;ustawienie timera T0
       mov    TL0,#low(Time)   ;ustawienie timera T0
       cjne   licz,#255,PWM_R   ;sprawdź czy licznik bitów PWM nie jest pełny (255)
       mov   Licz,#0      ;licznik = 255, wyzeruj licznik bitów generowania PWM
       mov   ZmR,LevR   ;po wyzerowaniu przepisz wartość jasnosci R do rejestru roboczego
       mov   ZmG,LevG   ;po wyzerowaniu przepisz wartość jasnosci G do rejestru roboczego   
       mov   ZmB,LevB   ;po wyzerowaniu przepisz wartość jasnosci B do rejestru roboczego
    PWM_R:   
       cjne   ZmR,#0,nextR   ;licznik nie jest pełny - sprawdz czy rejestr roboczy ZmR nie jest zerowy
       clr   OutR      ;rejestr ZmR był zerowy - wyzeruj bit wyjścia PWM-R,
       jmp   PWM_G      ;a następnie skocz do obsługi wyjścia PWM-G
    nextR:   setb   OutR      ;rejestr ZmR nie był zerowy - ustaw bit wyjścia PWM-R,
       dec   ZmR      ;następnie zmniejsz wartość rejestru ZmR

    PWM_G:   cjne   ZmG,#0,nextG   ;sprawdz czy rejestr roboczy ZmG nie jest zerowy
       clr   OutG      ;rejestr był zerowy - wyzeruj bit wyjścia PWM-G,
       jmp   PWM_B      ;a następnie skocz do obsługi wyjścia PWM-B
    nextG:   setb   OutG      ;rejestr ZmG nie był zerowy - ustaw bit wyjścia PWM-G,
       dec   ZmG      ;następnie zmniejsz wartość rejestru ZmG

    PWM_B:   cjne   ZmB,#0,nextB   ;sprawdz czy rejestr roboczy ZmB nie jest zerowy
       clr   OutB      ;rejestr był zerowy - wyzeruj bit wyjścia PWM-B,
       jmp   nast      ;a następnie zwiększ licznik bitów PWM
    nextB:   setb   OutB      ;rejestr ZmB nie był zerowy - ustaw bit wyjścia PWM-B,
       dec   ZmB      ;następnie zmniejsz wartość rejestru ZmB

    nast:   inc   Licz      ;zwiększ wartość licznika bitów generowania PWM
       reti

    OutR OutG OutB to wyprowadzenia sterujące diodą RGB
    LevR itp to zadany współczynnik wypełnienia
    ZmR itp to rejestry robocze

    Proszę o pomoc w znalezieniu rozwiązania na 3-kanałowe generowanie PWM zabierające minimum czasu na jego realizację przez mikrokontroler (może na 3 przerwaniach da się, ale jak?) Pozdrawiam

    0 10
  • #2 06 Wrz 2010 22:05
    Father
    Poziom 26  

    Użyj AVR-a z zegarem 20MHz (ATtiny2313). Na AT89C2051 nie masz szans tego zrobić, mikrokontroler jest po prostu za wolny... proponuję dokonać prostych obliczeń, aby sprawdzić jaką częstotliwością musiałby być taktowany mikrokontroler, żeby uzyskać 3-kanałowy, 8-bitowy PWM o częstotliwości 100Hz....

    0
  • #3 07 Wrz 2010 10:24
    darkonel
    Poziom 19  

    Moim założeniem jest AT89C2051 i nie chcę tego zmieniać (zegar 20 MHz). Mam jednak pewną koncepcję dotyczącą realizacji PWM:
    Zakładając, że:

    - PWM 8-bitowy (255 kroków na jeden okres),
    - Zmienna wypełnienia R0 w zakresie 0 ÷ 255,
    - suma R0 oraz /R0 daje zawsze 255 (czyli pełny okres),

    można by pokusić się o zrealizowanie PWM skacząc do procedury obsługi przerwania nie 256 razy na okres (jak w kodzie - mój post wyżej), lecz tylko 2 razy. Pierwszy raz, gdy chcemy ustawić H na wyjściu OutR sterującym diodą RGB przez czas /R0 wpisany do timera na początku procedury, a drugi raz (po odliczeniu czasu /R0), gdy chcemy ustawić L na wyjściu OutR sterującym diodą RGB przez czas R0 wpisany do timera na początku procedury. Przełączanie wczytywania czasów /R0 oraz R0 odbywałoby się wykorzystując jakąś flagę.
    Moim zdaniem dla potrzeb sterowania diodą RGB miałoby to jakiś sens (pomijając drobne krótkotrwałe zakłamania przy wypełnieniu na krańcach, związane z czasem realizacji procedury przez mikrokontroler), bo przecież działałoby to w skrócie tak:

    Code:

    -FLAGA = 0
    -uruchom timer
    Przerwanie:
        -jeśli FLAGA = 1, skocz do SKOK
            -wpisz do timera czas /R0
            -ustaw H na porcie (bit OutR)
            -ustaw FLAGĘ
            -RETI
        SKOK:
            -wpisz do timera czas R0
            -ustaw L na porcie (bit OutR)
            -zdejmij FLAGĘ
            -RETI


    Tylko to odnosi się do sterowania jednego koloru diody RGB (w tym wypadku R). Pytanie, jak w podobny sposób sterować 3 kanałami wykorzystując jeden timer? Może jakieś pomysły?

    0
  • #4 07 Wrz 2010 11:01
    Father
    Poziom 26  

    Nawet jeśli AT89C2051 pracuje z zegarem 24MHz, to rdzeń wykonuje instrukcje 12 razy wolniej czyli pracuje z częstotliwością 2MHz. Poza tym większość instrukcji to 2 lub 4 cykle zegarowe, co daje realną częstotliwość rzędu 1MHz. Zatem nawet jeśli uda się zrobić PWM-a, to o pozostałych rzeczach, którymi ma się zająć uC można zapomnieć, bo nie starczy na nie czasu... Proponuję jeszcze raz głęboko przemyśleć założenia...

    0
  • #5 07 Wrz 2010 14:01
    darkonel
    Poziom 19  

    Father napisał:
    Nawet jeśli AT89C2051 pracuje z zegarem 24MHz, to rdzeń wykonuje instrukcje 12 razy wolniej czyli pracuje z częstotliwością 2MHz. Poza tym większość instrukcji to 2 lub 4 cykle zegarowe, co daje realną częstotliwość rzędu 1MHz. Zatem nawet jeśli uda się zrobić PWM-a, to o pozostałych rzeczach, którymi ma się zająć uC można zapomnieć, bo nie starczy na nie czasu... Proponuję jeszcze raz głęboko przemyśleć założenia...


    Założenia nie zmienię z pewnych powodów, natomiast częstotliwość PWM jaka mnie interesuje to 1 ÷ 4 kHz. Stosując "myk" z 2 przerwaniami o czasie trwania /R0 (zanegowane) oraz R0 (bez negacji) na sterowanie jednego kanału mogę spokojnie zrealizować taki PWM.
    Teraz pytanie do mistrzów, które mnie nurtuje -jak to zrobić dla 3 kanałów wykorzystując 1 timer? Oczywiście dopuszcza się niewielkie przesunięcie fazowe, bo to tylko sterowanie LED RGB.

    0
  • #6 07 Wrz 2010 14:38
    Zbych_
    Poziom 24  

    darkonel napisał:
    Teraz pytanie do mistrzów, które mnie nurtuje -jak to zrobić dla 3 kanałów wykorzystując 1 timer?


    Software'owo? Prosto:
    Code:

    unsigned char pwm1;
    unsigned char pwm2;
    unsigned char pwm3;


    void TimerISR(){
       static unsigned char cnt;

       /* Przeladuj timer */
       TH0 = ....;
       TL0 = ....;

       cnt++;

       if (pwm1 < cnt) OUT1 = 1;
       else OUT1 = 0;

       if (pwm2 < cnt) OUT2 = 1;
       else OUT2 = 0;

       if (pwm2 < cnt) OUT3 = 1;
       else OUT3 = 0;
    }


    Wada jest taka, że przerwanie musi chodzić z częstotliwością 2^n razy większą niż częstotliwość PWM (n - ilość bitów zmiennej cnt).

    0
  • #7 07 Wrz 2010 15:00
    darkonel
    Poziom 19  

    Zbych_ napisał:
    darkonel napisał:
    Teraz pytanie do mistrzów, które mnie nurtuje -jak to zrobić dla 3 kanałów wykorzystując 1 timer?


    Software'owo? Prosto:
    Code:

    unsigned char pwm1;
    unsigned char pwm2;
    unsigned char pwm3;


    void TimerISR(){
       static unsigned char cnt;

       /* Przeladuj timer */
       TH0 = ....;
       TL0 = ....;

       cnt++;

       if (pwm1 < cnt) OUT1 = 1;
       else OUT1 = 0;

       if (pwm2 < cnt) OUT2 = 1;
       else OUT2 = 0;

       if (pwm2 < cnt) OUT3 = 1;
       else OUT3 = 0;
    }


    Wada jest taka, że przerwanie musi chodzić z częstotliwością 2^n razy większą niż częstotliwość PWM (n - ilość bitów zmiennej cnt).


    To ja już zrealizowałem (patrz pierwszy post), ale przecież pisałem powyżej, że zależy mi na minimalizacji przerwań, wykorzystując tylko jedną zmianę stanu podczas okresu PWM. Zrobiłem to dla 1 kanału (patrz powyżej) ale moje pytanie jest jak to zrobić dla 3 kanałów.
    Znów nasuwa mi się taka myśl, aby porównywać wartości wypełnienia LEV1 LEV2 oraz LEV3 poszczególnych kanałów PWM. Uporządkować ich od najkrótszego czasu trwania impulsu do najdłuższego, i generować 3 lub 6 przerwań:
    1) zmiana stanu z H na L dla najkrócej trwającego impulsu
    2) zmiana stanu z H na L dla średnio trwającego impulsu
    3) zmiana stanu z H na L dla najdłużej trwającego impulsu
    Przy czym przy każda zmiana stanu programuje timer zanegowaną wartością czasu trwania impulsu. Czy ma szanse działac coś takiego? Przypominam, iż nie jest tutaj wymagana jakaś większa precyzja - to ma sterować tylko diodą RGB (oświetlenie dekoracyjne).

    0
  • #8 07 Wrz 2010 15:11
    Zbych_
    Poziom 24  

    darkonel napisał:
    Przypominam, iż nie jest tutaj wymagana jakaś większa precyzja - to ma sterować tylko diodą RGB (oświetlenie dekoracyjne).


    A jest sens się szczypać? Jeśli to coś steruje tylko diodą i nie ma nic więcej do roboty, to takie uproszczone podejście powinno wystarczyć. Możesz zredukować też rozdzielczość PWMa. Nie wystarczy 16 poziomów na kanał (4096 kolorów)?

    0
  • #9 07 Wrz 2010 20:11
    darkonel
    Poziom 19  

    Zbych_ napisał:
    darkonel napisał:
    Przypominam, iż nie jest tutaj wymagana jakaś większa precyzja - to ma sterować tylko diodą RGB (oświetlenie dekoracyjne).


    A jest sens się szczypać? Jeśli to coś steruje tylko diodą i nie ma nic więcej do roboty, to takie uproszczone podejście powinno wystarczyć.


    To ma sterować tylko diodą RGB w sensie docelowym, ale przecież pisałem w 1 poście, iż to trzeba jeszcze ustawić pilotem oraz zapisać zmienne po IIC w EEPROM i na to potrzebuję czasu podczas generowania PWM, stąd chcę maksymalnie uprościć procedurę i przerwania odpowiedzialne za generowanie PWM.

    Zbych_ napisał:

    Możesz zredukować też rozdzielczość PWMa. Nie wystarczy 16 poziomów na kanał (4096 kolorów)?


    Nie wiem, może by wystarczyła a może nie - chodzi o to aby przejście koloru było płynne a nie skokowe



    A więc tak - zdecydowałem się jednak na ATtiny2313 AVR, tylko problem w tym, iż nigdy nie miałem do czynienia z programowaniem AVR (znam bardzo dobrze asembler pod '51). Pytanie nieco z innej beczki - Jak zacząć i na co zwrócić uwagę rozpoczynając przygodę z AVR mając świetnie opanowany asembler dla '51? Pozdrawiam i liczę na małą pomoc przy stworzeniu programu w asemblerze sterującego diodą RGB w taki sposób, iż kolory płynnie i wolno zmieniają się przechodząc od jednego zaprogramowanego koloru do drugiego zaprogramowanego koloru itd. Zaprogramowanych kolorów ma być 20 i każdy z nich może mieć R, G oraz B na dowolnym poziomie od 0 do 255. Pomożecie?

    0
  • #10 16 Wrz 2010 16:08
    jrk13
    Poziom 14  

    AVR to rejestry, brak Acc. Całkiem inna architektura. Nowe środowiska i programatory. I nowy asembler, do którego trzeba się przyzwyczaić. A to nadal 8 bitów.
    Omiń ten kawałek historii.
    Jak już coś nowego, to ARM. Procki już od paru zł

    A do tematu pytania:

    100Hz odświeżanie, ~13-bitowy kolor:
    T0 załadowany najmniejszą wartością wypełnienia z RGB.
    W przerwaniu maska na port wyłączająca LEDa z najkrótszym wypełnieniem.
    Do T0 załadowana różnica ([średnie wypełnienie] - [najmniejsze wypełnienie]).
    itd.
    4 - a przy większych wypełnieniach to tylko 3 przerwania na 10ms.
    Oczywiście, wartości masek i wartości dla T0 obliczane gdzieś poza przerwaniem

    0
  • #11 03 Lis 2010 13:14
    darkonel
    Poziom 19  

    No i w tym temacie rozpocząłem swoją przygodę z AVR, a konkretnie ATTINY2313. Nauczyłem się asemblera dla AVR, zrobiłem projekt lampki RGB z płynnym automatycznym mieszaniem kolorów w pętli, ustawianiem jasności świecenia w pełnym zakresie 0-100%, zdalnym sterowaniem wykorzystując pilot RC5. Lampka po starcie rozjaśnia się przez kilka sekund na kolorze białym, po czym automatycznie przechodzi w tryb pętli powolnego mieszania kolorów. Kolor bieżący można w dowolnej chwili rozjaśnić/ściemnić oraz zatrzymać. Dodatkowo można ręcznie przechodzić po zdefiniowanych 30 kolorach.
    Tak więc kwestia generowania PWM została rozwiązana. Pozdrawiam wszystkich

    0