Video 1: MacBook Black 13” (Mid 2007)
Video 2: MacBook PRO 15“ (Late 2011)
Relizácia je veľmi jednoduchá. Použijeme PWM výstup procesora, ktorý budeme patrične riadiť, aby sa plynule menil jas LED. Frekvencia PWM by mala byť aspoň 100Hz, aby to nepôsobilo rušivo. Na ukážku použijem procesor Atmega16 s pripojenými dvoma LED (na 2 PWM výstupy), aby sme mohli porovnať rôzne funkciu pre riadenie jasu.
Obr.1: Schéma zapojenia
Zdrojovy kód v c :
#include <avr/io.h> int main(void) { DDRD = (1<<DDD5) | (1<<DDD4); // porty PD.4 a PD.5 nastavene na vystup TCCR1A = (1<<COM1A1) | (1<<COM1B1) ;// nulovanie pri citani hore, nastavenie pri citani dole TCCR1B = (1<<WGM13) |(1<<CS12); // fazovo a frekvencne korektne PWM ICR1 = 0xFF; // horna hranica PWM OCR1A = 0x0A; OCR1B = 0xF0; while(1) { } }
Zdrojový kód obsahuje iba inicializáciu portov PD.4 a PD.5 ako výstupných a nastavenie PWM. Na porty OC1A a OC1B je pripojený časovač 1, ktorý je 16 bitový. Pre jednoduchosť som hornú hranicu PWM nastvil na 255 (ICR1), čiže v našom prípade sa správa ako bežný 8 bitový časovač. Frekvencia pre časovač je odvodená od frekvencie procesora. Tu je vydelná 256 (nastavený bit CS12), čo by mala byť frekvencia PWM okolo 122 Hz (podmienku aby to bolo aspoň 100Hz sme teda splnili). Intenzitu LED (striedu PWM) meníme hodnotami v registroch OCR1A (D1) a OCR1B (D2). 0 je strieda 0% (LED zhasnutá), 255 je strieda 100% (plná intenzita LED). Výsledok je vidieť na nasledovných obrázkoch. Dióda D1 má nízky jas (strieda 3.7%), dióda D2 ma takmer plný jas (strieda 93.9%).
Obr.2: Intenzita LED pri prvotnom nastavení PWM
Tak PWM funguje a môžeme sa pustiť do generovania funkcií pre PWM. Najprv vyskúšame lineárnu zmenu hodnôt od minima po maximum a následne od maxima po minimum (trojuholník). Aby bol efekt pozorovateľný, tak medzi jednotlivými zmenami je oneskorenie 10ms.
Obr.3: Priebeh PWM na osciloskope
Zdrojový kód v C:
#include <avr/io.h> #include <util/delay.h> int main(void) { int x; DDRD = (1<<DDD5) | (1<<DDD4); // porty PD.4 a PD.5 nastavene na vystup TCCR1A = (1<<COM1A1) | (1<<COM1B1) ;// nulovanie pri citani hore, nastavenie pri citani dole TCCR1B = (1<<WGM13) |(1<<CS12); // fazovo a frekvencne korektne PWM ICR1 = 0xFF; // horna hranica PWM OCR1A = 0x00; OCR1B = 0x00; while(1) { for (x = 0; x < 256; x++) { OCR1A = x; _delay_ms(10); } for (x = 255; x >= 0; x--) { OCR1A = x; _delay_ms(10); } } }
Video 3: Lineárna zmena
Ako vidno efekt nič moc . Otázka je akú použiť funkciu, aby to vyzeralo ako dýchanie. Na internete sa dá nájsť pokus o zistenie funkcie na stránke http://www.adafruit.com/blog/2010/08/26/reverse-engineering-the-mac-breathing-led-2/ . Tu je však použitá nepriama metóda, teda nemeria priamo PWM, ale jas LED na MacBooku. Každopádne to vyzerá ako absolútna hodnota sínusu. Taktiež sa dá nájsť, že aj túto srandičku má Apple patentovanú http://www.google.com/patents/US6658577
Obr. 4: Apple patent
Z obr. 4 je zrejmé, že priebeh je niečo podbné ako sínus. Poďme teda vyskúšať sínus. Pre jednoduchosť budeme počítať funkciu pre hodnoty do 0 do 255 a výstup funkcie by mal mať maximum 255 (plný jas LED). Úpravami dostaneme takúto funkciu:
Obr.5: Funkcia sin(x)
Tento priebeh budeme opakovať v nekonečnej slučke. Teda minimálny jas bude velmi krátku dobu, zatiaľ čo jas okolo maxima sa bude meniť pozvoľne. Pripravíme si preto ešte “inverznú“ funkciu tejto, ktorá by mala maximálny jas len krátku dobu, zatiaľ čo v oblasti minimálne jasu, by sa funkcia zdržala podstatne dlhšie. Vyzerá to takto:
Obr.6: Funkcia 1-sin(x)
Praktická realizácia týchto fukcií je v nasledujúcom výpise. Dióda D1 je budená otočeným sínusom, zatiaľ čo dióda D2 je budená bežným sínusom.
Zdrojový kód v C:
#include <avr/io.h> #include <util/delay.h> int main(void) { int x; double funkcia; DDRD = (1<<DDD5) | (1<<DDD4); // porty PD.4 a PD.5 nastavene na vystup TCCR1A = (1<<COM1A1) | (1<<COM1B1) ;// nulovanie pri citani hore, nastavenie pri citani dole TCCR1B = (1<<WGM13) |(1<<CS12); // fazovo a frekvencne korektne PWM ICR1 = 0xFF; // horna hranica PWM OCR1A = 0x00; OCR1B = 0x00; while(1) { for (x = 0; x < 256; x++) { // 1 - sinus funkcia = (1-( sin( x * (M_PI / 255.0) ) ) )*255.0; OCR1A = (uint8_t) funkcia; // sinus funkcia = ( sin( x * (M_PI / 255.0) ) ) *255.0; OCR1B = (uint8_t) funkcia; _delay_ms(20); } } }
Video 4: Funkcia otočeného sínusu a sínusu
Výsledok je zachytený na videu.Je zrejmé, že otočený sínus poskytuje lepší výsledok optoti obyčajnému sínusu. V článku na adafruit jeden z komentárov uvádza, že funkcia by mala byť exp(sin(x)) aby korigovala logaritmickú odozvu optického systému. Úprava tejto funkcie pre požadovaný rozsah hodnôt je už trošičku zložitejšia. Celý postup úpravy funkcie tu nebudem rozpisovať – kto ovláda trochu matematiku, tak si to dokáže odvodiť sám. Výsledná funkcia vyzerá takto:
Obr.7: Funkcia exp(sin(x))
Obr.8: Zelená – otočený sinus, ružová – exponenciálna funkcia
Zdrojový kód pre porovnanie jasu diód. D1 je 1-sin(x) a D2 je exp(sin(x)).
Zdrojový kód v C:
#include <avr/io.h> #include <util/delay.h> int main(void) { int x; double funkcia; DDRD = (1<<DDD5) | (1<<DDD4); // porty PD.4 a PD.5 nastavene na vystup TCCR1A = (1<<COM1A1) | (1<<COM1B1) ;// nulovanie pri citani hore, nastavenie pri citani dole TCCR1B = (1<<WGM13) |(1<<CS12); // fazovo a frekvencne korektne PWM ICR1 = 0xFF; // horna hranica PWM OCR1A = 0x00; OCR1B = 0x00; while(1) { for (x = 0; x < 256; x++) { // exp(sin(x)) funkcia = ( ( (-1.0/M_E) + ( exp ( cos( (2.0 * M_PI * x) / 255.0) ) ) ) / M_E ) * ( 255.0 / ( 1 - ( 1 / (M_E * M_E) ) ) ); OCR1A = (uint8_t) funkcia; // 1 - sinus funkcia = (1-( sin( x * (M_PI / 255.0) ) ) )*255.0; OCR1B = (uint8_t) funkcia; _delay_ms(20); } } }
Video 5: výsledok:
Exponenciálny sínus verzus otočený sínus. Rozdiel nie je moc markantný. Otázne je či treba zabíjať procesorový čas počítaním zložitých funkcií kvôli indikačnej dióde (kto nechce môže samozrejme použiť LUT – lookup table). Osobne používam funkciu 1-sin(x) a oneskorenie okolo 10ms. Tí čo chcú presnú kópiu Apple LED sa musia pohrať s časovaním a pridaním oneskorenia medzi jednotlivé priebehy. Presná kópia však nebola cieľom článku. Skôr som chcel predostrieť spôsob realizácie jednoduchého efektu.