Forum: Mikrocontroller und Digitale Elektronik WS2811 mit STM32F1 Blue Pill


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Grid U. (griduser)


Angehängte Dateien:

Lesenswert?

Gude,

ich hab mit der STM32CubeIDE ein Projekt mit Timer und DMA geschrieben 
um WS2811 leds mithilfe eines Bluepill boards anzusteuern.
Nur leider flackern diese wie wild und ich kann mir nicht erklären woran 
es liegt. Ich hoffe jemand kann helfen.
Mir ist bewusst, dass ein delay in Kombination mim DMA nicht die beste 
Idee ist, allerdings flackern die LEDs ohne diesen delay ohne 
erkennbaren Ziel Zustand. Mit dem delay leuchten die LEDs grundsätzlich 
in der richtigen Farbe, allerdings nicht stabil.
Nach dem Datenblatt der WS2811er brauch er mindestens 280 us LOW pegel 
für den reset, was ich einfach umgesetzt habe, in dem ich das pwm data 
array entsprechend mit 230 LOW pegeln fülle (800kHz -> 1.25us pro takt). 
Gibt sicher schönere wege das zu lösen, aber Speicher ist ausreichend 
vorhanden. In erster Etappe muss der Spaß erstmal laufen.
400 kHz hab ich auch schonmal ausprobiert, funktioniert auch nicht 
besser, muss also irgendwas von mir creepy sein....woran ich auch schon 
gedacht hab wäre manglende Qualität des 8 mhz quarzes auf der bluepill.

Die SysClk ist auf 72Mhz konfiguriert, mit dem HSE Eingang (8 Mhz) und 
entsprechender Konfiguration in der PLLCLK.
(s. Bild clk-config)

Der Timer ist konfiguriert mit:
Prescaler: 0
ARR: 90-1 (72Mhz / 90 = 800 KHz)
(s. Bild tim-config)

DMA läuft in normal operation, memory to pheriphal
Memory mit Byte
Pheriphal mit Halfword
(s. Bild dma-config)

Nachfolgend der Code:

zunächst das header file:
1
#ifndef __ws281x
2
#define __ws281x
3
4
#include "../config.h"
5
#include "../../Core/Inc/main.h"
6
7
//#define NUM_LED         NUM_LED defined in config.h
8
#define USE_BRIGHTNESS 0
9
#define WS2811_RESET_PERIOD 230
10
#define PWM_DATA_ARR_LENGTH ((24*NUM_LED)+WS2811_RESET_PERIOD) // 24Bits per LED + Reset bits
11
12
void ws281x_init();
13
void ws281x_send();
14
15
void setLED(uint8_t LEDnum, uint8_t red, uint8_t green, uint8_t blue);
16
void setAllLEDs(uint8_t red, uint8_t green, uint8_t blue);
17
void setBrightness (uint8_t brightness);
18
19
void ws281x_reset();
20
21
22
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim);
23
24
#endif

und die zugehörige .c:
1
#include "ws281x.h"
2
3
bool dataSentFlag; // True if DMA action is finished and the next one can start
4
5
uint8_t ledData[NUM_LED][3]; // Store led color data in RGB
6
uint8_t pwmData[PWM_DATA_ARR_LENGTH]; // Store led color and brightness data in bit format for the WS281x Led's
7
8
9
void ws281x_init(void) {
10
  dataSentFlag = 0;
11
12
  setAllLEDs(0,0,0);  // Init led data array
13
  ws281x_send();
14
}
15
16
void ws281x_send (void){
17
  uint16_t indx=0;
18
  uint32_t color;
19
20
  setAllLEDs(255,0,0);  // Init led data array
21
22
23
  for (int i= 0; i<NUM_LED; i++){
24
    // Safe led data in color, depending if brightness ctrl is used or not
25
    color = ((ledData[i][1]<<16) | (ledData[i][2]<<8) | (ledData[i][3]));
26
27
    // Translate RGB data in binary format for WS281x leds
28
    // The clk configuration in the .ioc must be set to:   clk:72 Mhz, Arr: 90-1
29
    for (int i=23; i>=0; i--){
30
      if (color&(1<<i))
31
        pwmData[indx] = 60;    // 2/3 of 90 (SysClk:72MHz/ARR:90 --> TimerFreq:800kHz)
32
      else
33
        pwmData[indx] = 30;    // 1/3 of 90
34
35
      indx++;
36
    }
37
  }
38
  for (int i=0; i<WS2811_RESET_PERIOD; i++){
39
      pwmData[indx] = 0;
40
      indx++;
41
    }
42
43
  HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)pwmData, indx);
44
  while (!dataSentFlag){};
45
  dataSentFlag = 0;
46
47
  HAL_Delay(10);
48
}
49
50
void setLED(uint8_t LEDnum, uint8_t red, uint8_t green, uint8_t blue){
51
  ledData[LEDnum][0] = green;
52
  ledData[LEDnum][1] = blue;
53
  ledData[LEDnum][2] = red;
54
}
55
56
void setAllLEDs(uint8_t red, uint8_t green, uint8_t blue){
57
  for(uint8_t i = 0; i<NUM_LED;i++){
58
    setLED(i,red,green,blue);
59
  }
60
}
61
62
void ws281x_reset(){
63
  for(int i=0; i<(PWM_DATA_ARR_LENGTH);i++)
64
    pwmData[i] = 0;
65
66
  HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t *)pwmData, PWM_DATA_ARR_LENGTH);
67
  while (!dataSentFlag){};
68
  dataSentFlag = 0;
69
}
70
71
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
72
{
73
  HAL_TIM_PWM_Stop_DMA(htim, TIM_CHANNEL_1);
74
  dataSentFlag = 1;
75
}

Danke und viele grüße

von Sebastian W. (wangnick)


Lesenswert?

Wie sieht denn das erzeugte Signal im Logikanalysator aus? Konform?

LG, Sebastian

von Werner P. (werner4096)


Lesenswert?

weiss jetzt nicht ob dir das weiterhilft.

ich benutze eine LED Matrix mit WS2812 und schicke die Daten mit SPI / 
DMA raus.

Wenn ich SPI auf 4 MHz einstelle dann blinken alle LEDs weiss. Nehme ich 
8 MHz dann funktioniert es super. Bei 16 MHz blinken einzelne LEDs wild 
durcheinander.

Grüße, Werner

von B. W. (yesitsme)


Lesenswert?

Passt die Spannung des Datensignals oder brauchst du noch einen 
Levelshifter?

von Fritz G. (fritz65)


Lesenswert?

Werner P. schrieb:
>ich benutze eine LED Matrix mit WS2812 und schicke die Daten mit SPI /
DMA raus.
> Wenn ich SPI auf 4 MHz einstelle dann blinken alle LEDs weiss. Nehme ich
> 8 MHz dann funktioniert es super. Bei 16 MHz blinken einzelne LEDs wild
> durcheinander.

Ich mache das für eine Kette von WS2812B auch mit SPI und DMA, wobei ein 
Byte 2 WS2812-bits entspricht, 1000 für ein 0-bit, 1110 für ein 1-bit. 
Bittakt ist 3MHz. Dadurch komme ich auf eine Bit-Zeit von 1.33µs, was 
gut zu den im Datenblatt angegebenen Werten passt. Lediglich die 
T1H-Zeit ist mit 1µs etwas länger als die spezifizierten 950ns, was aber 
die Funktion nicht beinträchtigt.  Die komplette Impulsfolge wird in 
einem Puffer abgelegt, mit 160 Null-Bits als Reset-Vorspann. 
Funktioniert mit 60 LEDs problemlos.

Bei WS2811 ist das Timing langsamer. Es sollte aber mit einer 
SPI-Frequenz von 1.5 MHz funktionieren, was einer Bitzeit von 0.667µs 
entspricht:
T0H: 1bit , Datenblatt sagt < 0.5+-0.15µs, also ganz knapp daneben, 
funzt aber vermutlich auch.
T0L: 3bits = 2.0µs, entspricht exakt der Spezifikation
T1L: 2bits = 1.33µs, Datenblatt sagt 1.3 +- 0.15µs
T1H: 2bits = 1.33µs, Spezifikation ist 1.2 +- 0.15µs

Alternativ wäre eine Bitdauer von 250µs möglich, was einem SPI-Takt von 
4MHz entspricht. Dann wären allerdings 10 SPI-bits für ein WS2811-Bit 
nötig, was einen höheren RAM-Bedarf mit sich zieht.
T0 entspräche dann der Bitfolge 1100000000, T1 1111100000

von Grid U. (griduser)


Angehängte Dateien:

Lesenswert?

Hi Sebastian,

ja an sich sieht die Logic passend aus (s. locig_detail.png) und es gibt 
auch keine Ausschläge die nicht in der Toleranz der LEDs liegen würden.

Beim rauszoomen fällt mir allerdings auf, dass da anscheinend eine 
unregelmäßigkeit in der Rate wie die Daten gesendet werden auftritt, bin 
mir aber nicht sicher ob das vlt auch nur durch die Darstellung oder die 
Auflösung des USB Logic Analyzers so aussieht. (s. long-time-view.png)

Hab im übrigen in Git ein Repo gefunden, in dem beschrieben ist, dass 
für WS2811 eine reset dauer von 270 us notwendig ist, und nicht wie im 
datenblatt von adafruit 50us. Damit kann ich den delay nun auch 
weglassen, das flimmern ist jedoch weiterhin beständig.

Grüße Luca

von Grid U. (griduser)


Lesenswert?

>Passt die Spannung des Datensignals oder brauchst du noch einen
Levelshifter?

Gute Frage, an die ich bisher tatsächlich gar nicht gedacht hatte....
laut dem Datenblatt von Adafruit brauchen die LEDs VIH = 0.7VDD.
Ich hab an dem WS2811 direkt gemessen, was anliegt wenn der Strip mit 
12V versporgt wird, und messe hier 5,4V

Demzufolge wären eigentlich 3,7V notwendig, meine Bluepill gibt 
natürlich nur 3V3 aus.
Ich hab parallel auch den Hersteller BFT-Lightning angeschrieben und 
dazu befragt.

Zitat:
>"Es sollte funktionieren, wenn das Steuergerät (angeschlossen an eine 
5-V-Stromversorgung) und der 12-V-Streifen (angeschlossen an eine 
12-V-Stromversorgung) separat und getrennt voneinander mit Strom versorgt werden.
Aber wir haben nicht versucht zu sehen, ob es bei 3V funktioniert.
Vielleicht können Sie es versuchen."

So blöd wies klingt hat mich das auf einen andren Fehler meinerseits 
aufmerksam gemacht: Ich hab den GND des Strips und der Bluepill nicht 
miteinander verbunden.
Gesagt getan nun funktionierts einwandfrei, bissl widersprüchlich zu den 
Datenblatt aber ich werd mich nicht beklagen.

Abschließend war das Problem vermutlich die Kombination aus timing 
problem durch den Delay und die falsch angesetzte reset dauer und die 
fehlende GND Verbindung. Ich hatte beides beim testen und dem Versuch 
den Fehler zu finden bereits behoben, allerdings natürlich nicht beides 
gleichzeitig.

Danke an alle Helfer.

Grüße

: 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.