78-WS2812-Library (STM32F4)

Hier eine Library um eine (oder mehrere) RGB-LEDs vom Typ WS2812 zu betreiben.
(Bis zu 4 LED-Ketten können angeschlossen werden)

Es gibt noch eine andere Library mit der bis zu 8 Ketten betrieben werden können
(88=WS2812_8CH-Lib)

Die WS2812-LED wird über eine einzelne Datenleitung mit der CPU verbunden. Über diese Leitung wird der LED der Farbwert (als RGB888) übermittelt.

Da jede WS2812 je einen Input und einen Output Pin hat, können mehrere LEDs als “Kette” verschaltet werden und somit kann man beliebig viele LEDs mit nur einem einzigen CPU-Pin steuern.

Die LEDs gibt es in verschiedenen Variationen zu kaufen. Entweder einzeln (als SMD oder im 8mm Gehäuse), oder schon fertig verbunden als Meterware (trennbar an jeder beliebigen Stelle)

Ich habe die LEDs als “5er Platinen Streifen” gekauft. Weil ich nicht viel löten wollte und auch nicht mehr brauche als 5 Stück.

Ab der Version 1.1 der Library können bis zu 4 LED-Ketten an die CPU angeschlossen werden. Diese können dann unabhängig voneinander geschaltet werden.

Im H-File der Library muss eingestellt werden, wie viele LEDs an den jeweiligen Kanälen angeschlossen sind (Als Länge “0″ eintragen, wenn eine Kette nicht benutzt wird)

Es müssen auch die GPIO-Pins für die Datenleitungen eingestellt werden,
diese sind nicht völlig frei wählbar, sondern hängen vom benutzten Timer ab.

Zur Software :

Nach dem start werden zuerst alle LEDs ausgeschaltet und die LED-Kette mit der niedrigsten Kanal-Nr ist aktiv. Alle Befehle (Farbe ändern, schieben rotieren usw) beziehen sich immer nur auf die gerade aktive LED-Kette.

Mit der Funktion “UB_WS2812_SetChannel” kann die aktive LED-Kette gewechselt werden.

Die RGB-Farbwerte der LEDs müssen in einem Array eingetragen werden “WS2812_LED_BUF_CHx”. Das kann entweder “manuell” in der Software gemacht werden oder mit den Funktionen die ich dafür geschrieben habe. Damit die Farbwerte an die LEDs gesendet werden muss danach die Funktion “UB_WS2812_Refresh()” einmal aufgerufen werden.

Wer lieber im HSV-Farbraum arbeitet für den habe ich eine Umrechenfunktion von “HSV” in “RGB” dazugepackt. (die Funktion habe ich von Ulrich-Radig seiner Webseite)

Spannungsversorgung / Strom :

Wen die Datenleitung der ersten LED direkt an die CPU angeschlossen wird, darf die Versorgungsspannung der LEDs nicht > als 3,3V sein !! (sonst wird der Hi-Pegel u.U. nicht richtig erkannt)

Und auch auf die Stromaufnahme achten !! Wenn alle LEDs “weiss” bei 100% Helligkeit anzeigen, kommt da in der Summe schon was zusammen !!

Bilder :

ws2812

Benutzte Pins :

1
2
3
4
CH1 = PC6
CH2 = PB5
CH3 = PB0
CH4 = PB1

Voraussetzungen :

1
2
Benutzte Module der CooCox-IDE : GPIO, TIM, DMA, MISC
Benutzte Librarys : keine

Enumerationen :

1
2
3
4
5
typedef struct {
  uint8_t red;    // 0...255 (als PWM-Wert)
  uint8_t green;  // 0...255 (als PWM-Wert)
  uint8_t blue;   // 0...255 (als PWM-Wert)
}WS2812_RGB_t;

:

1
2
3
4
5
typedef struct {
  uint16_t h;     // 0...359 (in Grad, 0=R, 120=G, 240=B)
  uint8_t s;      // 0...100 (in Prozent)
  uint8_t v;      // 0...100 (in Prozent)
}WS2812_HSV_t;

Funktionen :

1
2
3
4
5
6
7
8
9
10
11
12
void UB_WS2812_Init(void);                                                       // zum init der WS2812-Kette
void UB_WS2812_SetChannel(uint8_t ch);                                           // aktive LED-Kette auswählen
void UB_WS2812_Refresh(void);                                                    // RGB-Array an die LEDs senden
void UB_WS2812_RGB_2_HSV(WS2812_HSV_t hsv_col, WS2812_RGB_t *rgb_col);           // HSV-Wert in RGB umrechnen
void UB_WS2812_One_Led_RGB(uint32_t nr, WS2812_RGB_t rgb_col, uint8_t refresh);  // eine LED setzen (als RGB Wert)
void UB_WS2812_All_Led_RGB(WS2812_RGB_t rgb_col, uint8_t refresh);               // alle LEDs setzen (als RGB Wert)
void UB_WS2812_One_Led_HSV(uint32_t nr, WS2812_HSV_t hsv_col, uint8_t refresh);  // eine LED setzen (als HSV Wert)
void UB_WS2812_All_Led_HSV(WS2812_HSV_t hsv_col, uint8_t refresh);               // alle LEDs setzen (als HSV Wert)
void UB_WS2812_Shift_Left(void);                                                 // alle LEDs einen Step links schieben
void UB_WS2812_Shift_Right(void);                                                // alle LEDs einen Step rechts schieben
void UB_WS2812_Rotate_Left(void);                                                // alle LEDs einen Step links rotieren
void UB_WS2812_Rotate_Right(void);                                               // alle LEDs einen Step rechts rotieren

Beispiel :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//--------------------------------------------------------------
// File     : main.c
// Datum    : 20.03.2014
// Version  : 1.0
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F4
// IDE      : CooCox CoIDE 1.7.4
// GCC      : 4.7 2012q4
// Module   : CMSIS_BOOT, M4_CMSIS_CORE
// Funktion : Demo der WS2812-Library
// Hinweis  : Diese zwei Files muessen auf 8MHz stehen
//              "cmsis_boot/stm32f4xx.h"
//              "cmsis_boot/system_stm32f4xx.c"
//--------------------------------------------------------------
 
#include "main.h"
#include "stm32_ub_ws2812.h"
 
void Delay(volatile uint32_t nCount)
{
  while(nCount--)
  {
  }
}
 
int main(void)
{
  WS2812_HSV_t led1,led2,led3,led4,led5;
 
  SystemInit(); // Quarz Einstellungen aktivieren
 
  // init der WS2812-Kette an PC6 (5 LEDs)
  UB_WS2812_Init();
 
  led1=WS2812_HSV_COL_RED;    // LED-1 auf "rot"
  led2=WS2812_HSV_COL_YELLOW; // LED-2 auf "gelb"
  led3=WS2812_HSV_COL_GREEN;  // LED-3 auf "gruen"
  led4=WS2812_HSV_COL_CYAN;   // LED-4 auf "cyan"
  led5=WS2812_HSV_COL_BLUE;   // LED-5 auf "blau"
 
  // Farbwerte setzen
  UB_WS2812_One_Led_HSV(0,led1,0);
  UB_WS2812_One_Led_HSV(1,led2,0);
  UB_WS2812_One_Led_HSV(2,led3,0);
  UB_WS2812_One_Led_HSV(3,led4,0);
  UB_WS2812_One_Led_HSV(4,led5,1);  // mit Refresh
 
  while(1)
  {
	// kleine pause
    Delay(50000);
 
    // Farbe von allen Leds aendern
    led1.h++; if(led1.h>359) led1.h=0;
    led2.h++; if(led2.h>359) led2.h=0;
    led3.h++; if(led3.h>359) led3.h=0;
    led4.h++; if(led4.h>359) led4.h=0;
    led5.h++; if(led5.h>359) led5.h=0;
 
    // neue Farbwerte setzen
    UB_WS2812_One_Led_HSV(0,led1,0);
    UB_WS2812_One_Led_HSV(1,led2,0);
    UB_WS2812_One_Led_HSV(2,led3,0);
    UB_WS2812_One_Led_HSV(3,led4,0);
    UB_WS2812_One_Led_HSV(4,led5,1);  // mit Refresh
  }
}

