Dodatki‎ > ‎

PWM programowy

PortD (ten z 8 LEDami i buforem HC573) nadaje się do eksperymentowania z bitami zebranymi w bajt. Egzemplarze PCB z wlutowanymi zielonymi diodami o wysokiej jasności wręcz zmuszają do rozwiązania 'problemu' z oślepiającym blaskiem, jaki wydobywa się z 8 punktów przy krawędzi płytki.

PWM to bardzo szybkie, cykliczne włączanie i wyłączanie diody świecącej, przez co uzyskujemy wrażenie mniejszej jasności. Jeśli dioda świeci tylko przez 50 % cyklu PWM, to skuteczna wartość prądu przez nią płynącego (a więc również liczba wyemitowanych w tym czasie fotonów) wynosi 50% wartości maksymalnej. 50% liczby emitowanych fotonów nie oznacza, że obserwujemy jasność 50% - wynika to z nieliniowej wrażliwości oka na natężenie światła.

Ten trik pozwala uzyskać wrażenie [prawie] płynnie zmienianej jasności pomimo tego, że sygnał sterujący jest czysto cyfrowy (1/0, świeci/nie świeci). Warunkiem sukcesu jest na tyle szybkie włączanie i wyłączanie każdej diody, żeby oko tego nie dostrzegało.
Rysunek: zasada działania wielokanałowego, synchronicznego PWM (wszystkie kanały startują w tym samym momencie z identycznymi parametrami).

Przykład 1: zdjęcie 1zdjęcie 2
Okres timera: 50 us
Liczba kroków PWM na pełny cykl: 255
Długość cyklu: 12.750 ms (50 us*255), co daje odświeżanie ok. 80 Hz - z taką częstotliwością miga każda dioda.
Jasność diody (czas świecenia w cyklu): 0 (zgaszona), 1/255 (najmniejsza jasność), 255/255 (największa jasność).

Widoczne na zdjęciach różne jasności świecenia elementów linijki uzyskano wykonując napisaną w tym celu funkcję set_brightness(led, value):
  • set_brightness(0, 1); //D0 - najmniejsza jasność
  • set_brightness(1, 4);
  • set_brightness(2, 12);
  • set_brightness(3, 25);
  • set_brightness(4, 32);
  • set_brightness(5, 64);
  • set_brightness(6, 128);
  • set_brightness(7, 255); //D7 - największa jasność
Po to właśnie jest potrzebne aż 256 poziomów jasności - między 128 a 255 nie widać różnicy, natomiast bardzo duże zmiany są obserwowane między jasnościami 0 a 1 (to akurat jest oczywiste) oraz między 1 a 4. 
Najlepsze wartości (tzn. uzyskanie subiektywnej liniowej skali jasności) należy dobrać doświadczalnie, pomocne mogą w tym być logarytmy naturalne :-)

Przykład 2: Prosta animacja wykonana z użyciem powyższego mechanizmu.
Oba przykłady korzystają z timera, zatem algorytm działa w tle, nie angażując głównej pętli programu. Raz zaprogramowana sekwencja "żyje własnym życiem".
W dowolnym momencie można wykonać set_brightness(...), co zmieni obserwowaną jasność odpowiedniej diody.
Zmiany jasności diod dokonywane okresowo według zaprogramowanego schematu dają wrażenie "płynącego światła" lub rozbłysku.

Zadanie 1: Napisz program, który odtwarza przygotowany wcześniej schemat animacji.
Animacja z przykładowego  filmu jest zakodowana następująco:
const int NSTEPS = 38; //liczba kroków animacji
const byte x[NSTEPS][8] = 
//tablica 2-wymiarowa, umieszczona w pamięci Flash (const)

{ 0, 0, 0, 0, 0, 0, 0, 0}, //nic nie świeci
{ 1, 0, 0, 0, 0, 0, 0, 0}, //pojawia się nikłe światełko z boku linijki
{ 4, 1, 0, 0, 0, 0, 0, 0},
{ 12, 4, 1, 0, 0, 0, 0, 0},
{ 25, 12, 4, 1, 0, 0, 0, 0},
{ 32, 25, 12, 4, 1, 0, 0, 0},
{ 64, 32, 25, 12, 4, 1, 0, 0},
{128, 64, 32, 25, 12, 4, 1, 0},
{255,128, 64, 32, 25, 12, 4, 1},
{128,255,128, 64, 32, 25, 12, 4},
{ 64,128,255,128, 64, 32, 25, 12},
{ 32, 64,128,255,128, 64, 32, 25}, //apogeum - najjaśniejszy punkt mniej więcej w połowie
{ 25, 32, 64,128,255,128, 64, 32}, //j.w.
{ 12, 25, 32, 64,128,255,128, 64},
{ 4, 12, 25, 32, 64,128,255,128},
{ 1, 4, 12, 25, 32, 64,128,255},
{ 0, 1, 4, 12, 25, 32, 64,128},
{ 0, 0, 1, 4, 12, 25, 32, 64},
{ 0, 0, 0, 1, 4, 12, 25, 32},
{ 0, 0, 0, 0, 1, 4, 12, 25},
{ 0, 0, 0, 0, 0, 1, 4, 12},
{ 0, 0, 0, 0, 0, 0, 1, 4},
{ 0, 0, 0, 0, 0, 0, 0, 1},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 4, 0, 0, 0, 0}, //początek rozbłysku
{ 0, 0, 0, 12, 0, 0, 0, 0},
{ 0, 0, 0, 32, 0, 0, 0, 0},
{ 0, 0, 0, 64, 0, 0, 0, 0},
{ 0, 0, 0,255, 0, 0, 0, 0}, //maksimum rozbłysku
{ 0, 0, 0, 64, 0, 0, 0, 0},
{ 0, 0, 0, 32, 0, 0, 0, 0},
{ 0, 0, 0, 12, 0, 0, 0, 0},
{ 0, 0, 0, 4, 0, 0, 0, 0}, //koniec rozbłysku
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0}
};

Zadanie 2: Ładowanie animacji z terminala znakowego (przesyłanie schematu zmian jasności przez UART), zamiast "na sztywno" zaprogramowanej tablicy w pamięci mikrokontrolera.

ą
pwm-1.png
(42k)
Krzysztof Urbański,
20 lut 2012, 10:52