Hallo an Alle! Ich habe ein kleines Problem und brauche eine Idee oder einen Denkanstoß. Meine 10W-LED betreibe ich an einem LED-Treiber (LM3404), den ich per Tiny dimmen kann. Der Sollwert wird durch ein Poti eingestellt (10bit-ADC) und meine 10bit-PWM läuft mit 122Hz. Um die LED für unser Auge linear zu dimmen nutze ich ein Array mit 64 Werten so wie es im Artikel "LED-Fading" hier im Forum beschrieben ist. Das Problem ist, dass es bei manchen Potieinstellungen die LED flackert. Dies kommt daher, da die PWM zwischen zwei Werten des Arrays springt. Wie kann ich das verhindern? Grüße und Danke, Nico
An Deiner Stelle würde ich kleinere Änderungen am Poti (ADC) ignorieren, d.h. erst mit einer PWM-Änderung reagieren, wenn der gemessene Unterschied zur letzten Spannung am ADC einen Schwellwert überschreitet.
Nico ... schrieb: > ies kommt daher, da die PWM zwischen zwei Werten des Arrays springt. Eventuell reicht es wenn du zwischen Poti Ausgang und Masse einen kleinen Kondensator machst (220nF - 1µF) Ausserdem nur die oberen 8 oder gar nur 6 Bit des Wandlers auswerten, mehr brauchst du bei 64 PWM Stufen eh nicht. Wenn das nicht reicht musst du softwareseitig einen Schmitt-Trigger einbauen. Sprich du merkst dir den letzten Potiwert bei dem du die PWM verändert hast und machst die nächste Änderung der PWM erst wenn sich der aktuelle Potimesswert um 1/256 oder 1/128 des Gesamtbereichs verändert hat.
Hallo! @urschmidt Daas "Glätten" mit Kondensator oder die Beschneidung auf weniger Bits hilft nicht. Es gibt immer einen Punkt wo sich - auch wenn nur das LSB des AD-Wandlers springt - auch z.B. das 6-te Bit "springt". Da ist es egal ob du die untersten 5 ausblendest. Die einzige Lösung ist die (Software-)Hysterese.
Route 66 schrieb: > Es gibt immer einen Punkt wo sich - auch wenn nur das LSB > des AD-Wandlers springt - auch z.B. das 6-te Bit "springt". Da ist es > egal ob du die untersten 5 ausblendest. Prinzipiell richtig, aber je nachdem wie stark die Messwerte 'zappeln' wird der Effekt durch den Tiefpass so gering daß er nicht mehr stört.
Hallo route_66, wie Du schon erklärt hast bringen der Kondensator oder das Abschneiden der Bits nichts. Aber wie implementiere ich eine Software-Hysterese?
Selbst dann gibt es immer noch die Möglichkeit, dass der Messwert genau am Schaltpunkt hin und her kippelt. Eine Hysterese in Software ist doch kein Hexenwerk. Man braucht dazu keine externe Beschaltung und auch sonst nichts ausser ein wenig Programmieren.
Nico ... schrieb: > Aber wie implementiere ich eine Software-Hysterese? Habe ich doch in Beitrag "Re: LED flackert bei Dimmung per PWM" beschrieben.
Oder man nimmt im Jahr 2012 gleich einen Drehgeber mit RICHTIGER Auswertung, dann hat man digiale Genauigkeit und Wiederholbarkeit OHNE Zappeln.
Nico ... schrieb: > Hallo route_66, wie Du schon erklärt hast bringen der Kondensator oder > das Abschneiden der Bits nichts. > > Aber wie implementiere ich eine Software-Hysterese? Wie sieht denn dein Programm jetzt aus? (Hättest du am Anfang schon zeigen sollen) Eine Hysterese ist ganz einfach: 'Hochgeschaltet' wird bei einem etwas größerem Wert als der Schaltschwelle. 'Runtergeschaltet' bei einem etwas kleineren. Sinn der Sache ist es, dass bei einem Wert der gleich (bzw. ungefähr gleich) der Schaltschwelle ist, immer die Stufe bevorzugt wird, die vorher schon vorhanden war. Wie man das konkret macht, hängt von der jeweiligen Implementierung bzw. den Details der Aufgabenstellung ab.
Nico ... schrieb: > Dies kommt daher, da die PWM zwischen zwei Werten des Arrays springt. > Wie kann ich das verhindern? Wenn das zwei benachbarte Werte des Arrays sind, hilft nur, die Tabelle feiner zu machen bzw. zwischen den Tabellenwerten zu interpolieren. Mittelwertbildung/Tiefpassfilterung würde da nicht helfen, weil damit allenfalls die Häufigkeit der Sprünge verringert wird, nicht aber die Sprunghöhe unsichtbar wird.
Karl Heinz Buchegger schrieb: > Wie sieht denn dein Programm jetzt aus?
1 | #include <avr/io.h> |
2 | |
3 | const uint16_t pwmtable[64] = |
4 | {
|
5 | 0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, |
6 | 11, 12, 13, 15, 17, 19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55, |
7 | 61, 68, 76, 85, 94, 105, 117, 131, 146, 162, 181, 202, 225, 250, |
8 | 279, 311, 346, 386, 430, 479, 534, 595, 663, 739, 824, 918, 1023 |
9 | };
|
10 | |
11 | int main(void) |
12 | {
|
13 | |
14 | // PA6 = Power-LED-PWM
|
15 | // PA7 = Poti
|
16 | |
17 | DDRA |= (1<<DDA6); |
18 | |
19 | ACSR |= (1<<ACD); |
20 | |
21 | TCCR1A |= (1<<COM0A1) | (1<<WGM11) | (1<<WGM10); //Timer1 |
22 | TCCR1B |= (1<<WGM12) | (1<<CS11); |
23 | OCR1A = 0; |
24 | |
25 | ADMUX |= (1<<MUX0) | (1<<MUX1) | (1<<MUX2); //ADC |
26 | ADCSRA |= (1<<ADEN) | (1<<ADPS1); |
27 | ADCSRB |= (1<<ADLAR); |
28 | DIDR0 |= (1<<ADC7D); |
29 | |
30 | ADCSRA |= (1<<ADSC); //Dummy-Messung |
31 | while(ADCSRA & (1<<ADSC)); |
32 | |
33 | unsigned int wert; |
34 | |
35 | while(1) |
36 | {
|
37 | wert = 0; |
38 | for(unsigned char i=0; i<16; i++) |
39 | {
|
40 | ADCSRA |= (1<<ADSC); |
41 | while(ADCSRA & (1<<ADSC)); |
42 | wert = wert + (ADCH / 4); |
43 | }
|
44 | wert = wert / 16; |
45 | |
46 | if(wert == 0) TCCR1B &= ~(1<<CS11); |
47 | else TCCR1B |= (1<<CS11); |
48 | |
49 | OCR1A = pwmtable[wert]; |
50 | |
51 | }
|
52 | return(0); |
53 | }
|
Nico ... schrieb: In dem Fall würde ich es so machen > wert = 0; > for(unsigned char i=0; i<16; i++) > { > ADCSRA |= (1<<ADSC); > while(ADCSRA & (1<<ADSC)); > wert = wert + (ADCH / 4); > }
1 | wert2 = wert / 16; |
2 | reminder = wert % 16; |
3 | |
4 | if( reminder > 1 && reminder < 14 ) |
5 | wert = wert2; |
Sinn der Sache ist es hier +-------+-------+-------+-------+-------+-------+ -> ADC Wert jweils kleine Bereiche einzubauen +-------+-------+-------+-------+-------+-------+ -> ADC Wert xxx xxx xxx xxx xxx xxx In denen sich die PWM-Stufe nicht ändert. Bewegt sich der ADC Messwert in diesem Bereich, dann wird einfach immer der letzte Zustand beibehalten. Erst wenn der Messwert eindeutig von diesem Umschaltpunkten entfernt ist, dann wird er für die Bestimmung der PWM Stufe herangezogen. De facto werden daher nur diese ADC-Messwerte +-------+-------+-------+-------+-------+-------+ -> ADC Wert ##### ##### ##### ##### ##### ##### zur Bestimmung der PWM-Stufe herangezogen. In den Bereichen dazwischen gilt immer die Stufennummer, die vorher eingestellt war, und ändert sich nicht. Wird dieser Totbereich 'von oben' betreten, dann gilt die obere Stufennummer. Wird er 'von unten' betreten, dann gilt die untere weiterhin.
Nachteilig bleibt bei jeder Art von Software-Hysterese, die mangelhafte Haptik - das "Spiel" bei der Einstellung (d.h. das sich beim Hoch- bzw. herunterdrehen die Zuordnung Drehwinkel zu Spannung ändert), kann bemerkbar und störend sein. Überlegt man, welche Funktionalität man sich eigentlich wünscht: - stetige, spielfreie und feinfühlige Umwandlung von Drehwinkel in Spannung, solange man am Poti dreht - Nachdem die Hand das Poti verlassen hat: Ein Festhalten des letzten vom Poti gelieferten und gespeicherten (also nicht mehr fortlaufend neu digitalisierten) Spannungswertes. ...sollte man vielleicht für den Controller erkennbar machen, ob sich gerade eine Hand am Potiknopf zu schaffen macht oder eben nicht, und dazu eine "Knopf-Touch"-Erkennung vorsehen. Bspw. mit Metallknopf und kapazitiv...
Nico ... schrieb: > Der Sollwert wird durch ein Poti eingestellt (10bit-ADC) und meine > 10bit-PWM läuft mit 122Hz. meine Meinung: Entweder man macht das ganz digital: Falk Brunner schrieb: > gleich einen Drehgeber mit RICHTIGER > Auswertung oder man bleibt analog: > Statt dem Tiny einen schnöden NE555 verbauen. > Damit geht die PWM Stufenlos mit dem Poti zu regeln, und mehr als 122 Hz > sind auch drinnen. > (Wegen der "pwmtable": Potis gibts mit passender Kennlinie fertig zu kaufen)
Ich würde den 10bit Wert nehmen und anhand dessen entscheiden, welche beiden Felder der LUT relevant sind und dazwischen linear interpolieren.
1 | lutInd = adcVal/16; // 10bit -> 6bit |
2 | remainder = adcVal%16; // nachkommaanteil <-- remainder mit a |
3 | |
4 | val1 = pwmtable[lutInd]; |
5 | val2 = pwmtable[lutInd+1]; // hier aufpassen wegen +1 |
6 | |
7 | pwmval = val1 + ((val2-val1)*remainder)/16; // hier wegen überläufen aufpassen |
Dann braucht man keine Hysterese mehr, weil das Kippeln von ein paar bits nicht stören sollte. eventuell kann man noch einen Mittelwert- oder Medianfilter davorsetzen, der sollte aber nicht zu lang sein, weil es sonst auch zu Verzögerungen kommt. Noch was: ich würde die PWM-Frequenz wenigstens nochmal verdoppeln. 120Hz ist ziemlich nervig.
Hallo Karl Heinz und danke für die Idee. Leider scheint es nicht zu funktionieren. Die LED blitz so nur bei Potiveränderung auf und ist sonst aus. Woher das kommt, verstehe ich noch nicht. Hallo Ernst, der Tiny hat den Sinn noch andere Aufgaben zu erledigen...deswegen ein µC. Die 122Hz haben den Sinn, dass der Treiber inkl. Beschaltung keine hörbaren Töne von sich gibt!
Fahr doch einfach an den nächsten Tabellenwert heran, statt dort hin zu springen. Dann sind die Flackereien "smooth". der Hannes macht das bei den Modellbaureglern so. Die Vorgabe erreicht den Motor nicht direkt, sondern es wird an die Vorgabe innerhalb einer bestimmten Zeit herangefahren. ich hatte seinerzeit mal die Ansteuerung eines Gokart-Elektromotors gepostet. da habe ich das auch so gelöst. Dort gab es eine Beschleunigungstabelle und abhängig von der "Gas" Stellung wurde die Leistung des Motors nicht sofort aus der Tabelle genommen, sondern erstmal mit dem eingegebenen Wert verglichen und alle 10mSek an den Tabelleneintrag angeglichen. Wenn Du willst, suche ich das mal raus. Axelr.
Εrnst B✶ schrieb: > oder man bleibt analog: > >> Statt dem Tiny einen schnöden NE555 verbauen. >> Damit geht die PWM Stufenlos mit dem Poti zu regeln, und mehr als 122 Hz >> sind auch drinnen. >> (Wegen der "pwmtable": Potis gibts mit passender Kennlinie fertig zu kaufen) dann hat man aber keine Logarithmierung der Kennlinie.
Hallo Axelr., eine kleine Regelung sozusagen!? Hätte nicht gedacht, dass dies so kompliziert wird. Nach der Beschneidung des ADC-Wertes auf 6Bit ist das Umspringen schon stark zurück gegangen. Ich denke das reicht mir erstmal so! Danke erstmal an Alle!
axelr schrieb: > Fahr doch einfach an den nächsten Tabellenwert heran, das könnte man wunderbar mit meinem Vorschlag kombinieren in dem man am ende jedes (oder einem vielfachen davon) PWM-Zyklus den PWM wert auf den nach obigen Ansatz berechneten heranführt Hat den Nachteil, dass wenn der Benutzer schnell an dem Poti dreht, die Änderung unangenehm nachläuft.
Nico ... schrieb: > Ich denke das reicht mir erstmal so! wenn der adcwert aber zwischen 9876543210 0010000000 und 0001111111 tocgelt, bist du wieder da. Die Symptome werden nicht abgeschwächt nur die Häufigkeit auf 1/16tel reduziert.
Nico ... schrieb: > Hallo Karl Heinz und danke für die Idee. Leider scheint es nicht zu > funktionieren. Die LED blitz so nur bei Potiveränderung auf und ist > sonst aus. Woher das kommt, verstehe ich noch nicht. Dein Code? Wenn du Hilfe mit deinem Code brauchst, dann zeige immer deinen Code. Auch wenn dir jemand ein Codestückchen gibt: zeige es her! Es besteht immer noch die Chance, dass du das Stückchen nicht so eingebaut hast, wie sich das der Autor vorgestellt hat. :-) Oder er hat einen Fehler gemacht. Du darfst natürlich das OCR Register auch nur dann beschreiben, wenn du einen neuen Wert hast. Du hast ja vorher die Variable 'wert' mit dem ADC überschrieben. Daher taugt der nicht mehr als Array-Index.
1 | wert2 = wert / 16; |
2 | reminder = wert % 16; |
3 | |
4 | if( reminder > 1 && reminder < 14 ) { |
5 | if(wert2 == 0) |
6 | TCCR1B &= ~(1<<CS11); |
7 | else
|
8 | TCCR1B |= (1<<CS11); |
9 | |
10 | OCR1A = pwmtable[wert2]; |
11 | }
|
12 | }
|
Sorry!
1 | ...
|
2 | while(1) |
3 | {
|
4 | wert = 0; |
5 | for(unsigned char i=0; i<16; i++) |
6 | {
|
7 | ADCSRA |= (1<<ADSC); |
8 | while(ADCSRA & (1<<ADSC)); |
9 | wert = wert + (ADCH / 4); |
10 | }
|
11 | wert2 = wert / 16; |
12 | reminder = wert % 16; |
13 | |
14 | if( reminder > 1 && reminder < 14 ) wert = wert2; |
15 | |
16 | if(wert == 0) TCCR1B &= ~(1<<CS11); |
17 | else TCCR1B |= (1<<CS11); |
18 | |
19 | OCR1A = pwmtable[wert]; |
20 | |
21 | }
|
22 | ...
|
@Vlad Tepesch (vlad_tepesch) >wenn der adcwert aber zwischen >9876543210 >0010000000 >und >0001111111 >tocgelt, bist du wieder da. Kauf einen Poti mit Rastung ;-) >>> Damit geht die PWM Stufenlos mit dem Poti zu regeln, und mehr als 122 Hz >>> sind auch drinnen. >>> (Wegen der "pwmtable": Potis gibts mit passender Kennlinie fertig zu >>> kaufen) >dann hat man aber keine Logarithmierung der Kennlinie. Doch, deswegen ja "Potis gibts mit passender Kennlinie", aka logarithmische Kennlinie. Naja, man kriegt das schon hin mit dem Poti. Und es wird sicher nicht EINE Massnahme allein wirken, sondern erst mehrere zusammen. 1.) Filter, damit das Zappeln auf ein Minimum reduziert wird. 2.) Bitbreite der ADC-Messung sinnvoll wählen, 10 Bit sind schon sehr ambitioniert, wenn gleich möglich. 2.) Dithering. Man merkt sich immer den vorhergehenden, verschiedenen Wert. Zwischem dem und dem aktuellen springt man immer hin und her, mit hier 122Hz. Damit hat man das Flackern ausgetrickst, weil es sowieso immer "flackert". Ggf. muss man etwas höher in der Frequenz gehen, so 200-300 Hz, damit effektiv 100-150 Hz rauskommen. Muss man mal probieren.
Nico ... schrieb: > Sorry! Jau. Mein Fehler. Siehe das Posting über deinem Beitrag "Re: LED flackert bei Dimmung per PWM"
Nico ... schrieb: > Die 122Hz haben den Sinn, dass der Treiber > inkl. Beschaltung keine hörbaren Töne von sich gibt! O_o Wenn das passiert ist irgendwas falsch und nein, nicht die Frequenz
Das funktioniert im übrigen allerdings nur, wenn du mit der Hand drehst, weil dein µC schneller ist, als du drehen kannst und es daher nicht passieren kann, dass eine Stufe übersprungen wird.
Sorry, falls ich etwas übersehen habe. Aber warum machst Du die Lookup-Tabelle nicht einfach größer, also z.B. 256 Einträge? Wenn das RAM nicht reicht, kannst Du sie ja per PROGMEM in den Flash legen. Oder Du interpolierst zwischen zwei Werten.
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.