Hallo,
ich habe nun endlich meine tolle, aber leider kaputte, LED-Lampe
repariert und bei dieser Gelegenheit gleich eine kleine Steuerung mit
einem Attiny45 gebaut.
Folgende Features sind im Code enthalten:
- 3-Kanal PWM für die RGB-LEDs (theoretisch beliebig erweiterbar)
- Gammakorrektur der Helligkeit, da die Lichtempfindlichkeit des Auges
eher logarithmischer denn linearer Natur ist
- simultanes Fading der LEDs an einer Rampe von einem beliebigen Istwert
zu einem beliebigen Sollwert in einer beliebigen Zeit
Mit dem Code könnte man recht einfach mit einem größeren Controller ein
Moodlight nachbauen, bei mir soll es bei der Lampe bleiben ;-)
Viel Spaß beim Verwenden, Kommentare sind höchst erwünscht...
Grüße,
Christoph
Hallo Christoph,
Ich habe heute mal deine Schaltung probiert, ich hatte mir 2 neue
Atiny'45 zukommen lassen. Den habe ich mit AVR Studio 5 dann
programmiert bzw das Programm kompiliert. Leider Funktioniert es nicht
so wie gedacht. Die Led bei mir ist es jetzt die Rote an Pin5 des MC,
blitz nur ganz kurz auf, so ca. alle 2 sek. wenn ich auf die Taster
drücke und festhalte tut sich etwas so das die anderen auch aufblitzen
bzw ganz schnell flackern. Muss ich bei den Fuses etwas beachten habe
diese auf Standart gelassen und er läuft jetzt mit int Takt bei 8Mhz.
Gruß, Daniel J.
Problem gefunden, läuft nun Super. Echt tolle Sache, wobei ich die
Taster noch nicht ganz verstanden habe was die machen sollen.
Trotzdem ein Lob an dich.
Grüße, Daniel J.
Daniel Joachims schrieb:> Echt tolle Sache, wobei ich die> Taster noch nicht ganz verstanden habe was die machen sollen.
Die Sache mit den Tastern ist ein Problem, das speziell bei mir auftrat:
Meine Lampe hat ein Gehäuse, wo bereits 3 Taster eingebaut waren. Einer
ging drauf für an und aus, die anderen beiden habe ich dann einfach
genutzt, um damit "binär" den Modus auszuwählen. Ich wollte einfach mehr
als 2 Modi haben und kam so mit 2 Tastern auf 4 Modi ;-) Außerdem hatte
ich keine Portpins mehr frei, hat also super gepasst...
Drum die etwas umständliche Modusauswahl.
Grüße,
Christoph
Ja das habe ich auch herausgefunden mit der Mehrfachbelegung der Taste
nur ich habe noch nicht raus bekommen mit welcher Taste ich welchen Modi
einstelle bzw was die genauen Funktionen der Taser kombinationen ist.
Habe jetzt etwas rum probiert und auch den Source Code mal
durchgeschaut. Da kann ich halt die einzelnen Farben durchgehen und nach
einer Zeit läuft der Fading wieder weiter. Vielleicht könntes du ja
freundlicherweise noch ein paar Info's zu den Modi's geben. Ich habe
zurzeit die Tastenfunktion komplett rausgenommen sowie die Modi's
rausgenommen die ich eigentlich nicht brauche. Dann habe ich noch die
Fading Zeit vergrößert somit läuft jetzt ein ganz langsamer Fading wobei
ich noch den zustand weiß eingebaut habe :)
Auf jedenfall eine Super Sache.
Die Buttons werden bei einem Pin Change Interrupt abgefragt mit
1
input=(PINB>>3)&3;
und dieser Wert wird weiter unten mit den Konstanten
1
#define ALL 3
2
#define RED 2
3
#define GRN 0
4
#define BLU 1
verglichen. Die Taster sind low-aktiv, deshalb ist der Modus ALL aktiv,
wenn beide Taster nicht gedrückt sind, RED ist aktiv wenn der erste
Taster gedrückt ist (binär 2: 10 also erster Taster low, zweiter high)
und so weiter...
Dabei ist mir auch gleich aufgefallen, dass die beiden Zeilen
1
#define BUTTON1 PB3
2
#define BUTTON2 PB4
nutzlos sind, die können gelöscht werden.
Ich hoffe das war etwas verständlich ;-)
Ja ich habe es soweit verstanden, gut ich hatte eine anderen Funktion
erwartet deshalb habe ich es nicht verstanden. Ja dein Programm habe ich
in soweit abgeändert das ein normaler RGB-Moodlight ist welches ganz
langsam die Farben durchgeht. Habe noch den Zustand Weiß hinzugefügt und
die Funktion der Taster entfernt. Ich bin am überlegen wie ich es nun
hinbekomme das ich mit einem Taster das Programm pausier, wenn zb die
Farbe orange leuchtet das ich eine Taste drücke und dann bleibt er dort
stehen bis er erneut gedrückt wird.
Ansonsten Super Arbeit dein Programm.
Gruß, Daniel J.
Moin Christoph Brennig,
ich finde dein Projekt klasse und würde das am wochenende mal schnell
zusammen stecken.
ich werde nen atmega8 verwenden und hoffe das der code so 1 zu 1
übernommen werden kann!?
ich hab mich jetzt duch den quellcode gekämpft und ihn hoffentlich
ausreichend verstanden, was ich absolut nicht verstanden habe ist die
"rasterization algorithms"! wie funtioniert der und was macht der????
wenn ich kurz kritisch werden darf, du hast an machen stellen zu dürftig
kommentare verwendet, gerade wenn es darum geht das du makros
verwendest, oder so "einfache" sachen, warum du gerade das so gemacht
hast, wie du es gemacht hast, z.B. mit dem "temp = SREG;" und "SREG =
temp;" befehlen.
ich würde mich freuen wenn du mir noch ein paar hinweise geben kannst!
mfg timo
Hi Timo,
ja das mit der Kommentierung ist eine schlechte Angewohnheit, häufig
mach ich garkeine Kommentare ;-)
Also zum Rasterungs-Algorithmus:
Das Problem besteht ja darin, mit dem PWM-Sollwert in einer vorgegebenen
Zeit von einem Startwert zu einem Endwert zu gelangen, und das möglichst
gleichmäßig, also linear. Da ist mir eingefallen, dass es die gleiche
Problematik ist, wie wenn man z.B. im Paint eine Linie zieht (ohne
Kantenglättung), es musste also ein Algorithmus her, der das
Stufenpattern erzeugt. Ich bin im Wikipedia-Eintrag
http://de.wikipedia.org/wiki/Rasterung_von_Linien fündig geworden und
habe den dort beschriebenen einfachen Algorithmus für Linien zwischen 0°
und 45° verwendet (Anstieg m zwischen -1 und 1, negativ für die negative
Richtung).
Und das funktioniert so:
Es wird eine Hilfsvariable mit 0,5 initialisiert (im Programm aX mit X
für die Farben R,G,B) und der Anstieg der Linie mX ausgerechnet (im
Programm in der fade-Funktion). m wird in jedem Schritt auf a addiert.
Wenn a>=1, wird der PWM-Sollwert um 1 erhöht und a um 1 dekrementiert.
Danach folgt der nächste Schritt, bis der PWM-Endwert erreicht ist.
Ich habe für den ganzen Spaß Ganzzahlen verwendet, mit der Konstante
MULTI entsprechend der 1 und MULTI/2 entsprechend der 0,5 um es für den
AVR einfacher und schneller zu gestalten. Zusätzlich habe ich noch eine
Fallunterscheidung für die Abwärtsrichtung (m negativ) eingebaut. Und
das ist der ganze Zauber dahinter.
(Mir ist eben aufgefallen, dass ich aX nicht mit MULTI/2, sondern mit
127 initialisiere, das heißt der erste Schritt wird falsch errechnet
>facepalm< )
Ach ja: MULTI ist kleiner als 127, da bei der Berechnung von mX eine
Multiplikation ausgeführt wird, die bei höheren Werten größer als 32768
werden kann, was aber der höchste Wert in einer int16_t-Variable ist.
Als nächste mögliche Unklarheit ist da vielleicht noch die
Gammakorrektur. Das menschliche Auge hat eine eher logarithmische als
eine lineare Helligkeitswahrnehmung, also würde ein linear ansteigender
PWM-Sollwert so wahrgenommen, als ob die LED anfangs schnell an
Helligkeit gewinnen würde, im oberen Bereich jedoch immer langsamer hell
wird.
Um diesen Effekt zu eliminieren, wird der (lineare) PWM-Sollwert
zwischen 0 und 255 anschließend über eine Vergleichstabelle durch eine
vorher errechnete Logarithmusfunktion gejagt.
Auch beschrieben im Artikel LED-Fading
Wenn noch mehr Fragen bestehen, immer her damit ;-)
Grüße,
Christoph
edit:
Zur Portabilität müssten wahrscheinlich nur die Timer-Register-Namen
abgeändert werden, ggf. noch die Prescaler. Sonst kanns denk ich gleich
bleiben.
nochmal edit:
Wenn man so viel schreibt, vergisst man trotzdem die Hälfte ;-)
Zu der temp = SREG-Geschichte:
Das ist im Prinzip eine Sicherheitsmaßnahme bei Interrupts. Falls der
Interrupt während einer umfangreicheren Berechnung ausgelöst wird und
diese unterbricht, wird z.B. das Vorzeichenflag auf diesem Wege
erhalten. Ob das an der Stelle wirklich notwendig ist, weiß ich nicht,
es schadet auf jeden Fall nicht...
moin christoph,
das ging ja schnell ;-)
das mit dem algorythmus nehme ich jetzt so mal hin, ich selber wäre
warscheinlich net in der lage das so umzusetzten???
die gamma korrektur war mir schon bekannt, ich kenne den forumsbeitrag
von hier.
ich werde das morgen vormittag mal versuchen in betrieb zu nehmen, mal
sehen ob das funzt ;-)
mfg timo
PS: Doch einen hab ich noch....
dieser befehl "R_g = pgm_read_byte(&led_gamma[R]);" ruft doch die
adresse des arrays auf um die wert zu übergeben!!!???? sehe ich das
richtig????
gibt es dafür noch eine einfache variante, die ohne makros in c++
geschriebn werden kann???
@ Timo:
Das große Array steht nur im Flash drin, weil der Attiny45 nur 256 Byte
SRAM hat und das Array aber schon so groß ist, somit bleibt kein Platz
für andere Variablen.
Der Atmega8 z.B. hat 1K SRAM, dann ist pgm_read_byte() nicht nötig und
es kann einfach das Array ohne das Schlüsselwort PROGMEM initialisiert
werden und wie jedes andere Array auch aufgerufen werden. Das
entsprechende #include kann dann natürlich auch entfallen.
Das & vor dem Array wird von pgm_read_byte() benötigt, da es die
Start-Adresse des Arrays erwartet.
@ Thomas:
Bitteschön ;-)