RGB LED Neopixel, které mají přímo v sobě řadič a dají se i v pásku jednotlivě adresovat, byly podrobněji popsány v jednom z minulých článků. Snadno se ovládají třeba Arduinem, ale jak na ně, když používáme jiné a třeba podstatně méně výkonné platformy?
Ovládání LED Neopixel, zejména ve větším počtu, vyžaduje použít mikrokontrolér schopný poměrně rychle vyslat velké množství sériových dat pro obvody WS2812B použité jako řadič v každé LED. Když chceme ovládat LED mikrokontroléry Picaxe, narazíme na problém. I když samotný mikrokontrolér PIC má dostatečný výkon a zvládl by to s velkou rezervou, pod „vrstvou“ Picaxe už toto jednoduše udělat nejde a psát si obslužný program v assembleru není pro typické uživatele Picaxe dostupná cesta.
Bylo by jistě možné navrhnout „speciální“ řadič (převodník) pro Neopixel tak, aby zakrýval, že využívá toho nejsnažšího způsobu, totiž Arduina a jeho hotových a prověřených knihoven. Ale proč to dělat? Jednodušší a lacinější je vzít si (koupit) přímo Arduino Nano s již nahraným programem, který přijímá povely pro LED přes standardní sériovou linku nebo USB rozhraní nízkou rychlostí a LED obslouží.
O Arduinu jako takovém ani jeho programování není potřeba vůbec nic vědět. K modulu, který se jen jakousi shodou okolností prý nazývá Arduino, připojíme jeden vstupní signál (a případně další výstupní, který potvrzuje připravenost přijímat další povely) a jeden výstupní signál pro Neopixel. Polohou jedné propojky nastavíme přenosovou rychlost sériové linky a druhou propojkou to, jestli se budou přicházející data brát přímo jako povely byte po bytu, nebo jako text, který obsahuje povely ve znakové podobě. Ovládaných LED může být až 239 v jedné řadě. To je vše.
Začneme popisem zapojení z pohledu uživatele Picaxe nebo libovolného jiného mikrokontroléru schopného generovat sériový signál (rozhraní UART).
V prvním kroku zvolíme přenos dat nebo textu. Když vývod popsaný D4 spojíme se zemí, bude se očekávat textová podoba, když jej ponecháme volně, budou se očekávat data.
V druhém kroku zvolíme přenosovou rychlost připojením jednoho z následujících vývodů na zem. Když nespojíme se zemí žádný z vývodů, bude nastavena přenosová rychlost 115200 Bd. Ve všech případech je přenos s parametry: 8 bitů, bez parity, jeden stop bit.
D5 ... 1200 Bd D6 ... 2400 Bd D7 ... 4800 Bd D8 ... 9600 Bd D9 ... 19200 Bd
Vývod D3 je výstup pro ovládání první z řady zapojeních LED Neopixel, spojíme jej s LED.
Vývod Rx je vstup pro ovládací signál z nadřízeného mikrokontroléru, spojíme jej s výstupem Tx na něm.
Vývod D13 je výstup, který může, ale nemusí být použit. Jeho stav je vidět na jedné z LED na modulu Arduina. Pokud LED svítí (na výstupu je stav H), je buffer pro povely přicházející na vstup volný a modul čeká na další povel. Je-li LED zhasnutá, je mikrokontrolér zaneprázdněn. Když je potřeba spolehlivě hlídat, aby určitě žádný z povelů modulu „neutekl“, je nutné sledovat stav na D13 a posílat další povely až když je buffer prázdný (D13 ve stavu H). V podstatě jde o standardní signál DTR. Do bufferu je možné přijmout více povelů, podle jejich délky 16 až 64.
Používané kódy povelů jsou následující:
kód |
popis činnosti |
0 .. 238 |
Byte se považuje se za adresu LED, která bude ovládána. První LED v řadě má adresu 0. Za touto adresou se očekávají další 3 byty určující po řadě svit R,G a B barevné složky. Po provedení není změna vidět, dělá se jen v paměti, v "obrazu" stavu LED, skutečně se vyšle až povelem 255. Příklad: 72 0 0 128 - LED s adresou 72 svit poloviční modré barvy. |
239 |
Omezí jas všech ovládaných LED na polovinu. Přenese se do LED ihned. |
240 |
Povel pro nastavení rychlosti průbehu světelných efektů. Následuje jeden byte parametru, 0 znamená nejrychlejší průběh, 255 nejpomalejší. Implicitní hodnota je 30. |
241 |
Povel pro nastavení počtu ovládaných LED v řadě. Následuje jeden byte parametru, hodnoty jsou od 1 do 238 (případně 1 až 100 pro Arduino s mikrokontrolérem ATmega168). |
242 |
Povel pro efekt běžícího světla od konce k začátku pásku. Barva modrá. Přenese se do LED ihned. |
243 |
Povel pro efekt běžícího světla od konce k začátku pásku. Barva zelená. Přenese se do LED ihned. |
244 |
Povel pro efekt běžícího světla od konce k začátku pásku. Barva červená. Přenese se do LED ihned. |
245 |
Povel pro efekt běžícího světla od konce k začátku pásku. Barva bílá. Přenese se do LED ihned. |
246 |
Povel pro efekt běžícího světla od začátku ke konci pásku. Barva modrá. Přenese se do LED ihned. |
247 |
Povel pro efekt běžícího světla od začátku ke konci pásku. Barva zelená. Přenese se do LED ihned. |
248 |
Povel pro efekt běžícího světla od začátku ke konci pásku. Barva červená. Přenese se do LED ihned. |
249 |
Povel pro efekt běžícího světla od začátku ke konci pásku. Barva bílá. Přenese se do LED ihned. |
250 |
Povel pro rozsvícení všech LED modře. Přenese se do LED ihned. |
251 |
Povel pro rozsvícení všech LED zeleně. Přenese se do LED ihned. |
252 |
Povel pro rozsvícení všech LED červeně. Přenese se do LED ihned. |
253 |
Povel pro rozsvícení všech LED bíle. Přenese se do LED ihned. |
254 |
Povel pro zhasnutí všech LED. Přenese se do LED ihned. |
255 |
Povel pro přenos stavu LED z paměti Arduina do pásku. |
Příklad sekvence: 254,246,242,10,255,0,0,255,239,239,239,239,239,239,239,239,
- – 254 – zhasne všechny LED (provede se ihned)
- – 246 – modré světlo proběhne od začátku ke konci (provede se ihned)
- – 242 – modré světlo proběhne od konce k začátku (provede se ihned)
- – 10 255 0 0 – LED s adresou 10 se rozvítí červeně (jen v paměti, není vidět hned)
- – 255 – stav paměti se zobrazí (až teď to bude vidět)
- – 239 (8x) – v každém kroku zeslabí svit na polovinu až zmizí (provede se ihned)
Pokud bychom tuto sekvenci zadali bez prodlev, proběhne dost rychle, přeběhy světla vidět budou (podle nastavení rychlosti), ale třeba rozsvícení červené LED v kroku 3 a její postupné zhasnutí v osmi krocích proběhne tak rychle, že vidět vlastně nebude. Časování musí hlídat nadřízený mikrokontrolér a posílat odpovídající kódy ve správný čas.
Pokud je nastavený textový mód vstupu, zadávají se čísla v dekadické podobě a jako jejich oddělovač mohou sloužit znaky mezera (SPC), středník, tečka, čárka, LF (kód 10) nebo CR (kód 13).
Textový mód se dá výhodně použít třeba k tomu, že se data pro LED připravují na PC v sériovém terminálu nebo se uloží do textového souboru, který je následně pomalu odesílán po sériové lince. Pro řízení z jiného mikrokontroléru se pravděpodobně vždy použije datový mód.
Výpis programu pro Arduino ve funkci převodníku z rozhraní UART a řadiče LED Neopixel:
// Převodník COM - LED Neopixel s řadičem WS2812B #include <Adafruit_NeoPixel.h> Adafruit_NeoPixel LED = Adafruit_NeoPixel(239, 3, NEO_GRB + NEO_KHZ800); // max pocet LED, pin pro vystup, rezim byte povel, R, G, B, pocet, rezim; byte cas = 30; // rychlost přejezdu světla v efektech void setup() { pinMode(3, OUTPUT); // pin pro ovládáni LED pinMode(4, INPUT_PULLUP); // nastavení byte / ASCII pinMode(5, INPUT_PULLUP); // volba 1200 Bd pinMode(6, INPUT_PULLUP); // volba 2400 Bd pinMode(7, INPUT_PULLUP); // volba 4800 Bd pinMode(8, INPUT_PULLUP); // volba 9600 Bd pinMode(9, INPUT_PULLUP); // volba 19200 Bd pinMode(13, OUTPUT); // kontrola - LED svítí, když je volný buffer Serial.begin(115200); if (digitalRead(5) == LOW) Serial.begin(1200); if (digitalRead(6) == LOW) Serial.begin(2400); if (digitalRead(7) == LOW) Serial.begin(4800); if (digitalRead(8) == LOW) Serial.begin(9600); if (digitalRead(9) == LOW) Serial.begin(19200); pocet = 30; // implicitní počet LED (mění se povelem) if (digitalRead(4) == LOW) rezim = 0; else rezim = 1; // režim ASCII/byte LED.begin(); LED.show(); digitalWrite(13, HIGH); // signalizace buffer volný } void loop() { povel = cti(); switch (povel) { case 255: // přenos dat do LED LED.show(); break; case 254: // zhasnout všechny LED for (int i = 0; i < pocet; i++) { LED.setPixelColor(i, 0, 0, 0); } LED.show(); break; case 253: // plný svit všech LED for (int i = 0; i < pocet; i++) { LED.setPixelColor(i, 255, 255, 255); } LED.show(); break; case 252: // plný svit všech LED for (int i = 0; i < pocet; i++) { LED.setPixelColor(i, 255, 0, 0); } LED.show(); break; case 251: // plný svit všech LED for (int i = 0; i < pocet; i++) { LED.setPixelColor(i, 0, 255, 0); } LED.show(); break; case 250: // plný svit všech LED for (int i = 0; i < pocet; i++) { LED.setPixelColor(i, 0, 0, 255); } LED.show(); break; case 249: // běžící světlo bílé od začátku LED.setPixelColor(0, 255, 255, 255); LED.show(); delay(cas); for (int i = 1; i < pocet; i++) { LED.setPixelColor(i - 1, 0, 0, 0); LED.setPixelColor(i, 255, 255, 255); LED.show(); delay(cas); } LED.setPixelColor(pocet - 1, 0, 0, 0); LED.show(); break; case 248: // běžící světlo červené od začátku LED.setPixelColor(0, 255, 0, 0); LED.show(); delay(cas); for (int i = 1; i < pocet; i++) { LED.setPixelColor(i - 1, 0, 0, 0); LED.setPixelColor(i, 255, 0, 0); LED.show(); delay(cas); } LED.setPixelColor(pocet - 1, 0, 0, 0); LED.show(); break; case 247: // běžící světlo zelené od začátku LED.setPixelColor(0, 0, 255, 0); LED.show(); delay(cas); for (int i = 1; i < pocet; i++) { LED.setPixelColor(i - 1, 0, 0, 0); LED.setPixelColor(i, 0, 255, 0); LED.show(); delay(cas); } LED.setPixelColor(pocet - 1, 0, 0, 0); LED.show(); break; case 246: // běžící světlo modré od začátku LED.setPixelColor(0, 0, 0, 255); LED.show(); delay(cas); for (int i = 1; i < pocet; i++) { LED.setPixelColor(i - 1, 0, 0, 0); LED.setPixelColor(i, 0, 0, 255); LED.show(); delay(cas); } LED.setPixelColor(pocet - 1, 0, 0, 0); LED.show(); break; case 245: // běžící světlo bílé od konce LED.setPixelColor(pocet - 1, 255, 255, 255); LED.show(); delay(cas); for (int i = pocet - 1; i > 0; i--) { LED.setPixelColor(i, 0, 0, 0); LED.setPixelColor(i - 1, 255, 255, 255); LED.show(); delay(cas); } LED.setPixelColor(0, 0, 0, 0); LED.show(); break; case 244: // běžící světlo červené od konce LED.setPixelColor(pocet - 1, 255, 0, 0); LED.show(); delay(cas); for (int i = pocet - 1; i > 0; i--) { LED.setPixelColor(i, 0, 0, 0); LED.setPixelColor(i - 1, 255, 0, 0); LED.show(); delay(cas); } LED.setPixelColor(0, 0, 0, 0); LED.show(); break; case 243: // běžící světlo zelené od konce LED.setPixelColor(pocet - 1, 0, 255, 0); LED.show(); delay(cas); for (int i = pocet - 1; i > 0; i--) { LED.setPixelColor(i, 0, 0, 0); LED.setPixelColor(i - 1, 0, 255, 0); LED.show(); delay(cas); } LED.setPixelColor(0, 0, 0, 0); LED.show(); break; case 242: // běžící světlo modré od konce LED.setPixelColor(pocet - 1, 0, 0, 255); LED.show(); delay(cas); for (int i = pocet - 1; i > 0; i--) { LED.setPixelColor(i, 0, 0, 0); LED.setPixelColor(i - 1, 0, 0, 255); LED.show(); delay(cas); } LED.setPixelColor(0, 0, 0, 0); LED.show(); break; case 241: // nastavení počtu LED pocet = cti(); if (pocet < 1) pocet = 1; if (pocet > 240) pocet = 240; break; case 240: // nastavení rychlosti přeběhu světla (času na jeden krok) cas = cti(); if (cas < 1) cas = 1; if (cas > 250) cas = 250; break; case 239: // snížení jasu 2x pro všechny LED for (int i = 0; i < pocet; i++) { R = (LED.getPixelColor(i) >> 16); R = R / 2; G = (LED.getPixelColor(i) >> 8); G = G / 2; B = (LED.getPixelColor(i)); B = B / 2; LED.setPixelColor (i, R, G, B); } LED.show(); break; default: if (povel < pocet) { R = cti(); G = cti(); B = cti(); LED.setPixelColor(povel, R, G, B); } } } int cti() { if (rezim == 1) { //ctu bajt while (true) { if (Serial.available() > 0) { digitalWrite(13, LOW); return Serial.read(); } else { digitalWrite(13, HIGH); } } } else { //ctu ASCII char c; byte vysledek = 0; boolean nic = true; while (true) { if (Serial.available() > 0) { digitalWrite(13, LOW); c = Serial.read(); boolean oddelovacPritomen = (c == ' ' || c == ';' || c == '.' || c == ',' || c == 10 || c == 13); if (oddelovacPritomen && nic)continue; if (oddelovacPritomen)break; if (c < '0' || c > '9') continue; nic = false; int numero = c - '0'; vysledek = 10 * vysledek + numero; } else { digitalWrite(13, HIGH); } } return vysledek; } }
K předpokládaným úpravám programu v Arduinu:
- Na začátku v definici Adafruit_Neopixel je nutné přizpůsobit maximální počet LED typu mikrokontroléru Arduina. Pro ATmega328 použijeme 239 (je to omezené počtem volných kódů a možností adresovat LED 0 .. 238), pro ATmega168 použijeme nejvýš 100 (je to omezené velikostí paměti)
- Ve stejné definici můžeme nastavit nižší rychlost přenosu dat do LED pro starší a pomalejší řadiče WS2811.
- Pokud by bylo třeba použití nižší přenosové rychlosti než 1200 Bd, není to problém, Arduino zvládá standardně až 300 Bd, omezený sortiment na nejčastější rychlosti je jen kvůli tomu, aby voleb nebylo příliš mnoho.
Příklad řídícího přigramu v Picaxe 08M2 (funkce je popsaná v komentářích):
REM Picaxe 08M2 test komunikace s NeoPixel - několik efektů REM pin c.4 - výstup řídících dat REM pin c.3 - vstup hlášení od řadiče, že je buffer volný REM pásek je dlouhý 30 LED, nastaven datový přenos přímo bytů (ne textu), rychlost 9600 Bd pause 5000 setfreq m32 SEROUT C.4,T9600_32,(241,30) ;nastavení počtu LED na 30 do ;začátek hlavní smyčky programu rem for b1=0 to 3 ;pro 4 barvy (po řadě modře, zeleně, červeně, bíle) b2=246+b1 SEROUT C.4,T9600_32,(b2,255) ;přeběhne světlo od začátku ke konci b2=242+b1 SEROUT C.4,T9600_32,(b2,255) ;a pak stejná barva zpět next b1 gosub cekej ;čeká se na vyprázdnění bufferu gosub blik ;vše 3x bíle blikne for b4 = 1 to 2 ;2x po sobě for b0=0 to 255 step 20 ;změna barev ve 12 krocích for b1=12 to 18 ;vždy jen na LED s adresou 12 až 18 (ne celý pás) b3=255-b0 SEROUT C.4,T9600_32,(b1,b0,b3,0,255) ;bez červené složky tj. žlutá na červenou gosub cekej ;čeká se na volný buffer next b1 next b0 for b0=0 to 255 step 20 ;změna barev ve 12 krocích for b1=12 to 18 b3=255-b0 SEROUT C.4,T9600_32,(b1,b3,0,b0,255) ;bez zelené složky tj. červená na modrou gosub cekej ;čeká se na volný buffer next b1 next b0 for b0=0 to 255 step 20 ;změna barev ve 12 krocích for b1=12 to 18 b3=255-b0 SEROUT C.4,T9600_32,(b1,0,b0,b3,255) ;bez červené složky tj. modrá na žlutou gosub cekej ;čeká se na volný buffer next b1 next b0 next b4 gosub blik ;vše 3x bíle blikne for b1=1 to 8 ;s dosvitem přes 8 LED for b0=15 to 29 ;přejede světlo od středu pásku ke kraji b4=29-b0 SEROUT C.4,T9600_32,(b0,255,255,255,b4,255,255,255,255,239) pause 400 gosub cekej next b0 next b1 SEROUT C.4,T9600_32,(254) ;vše zhasne for b1=1 to 8 ;s dosvitem přes 8 LED for b0=0 to 14 ;přejede světlo od krajů do středu pásku b4=29-b0 SEROUT C.4,T9600_32,(b0,255,255,255,b4,255,255,255,255,239) pause 400 gosub cekej next b0 next b1 SEROUT C.4,T9600_32,(254) ;vše zhasne gosub blik ;bíle zabliká celým páskem loop ;konec hlavní smyčky programu blik: ;3x bíle blikne celým páskem for b0=1 to 3 SEROUT C.4,T9600_32,(253,255) pause 1000 SEROUT C.4,T9600_32,(254,255) pause 1000 next b0 gosub cekej return cekej: ;čeká na signál o vyprázdění bufferu if pinc.3=0 then pause 10 high C.2 ;pin C.2 signalizuje čekání na volný buffer goto cekej ;(nemusí se využít, jen pro názornost) else low c.2 endif return