MagicLed - adresovatelný LED pásek, Arduino, multitasking

30.08.2017 Arduino #arduino #led #multitasking

Adresovatelný LED pásek ovládáný pomocí arduina v režimu multitasking.


V článku Magic Led - základní příklady, je uveden základní postup jak programovat LED pásky s programovatelným čipem. Vše funguje jak má, ale pokud se má použít více pásek, tak nastává problém s tím, aby animace LED pásek opravdu probíhali v plynulé sekvenci. Arduino program probíhá v neustálé smyčce LOOP a tím již ze samotné podstaty nelze zajisti to, aby více pásek běželi paralelně a dochází k neustálému přepínání a animace nedoběhnou dokonce. Existuje, ale alternativa, která dokáže vytvořit virtuální multitasking, který lze přiblížit jako rychlé přepínání (řádově milisekundy) mezi fragmenty kódů, které zajišťují animace pro každou pásku. Tím se animace všech pásek jeví plynulé a tváří se jako v paralelním procesu.

schema_fritzing_led

Na uvedeném schématu je znázorněné zapojení s napájením pomocí LiPo baterie. Vzhledem k tomu, že pásky pracují s napětím 5V, a baterie má 11.1V, jsou do obvodu přidány regulátory, které zajistí na svém výstupu stabilizované napětí požadovaných 5V a tím nedojde ke zničení LED pásek. Řízení pásek pásek je pak zajištěno datovým vstupem na pinech 10, 11, 12.

Sketch

#include <Adafruit_NeoPixel.h>

// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum  direction { FORWARD, REVERSE };
 
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
    public:
 
    // Member Variables:  
    pattern  ActivePattern;  // which pattern is running
    direction Direction;     // direction to run the pattern
    
    unsigned long Interval;   // milliseconds between updates
    unsigned long lastUpdate; // last update of position
    
    uint32_t Color1, Color2;  // What colors are in use
    uint16_t TotalSteps;  // total number of steps in the pattern
    uint16_t Index;  // current step within the pattern
    
    void (*OnComplete)();  // Callback on completion of pattern
    
    // Constructor - calls base-class constructor to initialize strip
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
    :Adafruit_NeoPixel(pixels, pin, type)
    {
        OnComplete = callback;
    }
    
    // Update the pattern
    void Update()
    {
        if((millis() - lastUpdate) > Interval) // time to update
        {
            lastUpdate = millis();
            switch(ActivePattern)
            {
                case RAINBOW_CYCLE:
                    RainbowCycleUpdate();
                    break;
                case THEATER_CHASE:
                    TheaterChaseUpdate();
                    break;
                case COLOR_WIPE:
                    ColorWipeUpdate();
                    break;
                case SCANNER:
                    ScannerUpdate();
                    break;
                case FADE:
                    FadeUpdate();
                    break;
                default:
                    break;
            }
        }
    }
  
    // Increment the Index and reset at the end
    void Increment()
    {
        if (Direction == FORWARD)
        {
           Index++
           if (Index >= TotalSteps)
            {
                Index = 0;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
        else // Direction == REVERSE
        {
            --Index;
            if (Index <= 0)
            {
                Index = TotalSteps-1;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
    }
    
    // Reverse pattern direction
    void Reverse()
    {
        if (Direction == FORWARD)
        {
            Direction = REVERSE;
            Index = TotalSteps-1;
        }
        else
        {
            Direction = FORWARD;
            Index = 0;
        }
    }
    
    // Initialize for a RainbowCycle
    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = RAINBOW_CYCLE;
        Interval = interval;
        TotalSteps = 255;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
        }
        show();
        Increment();
    }
 
    // Initialize for a Theater Chase
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = THEATER_CHASE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
   }
    
    // Update the Theater Chase Pattern
    void TheaterChaseUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            if ((i + Index) % 3 == 0)
            {
                setPixelColor(i, Color1);
            }
            else
            {
                setPixelColor(i, Color2);
            }
        }
        show();
        Increment();
    }
 
    // Initialize for a ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = COLOR_WIPE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Color Wipe Pattern
    void ColorWipeUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }
    
    // Initialize for a SCANNNER
    void Scanner(uint32_t color1, uint8_t interval)
    {
        ActivePattern = SCANNER;
        Interval = interval;
        TotalSteps = (numPixels() - 1) * 2;
        Color1 = color1;
        Index = 0;
    }
 
    // Update the Scanner Pattern
    void ScannerUpdate()
    { 
        for (int i = 0; i < numPixels(); i++)
        {
            if (i == Index)  // Scan Pixel to the right
            {
                 setPixelColor(i, Color1);
            }
            else if (i == TotalSteps - Index) // Scan Pixel to the left
            {
                 setPixelColor(i, Color1);
            }
            else // Fading tail
            {
                 setPixelColor(i, DimColor(getPixelColor(i)));
            }
        }
        show();
        Increment();
    }
    
    // Initialize for a Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = FADE;
        Interval = interval;
        TotalSteps = steps;
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Fade Pattern
    void FadeUpdate()
    {
        // Calculate linear interpolation between Color1 and Color2
        // Optimise order of operations to minimize truncation error
        uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
        uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
        uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
        
        ColorSet(Color(red, green, blue));
        show();
        Increment();
    }
   
    // Calculate 50% dimmed version of a color (used by ScannerUpdate)
    uint32_t DimColor(uint32_t color)
    {
        // Shift R, G and B components one bit to the right
        uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
        return dimColor;
    }
 
    // Set all pixels to a color (synchronously)
    void ColorSet(uint32_t color)
    {
        for (int i = 0; i < numPixels(); i++)
        {
            setPixelColor(i, color);
        }
        show();
    }
 
    // Returns the Red component of a 32-bit color
    uint8_t Red(uint32_t color)
    {
        return (color >> 16) & 0xFF;
    }
 
    // Returns the Green component of a 32-bit color
    uint8_t Green(uint32_t color)
    {
        return (color >> 8) & 0xFF;
    }
 
    // Returns the Blue component of a 32-bit color
    uint8_t Blue(uint32_t color)
    {
        return color & 0xFF;
    }
    
    // Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos)
    {
        WheelPos = 255 - WheelPos;
        if(WheelPos < 85)
        {
            return Color(255 - WheelPos * 3, 0, WheelPos * 3);
        }
        else if(WheelPos < 170)
        {
            WheelPos -= 85;
            return Color(0, WheelPos * 3, 255 - WheelPos * 3);
        }
        else
        {
            WheelPos -= 170;
            return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
        }
    }
};

