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ß
Warum hast Du denn IRMP durch einen simplen RC5-Decoder ersetzt? Ist doch eigentlich ein Rückschritt - zumal IRMP auch RC5 decodieren kann.
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_t mode; /* the active moodlight mode */ |
3 | uint8_t delay; /* run moodlight_step() every "delay" PWM cycles */ |
4 | uint16_t cycle; /* counter used by modes */ |
5 | uint8_t flags; /* 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ß
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.