RGB LED tárolt vezérlése

2019-05-30

 

Major László (kepszobor.hu) keresett meg a problémájával, hogy szeretné az alkotásait LED-ekkel megvilágítani, és ezt az általa elképzelt program szerint változtatni. Elmondta, hogy nem annyira foglalkozott még mikrovezérlőkkel, programozással. Azt gondoltam, hogy mint korábban, egy Arduino mini-vel vezérelt RGB LED-ekkel lehetne a feladatot megoldani. Előszőr az volt az elképzelésem, hogy ne kelljen az Arduino-t programozni, az EEPROM-ba tárolom el az adatokat, és az Arduino-t soros porton keresztül különböző parancsokkal lehetne feltölteni. Sajnos elég korlátozott az EEPROM mérete még egy Arduino Mega-nak is, és a felprogramozás is elég macerás egy terminál ablakon keresztül. Miközben gondolkodtam, eszembe jutott hogy kellene egy kezdőállapot, amit a program memóriából lehetne betölteni egy Reset hatására. Végül úgy döntöttem, hogy kihajítom az egész addigi elképzelésemet. A változtatás az Arduino programozásával történik de viszonylag egyszerűen, a LED-ek állapotait a nagyobb kapacitású program memóriába lehet letárolni.

Sok sötét és szivárványosan villogó LED-en küzdöttem át magam. Arra is rájöttem, hogy a FastLED elrontja a soros vonal Baud Rate-jét, ezért a program memóriába való adat tárolást az alábbi programmal mutatom be, amiben még nincs benne a FastLED. A program beolvassa, majd soros vonalon keresztül kilistázza a letárolt számokat. A 0 jelzi, hogy ez az utolsó adat, a programot itt megállítom, pontosabban végtelen ciklusba küldöm.


#include <avr/pgmspace.h>


const byte n[] PROGMEM=
{
    1, 2, 3, 4, 5, 0
};


int p= 0;
byte a;


void setup()
{ 
   Serial.begin( 9600);
   Serial.println( "Start");
}


void loop()
{ 
   a= pgm_read_byte( n+ p++);
   Serial.println( a);
   delay( 500);
   if( a == 0) while( 1);
}

A következő programot korábban már bemutattam. Most csupán a FastLED használatát szeretném felidézni vele. Én egy 24 LED-es körívvel kísérleteztem. Az alábbi program ezen futtat körbe egy kék fénypontot. A NUM_LEDS mutatja hány LED-ünk van, a DATA_PIN, pedig az határoza meg, azArduino panel melyik digit lábára kell csatlakoztassuk a LED sorunk DI lábát. Ne feledkezünk meg a FastLED könyvtár letöltéséről, a program anélkül nem fog lefordulni.


#include <FastLED.h>
#define NUM_LEDS 24
#define DATA_PIN 3

CRGB leds[ NUM_LEDS];

int i;

void setup() { 
   FastLED.addLeds<WS2812, DATA_PIN, GRB>( leds, NUM_LEDS);
}


void loop() { 
   if( i == 0) leds[ NUM_LEDS- 1]= CRGB::Black;
   else leds[ i- 1]= CRGB::Black;
   leds[ i]= CRGB::Blue; 
   FastLED.show();
   delay( 100);
   if( i < NUM_LEDS- 1) i++;
   else i= 0;
}

A következő program alkalmas a LED-ek állítására, programozására. Az utolsó program annyiban lesz több, hogy az egyes lépések/állapotok közötti átmenetet is támogatni fogja. Azért tettem be ezt a program verziót, mert ennek a logikája egyszerűbben követhető. A LED-ek állapotai a kotta nevű tömbben kerülnek letárolásra. Egy sor egy állapotot határoz meg. Az első byte (tartas - 1..255) megadja 0.1 sec-ben, hogy meddig világítson a LED. A második byte (valtas - 0..255), ebben a programban még nem kerül felhasználásra, de 0.1 secben azt fogja megadni, hogy ezt az állapotot mennyi idő alatt érje el. Azután LED-enként 3 byte (R- piros - 0..255, G- zöld - 0..255, B- kék - 0..255) határozza meg az 1. LED, a 2. LED... szinét. A 4. sorban az első byte-ba írt 0 jelzi a programnak, hogy véget ért a kotta, ugorjon az első sorra és majd ciklusan ezt ismételje. Oda kell figyelni, hogy a NUM_LEDS-ben megadott LED számnak (legfeljebb 255) megfelelő mennyiségű adatot tároljunk, mert a programnak nem tudja ellenőrizni ezt, és mindeféle hibás kombinációt fogunk kapni, ahogy megpróbálja értelmezni az Arduino egyéb memória tartalmát. Az alábbi kotta szerint első lépésben az első LED fog pirosan világítani 2 sec-ig, majd a második, végül a harmadik, majd az egész kezdődik előlről. Alább látható az áarmör, és a program.



