Forum: Mikrocontroller und Digitale Elektronik Led effekte C


von LEDS (Gast)


Lesenswert?

Hallo,

ich bin auf der Suche für meinen WS2812B Strip nach Effekten geschrieben 
in C. Gibt es dort eine gute Quelle, wo einiges bereits zusammengetragen 
worden ist?

Interessant vorallem irgendwelche Komentenschweife etc.

von gernot (Gast)


Lesenswert?

LEDS schrieb:
> ich bin auf der Suche für meinen WS2812B Strip nach Effekten geschrieben
> in C. Gibt es dort eine gute Quelle, wo einiges bereits zusammengetragen
> worden ist?

Suche ich auch. Würdest du es bitte hier schreiben, wenn du was gefunden 
hast.

von Philipp H. (hansimglueck2)


Lesenswert?

Abo

von Bernhard (Gast)


Lesenswert?

In dem Beispiel für den PSoC sind einige Effekte drin:

http://www.cypress.com/forum/psoc-community-components/neopixel-ws281112-component

von LEDS (Gast)


Lesenswert?

Hallo,

ich habe mir gerade mal die FastLED Lib angeschaut. Da gibt es in 
DemoReel100 ein paar schöne Effekte. Vorallem der Komentenschweif 
(sinelon()). Kann mir einer sagen, wie man den ohne FastLed schreibt?

von Bernd G. (bege)


Lesenswert?

Schaut mal hier so ab Zeile 600:

https://github.com/charlie-x/psoc4-esp8266-ws1812/blob/master/code/PSOC%20Firmware/Bootloadable%20XMAS.cydsn/main.c

Ist zwar auch für PSOC4 von Cyress, läßt sich aber einfach portieren, da 
im Prinzip nur ein Speicherarray beschrieben wird. Die Ausgabe zum 
WS2812-Stripe ist in Hardware gelöst. (Der PSOC hat etwas 'frei' 
programmierbare Logik on Chip.)

von LEDS (Gast)


Lesenswert?

Danke für den Link,

ich muss mal schauen, ob sich etwas interessantes ableiten lässt.

Hab mich mal mit dem Kometenschweif auseinander gesetzt. Das klappt so 
ein bisschen schon.

width : gibt die länge des Schweifes an
*pos  : aktuelle Position der ersten Led
value : Max. Helligkeit
1
void sinelon_new(struct colorRGB *leds, uint8_t numLeds, uint8_t width, int16_t *pos, uint8_t value)
2
{
3
  /*
4
  * pos - 7    value / 7 * 0 >>>> setzt letzte Led auf 0
5
  * pos - 6    value / 7 * 1
6
  * pos - 5    value / 7 * 2
7
  * pos - 4    value / 7 * 3
8
  * pos - 3    value / 7 * 4
9
  * pos - 2    value / 7 * 5
10
  * pos - 1    value / 7 * 6
11
  * pos - 0    value / 7 * 7
12
  */
13
14
  uint8_t val;
15
  for(uint8_t led = 0; led < width; led++)
16
  {
17
    val = (value / (width-1)) * ((width-1)-led);
18
    leds[*pos - led].r = correction[val]; // gamma-correction
19
  }
20
  if(++(*pos) >= numLeds+width) *pos = 0;
21
}

Könnte mir jemand hierbei helfen, das ganze etwas besser aufzubauen? Ich 
habe hier die Befürchtung, dass es zu unerlaubten überläufen kommt, wenn 
in ein nicht vorhandenes Register geschrieben wird.

Es soll ein struct mit numLeds-Elementen beschrieben werden.
1. Runde/Start: Die erste Led kommt in der vollen Helligkeit.
2. Runde: Die erste Led wandert um +1 nach rechts. Die Nachfolgende Led 
soll mit value/7*8   (value/(width-1)) * ((width-1)-led) leuchten. usw.
Wenn die schwächste Led (in diesem Beispiel pos-7 am Ende von numLeds 
angekommen ist, soll nichts mehr zu sehen sein, ehe durch einen 
Tastendruck die erste Led wieder an der ersten Pos ankommt.

Hoffe, dass mir wer da etwas helfen kann.

von LEDS (Gast)


Lesenswert?

So, den ungewollten Überlauf schaff ich :((
1
void sinelon_new(struct colorRGB *leds, uint8_t numLeds, uint8_t width, int16_t *pos, uint8_t c, uint8_t value)
2
{
3
  /*
4
  * pos - 7    value / 7 * 0 >>>> setzt letzte Led auf 0
5
  * pos - 6    value / 7 * 1
6
  * pos - 5    value / 7 * 2
7
  * pos - 4    value / 7 * 3
8
  * pos - 3    value / 7 * 4
9
  * pos - 2    value / 7 * 5
10
  * pos - 1    value / 7 * 6
11
  * pos - 0    value / 7 * 7
12
  */
13
14
  for(uint8_t led = 0; led < width; led++)
15
  {
16
    uint8_t val = (value / (width-1)) * ((width-1)-led);
17
    
18
    if(*pos - led >= 0 && *pos - led < numLeds)
19
    {
20
      switch(c)
21
      {
22
        case 0: leds[*pos - led].r = correction[val]; break;
23
        case 1: leds[*pos - led].g = correction[val]; break;
24
        case 2: leds[*pos - led].b = correction[val]; break;
25
      }
26
    }
27
  }
28
  if(++(*pos) >= numLeds+width) *pos = 0;
29
}

Sobald der Effekt das 3x durchläuft blinken alle Leds nur noch doof!

Eingebunden ist das ganze wie folgt
1
#define NUM_LEDS    18
2
struct cRGB leds[NUM_LEDS];
3
4
//...
5
6
    while(nextStep > 0)
7
    {
8
      static int16_t pos_r = 0;
9
      static int16_t pos_g = 6;
10
      static int16_t pos_b = 12;
11
12
    //  sinelon_new(leds, NUM_LEDS0, 8, &pos_r,0, value);
13
    //  sinelon_new(leds, NUM_LEDS0, 8, &pos_g, 1,value);
14
      sinelon_new(leds, NUM_LEDS, 8, &pos_b, 2,value);
15
      ws2812_setleds(leds, NUM_LEDS);
16
      
17
18
      nextStep--;
19
    }
20
21
//..

von Markus M. (adrock)


Lesenswert?

Hi,

also ich würde da etwas anders rangehen.

Ich habe mir für sowas verschiedene kleine Routinen geschrieben, die 
jeweils nur eine einfache Operation auf die LEDs machen. Diese kann man 
dann ggf. kombinieren.

Für Deinen "Kometenschweif" würde ich es so machen:

- Funktion scroll_right()

Schiebt den Inhalt des LED-Feldes eine Position nach rechts.
1
void scroll_right(ctruct colorRGB *leds, int numleds) {
2
   int i;
3
4
   for(i = numleds-1; i > 0; i--) {
5
      leds[i] = leds[i-1];
6
   }
7
}


- Funktion schweif_next()
1
void schweif_next(struct colorRGB *leds, int numleds, uint8_t *brightness, uint8_t step) {
2
   scroll_right(leds, numleds);
3
   leds[0].r = correction(*brightness);
4
   if(*brightness >= step) {
5
      (*brightness) -= step;
6
   }
7
   else {
8
      *brightness = 0;
9
   }
10
}

- Hauptprogramm:
1
#define MAXBRIGHT 100
2
#define BRIGHTSTEP 10
3
#define NUMLEDS 20
4
5
uint8_t brightness = MAXBRIGHT;
6
struct colorRGB leds[NUMLEDS];
7
8
while(1) {
9
   schweif_next(leds, NUMLEDS, &brightness, BRIGHTSTEP);
10
   ws2812_ausgaberoutine(leds, NUMLEDS);
11
   verzoegerung();
12
   if(check_taster()) {
13
      brightness = MAXBRIGHT;
14
   }
15
}

Es werden also immer die LEDs nach rechts weiter "gescrollt", und der 
wert für die jeweils erste LED ganz links beginnend von der max. 
Helligkeit immer weiter reduziert.

Das ganze kommt sogar ohne Multiplikation / Division aus und läuft 
dadurch sehr schnell.

: Bearbeitet durch User
von Stefan S. (sschultewolter)


Lesenswert?

Hallo Markus,

deine Idee für den Fade finde ich Klasse. Habe mich mal rangesetzt, und 
den Code etwas angepasst, soweit da nichts gegen spricht. ;)

Vielleicht kann es dem TE und anderen helfen.
1
//..
2
3
#define RED    0
4
#define  GREEN  1
5
#define BLUE  2
6
7
/* Funktion    :  leds_comettail_finished()
8
* Beschreibung  :  Prueft, ob colorRGB wieder geleert ist
9
*/
10
uint8_t leds_comettail_finished (struct colorRGB *leds, uint16_t numLeds, uint8_t color)
11
{
12
  for (uint16_t i = 0; i < numLeds; i++)
13
  {
14
    if (color == RED)        { if(leds[i].r > 0) return 0; }
15
    else if (color == GREEN)    { if(leds[i].g > 0) return 0; }
16
    else /*(color == BLUE)*/    { if(leds[i].b > 0) return 0; }
17
  }
18
  return 1;
19
}
20
21
/* Funktion    :  scroll_left()
22
* Beschreibung  :  Schiebt den Inhalt eines Structs nach links
23
*/
24
void scroll_left (struct colorRGB *leds, uint16_t numLeds, uint8_t color)
25
{
26
  /*
27
  * i = 0 -> leds[0] = leds[1]
28
  *  i = 1 -> leds[1] = leds[2]
29
  *  i = 2 -> leds[2] = leds[3]
30
  *  i = n -> leds[n] = leds[n+1]
31
  */
32
  for (uint16_t i = 0; i < numLeds-1; i++)
33
  {
34
    if (color == RED)      leds[i].r = leds[i+1].r;
35
    else if (color == GREEN)  leds[i].g = leds[i+1].g;
36
    else /*if (color == BLUE)*/  leds[i].b = leds[i+1].b;
37
  }
38
}
39
40
/* Funktion    :  scroll_right()
41
* Beschreibung :  Schiebt den Inhalt eines Structs nach rechts
42
*/
43
void scroll_right (struct colorRGB *leds, uint16_t numLeds, uint8_t color)
44
{
45
  /*
46
  * i = n -> leds[n] = leds[n-1]
47
  * i = 3 -> leds[3] = leds[2]
48
  * i = 2 -> leds[2] = leds[1]
49
  * i = 1 -> leds[1] = leds[0]
50
  */
51
  for(uint16_t i = numLeds-1; i > 0; i--)
52
  {
53
    if(color == RED)      leds[i].r = leds[i-1].r;
54
    else if(color == GREEN)    leds[i].g = leds[i-1].g;
55
    else /*if (color == BLUE)*/  leds[i].b = leds[i-1].b;
56
  }
57
}
58
59
/* Funktion    :  leds_comettail_left()
60
* Beschreibung :  Kometenschweif von links nach rechts
61
*/
62
void leds_comettail_left (struct colorRGB *leds, uint16_t numLeds, uint8_t color, uint8_t *value, uint8_t step)
63
{
64
  scroll_left (leds, numLeds, color);
65
  if (color == RED)      leds[numLeds-1].r = correction[*value];
66
  else if (color == GREEN)  leds[numLeds-1].g = correction[*value];
67
  else /*if (color == BLUE)*/  leds[numLeds-1].b = correction[*value];
68
  
69
  if (*value >= step)      (*value) -= step;
70
  else            *value = 0;
71
}
72
73
/* Funktion    :  leds_comettail_right()
74
* Beschreibung :  Kometenschweif von rechts nach links
75
*/
76
void leds_comettail_right(struct colorRGB *leds, uint16_t numLeds, uint8_t color, uint8_t *value, uint8_t step)
77
{
78
  scroll_right (leds, numLeds, color);
79
  if (color == RED)      leds[0].r = correction[*value];
80
  else if (color == GREEN)  leds[0].g = correction[*value];
81
  else /*if (color == BLUE)*/  leds[0].b = correction[*value];
82
  
83
  if (*value >= step)      (*value) -= step;
84
  else            *value = 0;
85
}
86
//..

1
//..
2
  while(1)
3
  {
4
    static uint8_t value = 255;
5
    static uint16_t lastTicks = 0;    
6
    if(ticks - lastTicks >= 5) // ticks * 10ms
7
    {
8
      lastTicks = ticks;
9
      static uint8_t val_r = 255;
10
      static uint8_t state_r = 0;
11
      if(state_r) leds_comettail_left(leds, NUM_LEDS, RED, &val_r, 5);
12
      else leds_comettail_right(leds, NUM_LEDS, RED, &val_b, 25);
13
      if(leds_comettail_finished(leds, NUM_LEDS, RED)) { val_r = value; state_r ^= 1; }
14
15
      static uint8_t val_g = 255;
16
      static uint8_t state_g = 1;
17
      if(state_g) leds_comettail_left(leds, NUM_LEDS, GREEN, &val_g, 15);
18
      else leds_comettail_right(leds, NUM_LEDS, GREEN, &val_g, 15);
19
      if(leds_comettail_finished(leds, NUM_LEDS, GREEN)) { val_g = value; state_g ^= 1; }
20
      
21
      static uint8_t val_b = 0;
22
      static uint8_t state_b = 0;
23
      if(state_b) leds_comettail_left(leds, NUM_LEDS, BLUE, &val_b, 25);
24
      else leds_comettail_right(leds, NUM_LEDS, BLUE, &val_b, 5);
25
      if(leds_comettail_finished(leds, NUM_LEDS, BLUE)) { val_b = value; state_b ^= 1; }
26
      
27
      ws2812_setleds(leds, NUM_LEDS);
28
    }
29
  }
30
//..

von Markus M. (adrock)


Lesenswert?

Ja, danke, sehr schön :)

Der Charme der einzelnen Funktionen ist es eben, dass man sie auch für 
andere Effekte benutzen/kombinieren kann.

Ich hatte das ganze bei einer LED-Matrix mal so weit getrieben, dass ich 
am Ende den gesamten Ablauf über Tabellen bzw. Strukturen steuern 
konnte.

Man konnte "Szenen" definieren, die aus einer Generatorfunktion und 
beliebeig vielen nachgeschalteten Effektfunktionen bestanden, die dann 
die Ausgabe der Generatorfunktion noch weiter verändert haben.

So konnte man durch geschickte Kombination interessante Effekte 
generieren. Z.B. ein Muster nur in 1/4 der Matrix generieren lassen und 
dann in die drei anderen Quadranten spiegeln, faden etc.

: Bearbeitet durch User
von Stefan S. (sschultewolter)


Lesenswert?

Ja,

das sehe ich auch so Markus. Ich nutze selber hauptsächlich eine 
angepasste Version der LIGHT WS2812B Lib. Vorteil ist, hier ist so gut 
wie nichts bereits vorhanden. Es beschränkt sich auf die eigentliche 
Ansteuerung.

Kenne die FastLed Lib. Dort sind einige nette Effekte drin. Auch ist die 
Farbanpassung sehr gut gelungen, aber bei den meisten Effekten hat man 
dann nur noch wenig Möglichkeiten, großartig einzugreifen.

Ich bin dabei, davon die ein oder anderen Math-Operation zu portieren.

von Karl H. (kbuchegg)


Lesenswert?

Aber auch im geposteten Original lassen sich ein paar Dinge lernen
1
    uint8_t val = (value / (width-1)) * ((width-1)-led);
das willst du nicht so berechnen.
Du willst in derartigen Berechnungen die Division immer ganz zum Schluss 
machen.
Denn 12 / 7 ergibt 1. Und zwar 1 geradeaus!
Das ist eine Division Integer durch Integer. Und damit ist das Ergebnis 
ebenfalls ein Integer. Keine Kommastellen!
1
void sinelon_new(struct colorRGB *leds, uint8_t numLeds, uint8_t width, int16_t *pos, uint8_t c, uint8_t value)

ich sehe jetzt ehrlich gesagt nicht, wozu pos ein Pointer sein muss. Das 
weitersetzen des Effekts an die nächste Position kann der Aufrufer 
genausogut machen und da er die Länge einer Led Zeile kennt, kann er 
auch den Überlauf entsprechend handhaben, so einer eintreten sollte.

warum sollte ich das für jede Farbe getrennt aufrufen?
Speziell wenn die Funktion dann eigenmächtig pos weiterzählt ist damit 
faktisch schon fast garantiert, dass ich bei 3-fach notwendigem Aufruf 
der Funktion jede Farbe an eine andere Stelle bekomme.

Die Funktion hat die Aufgabe die Farbwerte der Led-Zeile aufgrund des zu 
implementierenden Effektes und der Vorgabe einer Farbe zu füllen. Dann 
gibt der Funktion aber auch die Farbe als ganzes. Wenn eine Farbe 
mittels einer struct colorRGB beschrieben wird, dann übergib auch genau 
so ein Ding um die Farbe in ihrer Gesamtheit zu beschreiben. Dann ist 
dann auch dieser Nonsense
1
      switch(c)
2
      {
3
        case 0: leds[*pos - led].r = correction[val]; break;
4
        case 1: leds[*pos - led].g = correction[val]; break;
5
        case 2: leds[*pos - led].b = correction[val]; break;
6
      }
nicht mehr nötig.

Wenn schon Farbe, dann auch wirklich Farbe:
1
void sinelon_new(struct colorRGB *leds, uint8_t numLeds, uint8_t width, int16_t pos, struct colorRGB baseColor )

oder
1
void sinelon_new(struct colorRGB *leds, uint8_t numLeds, uint8_t width, int16_t pos, struct colorRGB *baseColor )
(Der Unterschied ist nur, dass das eine mal ein Farb-Objekt direkt 
übergben wird, das andere mal ein Pointer darauf)

Dann muss eben die Funktion in jedem Schritt jeweils die Werte für Rot, 
Grün und Blau berechnen.

Jetzt erst mal davon abgesehen, dass so ein Schleifenkonstrukt beim 
Aufrufer nicht unbedingt eine gute Idee ist, spricht ja erst mal nichts 
gegen
1
    struct cometColor = { 0, 6, 12 };
2
3
    ...
4
    for( i = 0; i < NUM_LEDS0 + 8; i++ )
5
    {
6
      sinelon_new( leds, NUM_LEDS, 8, i, cometColor );
7
      ws2812_setleds( leds, NUM_LEDS );
8
    }

Dann klappts auch mit dem Effekt.

(Wobei natürlich so ein Basisbaukasten, aus dem man dann weitere Dinge 
zusammensetzt, schon eine ganz andere Liga darstellt)

: Bearbeitet durch User
von Ben W. (ben_w)


Lesenswert?

ich hatte mich mal an einer kleinen "animations Engine" wenn man das 
überhaupt so nennen kann in C++ versucht.
(https://github.com/benwilliam/equinox_clock/tree/master/WS2812b_SPI/ws2811_2/graphics)

von Stefan S. (sschultewolter)


Lesenswert?

Hier noch einmal ein alternativer Ansatz. Die Funktionen sind von 
einander getrennt, da es hier doch eher nur noch wenige Stellen 
identisch sind.


1
void leds_comettail(struct cRGB *ledarray, const int16_t numLeds, int16_t offset, uint8_t color, int16_t *posLed, uint8_t *revLeds)
2
{
3
  #define DIM_CORRECTION(x)  dim_curve[x]
4
  
5
  const int16_t WIDTH = numLeds/2;
6
  const uint8_t VALUE_MAX = 127;
7
  const uint8_t SHOW_LAST_DOT = 0;
8
  const uint8_t WAIT_FOR_NEW_RUN = 1;
9
  
10
  int16_t maxPos = numLeds - 1;
11
  int16_t minPos = 0;
12
  
13
  // Limits setzen
14
  if (WAIT_FOR_NEW_RUN)
15
  {
16
    maxPos = maxPos + (WIDTH + 1);
17
    minPos = minPos - (WIDTH + 1);
18
  }
19
  
20
  // Richtungsaenderung abfragen
21
  if (*posLed >= maxPos)
22
  {
23
    *posLed = maxPos;
24
    *revLeds = 1;
25
  }
26
  else if (*posLed <= minPos)
27
  {
28
    *posLed = minPos;
29
    *revLeds = 0;
30
  }
31
  
32
  int16_t pos = *posLed + offset;
33
  uint8_t value;
34
  
35
  // Nachlaufende Leds
36
  if (*revLeds)
37
  {
38
    for (uint8_t i = 0; i <= WIDTH; i++)
39
    {
40
      if(*posLed + i < numLeds && *posLed + i >= 0)
41
      {
42
        value = (WIDTH - i) * (VALUE_MAX / WIDTH);
43
        ledarray[pos + i].raw[color] = DIM_CORRECTION(value);
44
      }
45
    }
46
  }
47
  else
48
  {
49
    for (uint8_t i = 0; i <= WIDTH; i++)
50
    {
51
      if (*posLed - i >= 0 && *posLed - i < numLeds)
52
      {
53
        value = (WIDTH - i) * (VALUE_MAX / WIDTH);
54
        ledarray[pos - i].raw[color] = DIM_CORRECTION(value);
55
      }
56
    }
57
  }
58
  
59
  // Aktuelle Led
60
  if (*posLed >= 0 && *posLed < numLeds-1) ledarray[pos].raw[color] = DIM_CORRECTION(VALUE_MAX);
61
  if (SHOW_LAST_DOT)
62
  {
63
    if (*posLed < 0) ledarray[0 + offset].raw[color] = DIM_CORRECTION(VALUE_MAX);
64
    else if (*posLed > numLeds-1) ledarray[(numLeds - 1) + offset].raw[color] = DIM_CORRECTION(VALUE_MAX);
65
  }
66
  
67
  // Positionberechnung fuer naechsten Durchlauf
68
  if(*revLeds)  --(*posLed);
69
  else      ++(*posLed);
70
}


1
void leds_knightrider (struct cRGB *ledarray, int16_t numLeds, int16_t offset, uint8_t color, int16_t *posLed, uint8_t *revLeds)
2
{
3
  #define DIM_CORRECTION(x)  dim_curve[x]
4
  
5
  const int16_t WIDTH = numLeds/3;
6
  const uint8_t VALUE_MAX = 255;
7
8
  // Richtungsaenderung abfragen
9
  if (*posLed >= numLeds-1)
10
  {
11
    *posLed = numLeds-1;
12
    *revLeds = 1;
13
  }
14
  else if (*posLed <= 0)
15
  {
16
    *posLed = 0;
17
    *revLeds = 0;
18
  }
19
20
  int16_t pos = *posLed + offset;
21
  uint8_t value;
22
  
23
  // Leds links der aktuellen Led
24
  for(uint8_t i = 0; i <= WIDTH; i++)
25
  {
26
    value = (WIDTH - i) * (VALUE_MAX / WIDTH);
27
    if (*posLed - i >= 0) ledarray[pos - i].raw[color] = DIM_CORRECTION(value);
28
  }
29
  
30
  // Aktuelle Led
31
  ledarray[pos].raw[color] = DIM_CORRECTION(VALUE_MAX);
32
  
33
  // Leds rechts der aktuellen Led
34
  for(uint8_t i = 0; i <= WIDTH; i++)
35
  {
36
    value = (WIDTH - i) * (VALUE_MAX / WIDTH);
37
    if (*posLed + i < numLeds) ledarray[pos + i].raw[color] = DIM_CORRECTION(value);
38
  }
39
  
40
  // Positionberechnung fuer naechsten Durchlauf
41
  if (*revLeds)  --(*posLed);
42
  else      ++(*posLed);
43
}

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.