Jak na LED Neopixel třeba i z Picaxe

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.

radic_neopixel

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,

  1. – 254 – zhasne všechny LED (provede se ihned)
  2. – 246 – modré světlo proběhne od začátku ke konci (provede se ihned)
  3. – 242 – modré světlo proběhne od konce k začátku (provede se ihned)
  4. – 10 255 0 0 – LED s adresou 10 se rozvítí červeně (jen v paměti, není vidět hned)
  5. – 255 – stav paměti se zobrazí (až teď to bude vidět)
  6. – 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