/******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2019.05.27.
*   Chip         -  Arduino, WS2812 LED-ek
*   Compiler     -  Arduino IDE
*
*   WS2812 LED-ek vezerlese
*   
*******************************************************************************/
#include <FastLED.h>
#include <avr/pgmspace.h>


#define NUM_LEDS 6
#define DATA_PIN 3


// tart, valt, 1R,  1G,  1B,              2,              3,              4,              5,             6 

const byte kotta[] PROGMEM=
{
    20,  20,  255,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,
    20,  20,    0,   0,   0,  255,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,
    20,  20,    0,   0,   0,    0,   0,   0,  255,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,
     0
};


CRGB leds[ NUM_LEDS];
byte tartas= 0;
byte valtas= 0;
int p= 0;
byte i;


void setup()
{ 
   FastLED.addLeds<WS2812, DATA_PIN, GRB>( leds, NUM_LEDS);
}


void loop()
{ 
   tartas= pgm_read_byte( kotta+ p++);
   if( tartas)
   {   
      valtas= pgm_read_byte( kotta+ p++);
      for( i= 0; i < NUM_LEDS;)
      {
         leds[ i].red=   pgm_read_byte( kotta+ p++);
         leds[ i].green= pgm_read_byte( kotta+ p++);
         leds[ i].blue=  pgm_read_byte( kotta+ p++);
         i++;
      }
      FastLED.show();
      delay( tartas* 100);
   }
   else p= 0;
}

Lássuk a medvét, a végleges programot. Mondtam, hogy kicsit komplikáltabb. Az egyik probléma az volt, hogy tulajdonképpen a uP-nek elég sokat kell dolgoznia az átmeneti pillanatnyi állapotok kiszámolásához, kiemelten figyelemmel a szorzásra és az osztásra. Ezért úgy oldottam meg, hogy a kezdó, vagyis az aktuális állapotot, és es a következő állapotot jelentő szineket 8 bites byte változóból konvertáltam 16 bites int-be, hogy rendesen ki lehessen számolni változáshoz a lépésenkénti differenciát. És mivel differenciára, különbségre van szükségünk, ezért signed int-et (előjeles) kell használnunk, vagyi a legelső bit az előjel, így 15 bit áll a rendelkezésünkre. Biztos van aki nem ismeri, kettes számrendszerbe a balra léptetés 2-vel való szorzást jelent. A léptetést viszonylag nagyon gyorsan végre tudja a uP hajtani. Esetünkben az alábbi utasítás azt jelenti, hogy beolvassuk a következő 1 byte adatot, amit konvertálunk int-re (16 bit), és 7 bittel balra léptetjük, vagyis szorozzuk 128-cal.

(int)pgm_read_byte( kotta+ p++) << 7;

