Forum: Mikrocontroller und Digitale Elektronik WS2812 Timing


You were forwarded to this site from EmbDev.net. Back to EmbDev.net
von Tobias N. (silberkristall)


Angehängte Dateien:

Lesenswert?

Hallo,

seit einigen Stunden mache und tue ich aber das Timing will einfach 
nicht passen.

Ich möchte RGB LEDs mit integriertem WS2812 Controller ansteuern. Dieser 
verlangt als Timing für eine 1 ein High von 0,7us sowie ein Low von 
0,6us. Für eine 0 beläuft sich das Timing auf 0,35us High sowie 0,8us 
Low.

Wenn ich nun den Mega mit 8 Mhz betreibe beträgt die Zeit pro Takt 
1/8000000, sowmit also 125ns.

Laut Datenblatt beträgt die Zeit für eine 1 oder 0 je 1,25µs / 1250ns 
sowie eine Toleranz von 150ns, somit eine Gesamtzeit von min. 
1,1µs/1100ns und max. 1,4µs/1400ns. Daher dachte ich mir nun folgendes:

Für eine 1:

Pin auf High
6 takte warten (750ns)
Pin auf Low
5 takte warten (625ns)

Gesamt: 1,375µs/1375ns

Für eine 0:

Pin auf High
3 takte warten (375ns)
Pin auf Low
7 takte warten (875ns)

Gesamt: 1,25µs/1250ns

Gilt nur das High/Low auch schon als 1 Takt also muss ich diesen mit 
"einberechnen"?

Anbei noch das Datenblatt

Danke euch schonmal.

von Bugs (Gast)


Lesenswert?

Tada:
Beitrag "Re: Touch bauen, suche günstigere alternativen"

:-D

Hängs an SPI und überleg Dir , welchen SPI_Takt du brauchst um mit den 
entsprechenden BitMustern das Timing zu bekommen ;)
Ansatzpunkt wäre 001 für ne 0 und 011 für ne 1. musst hat nur die 
helligkeitswerte in solch ein Muster umwandeln. Evtl kommst auch mit ner 
5 stelligen Bitfolge pro Bit besser hin.
Aber wie ich schon schrieb, das Timing trau ich die schon zu um die 
800kbs zu schaffen, nur wird dein Problem das du nicht schnell genug die 
Daten nach einander rausschiebst und die Pause als reset(datenübername) 
interpretiert wird ;)
Was auch ginge wäre PWM ohne OCR_Buffer . Musst nur 2 verschiedene Werte 
zur richtigen zeit ins OCR laden, also einen für ne 1 und einen anderen 
für die 0.
Hier hättest allerdings gleich 2 timingprobleme, OCR zur richtigen zeit 
und keine Lücke größer als die resetzeit.

Aber wie wir dich alle kennen, findest DU schon einen eigenen weg, der 
genau und nur für deinen aktuellen fall funktionieren wird...

MfG Bugs

von Thomas T. (knibbel)


Lesenswert?

Vielleicht passt mein Beitrag ja für Dein Problem:

Beitrag "WS2812 Ansteuerung in AVR-Assembler (ohne SPI o.ä., beliebige Pins verwendbar)"

Schönen Abend...

Gruß,
Thomas

von Tobias N. (silberkristall)


Lesenswert?

Nunja, da ich weder was von Bascom nund noch weniger von ASM verstehe, 
und damit ich den code hinterher auch selber gut verstehe würde, würde 
ich das ganze schon gerne selber machen. Deswegen fragte ich halt nach 
dem Timing. Ich habe gestern mal einen externen 12Mhz Quarz 
angeschlossen und gefused. Damit sollte ich eigentlich einen takt von um 
die 80ns erzeugen können.

Kurzes test programm.

int main void()
{
DDRB |= (1<<PD3);

while(1)
{
PORTD |= (1<<PD3);
PORTD &= ~(1<<PD3);
}
}

Oszi dran und messen. Beim High bekomme ich einen takt von 186ns, bei 
LOW einen takt von 224ns.

Ich bin gerade ein wenig am verzweifeln.

Weiss jemand rat?

von Mr. Tom (Gast)


Lesenswert?

Tobias N. schrieb:
> Gilt nur das High/Low auch schon als 1 Takt also muss ich diesen mit
> "einberechnen"?

Das kommt drauf an, ob dein abstrakter µC für die Ausführung des Befehls 
Zeit braucht. Jeder normale µC muß den Befehl lesen und ausführen. Sonst 
gäbe es keine Programmlaufzeiten - davon träumen viele.

von Tobias N. (silberkristall)


Lesenswert?

