WS2812B: Ideen für Gruppierung

Hallo,

ich nutze die light_ws2812 (AVR) Libary von cpldcpu (GitHub - cpldcpu/light_ws2812: Light weight library to control WS2811/WS2812 based LEDS and LED Strings for 8-Bit AVR microcontrollers.) für die Ansteuerung der WS2812B.

Mir war die FastLed für die Ansteuerung mit einem Attiny etwas zu mächtig. Viele Funktionen sind für mich nicht nötig, und belegen unnötig Speicher. Daraufhin habe ich die light_ws2812 hingegen soweit aufgebohrt, dass diese auch in der AVR Variante nun die RGB nach HSV Berechnung durchführt.

/*
* bottlelight.c
*
* Created: 03.11.2015 20:58:32
* Author : sschultewolter
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include "ws2812.h"

Nun geht es darum, wie ich die Leds am besten Gruppierung kann, ohne viel Speicher dafür zur belegen. Die Gruppierung soll zur Laufzeit geschehen. Da ich bei der Beleuchtung die Gruppierung umschalten möchte für einzelne Effekte. Die Anordnung der Leds ist hier zu sehen.

/*
* 01 02 03|10 11 12|19 20 21|28 29 30|..
* 04 05 06|13 14 15|22 23 24|31 32 33|..
* 07 08 09|16 17 18|25 26 27|34 35 36|..
*/

#define NUM_PANELS 2
#define NUM_COLS_EACH_PANEL 3
#define NUM_ROWS_EACH_PANEL 3
#define NUM_LEDS_EACH_PANEL (NUM_COLS_EACH_PANEL*NUM_ROWS_EACH_PANEL)
#define NUM_LEDS (NUM_PANELS * NUM_LEDS_EACH_PANEL)

#define GROUP_SINGLE 0
#define GROUP_COLS 1
#define GROUP_ROWS 2
#define GROUP_PANEL 3

struct cRGB leds[NUM_LEDS];


volatile uint32_t ms;
ISR(TIMER0_COMPA_vect) { ms++; }
uint32_t millis(void) { return ms; }

Derzeit mache ich die Umrechnung der Gruppierung vermutlich etwas unnötig kompliziert. Zumal das ganze auch nicht so flexibel ist, wie ich mir das erhoffe.

void ws2812_hsv_group(struct cRGB *ledarray, uint8_t led, uint8_t groupMode, uint16_t hue, uint8_t sat, uint8_t val)
{
 switch(groupMode)
 {
 case GROUP_SINGLE:
 ws2812_hsv(ledarray, led, hue, sat, val);
 break;
 
 case GROUP_COLS: // y = y1 + ... + yn
 for(uint8_t i = 0; i < NUM_ROWS_EACH_PANEL; i++)
 {
 ws2812_hsv(ledarray, ((led / NUM_COLS_EACH_PANEL)*NUM_LEDS_EACH_PANEL)+ ((led % NUM_COLS_EACH_PANEL) + (i*NUM_ROWS_EACH_PANEL)), hue, sat, val);
 }
 break;
 
 case GROUP_ROWS: // x = x1 + ... + xn
 for(uint8_t j = 0; j < NUM_PANELS; j++)
 {
 for(uint8_t i = 0; i < NUM_COLS_EACH_PANEL; i++)
 {
 ws2812_hsv(ledarray, (j*NUM_LEDS_EACH_PANEL)+(led*NUM_COLS_EACH_PANEL)+i, hue, sat, val);
 }
 }
 break;

 case GROUP_PANEL: // xy = xy1 + ... xyn
 for(uint8_t i = 0; i < NUM_LEDS_EACH_PANEL; i++)
 {
 ws2812_hsv(ledarray, (led * NUM_LEDS_EACH_PANEL) + i, hue, sat, val);
 }
 break;
 }
}

void rainbow(struct cRGB *ledarray, uint8_t numLeds, uint8_t groupMode, uint8_t width, uint8_t sat, uint8_t val)
{
 uint16_t hue = 0;
 static uint16_t shift = 0;

 for(uint8_t led = 0; led < numLeds; led++)
 {
 hue = ((led * 360 / width) + shift) % 360;
 ws2812_hsv_group(leds, led, groupMode, hue, sat, val);
 }
 shift++;
}

int main(void)
{
 TCCR0A = (1<<WGM01); // CTC Mode
 TCCR0B |= (1<<CS01) | (1<<CS00); // Prescaler 64
 OCR0A = 249; // ((16000000 / 64) / 1000) - 1 = 249
 
 TIMSK0 |= (1<<OCIE0A);
 sei();
 
 ws2812_setleds(leds, NUM_LEDS);
 
 while(1)
 {
 static uint32_t lastMillis = 0;
 static uint16_t i = 0;
 static uint8_t j = 0;
 if(millis() - lastMillis >= 10)
 {
 lastMillis = millis();

 const uint8_t width = 6;
 const uint8_t saturation = 255;
 const uint8_t value = 127;
 
 uint8_t numLeds = 0;
 switch(j)
 {
 case GROUP_SINGLE: numLeds = NUM_LEDS; break;
 case GROUP_COLS: numLeds = NUM_PANELS*NUM_COLS_EACH_PANEL; break;
 case GROUP_ROWS: numLeds = NUM_ROWS_EACH_PANEL; break;
 case GROUP_PANEL:  numLeds = NUM_PANELS; break;
 }
 rainbow(leds, numLeds, j, width, saturation, value);

 ws2812_setleds(leds, NUM_LEDS);
 
 if(++i >= 500) {
 i = 0;
 if(++j >= 4) j = 0;
 }
 }
 }
}