void StickComplete(); 
void Stick1Complete();
void Stick2Complete();

 
// Define some NeoPatterns for the two rings and the stick
//  as well as some completion routines
NeoPatterns Stick(20, 12, NEO_GRB + NEO_KHZ800, &StickComplete);
NeoPatterns Stick1(20, 10, NEO_GRB + NEO_KHZ800, &Stick1Complete);
NeoPatterns Stick2(20, 11, NEO_GRB + NEO_KHZ800, &Stick2Complete);
 
// Initialize everything and prepare to start
void setup()
{
  Serial.begin(9600);
    
    // Initialize all the pixelStrips
    Stick.begin();
    Stick1.begin();
    Stick2.begin();
   
    
    // Kick off a pattern
    Stick.Scanner(Stick.Color(255,0,0), 55);
    //Stick.ColorWipe(Stick.Color(255,0,0), 55);
    //Stick.Fade(Stick.Color(255,255,0), Stick.Color(0,0,50), 50, 100);
    Stick1.TheaterChase(Stick1.Color(255,255,0), Stick1.Color(0,0,50), 100);
    Stick2.RainbowCycle(3);
    Stick2.Color1 = Stick1.Color1;
   
}
 
// Main loop
void loop()
{
    // Update the rings.
    Stick.Update();
    Stick1.Update();
    Stick2.Update();    
    
    
        // Restore all pattern parameters to normal values

        Stick1.ActivePattern = THEATER_CHASE;
        Stick1.Interval = 100;
        Stick2.ActivePattern = RAINBOW_CYCLE;
        Stick2.TotalSteps = 255;
        Stick2.Interval = min(10, Stick2.Interval);

        
      
}
 
//------------------------------------------------------------
//Completion Routines - get called on completion of a pattern
//------------------------------------------------------------
// Stick Completion Callback
void StickComplete()
{
    // Random color change for next scan
    Stick.Color1 = Stick.Wheel(random(255));
    
}

// Stick1 Completion Callback
void Stick1Complete()
{
    
      Stick1.Reverse();
    
}
 
// Ring 2 Completion Callback
void Stick2Complete()
{
    
        Stick2.RainbowCycle(random(0,10));
    
}
 

Ke stažení

multitasking