Also das problem ist mit einem mega8, mega32 und mega128 das gleiche. 
Low ist länger als high und bei 12mhz sollte ein takt doch 83ns 
betragen. Also ich verstehe nicht warum die takte unterschiedlich sind 
und warum sie nichtmals annähernd an die 80ns kommen.

von Bugs (Gast)


Lesenswert?

Schau dir doch mal den erzeugten ASM_Code an...

von MWS (Gast)


Lesenswert?

Tobias N. schrieb:
> Also ich verstehe nicht warum die takte unterschiedlich sind
> und warum sie nichtmals annähernd an die 80ns kommen.

Der Compiler wird's nicht in SBI/CBI umsetzen kônnen, môglicherweise 
Optimierung aus, Portadresse außerhalb des Bereichs, dann ist's Port 
lesen, or/and und zurückschreiben.
Das geht nicht in einem Takt. Das JMP am Ende braucht zusätzlich, darum 
dauert Low länger als High.

von Tobias N. (silberkristall)


Lesenswert?

also ich compiliere mit 12000000 UL

von MWS (Gast)


Lesenswert?

Tobias N. schrieb:
> also ich compiliere mit 12000000 UL

Und was hat das damit zu tun?

von James (Gast)


Lesenswert?

Das Tastverhältnis bei den Bits iist nicht so kritisch, aber der Takt 
/1.25us / Bit) muß konstant sein. Die Angaben in den Datenblättern sind 
da z.T. einfach falsch.

Mit freundlichen Grüßen,

James

von Tobias N. (silberkristall)


Angehängte Dateien:

Lesenswert?

so habe mal ein paar dateien angehangen. Also ich verstehe da echt 
nichts mehr.

von Dominic A. (neo123)


Lesenswert?

Probier einmal soetwas:
1
  while(1)
2
  {
3
  PORTD |= (1<<LED);
4
  PORTD &= ~(1<<LED);
5
  PORTD |= (1<<LED);
6
  PORTD &= ~(1<<LED);
7
  PORTD |= (1<<LED);
8
  PORTD &= ~(1<<LED);
9
  PORTD |= (1<<LED);
10
  PORTD &= ~(1<<LED);
11
  PORTD |= (1<<LED);
12
  PORTD &= ~(1<<LED);
13
  PORTD |= (1<<LED);
14
  PORTD &= ~(1<<LED);
15
  }

Ich hatte das selbe wie du auch schon mal auf einem PIC. Die Ausführung 
der While Schlaufe benötigt halt auch ein bisschen Zeit. Dadurch bleibt 
der Pin länger auf Low als auf High. Dieser Code hier ist zwar extrem 
unschön jedoch sollten dann ein paar high/low Takte gleich lange Zeiten 
haben.

von egbert (Gast)


Lesenswert?

Tobias N. schrieb:
> so habe mal ein paar dateien angehangen. Also ich verstehe da echt
> nichts mehr.

Das Du nichts verstehst, ist für jeden auch nur halbwegs 
vernunftbegabten Menschen offensichtlich!

Eine etliche Anzahl von Forumsmitgliedern und Gästen rät Dir, zunächst 
erstmal mit den LEDs zu experimentieren.

"bla bla bla, alles kein Problem für mich!"

Gleiches gilt für Deine Theorieren zu der "Touch"-Erkennung.

Nun ja, wenigsten hast Du jetzt einen hübschen großen hölzernen 
Setzkasten, in welchen Du Deinen Elektroschrott stilvoll zur Schau 
stellen kannst...

von Tobias N. (silberkristall)


Lesenswert?

jau, jetzt sind sie exakt 186ns High, 186ns Low

von Sebastian W. (sebastian_w29)


Lesenswert?

Tobias N. schrieb:
> jau, jetzt sind sie exakt 186ns High, 186ns Low

Das sollte auch in C noch schneller, also in einem Takt anstatt in zwei 
Takten gehen:

unsigned char off = PORTD & ~(1<<LED);
unsigned char on = off | (1<<LED);

PORTD = on;
PORTD = off;
PORTD = on;
PORTD = off;
PORTD = on;
PORTD = off;
PORTD = on;
PORTD = off;

Nur: Du möchtest ja nicht die gesamte Bitfolge fest programmieren. Du 
musst also irgendwann neue Daten ranschaffen. Dazu hast du aber gar 
keine Zeit. Wie willst du das lösen?

LG, Sebastian

von Tim  . (cpldcpu)


Lesenswert?

Für extrem timingkritisches "Bitbanging" ist C nicht die richtige 
Sprache. Selbst wenn es irgendwie funktionieren sollte, besteht immer 
das Risiko dass durch Änderungen an anderen Stellen im Programm 
(Registernutznug) oder andere Optimierungssetting das Timing des 
erzeugten Codes wieder verändert wird.

