Forum: Mikrocontroller und Digitale Elektronik LED flackert bei Dimmung per PWM


von Nico (nico123)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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.

von Hans M. (hansilein)


Lesenswert?

Kondensator am Potiausgang?

von Route_66 H. (route_66)


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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.

von Nico (nico123)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Nico ... schrieb:
> Aber wie implementiere ich eine Software-Hysterese?

Habe ich doch in

  Beitrag "Re: LED flackert bei Dimmung per PWM"

beschrieben.

von Falk B. (falk)


Lesenswert?

Oder man nimmt im Jahr 2012 gleich einen Drehgeber mit RICHTIGER 
Auswertung, dann hat man digiale Genauigkeit und Wiederholbarkeit OHNE 
Zappeln.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

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.

von Nico (nico123)


Lesenswert?

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
}

von Karl H. (kbuchegg)


Lesenswert?

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.

von Fred (Gast)


Lesenswert?

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...

von Εrnst B. (ernst)


Lesenswert?

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)

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

von Nico (nico123)


Lesenswert?

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!

von axelr (Gast)


Lesenswert?

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.

von Vlad T. (vlad_tepesch)


Lesenswert?

Ε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.

von Nico (nico123)


Lesenswert?

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!

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

von Vlad T. (vlad_tepesch)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
  }

von Nico (nico123)


Lesenswert?

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
...

von Falk B. (falk)


Lesenswert?

@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.

von Karl H. (kbuchegg)


Lesenswert?

Nico ... schrieb:
> Sorry!

Jau.

Mein Fehler.
Siehe das Posting über deinem
Beitrag "Re: LED flackert bei Dimmung per PWM"

von Vlad T. (vlad_tepesch)


Lesenswert?

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

von Nico (nico123)


Lesenswert?

Danke Karl Heinz, jetzt funktioniert es!

von Karl H. (kbuchegg)


Lesenswert?

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.

von xfr (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.