Nun suche ich nach einer Idee, wie ich das am besten lösen kann, ohne die Animationen selber zu beeinflussen.

Erste Idee, welche hier noch nicht eingebaut wurde, wäre es, einen Wert zu hinterlegen, in dem numLeds und die Gruppierung hinterlegt ist.

So,

nun der erste Ansatz, wie ich mir das in etwas vorgestellt habe:

void rainbow(struct cRGB *ledarray, uint8_t width, uint8_t sat, uint8_t val)
{
	uint8_t numLeds = sizeof(ledarray);
	uint16_t hue = 0;
	static uint16_t shift = 0;
	
	for(uint8_t led = 0; led < numLeds; led++)
	{
		hue = ((led * 360 / width) + shift) % 360;
		ws2812_hsv(ledarray, led, hue, sat, val);
	}
	shift++;
}
void ws2812_groupPanel(struct cRGB *destination, struct cRGB *source)
{
	for(uint8_t i = 0; i < sizeof(source); i++)
	{
		for(uint8_t j = 0; j < NUM_LEDS_EACH_PANEL; j++)
		{
			destination[(i*NUM_LEDS_EACH_PANEL)+j] = source[i];
		}
	}
}
	while(1)
	{
		static uint32_t lastMillis = 0;
		if(millis() - lastMillis >= 10)
		{
			lastMillis = millis();

			const uint8_t width = 6;
			const uint8_t saturation = 255;
			const uint8_t value = 127;

			rainbow(ledPanels, width, saturation, value);
			ws2812_groupPanel(leds, ledPanels);
			ws2812_setleds(leds, NUM_LEDS);
		}
	}

Verstehe ich Recht: Du willst zur Laufzeit eine 3x3 Matrix auf die LEDs 1-9 oder 10-18 oder 19-27 usw. mappen?

Lösung dafür: Einfach einen Offset zur Nummer der ersten LED addieren?

Oder verstehe ich Dein Problem noch nicht?

Gruß,

Helmuth

Hallo Helmuth,

den Ansatz meines "Problems" hast du bereits verstanden. Ich versuche es noch etwas auführlicher zu erklären.

Für ein kleineres Beleuchtungsprojekt habe ich mir mehrere Module erstellt (noch nicht an den Fabrikant übermittelt).

Dabei besteht jede 5x5cm Platine über 3x3 WS2812B Leds. Die Durchnummerierung ist wie im Code Teil angegeben.

* 01 02 03|10 11 12|19 20 21|28 29 30|..
* 04 05 06|13 14 15|22 23 24|31 32 33|..
* 07 08 09|16 17 18|25 26 27|34 35 36|..

Die Beleuchtung mit nur 1 Led pro Platine war nicht ausreichend hell. Bei 3x3 ist dieses mehr als ausreichend gedeckt.

Nun habe ich aber verschiedene Effekte die ich mit einbauen möchte. Wenn einzelne Segmente aufleuchten sollen, reicht es, wenn ich mit dem Offset arbeite. Nun gibt es aber auch Effekte, bei denen ich die Spalten pro Platine noch einmal einzeln ansteuern möchte um weichere Überläufe zu generieren.

Habe oben bereits eine Lösung geschildert, die mir soweit schon recht gut gefällt und sich mit der Speicherauslastung in Grenzen hält.

Also ich würde das einmal in einer 3x3 Matrix rechnen.

Dieses XY Mapping ist ganz praktisch - je nach Bedarf bzw. physikalischem Layout kannst Du das auch noch kürzen:

uint16_t XY( uint8_t x, uint8_t y)
{
  uint16_t i;

  if( kMatrixSerpentineLayout == false) {
    i = (y * kMatrixWidth) + x;
  }

  if( kMatrixSerpentineLayout == true) {
    if( y & 0x01) {
      // Odd rows run backwards
      uint8_t reverseX = (kMatrixWidth - 1) - x;
      i = (y * kMatrixWidth) + reverseX;
    } else {
      // Even rows run forwards
      i = (y * kMatrixWidth) + x;
    }
  }

  return i;
}

Wenn Du das hast, kopierst Du es an die Stelle in Deinem CRGB, an der Du es brauchst - also mit Offset 0, 9, 18,...

Wenn Du es in mehreren 9er Segmenten sehen willst, kopierst Du es auch dorthin.

Unabhängig davon kannst Du trotzdem direkt in das gesamte CRGB "reinfassen" und dort Linien oder sonstwas zeichnen.

Ich verstehe das Problem immer noch nicht.

Alternative: Du organisierst gleich alles als x*3 Matrix und verschiebst nur den X-Offset in 3er Schritten.

wenn du gruppenorientiert programmieren möchtest würde ich erstmal eine basisklasse "Gruppe" anlegen mit den eigenschaften postion, size, arrbitmap und den methoden setPosition, update, getBitmap, show. wenn du spezielle untergruppen haben möchtest gegebenfalls diese mit der internen methode zb effekt anlegen und den rest ererben lassen...

Verberbung ist doch C++-typisch. Da will ich hier ja nicht. Da ist der Vorschlag von Helmuth besser, auch wenn ich denke, dass ich den für diesen einzelnen Fall nicht brauchen werden. Werde mir das mal anschauen, wie sich da der Speicher zu meinem Vorschlag verhält.