PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RGB-LED-Fading: Divisionsprobleme



Bääääär
18.02.2009, 21:38
Hallo alle zusammen!

ich sitze hier momentan an einer RGB-LED Steuerung. Dabei ergibt sich folgendes Problem: Ich dimmer hier per Software-PWM LEDs mit 6bit Farbtiefe. Ich möchte nun zwischen zwei Farben faden. Dazu könnte ich einfach die Farben nacheinander je um 1 erhöhen oder erniedrigen und damit aufhören, wenn die gewünschte Farbe erreicht ist. Das hat aber den Nebeneffekt, dass nicht alle Farben zur gleichen Zeit ihren gewünschten Wert erreichen. Ich will, dass die LEDs über eine bestimmte Anzahl von Schritten ihre neue Helligkeit erreichen, sprich bei jeden Schritt genau um soviel erhöht werden, dass sie am Ende ihre gewünschte Farbe erreicht haben. Dazu gehe ich wie folgt vor:

Ich errechne die Differenz zwischen momentaner und gewünschter Helligkeit und teile diese durch die Anzahl der Schritte (nennen wir sie "n"). Dabei erhalte ich den zu erhöhenden oder zu verringernden Wert pro Schritt. Dann starte ich einen Interrupt und lasse diesen n-mal durchlaufen. Dabei addiere ich für jede LED diesen errechneten Wert. Rein rechnerisch müssten nach den n Schritten alle LEDs ihre gewünschte Helligkeit erreicht haben.
Das ist aber nicht der Fall, und je öfter ich dieses Fading wiederhole, desto mehr werden die Abweichungen sichtbar (ziemlich heftig sogar). Ich habe gelesen, dass die Division sehr ungenau ist. Welche Möglichkeit habe ich denn noch, die LEDs zu dimmen?

Vielen Dank,
Bääääär

Bääääär
18.02.2009, 22:13
Hmm, das ist jetzt zwar blöde, aber ich hab inzwischen eine Lösung gefunden, die ich hier kurz posten will:

Man hat eine Funktion, in der das Fading initialisiert wird:


// Die Originalhelligkeit wird in einer Hilfsvariable gespeichert und
// dabei um 4 Bit nach links verschoben
FadeValue = (LEDValue<<4);

// Die Veränderung der Helligkeit pro Stufe ist dann:
FadeStep = (LEDValue - DestinationValue);

// Nun wird ein Counter initialisiert und der Timer gestartet
FadeCounter = 0;
StartTimer();


Im Timercode steh dann folgendes:


FadeCounter++;

// Helligkeitswert erhöhen
FadeValue = FadeValue + FadeStep;

// Helligkeit übernehmen und dabei wieder um 4 bit nach rechts verschieben
SetLEDBrightness((FadeValue>>4));

// Timer beenden, wenn nötig
if (FadeCounter == 16) {
StopTimer();
}


Das Ganze bedeutet eigentlich nur, dass man die Nachkommastellen in FadeStep umgeht, indem man alles "hochmultipliziert", also nach links verschiebt. Ich verschiebe um 4 Bit, also kann ich 16 Schritte machen, bis die Helligkeit übernommen wurde, Für 32 Schritte verschiebt man eben um 5 Bit hin und her.

Naja, ich hoffe, es hilft vielleicht jemanden.

Bääääär

The Man
19.02.2009, 14:48
Hallo Bääääär,

hast du deine Gründe, warum du Software PWM machst? Hast du auch wie viele andere den Mega8? Sonst würde ich
eine Lösung vorschlagen, basierend auf Timer2 und Timer1. Bliebe noch Timer0, der mittels Preload immer noch die Möglichkeit bietet, das Restprogramm zeitkritisch ablaufen zu lassen. Die Probleme mit dem Fading sind klar, da du nur ganzzahlig oder zumindest wertdiskret arbeiten kannst.

mfg,
The Man

SprinterSB
19.02.2009, 15:05
Hallo alle zusammen!
[...]
Ich errechne die Differenz zwischen momentaner und gewünschter Helligkeit und teile diese durch die Anzahl der Schritte (nennen wir sie "n"). Dabei erhalte ich den zu erhöhenden oder zu verringernden Wert pro Schritt. Dann starte ich einen Interrupt und lasse diesen n-mal durchlaufen. Dabei addiere ich für jede LED diesen errechneten Wert. Rein rechnerisch müssten nach den n Schritten alle LEDs ihre gewünschte Helligkeit erreicht haben.
Das ist aber nicht der Fall, und je öfter ich dieses Fading wiederhole, desto mehr werden die Abweichungen sichtbar (ziemlich heftig sogar). Ich habe gelesen, dass die Division sehr ungenau ist. Welche Möglichkeit habe ich denn noch, die LEDs zu dimmen?

Prinzipiell ist es egal, ob du Software- oder Hardware-PWM nimmst. Wenn die Ansteuerung nicht stimmt, ist es weder mit der einen noch mit der anderen Methode exakt...

Im Endeffekt läuft du Geraden mit unterschiedlicher Steigung entlang: die x-Achse entspricht der Zeit, die y-Achse entspricht der Helligkeit bzw. dem Duty der auszugebenden PWM. Vorgegeben sind Start- und Endhelligkeit(en) der einzelnen Kanäle, Start- und Endzeit(en) sind jeweils gleich.

Weil Division teuer und zudem ungenau ist, sollte man den Einsatz geschickterer Algorithmen erwägen.

Erwähnenswert ist hier der Bresenham-Algorithmus, wie er auch zum Zeichnen von Strecken benutzt wird und zB in vielen Grafik-Chips implementiert ist. Es ist seht effizient, kommt ohne Division aus und erreicht immer exakt seine Zielkoordinaten. Auch den AVR wird's freuen, da nur addiert, subtrahiert und verglichen wird:

http://de.wikipedia.org/wiki/Bresenham-Algorithmus

Ich verwende ihn zur Zeichnen von Linien in meiner Scope-CLock, aber für deine Zwecke ist er ebenfalls hervoragend geeignet.

Bääääär
19.02.2009, 17:03
Hallo!

Ja, ich habe gewisse Gründe für die Software-PWM. Hardware-PWM ist zwar weniger Software-lastig, aber ich bin unflexibler, was die Pins betrifft. Vorallem aber habe ich nur eine begrenzte Anzahl an Kanälen. Ich brauche schonmal mindestens 6 Kanäle mit hoher Auflösung und weitere 13 mit geringer Auflösung. (Diese 13 evtl. auch gemultiplext).

Ich muss sagen, dass das so, wie ich es jetzt handhabe sehr schnell und unproblematisch geht, dennoch werde ich mir den Bresenham-Algorithmus mal anschauen, wer weiß, wo man den noch braucht!

Vielen Dank,
Bääääär