És azért nem 8 bittel léptetjük balra, mert akkor belefuthatunk az előjel bitjébe. Így tehát a váltás lépéseit összeadással, majd jobbra léptetéssel (osztással) a uP elég tempósan ki tudja számolni. Mivel tartottam a program korábbi verzióiban, hogy a uP megcsúszik nagy számú LED-nél a számolással, ezért nem a delay() várakozást használtam. A szinek tömbjeinek kiszámolását követően a time változóba letárolom a millis() fügvénnyel lekérdezett Arduino belső órájának idejét. A váltás időtartamában 0.1 sec.enként növeli a program time értékét, majd addig vár, amig a belső óra el nem éri ezt az időt. A végén egyszerűn hozáadja a tartás időtartamát, és megvárja míg letelik. Most jut eszembe, annyi baj van ezzel, hogy a time long (32 bit) változó, és lehet, hogy 72 naponként ez kiakad :(. A tartás előtt még egyszer vissza kellett a LED-ek állapotát írni, mert váltást= 0 esetében, nem íródnak be.

A LED-ek fekete állapotból indolnak ( setup(), Ez a kotta szerint a 2 sec alatt az első LED bepirosodik, 2 sec-ig így marad, majd 2 sec alatt elhalványul, miközben a második LED bezöldül, 2 sec-ig így marad, majd 2 sec alatt elhalványul, miközben a harmadik LED bekékül, 2 sec-ig így marad, majd 2 sec alatt elhalványul, miközben a negyedik LED kifehéredik, 2 sec-ig így marad, majd 2 sec alatt elhalványul, miközben az első LED kipirosodik...


/******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2019.05.27.
*   Chip         -  Arduino, WS2812 LED-ek
*   Compiler     -  Arduino IDE
*
*   WS2812 LED-ek vezerlese szinatmenettel
*   
*******************************************************************************/
#include <FastLED.h>
#include <avr/pgmspace.h>


#define NUM_LEDS 6
#define DATA_PIN 3


// tart, valt, 1R,  1G,  1B,              2,              3,              4,              5,             6 

const byte kotta[] PROGMEM=
{
    20,  20,  255,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,
    20,  20,    0,   0,   0,    0, 255,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,    0,   0,   0,
    20,  20,    0,   0,   0,    0,   0,   0,    0,   0, 255,    0,   0,   0,    0,   0,   0,    0,   0,   0,
    20,  20,    0,   0,   0,    0,   0,   0,    0,   0,   0,  255, 255, 255,    0,   0,   0,    0,   0,   0,


     0
};


CRGB leds[ NUM_LEDS];                            // LED-ek tombje FastLED-hez
byte tartas= 0;                                  // tartas idotartama 100 msec-ben 
byte valtas= 0;                                  // valtas idotartama 100 msec-ben 
int p= 0;                                        // mutato beolvasashoz
byte i;                                          // ciklusokhoz
unsigned long time;                              // idopont msec-ben

int nr[ NUM_LEDS], ng[ NUM_LEDS], nb[ NUM_LEDS]; // kovetkezo szin
int ar[ NUM_LEDS], ag[ NUM_LEDS], ab[ NUM_LEDS]; // aktualis szin
int dr[ NUM_LEDS], dg[ NUM_LEDS], db[ NUM_LEDS]; // lepesenkent szin differencia


void setup()
{ 
   FastLED.addLeds<WS2812, DATA_PIN, GRB>( leds, NUM_LEDS);
   i= NUM_LEDS;
   while( i--) leds[ i]= CRGB::Black;
}


void loop()
{ 
   tartas= pgm_read_byte( kotta+ p++);            // sor beolvasasa
   if( tartas == 0)                               // ha tabla vegere ert, ugras az elejere
   {
      p= 0;
      tartas= pgm_read_byte( kotta+ p++);
   }
   valtas= pgm_read_byte( kotta+ p++);
   for( i= 0; i < NUM_LEDS;)
   {
      ar[ i]= (int)leds[ i].red << 7;                 // aktualis szin
      ag[ i]= (int)leds[ i].green << 7; 
      ab[ i]= (int)leds[ i].blue << 7; 
      nr[ i]= (int)pgm_read_byte( kotta+ p++) << 7;   // kovetkezo szin
      ng[ i]= (int)pgm_read_byte( kotta+ p++) << 7;
      nb[ i]= (int)pgm_read_byte( kotta+ p++) << 7;
      dr[ i]= ( nr[ i]- ar[ i])/ valtas;              // lepes
      dg[ i]= ( ng[ i]- ag[ i])/ valtas;
      db[ i]= ( nb[ i]- ab[ i])/ valtas;
      i++;
   }
   time= millis();                               // ido indul
   while( valtas--)                              // valtas
   {
      time+= 100;                                // lepesenkent 100 msec 
      for( i= 0; i < NUM_LEDS;)
      {
         ar[ i]+= dr[ i];
         ag[ i]+= dg[ i];
         ab[ i]+= db[ i];
         leds[ i].red=   ar[ i] >> 7; 
         leds[ i].green= ag[ i] >> 7; 
         leds[ i].blue=  ab[ i] >> 7; 
         i++;
      }
      FastLED.show();
      while( millis() <= time);                
   }
   for( i= 0; i < NUM_LEDS;)                     // valtas = 0 esetere
   {
      leds[ i].red=   nr[ i] >> 7; 
      leds[ i].green= ng[ i] >> 7; 
      leds[ i].blue=  nb[ i] >> 7; 
      i++;
   }
   FastLED.show();
   time+= tartas* 100;                           // tartas
   while( millis() <= time);                
}

Itt a vége, fuss el véle.