Hier die Library zum Download :

ub_stm32f4_ws2812_v102

Hier der komplette CooCox-Projektordner zum Download :

Demo_78_WS2812


41 Antworten auf 78-WS2812-Library (STM32F4)

  1. Cortex-Einsteiger sagt:

    diese Leds mit integriertem Controller sind wirklich toll, doch leider sind die extrem teuer

    • admin_ub sagt:

      darum hab ich auch nur 2 x 5 Stück gekauft :-)

  2. Hosentraeger sagt:

    wie immer eine sehr schöne Lib – vielen Dank! Mein 240er LED Strip läuft super damit.
    Es ist zwar eine riesige Verschwendung von RAM, aber der Controller hat ja genug davon :-)

    • admin_ub sagt:

      schön zu hören. Du kannst aus dem Array “WS2812_TIMER_BUF” auch ein “uint8_t” machen, das müsste auch laufen. Dann brauchst du bei 240 LEDs statt 12kByte nur 6kByte RAM.

  3. Hosentraeger sagt:

    ja, eigentlich müssten Bytes für die Timer-Werte reichen. Mit uint8_t läuft es aber nicht. Habe auch die DMA-Initialisierung von DMA_***DataSize_HalfWord in DMA_***DataSize_Byte geändert, leider kein Erfolg.
    Bei der dicken MCU kommt es auf die 6 kB aber auch wirklich nicht an :-)

  4. t4sound sagt:

    super Lib! :-)
    Herzlichen Dank dafür.

    Ich würde deine Lib gerne erweitern um vier Strips unabhängig voneinander ansteuern.
    Kannst Du mir einen Weg aufzeigen alle vier Kanäle des Timers einzeln zu verwenden. Oder ist das garnicht möglich?

    mfG: Michael

    • admin_ub sagt:

      Ab Version 1.1 sind jetzt bis zu 4 LED-Ketten benutzbar. Bitte mal ausprobieren und hier posten falls noch ein Fehler auftaucht.

  5. t4sound sagt:

    Hallo Uwe,

    bei der Inbetriebnahme von einem Discovery mit vier Strips ist mir nun aufgefallen das das PWM vom Kanal 2 auch immer auf Kanal 4 und anderst herum, ausgegeben wird. Dies fürt zu einem starken Flackern der LED.
    Hast Du eine Idee woran das liegen kann? Hardware?

    • admin_ub sagt:

      komisch…muss ich nochmal danach sehen

    • admin_ub sagt:

      hab es jetzt mal ausprobiert mit 4 Stripes (2×2 LEDs und 2×3 LEDs) und alles funktioniert wie es soll (Version 1.2) hier mein Testprogramm :

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      
        UB_WS2812_SetChannel(1);
        UB_WS2812_One_Led_RGB(0,WS2812_RGB_COL_BLUE,0);
        UB_WS2812_One_Led_RGB(1,WS2812_RGB_COL_GREEN,1);
       
        UB_WS2812_SetChannel(2);
        UB_WS2812_One_Led_RGB(0,WS2812_RGB_COL_RED,0);
        UB_WS2812_One_Led_RGB(1,WS2812_RGB_COL_WHITE,0);
        UB_WS2812_One_Led_RGB(2,WS2812_RGB_COL_CYAN,1);
       
        UB_WS2812_SetChannel(3);
        UB_WS2812_One_Led_RGB(0,WS2812_RGB_COL_MAGENTA,0);
        UB_WS2812_One_Led_RGB(1,WS2812_RGB_COL_YELLOW,1);
       
        UB_WS2812_SetChannel(4);
        UB_WS2812_One_Led_RGB(0,WS2812_RGB_COL_GREEN,0);
        UB_WS2812_One_Led_RGB(1,WS2812_RGB_COL_RED,0);
        UB_WS2812_One_Led_RGB(2,WS2812_RGB_COL_BLUE,1);
       
        UB_WS2812_SetChannel(2);
        while(1) {
          pause(100);
          UB_WS2812_Rotate_Left();
        }

      event hast du einen Fehler in deinem Programm beim umschalten der Kanäle
      es muss ja immer erst per “SetChannel” der aktivie Kanal ausgewählt werden !!

      • Michael sagt:

        Hallo Uwe,

        danke für den Hinweis. Ich hab es jetzt mit einem anderen Discovery Board versucht. Bei dem gibt es das Problem nicht. ???
        Also irgendwas stimmte mit der Hardware nicht. Danke für deine Hilfe.

        Ist es eigentlich möglich den DMA im Hintergrund immer weiterlaufen zu lassen und im Main das Ausgabe-Array nur bei Bedarf upzudaten? Ich frage weil beim umschalten auf einen anderen Strip der Prozessor immer warten muss bis der DMA fertig ist.

        • admin_ub sagt:

          wie viele LEDs hast du denn an einer Kette ?
          bei 300 LEDs liegt die “Wartezeit” bei ca. 9ms
          und bei 4 Ketten mit je 300 LEDs hätte man eine Refreshzeit von ca. 25Hz.
          Falls dir das nicht reicht, müsste man alle 4 Ketten parallel laufen lassen (bin mir nicht sicher ob das geht) und dafür 4mal so viel Speicher belegen.

        • admin_ub sagt:

          Nachtrag : das jede LED-Kette ihre Farben behällt, auch ohne “Refresh” aufzurufen ist hoffentlich klar. Also die Funktion “refresh” wird nur benötigt, wenn sich an einer LED-Kette die Farbe einer LED (odere mehrere) ändern sollen. Die muss nicht ständig aufgerufen werden.

          • Michael sagt:

            Die Anzahl der LED ist verschieden und bis zu 480 Stück. Du hast recht. Ich sollte die Aktualisierungs-Methode nochmal überdenken. Da ich viel mit dimming und schnellen Farben mache kommt es bedingt durch andere zyklische Aufrufe wie PID Regler zum ruckeln.

  6. Leddel sagt:

    Hello

    Sorry for my English post, but since I’m not German it’s very hard for me to properly write a German post.

    I was wondering if this library could be easily ported from the F4 processors to a F1 processor like the STM32F103. These devices also have DMA channels and timers. But are a lot slower than the F4. For example the STM32F103 can run up to 72MHZ.

    Or would it just be a matter of recalculating the timer value?

    My sincerly

    • admin_ub sagt:

      you dont need to write in german, but then you have to deal with my bad english :-) and to your question : i dont have a F1 to check a conversion but it should not be very tricky. Of course the timer settings must be changed to have the correct speed. There is one Timer (TIM3) running with 800kHz (setup in the H-File) to generate the Signal for the WS2812. So you have to check the values of “WS2812_TIM_PERIODE”, “WS2812_LO_TIME”, “WS2812_HI_TIME”
      Period must set to a value to reach 800 kHz (in F4 : 84MHz/1/105 = 800kHz)
      Lo-Time must set to reach about 0.35us (in F4 : 1/84MHz*29 = 0.345us)
      Hi-Time mus set to reach about 0.90us (in F4 : 1/84MHz*76 = 0.904us)
      So try this and leave a comment if it works.

    • admin_ub sagt:

      and you have to check if the dma settings are the same (Channel and Stream number) and if the Timer-3 Capture Pins for Channel1 to 4 are the same (PC6, PB5, PB0, PB1)

  7. Michael sagt:

    Hi,

    find deine Library und auch deine anderen Libraries total super. Komm von 8-bittern und daher noch Einsteiger was die STM32 uC angeht. Nur ich hab leider das Problem das ich die Library nicht zum laufen bekomme ;( Ich habe auch dein Demoprogramm getestet aber ich bekomm keine Datenausgabe (LEDs hab ich nicht dran nur ein Oscar, um zu gucken ob überhaupt was kommt)
    Ich habe nun auch die Verschiedensten Ausgabepins mal probiert die du mit TIM3 angibst, aber leider bei PC6 sowie PA6 und PA7 keine Ausgabe zu verzeichnen.
    Hast du eventuell eine Idee wodran es liegen könnte das so mal garnichts kommt? Es kommt lediglich bei einem Reset ein ganz kurzer High Pegel (Was aber die Initroutine sein wird).

    Dank dir schonmal
    Gruß Michael

    • Michael sagt:

      Ach was ich noch zu deiner Library erwähnen wollte, es wäre natürlich super wenn man die Längen der Strips dynamisch ermitteln könnte. Ich hab das an meine xMEGA und auch ATMega Steuerung mit einem einfachen Interrupt gelöst welcher am DOUT der letzten Led am Strip verbunden ist.
      Nur so als kleines “Featurerequest” :)

      • admin_ub sagt:

        ja, könnte man machen…

    • admin_ub sagt:

      die Software funktioniert zu 100% (programmier mal direkt das HEX-File) Der fehler liegt dann entweder in deiner Hardware oder du “Mist”,”Mist”. Welchen Compiler benutzt du ? Check nochmal das Oszi und den Messpunkt.

      • Michael sagt:

        Hi,

        so ich habe das Problem lösen können. Er hing in der Refresh Routine wo er auf das “ws2812_dma_status” flag wartet.
        Ich habe die Variable als “volatile” deklariert und nun geht es :)
        Echt super, dann kann ich meine Anlage doch vom xMega %) auf nen billigeren ARM portieren :)

        Gruß Michael

  8. Bjoern sagt:

    Hi der Herr :)

    Bin noch recht neu im Arm-Bereich und komme von den Atmel/Renesas 8bittern daher.
    Habe mir Coocox IDE installiert und einmal Deine tollen Libs getestet.
    Leider funktionieren diese alle scheinbar nicht mit der aktuellen Coocox Version die man herunter laden kann.
    Dort bekommt man nur noch eine beta der 2.0 – Mit der von einem Kumpel bekommenen 1.7.4 funktioniert alles wunderbar!

    Wenn ich die Lib im neuen Coocox öffnen möchte, steht im Compiler String in den Configurations ein “M0″ drin und es gibt keine Chance das Device einzustellen.
    Der Reiter “Device” fehlt einfach gänzlich…

    Da ich ausschliessen wollte, dass ich total dämlich bin, hat es mein Kumpel auch einmal getestet (Er hat schon einige Jahre Erfahrung mit den STMs).
    -> Auch gleiches Problem.

    Hast Du das schon einmal getestet?
    Es scheint so, als ob es da gravierende Änderungen zur 2.0 gab.

    Grüße, Bjoern

    • admin_ub sagt:

      also mal abgesehen das es die alten Versionen noch unter “old Website” zum download gibt, sind meine Files “standard” C-Files. Ich kann mir vorstellen das man ein komplettes Projekt von der alten Version nicht mehr laden kann aber man kann mit Sicherheit die einzelnen Files einfach per “add” zu einem 2.0-Projekt hinzufügen. (das geht übrigens auch in jeder anderen IDE so)

      • Bjoern sagt:

        Natürlich kann man die Sourcen in neue Projekte einklinken.
        Die Frage zielte eher auf die Projektdatei ab.
        Wenn man diese im neuen Coocox startet, wird der “M0″ gewählt.
        Konnte leider nicht herausfinden wie man das Projekt dann auf den F4 hievt…

        • admin_ub sagt:

          da kann ich dir nicht weiterhelfen. event. der Coocox support oder das forum.

          • Bjoern sagt:

            Okay, danke trotzdem.
            Falls nochmal so eine Rückfrage kommt, weißt Du an was es liegen könnte ;)

            Danke auf jeden Fall für die Veröffentlichung Deiner Libs.
            Diese helfen wirklich ungemein beim Einstieg!!!
            Dir noch einen schönen Abend.

  9. Cortex-Einsteiger sagt:

    Nun habe ich mich doch mal durchgerungen und für paar Euro Leds bestellt.
    Die Lib funktioniert sehr gut, schon allein die Demo ist beeindruckend.

    Aber wie habt ihr das Problem mit der Pegelwandlung gelöst, Controller mit 3V und Leds mit 5V gibt bei mir Probleme. Den Strip mit 3V zu versorgen funktioniert zwar, aber bei mehreren Leds habe ich da meine bedenken.
    Spontan fällt mir eine Doppel-Transistorstufe ein, aber das für 4 Channels aufzubauen… geht das nicht einfacher, evtl. per Software und Open Drain GPIO?

    • Bjoern sagt:

      Die Datensignalte fürfen doch 3v3 haben!?
      Die Schwelllspannung der Eingangslogik ist irgendwo um die 0,8-1V.
      Spannungsversorgung für die LEDs können ja trotzdem 5V sein.

      • Cortex-Einsteiger sagt:

        Bei mir funktioniert es nicht, da gehen Flanken verloren.
        controller mit 3,3V – 0,3V Schutzdiode vom Board und die Leds mit 5V versorgt.
        Ist es nicht möglich ähnlich zu I2C mit Pullups das ganz einfach zu lösen? die Pins würden ja 5V aushalten.

        • admin_ub sagt:

          probier es doch einfach aus. stell in der INIT-IO Funktion statt PushPull OpenDrain ein (den internen PullUp deaktivieren) und benutz einen externen PullUp auf 5V. Mehr als nicht funktionieren kann es nicht.

          • Cortex-Einsteiger sagt:

            Danke für die Hilfe!
            Funktioniert super.
            Einzig muss man vorher überprüfen ob der jeweilige I/O auch mit 5V arbeiten kann, ansonsten ist es die perfekte Lösung.

  10. Michael H sagt:

    Hallo Einsteiger,

    ich mache eine Pegelwandlung mit 74HCT08 Bausteinen (Logisch-UND).
    Das funktioniert super.

    Einfach beide Eingänge des Gatters zusammenlegen.

  11. Frank M. sagt:

    Hallo Uwe,

    danke für die lehrreiche Software Lib.

    Mir ist aber ein Fehler aufgefallen:

    Die Variable ws2812_dma_status muss volatile definiert sein, damit solche Schleifen wie:

    while(ws2812_dma_status!=0);

    nicht endlos hängen. Das fällt erst bei eingeschalteter Optimierung auf.

    Gruß,

    Frank

    • admin_ub sagt:

      ja, kann sein…Danke für den Hinweis.

  12. Christian S. sagt:

    Hallo,

    besten Dank für all deine Librarys. Für einen Anfänger wie mich ist deine Seite eine super Unterstützung.
    Nun aber habe ich das Problem, das ich diese Library (WS2812) mit HAL benutzen möchte. Leider kommt es schon bei der Initialisierung zu Fehlermeldungen.
    Besteht die Möglichkeit, das es deine Librarys auch für HAL geben wird?
    Ich benutze EWARM und CubeMx.
    Besten Dank für deine Unterstützung

    Gruß
    Christian

    • admin_ub sagt:

      HAL benutzte ich nur beim F7. Leider gibt es da zu viele Unterschiede um das auf die schnelle umzuprogrammieren. sorry

  13. Heiko sagt:

    Erst mal vielen Dank für deine Lib´s !
    Kannst du mir den Link zu Version 1 schicken ?
    Ich brauche für mein Projekt die restlichen DMA Channels und will nur eine “Kette” ansteuern.

    • admin_ub sagt:

      die alte Versionen habe ich nicht mehr. Wenn im H-File die Längen der 3 anderen Ketten auf 0 steht, werden die Timer und DMAs nicht initialisiert. Die kannst du also frei benutzen. Du musst nur im C-File die nicht benötigen ISR-Funktionen löschen. z.B.
      void WS2812_DMA_CH2_ISR(void)

      • Heiko sagt:

        Das hilft mir, vielen dank !

  14. Bob Bobsen sagt:

    genau so eine Libary habe ich gesucht :)
    ps.: super Seite für den Einstieg ! Vielen Dank…


Wie hat Dir dieser Artikel gefallen?

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (Noch keine Bewertungen)
Loading...

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.