Hallo allerseits,
ich habe das Moodlight hier
Beitrag "noch ein AVR Moodlight"https://www.mikrocontroller.net/attachment/137389/AVR-Moodlight-20120308.tar.gz
nachgebaut mit geringen Abwandlungen in der Hardware, die aber nicht
relevant sind. Den IRMP-Teil habe ich heraus genommen, um ihn nur durch
RC5-Empfang zu ersetzen. In den allerersten Versuchen ließ sich das
Lichtelein mit vier verschiedenen Fernbedienungen (vier Protokolle!)
über IRMP bedienen.
Es funktioniert! Auf Mega 48 und größer und sogar mit dem AT90S4433, der
ja nur 128 Bytes SRAM hat.
Aber die Steuerung über RC5 funtioniert auch, aber nicht gut, bzw nur
mit Einschränkungen, die vermutlich der Autor der Software so nicht
hatte:
- der RC5-Empfang ist fehlerfrei
- Umschaltung der Modi funktioniert
- A_KEY_PLAY - Taste funktioniert oft, aber nicht immer
- A_KEY_OPERATE -Taste schaltet zwar das Licht aus, aber nie wieder an.
Die Hauptschleife wird nur noch alle ca 10 Sekunden durchlaufen.
- A_KEY_FWD -Taste schaltet den Farbwechsel schneller, aber unerwünscht
auf maximale Geschwindigkeit. Abstufungen unmöglich.
- A_KEY_REV - Taste schaltet den Farbwechsel langsamer, aber direkt auf
minimale Gweschwindigkeit ohne jede Abstufung. Haupütschleife braucht ca
0,5 Sekunden.
Zur Korrektur der oben genannten Fehler bräuchte ich als Voraussetzung
erst mal das Verständnis der Funktionsweise dieser PWM.
Klar ist, was "ml.flags" macht.
In "void moodlight_step(void)" werden den Variablen level_r, level_g,
level_b neue Werte zugewiesen je nach Modus. Unklar ist, was genau
gemacht wird.
Unklar ist, was im Interrupt genau gemacht wird: ISR(TIMER1_COMP_vect).
Unklar ist ebenso, was hier genau gemacht wird: setup_pwm(void). Die
Erklärung dabei reicht mir nicht wirklich. Es wird sortiert und
geschaut, welche LEDs überhaupt bedient werden müssen.
Was mit dem Arrays genau gemacht wird: uint8_t pwm_next[4]; uint8_t
pwm_data[4]; uint16_t pwm_time[4];
Mir fehlt das Verständnis, wie diese PWM grundlegend funktioniert.
Exponentiell längeres Beschäftigen mit dem Thema bringt nichtmal linear
voranschreitenden Fortschritt.
Es wäre halt schön, wenn es richtig funktionieren würde. Natürlich
könnte man irgendwelche festen Werte einstellen und sich das wechselnde
Farbspiel
anschauen.
Vielleicht hat jemand da den Durchblick direkt, weil eine Software-PWM
immer nach dem gleichen Schema abläuft, ähnlich wie beim Berechnen
digitaler Filter ja jeder unbeteiligte von Weitem sofort sieht, daß da
auf dem Blatt eine SINC-Funtion drauf steht.
Kann mir da jemand bitte ansatzweise auf die Sprünge helfen?
mit freundlichem Gruß
Christian S. schrieb:> ich habe das Moodlight hier> Beitrag "noch ein AVR Moodlight"
Ja, das kenn ich ;)
> Den IRMP-Teil habe ich heraus genommen, um ihn nur durch> RC5-Empfang zu ersetzen.
Und warum das? IRMP kann doch selber schon RC5.
> Zur Korrektur der oben genannten Fehler bräuchte ich als Voraussetzung> erst mal das Verständnis der Funktionsweise dieser PWM.
...
> In "void moodlight_step(void)" werden den Variablen level_r, level_g,> level_b neue Werte zugewiesen je nach Modus. Unklar ist, was genau> gemacht wird.
Die drei level_* Variablen sind das Endergebnis aller Verarbeitung und
geben die Helligkeit der 3 LED vor. Wichtiger für das Verständnis ist
1
struct{
2
uint8_tmode;/* the active moodlight mode */
3
uint8_tdelay;/* run moodlight_step() every "delay" PWM cycles */
4
uint16_tcycle;/* counter used by modes */
5
uint8_tflags;/* on/off, stopped, direction */
6
}ml;
mode wählt eines der (derzeit 3) Programme aus. Die Variable wird in
moodlight_step() verwendet in der großen switch() Anweisung.
delay ist sozusagen der Vorteiler. Die Variable tick wird bei jedem
PWM-Zyklus eins runter gezählt. Beim Nulldurchgang wird dann
moodlight_step() aufgerufen und setzt tick wieder auf delay.
cycle ist der Hauptzähler jedes Moodlight-Programms. Die Idee dahinter
ist, daß jedes Programm eine zyklische Folge von Farbkombinationen
durchläuft. Wenn man die Farbkombinationen durchnumerieren würde, dann
wäre cycle einfach die Nummer der aktuellen Konbination. Und
moodlight_step() würde cycle einfach eins hochzählen und die nächste
Kombination aus z.B. einer Tabelle holen. Den "Anschlag" für den Zähler
setzt dabei jedes Programm für sich selber.
Da die beiden ersten Programme die drei Grundfarben einfach nur per
linearer Rampe hoch- bzw. runterdimmen, braucht man aber keine Tabelle,
sondern kann die Position jeder Farbe auf der Rampe direkt aus dem
Zählerstand ausrechnen. Das machen die if() Kaskaden innerhalb der
switch() Klammer in moodlight_step(). Das dritte Programm ist sogar noch
einfacher, weil es einfach nur zwischen 7 Mischfarben umschaltet.
> Unklar ist, was im Interrupt genau gemacht wird: ISR(TIMER1_COMP_vect).
Das ist das Arbeitspferd der PWM. Der Interrupt wird innerhalb einer
PWM-Periode maximal 4-mal aufgerufen. Einmal zum Beginn (Zählerstand 0)
und dann bis zu 3 weitere Male bei Zählerständen die durch die
Helligkeiten der 3 Kanäle bestimmt werden. Weil diese ISR sehr kurz sein
muß, ist sie tabellengesteuert über die 3 Arrays:
> uint8_t pwm_next[4]; uint8_t pwm_data[4]; uint16_t pwm_time[4];> Unklar ist ebenso, was hier genau gemacht wird: setup_pwm(void).
Hier werden die 3 vorgenannten Arrays anhand der level_* Variablen
befüllt. Auch die Gammakorrektur findet hier statt.
> Mir fehlt das Verständnis, wie diese PWM grundlegend funktioniert.
Ja, die ist etwas trickreich :D
Nehmen wir einfach mal an, unsere 3 PWM-Kanäle sollten mit 10%, 20% und
50% Helligkeit leuchten und wir hätten 10ms PWM-Periode (=100Hz
Wiederholrate). Dann hätten wir folgende Impulse zu erzeugen:
1
Pin1: ...##__________________##____________...
2
3
Pin2: ...####________________####__________...
4
5
Pin3: ...##########__________##########____...
6
7
<- PWM-Periode ->
8
9
ISR: x x x x
Innerhalb einer PWM-Periode ändern sich unsere 3 Ausgangspins genau 4
Mal:
1. zu Beginn werden alle Pins auf H gesetzt
2. nach 1ms wird Pin 1 wieder auf L zurück gesetzt
3. nach 2ms wird Pin 2 wieder auf L zurück gesetzt
4. nach 5ms wird Pin 3 wieder auf L zurück gesetzt
Eine herkömmliche (dumme) PWM-Implementierung löst die ISR bei jedem
möglichen Umschaltpunkt aus. Wenn sie z.B. eine Auflösung von 1:1000
schaffen will (knapp 10 Bit) dann alle 10ms/1000 = 10µs. Allerdings
wissen wir bereits, daß sie in 996 Fällen nichts zu tun haben wird.
Nur zu den 4 Zeitpunkten die ich oben mit x gekennzeichnet habe, ist
wirklich etwas zu tun. Und was ich nun mache: ich löse die ISR auch
wirklich nur 4-mal aus.
Das Array pwm_time[] enthält dazu die bis zu 4 Zählerstände, bei denen
die ISR gemäß obigem Schema per Output-Compare ausgelöst wird. Das Array
pwm_data[] enthält 3 Bits entsprechend den 3 Pins. Und pwm_next[]
verkettet die Zeitpunkte. Dadurch kann man Zeitpunkte überspringen. Das
braucht man, wenn z.B. 2 Kanäle die gleiche Helligkeit haben.
Im Extremfall sind alle 3 LED komplett aus (oder alle auf 100%). Dann
muß die ISR nur einmal beim Zählerstand 0 aufgerufen werden. Auch das
ist eine Besonderheit dieser Implementierung: sie erlaubt echte 0% und
100%, bei denen die LED nie an- bzw. nie ausgeschaltet wird.
Was wäre noch zu sagen? Die PWM-Periode entspricht einem Durchlauf des
(16 Bit) Timer1. Die Gamma-Tabelle mappt jeden der 204 möglichen
Helligkeitswerte auf einen Zählerstand. Der Abstand zwischen zwei
aufeinanderfolgenden Tabelleneinträgen muß dabei groß genug sein, daß
die ISR dazwischen fertig wird. Dadurch ist die Anzahl der
realisierbaren Helligkeitswerte begrenzt.
Es gibt noch einen kleinen Trick: durch die Gamma-Korrektur werden die
Abstände zwischen den möglichen Schaltpunkten nach rechts hin (gegen
Ende der PWM-Periode) immer größer. Da wir nun nicht wollen, daß
Änderungen der Helligkeiten der PWM dazwischen pfuschen, werden
sämtliche Änderungen an den 3 level_* Variablen (in moodlight_step())
bzw. den 3 Arrays (in setup_pwm()) in die letzte "Lücke" vor dem
Zähler-Nulldurchgang gelegt.
> Kann mir da jemand bitte ansatzweise auf die Sprünge helfen?
Wenn obiges nicht hilft, dann weiß ich auch keinen Rat mehr.
Hallo,
IRMP habe ich entfernt, da es mir recht umfangreich erscheint und es
auch in der Handhabung der diversen Einstellungen komplizierter ist,
weshalb ich mich auf den simplen RC5-Decoder beschränken wollte, auch um
den Code zu verkleinern.
In aller Regel verwende ich nur diese eine RC5-Fernbedienung für
µC-Experimente. Der Rückschritt war somit billigend in Kauf genommen.
Wie gesagt, es funktionierte ja zu Beginn mit immerhin vier
verschiedenen Fernbedienungen.
Es freut mich, daß Axel sich gemeldet hat, denn er kenn ja seinen Code
am besten.
Danke für die Erklärungen. Die Ausführungen muß ich erst mal in aller
Ruhe durcharbeiten. Vielleicht kann ich dann eine "ich habe es
kapiert"-Meldung bringen.
Es handelt sich wohl um eine PWM wie in diesem Artikel, Absatz
"Intelligenter Lösungsansatz"
https://www.mikrocontroller.net/articles/Soft-PWM#Noch_mehr_LEDs
.
mit freundlichem Gruß
Hallo,
ich möchte mich noch nachträglich bei Frank M für das IRMP-Projekt und
bei Axel für den zur Verfügung gestellten Moodlight-Code bedanken.
In der Zwischenzeit konnte ich Fehler beseitigen. Nur das
Einschalt-Problem nach dem Ausschalten ist noch geblieben und hält sich
hartnäckig.
mit freundlichem Gruß
Christian S. schrieb:> In der Zwischenzeit konnte ich Fehler beseitigen. Nur das> Einschalt-Problem nach dem Ausschalten ist noch geblieben und hält sich> hartnäckig.Christian S. schrieb:> A_KEY_OPERATE -Taste schaltet zwar das Licht aus, aber nie wieder an.> Die Hauptschleife wird nur noch alle ca 10 Sekunden durchlaufen.
Kannst du mal deinen Code zeigen?
Es ist wichtig, daß du beim Einschalten die tick Variable manuell auf
1 setzt. Das sorgt dafür, daß beim nächsten PWM-Zyklus sofort
moodlight_step() ausgeführt wird und die LED-Helligkeiten updated (die
waren ja beim Ausschalten auf 0 gesetzt worden). Sonst kann es bis zu 2
Sekunden dauern bis man den Effekt vom Einschalten sieht.
Ich habe den Code jetzt einige Male refactored und das alles in eine
Funktion moodlight_wakeup() ausgelagert. K.A. ob das in deiner Version
der Software schon drin ist. Wenn ja, reicht es diese Funktion
aufzurufen.
Hallo,
nach dieser Ermunterung zur Fehler-Diskussion anbei mein Quelltext.
Folgender Fehler ist beseitigt:
Das aprupte Umschalten der Geschwindigkeit von minimal auf maximal lag
an dem fehlenden Löschen des Variablen-Inhaltes der Variablen i,
die die gedrückte Taste nach "main" überträgt. Sie muß nach der
Auswertung gelöscht werden und nur dann. Ein Fehler also, den ich wohl
selbst vor mehr als einem Jahr beim Umbau von IRMP auf den simplen
RC5-Decoder eingebaut habe. Vorwärts-Rückwärts geht perfekt.
Da ich gestern meine Mitleser nicht so sehr mit Fehlermeldungen fordern
mochte, kommt nun die Fehlerbeschreibung zu dem verbliebenen Fehler.
Nach Korrektur der restlichen Fehler ist einer noch immer geblieben:
Die on/off-Umschaltung geht immer nur aus, aber nicht wieder an.
Löscht man diese Abfrage "if (ml.flags & ML_FLAG_OFF) { return; } in
"void moodlight_step(void)", läßt sich das on/off-Flag tatsächlich
korrekt umschalten.
Ist die Zeile mit drin, gelingt das Einschalten nie wieder und die
Hauptschleife in Main wird nur alle 10 Sekunden durchlaufen. (siehe
toggle LED in main)
Jedes Mal wenn die Toggle-Led aufgeblitzt hat, erscheinen 3...9 Ausgaben
auf dem Terminal, falls die UART-Ausgaben in moodlight.c aktiviert sind.
Mit einer Ausgabe aufs UART läßt sich das Flag überprüfen und es wird
das gesetzte Flag fortwährend angezeigt:
if (ml.flags == ML_FLAG_OFF) uartputs( " OFF \n\r" );
Definiert man eine zusätzliche Taste, die nur Einschalten kann,
funktioniert sie auch nicht. Das Flag bleibt immer gesetzt.
Gegenspieler sind diese beiden hier in "void moodlight_remote(void)":
case A_KEY_OPERATE: /* on/off */ //schaltet ab, aber nicht mehr
ein. Hauptschleife wird nur noch selten durchlaufen. Deshalb zu seltener
RC5-Empfang.
{
ml.flags ^= ML_FLAG_OFF;
if (ml.flags & ML_FLAG_OFF) {
level_r = 0;
level_g = 0;
level_b = 0;
} else {
moodlight_wakeup();
}
break;
}
und
case A_KEY_7:
{
ml.flags = 0;
//ml.flags &= ~ML_FLAG_OFF; //das einzelne Bit Nr7
löschen
moodlight_wakeup();
break;
}
Bisher habe ich mich nur mit der Beseitigung der Fehler bis zum
beschrieben Stand befasst, so daß dieses Thema hier
"... tick Variable manuell auf 1 setzt."
noch nicht erprobt ist. Jedenfalls geht es auch nach Minuten nicht mehr
an, da ja das Flag immer gesetzt bleibt.
Zum Quelltext:
Die UART-Ausgaben sind zur Zeit deaktiviert, funktionieren aber.
Die ungeschickt aufgebauten header-Dateinen möge man mir nachsehen, ich
bin an anderen Problemen hängen geblieben als diese zu ordnen.
Sleep-mode heraus genommen.
Der RC5-Decoder ist derjenige von Peter.
Alles mit Temperatur und deren Anzeige ignorieren, da dies zu einem
ehemaligen anderen kombinierten Projekt gehörte. Ist auskommentiert.
Die zusätzlich eingebaute Taste, die nur einschalten soll, geht auch
nicht.
Grundlage ist der Code von Axel: Datum: 08.03.2012 10:01 siehe Link
ganz oben
Hoffentlich ist es nicht wegen Stack-Überlauf.
LCD-Routines_3.c
main_test1.c
rc5_3.c
moodlight_3.c
uart_chr2.c
Device: at90s4433
Program: 3366 bytes (82.2% Full)
(.text + .data + .bootloader)
Data: 100 bytes (78.1% Full)
(.data + .bss + .noinit)
Es wäre schon schön, wenn das auch noch gehen würde.
Aus dem Originaltext: "In der Hoffnung, jemanden zu inspirieren :)"
mit freundlichem Gruß