Fertige Lösungen gibt es hier:

Meine Library
Beitrag "Lightweight WS2811/WS2812 Library"

Neopixel Library für Arduino
https://github.com/adafruit/Adafruit_NeoPixel

Oder mal nach FastSPI-Lib suchen.

von Tobias N. (silberkristall)


Lesenswert?

so das Timing passt jetzt inkl. reset. Getestet von 8Mhz intern über 
8Mhz Quarz über 10Mhz Quarz sowie 12Mhz Quarz. Mit allen lässt es sich 
steuern, leider noch nicht "dynamisch".

Das Problem vor dem ich stehe ich dei "umwandlung" von Ganzzahlen ins 
Binär vormat.

Also halt

x = 0;
y = [code];
y = 00000000;

oder

x = 255;
y = [code];
y = 11111111;

Leide finde ich zwar über google gazen listen welche Zahl was in Binär 
ist aber ich finde keine C funktion die mir den Zahlen direkt ins Binär 
ändert.

Derzeit gibt es zwei funktionen. writezero() und wirteone().

Nun will ich das ganze in writergb() erweitern, also
1
writergb(255,255,255);
Die funktion hierzu sollte in etwa so aussehen.
1
unsigned char writezero(int g, int r, int b)
2
{
3
4
// Hier die einzelnen werte in Binär ändern. Und dann in einzelne Zeichen zerlegen. Danach halt if 1 = writeone, if 0 = writezero
5
6
}

von Tim  . (cpldcpu)


Lesenswert?

...

von Tobias N. (silberkristall)


Lesenswert?

ich will euch ja nichts vorenthalten :)

Der Code ist nicht schön. Sicherlich gibt es hier welche die es "besser" 
können was "bei mir" ja nicht schwer sein sollte (wie alle sagen).

Jetzt muss ich nur noch rumtesten und probieren wie das mit dem 
Farbenmischen ist, also "automatisch" also was ich wie erhöhe um die 
passenden farben zu kriegen usw. Per hand die werte eingeben ist ja 
nicht das problem. Kommen auch schöne farben raus nur würde ich gerne 
das jeweilige "mischverhältnis" wissen.
1
#define LED    PD3  // Licht port
2
3
unsigned char init5050(void)
4
{
5
  _delay_us(5);
6
  return 0;
7
}
8
9
unsigned char reset5050(void)
10
{
11
  _delay_us(55);
12
  return 0;
13
}
14
15
unsigned char writezero(void)
16
{
17
  PORTD |= (1<<LED);
18
  PORTD |= (1<<LED);
19
  PORTD &= ~(1<<LED);
20
  PORTD &= ~(1<<LED);
21
  PORTD &= ~(1<<LED);
22
  PORTD &= ~(1<<LED);
23
  PORTD &= ~(1<<LED);
24
  PORTD &= ~(1<<LED);
25
  return 0;
26
}
27
28
unsigned char writeone(void)
29
{
30
  PORTD |= (1<<LED);
31
  PORTD |= (1<<LED);
32
  PORTD |= (1<<LED);
33
  PORTD |= (1<<LED);
34
  PORTD |= (1<<LED);
35
  PORTD &= ~(1<<LED);
36
  PORTD &= ~(1<<LED);
37
  PORTD &= ~(1<<LED);
38
  return 0;
39
}
40
41
unsigned char writegrb(int g, int r, int b)
42
{
43
  // Grün
44
  if (g & 1<<1) { writeone(); } else { writezero(); }
45
  if (g & 1<<2) { writeone(); } else { writezero(); }
46
  if (g & 1<<3) { writeone(); } else { writezero(); }
47
  if (g & 1<<4) { writeone(); } else { writezero(); }
48
  if (g & 1<<5) { writeone(); } else { writezero(); }
49
  if (g & 1<<6) { writeone(); } else { writezero(); }
50
  if (g & 1<<7) { writeone(); } else { writezero(); }
51
  if (g & 1<<8) { writeone(); } else { writezero(); }
52
  
53
  // Rot
54
  if (r & 1<<1) { writeone(); } else { writezero(); }
55
  if (r & 1<<2) { writeone(); } else { writezero(); }
56
  if (r & 1<<3) { writeone(); } else { writezero(); }
57
  if (r & 1<<4) { writeone(); } else { writezero(); }
58
  if (r & 1<<5) { writeone(); } else { writezero(); }
59
  if (r & 1<<6) { writeone(); } else { writezero(); }
60
  if (r & 1<<7) { writeone(); } else { writezero(); }
61
  if (r & 1<<8) { writeone(); } else { writezero(); }
62
  
63
  // Blau
64
  if (b & 1<<1) { writeone(); } else { writezero(); }
65
  if (b & 1<<2) { writeone(); } else { writezero(); }
66
  if (b & 1<<3) { writeone(); } else { writezero(); }
67
  if (b & 1<<4) { writeone(); } else { writezero(); }
68
  if (b & 1<<5) { writeone(); } else { writezero(); }
69
  if (b & 1<<6) { writeone(); } else { writezero(); }
70
  if (b & 1<<7) { writeone(); } else { writezero(); }
71
  if (b & 1<<8) { writeone(); } else { writezero(); }
72
  
73
  return 0;
74
}

von Tobias N. (silberkristall)


Angehängte Dateien:

Lesenswert?

So,

damit nicht wieder erzählt wird "das wird eh nichts" und bla bla bla 
hier mal ein Video von einem "Testfeld" und ein paar Fotos dazu. Sobald 
ich die weiteren Platinen fertig geätzt und "angehangen" habe folgt dann 
auch ein Video mit mehreren LEDs.

Als Codebasis fungiert mein Code von oben. Also läuft bisher super.

Hier mal das Video: http://www.vidup.de/v/j1QZB/

von Timmo H. (masterfx)


Lesenswert?

Vielleicht passt es ja hier rein, war gestern gerade auf 
hackaday.Driving the WS2811 at 800 kHz with an 8 MHz AVR 
http://rurandom.org/justintime/index.php?title=Driving_the_WS2811_at_800_kHz_with_an_8_MHz_AVR

von axelr. (Gast)


Lesenswert?

immerhin ;) siehste..

kannst ja mal die main() mit anhängen, damit mal sehen kann, wo die 
Farben durchgezählt werden.

von Tobias N. (silberkristall)


Angehängte Dateien:

Lesenswert?

so, morgen gehts ans ätzen. Anbei noch ein paar Bilder. :)

von Tobias N. (silberkristall)


Angehängte Dateien:

Lesenswert?

hier noch die Ätzvorlage. Es passen immer 15 Platinen auf eine 10 x 16 
cm Platine.

von Guest (Gast)


Lesenswert?

Tobias N. schrieb:
> hier noch die Ätzvorlage. Es passen immer 15 Platinen auf eine 10 x 16
> cm Platine.

Da hast Du aber arg dünne Leiterbahnen. Mach die dicker, Platz ist ja 
mehr als genug vorhanden!

von Tobias N. (silberkristall)


Lesenswert?

naja, was heisst arg dünn? 0,5mm für die Daten und die Versorgung für 
die LED. 1,2mm für die 5V die ja von oben nach unten weitergegeben 
werden. Weil ich werde immer 10 Stück in Reihe schalten (die Versorgung) 
und die Daten dann wiederrum verbinden. Daher 1A pro mm. Also habe ich 
1,2mm gewählt. Es wird ja "nur" die erste Platine in der Reihe "voll" 
belastet, bzw. die Leiterbahn, die weiteren "durchfahren" ja dann immer 
weniger Spannung.

Nur mit der LED "anbindung" bin ich noch am überlegen. Also wie ich die 
Daten durchgebe. Ich habe ja 10 mal 15 Felder

Soll ich die Verbindung nun von:

1 - 10 => 11 - 20 => 21 - 30 usw

oder

1 - 10 => 20 - 11 => 21 - 30 => 40 - 21 usw

oder

1 - 15 => 16 - 30 => 31 - 45 usw

oder

1 - 15 => 30 - 16 => 31 - 45 => 60 - 46 usw

?

Was wäre da also am sinnvollsten?

von Basti M. (counterfeiter)


Lesenswert?

Guest schrieb:
> Da hast Du aber arg dünne Leiterbahnen. Mach die dicker, Platz ist ja
> mehr als genug vorhanden!

Das lohnt leider nicht... der macht sein Ding... obs gut oder schlecht 
ist, ist erstmal egal... gibt schon Leute ;)

von klaus auf arbeit (Gast)


Lesenswert?

>Weil ich werde immer 10 Stück in Reihe schalten (die Versorgung)

Schreibfehler? Die kann man nicht in Reihe schalten...

Viel Erfolg!
KaA

von klaus auf arbeit (Gast)


Lesenswert?

Soll ich die Verbindung nun von:
1 - 10 => 11 - 20 => 21 - 30 usw
oder
1 - 10 => 20 - 11 => 21 - 30 => 40 - 21 usw
oder
1 - 15 => 16 - 30 => 31 - 45 usw
oder
1 - 15 => 30 - 16 => 31 - 45 => 60 - 46 usw
?
Was wäre da also am sinnvollsten?

Ist egal, macht man doch nachher über das Mapping im Framebuffer.

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.