LED flackert bei Dimmung per PWM

OP #2898892
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
#2898907
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.
#2898927
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.
#2898947
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.
Gast #2898956
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.
OP #2898971
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
}
#2898979
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.
Gast #2898993
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...
#2899030
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)
#2899033
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.
OP #2899039
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!
Gast #2899042
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.
#2899052
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.
#2899080
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
  }
OP #2899095
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
...
#2899099
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.
Gast #2899135
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren