WS2812 Timing

OP #3177388
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.
Angehängte Dateien:
Gast #3177424
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
OP #3177810
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?
Gast #3177815
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.
Gast #3178505
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.
Gast #3178540
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
#3178582
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.
Gast #3178586
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...
#3178631
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
#3178635
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.
OP #3178801
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
}
OP #3179218
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
}
OP #3182129
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/
Angehängte Dateien:
OP #3184974
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?
Gast #3185212
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren