Dimmen per PWM - höhere Auflösung bei niedrigen Werten

Hallo Gemeinde,

Ich steuere mit einem Nano Klon von Elegoo und einem NPN Transistor (BD911, 330 Ohm Basiswiderstand) ein “Abmi-Light” hinter meinem TV an. Dabei handelt es sich einfach um einen aufgeklebten warmweißen LED-Streifen. Ich habe also einen PWM-Pin mit dem Transistor verbunden und kann über die Funktion “analogWrite” nun das Licht in 255 Schritten dimmen, ich rede daher ab nun von Helligkeit 0 bis 255 und meine den Wert, mit dem analogWrite arbeitet.
Prinzipiell funktioniert das ganze, jetzt das Problem: Ich habe festgestellt, dass bei sehr niedrigen Helligkeiten (die minimale genutzte beträgt bei mir 8 ) die einzelnen Schritte deutlich wahrnehmbare Unterschiede machen, weshalb an ein langsames und dennoch gleichförmiges Dimmen leider nicht zu denken ist. Bei hohen Helligkeiten ist das irgendwann kein Problem mehr, selbst der Unterschied zwischen 200 und 220 z.B. ist kaum feststellbar. Das ganze ist auch nicht wirklich verwunderlich, sind doch mit LED und Transistor mehrere nichtlineare Bauteile im Spiel.

Meine Frage an euch: Habe ich dennoch irgend eine Chance, die Auflösung bei niedrigen Helligkeiten zu erhöhen ohne all zu viel Maximalhelligkeit einzubüßen? Meine Ideen: Anderer Transistor (Wenn ja, welcher könnte da besser sein?), größerer Basiswiderstand vorm Transistor (bringt das den gewünschten Effekt?) oder gar ein Kondensator mit Vorwiderstand vom PWM-Pin zu GND, der bei niedrigen PWM-Werten die Peaks glättet (und damit den Transistor weniger öffnen lässt???) und der beim Maximalwert keinen Einfluss haben dürfte, da ja dann die Spannung dauerhaft 5V beträgt.

Oder geht da sogar in der Software mehr, indem man mit anderen PWM Frequenzen arbeitet? Ich bin allerdings kein Professional was Software betrifft.

Bevor ich den Lötkolben nehme und ausprobiere würde ich da sehr gerne eure Meinung hören. Außerdem wird man ja nicht dümmer dadurch.

Code hier, Schaltplan im Anhang:

#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>

// zwischen der Verarbeitung von zwei Befehlen liegen ca. 100ms
const int RECV_PIN = 7;         // Receiver Pin
const int STAT_LED = 9;         // Status LED Pin
const int POWER_LED = 5;        // Output für Transistor Pin
const int T_Ein_Aus_blockiert = 1000;   // so lange kann man nicht wieder aus-/einschalten
int T_Heller = 400;           // Zeit zwischen zwei "Heller" Befehelen
int T_Dunkler = T_Heller;
bool Licht = LOW;             // Status Licht (An/AUS)
int Helligkeit = 0;           // Ist-Helligkeit des Lichts
int Soll_Helligkeit = 0;      // Soll-Helligkeit
int delta_Helligkeit = 5;     // Änderung der SOLL_Helligkeit IN EINEM SCHRITT (bei einem Befehl)
int last_Helligkeit = 70;     // vor letztem Ausschalten verwendete Helligkeit
unsigned long timer_Ein_Aus = 0;
unsigned long timer_Faden = 0;
unsigned long timer_Heller = 0;
unsigned long timer_Dunkler = 0;
IRrecv irrecv(RECV_PIN);       // An dieser Stelle wird ein Objekt definiert, dass den Infrarotsensor ausliest
decode_results results;        // Dieser Befehl sorgt dafür, dass die Daten, die per Infrarot eingelesen werden unter „results“ abgespeichert werden.


void setup() {

  Serial.begin(9600);
  pinMode (STAT_LED, OUTPUT);
  pinMode (POWER_LED, OUTPUT);
  irrecv.enableIRIn();          // Dieser Befehl initialisiert den Infrarotempfänger

}


void loop() {


  /////////// Daten von Fernbedienung auswerten und Soll-Helligkeit ermitteln ///////

  if (irrecv.decode(&results)) {                 // Wenn Daten empfangen wurden,

    if ((results.value == 3996512010 || results.value == 1624617592) && Licht == LOW && millis() - timer_Ein_Aus > T_Ein_Aus_blockiert) {   // Taste "Record" auf TV Fernbedienung, Wert 1 = 3996512010
      timer_Ein_Aus = millis();
      Soll_Helligkeit = last_Helligkeit;                // ANSCHALTEN
      Licht = HIGH;
    }

    if ((results.value == 3996512010 || results.value == 1624617592) && Licht == HIGH && millis() - timer_Ein_Aus > T_Ein_Aus_blockiert) {    // auch Taste "Record" auf TV Fernbedienung, Wert 2 (Werte alternieren) = 1624617592
      timer_Ein_Aus = millis();
      last_Helligkeit = Soll_Helligkeit;            // zuletzt verwendete Helligkeit merken
      Soll_Helligkeit = 0;                         // AUSSCHALTEN
      Licht = LOW;
    }

    if ((results.value == 1036336194 || results.value == 3264137720) && millis() - timer_Heller >= T_Heller) {    // Taste "Vorspulen" -> Heller
      timer_Heller = millis();
      Soll_Helligkeit = Soll_Helligkeit + delta_Helligkeit;
      Soll_Helligkeit = constrain(Soll_Helligkeit, 8, 255);
      Licht = HIGH;   // wennn das Licht aus war wird es nun eingeschaltet (auch per Vorspulen bzw. "Heller" möglich)
    }

    if ((results.value == 3062543560 || results.value == 3677454862) && millis() - timer_Dunkler >= T_Heller) {   // Taste "Zurückspulen" -> Dunkler
      timer_Dunkler = millis();
      Soll_Helligkeit = Soll_Helligkeit - 3 * delta_Helligkeit / 4; // delta wird verringert, da bei höherer Starthelligkeit ein größeres delta berechnet wird
      Soll_Helligkeit = constrain(Soll_Helligkeit, 8, 255);
    }

    irrecv.resume();                     // Der nächste Wert soll vom IR-Empfänger eingelesen werden
  }





  //////////// Helligkeit einstellen, abhängig von den empfangenen Daten ////////////////////


  int Abweichung_Soll_Helligkeit = abs(Soll_Helligkeit - Helligkeit);

  delta_Helligkeit = 40 * Helligkeit / 100 - Abweichung_Soll_Helligkeit;   // delta_Helligkeit abhängig von Helligkeit, bereits bestehender Abstand wird abgezogen, damit sich kein zu großer Abstand vom Sollwert aufaddiert
  constrain(delta_Helligkeit, 1, 50);

  int Geschw_Faden = (Helligkeit * Abweichung_Soll_Helligkeit) / 25; // Einzelschritte pro Sekunde beim Faden
  Geschw_Faden = constrain(Geschw_Faden, 1, 1000);                   // durch 0 teilen ist böse 
  
  int T_Faden = 1000 / Geschw_Faden;                       // Umrechnen auf Zeit pro Schritt beim Faden
  T_Faden = constrain(T_Faden, 1, 50);                      

  Serial.print(Geschw_Faden);
  Serial.print(" ");
  Serial.print(Soll_Helligkeit);
  Serial.print(" ");
  Serial.println(Helligkeit);

  if (Helligkeit != Soll_Helligkeit && millis() - timer_Faden >= T_Faden) {  // solange Helligkeit von Soll_Helligkeit abweicht -> Faden
    if (Helligkeit < Soll_Helligkeit) {
      ++Helligkeit;
    }
    else {
      --Helligkeit;
    }
    analogWrite(POWER_LED, Helligkeit);      // Licht einschalten/anpassen
    timer_Faden = millis();
  }

}

Danke schonmal und LG!

Auch Deine Augen sind nichtlinear.

Hier gibt es eine Betrachtung dazu und Tabellen, wie Du die Helligkeitswerte setzen kannst un eine fast gleichmäßige Änderung zu erzielen.

Wenn Dir die Tabellen mit 255 als Maximum nicht ausreichen, musst Du einen externen PWM-Baustein mit höherer Auflösung nutzen.

Gruß Tommy

Und ein Austausch der beteiligten Bauteile wird in diesem Fall keine Änderung bringen.

Ein Mosfet als Schalter hat den "kleinen" Vorteil, dass dieser im geschalteten Zustand niederohmiger ist und damit evtl. die Leds heller leuchten.

Tommy56:
Auch Deine Augen sind nichtlinear.

Hier gibt es eine Betrachtung dazu und Tabellen, wie Du die Helligkeitswerte setzen kannst um eine fast gleichmäßige Änderung zu erzielen.

Wenn Dir die Tabellen mit 255 als Maximum nicht ausreichen, musst Du einen externen PWM-Baustein mit höherer Auflösung nutzen.

Gruß Tommy

Tommy56:
Auch Deine Augen sind nichtlinear.

Das stimmt!

Tommy56:
Wenn Dir die Tabellen mit 255 als Maximum nicht ausreichen, musst Du einen externen PWM-Baustein mit höherer Auflösung nutzen.

Gruß Tommy

Um die Kurve für einen linearen Eindruck gehts mir nicht, das hab ich gelöst. Es ist wirklich die Auflösung um die es mir geht, denn im niedrigen Bereich ist einfach der Eindruck der Kontinuität nicht ganz da.
Externer Baustein ist eine Idee, merke ich vor.

Mit einem Texas Instruments TLC5940 hast du schonmal 12 statt 8 bit.

Ich glaube, der ist eher nicht geeignet. 16 Kanäle und Konstantstromausgang. Das ist für Transistorsteuerung wenig geeignet, das ist eher was für einzelne LED/LED-Gruppen.
Der TO hat uns ja nicht gesagt, was er verwendet.

Evtl. könnte er auch die PWM-Auflösung des AVR erhöhen. Das nur als Info. Ich habe es nicht getestet.

Gruß Tommy

Du kannst die PWM auch einfach „selbst erzeugen“. Mit delay()/millis() kannst Du je nach Code sehr brauchbares PWM erzeugen. Mit micros()/delayMicroseconds() geht's auch.

IMO müsste man das vollständig in Software hinbekommen können.

Gruß

Gregor

Tommy56:
Der TO hat uns ja nicht gesagt, was er verwendet.

1,2m davon:
LED Streifen

Tommy56:
Evtl. könnte er auch die PWM-Auflösung des AVR erhöhen. Das nur als Info. Ich habe es nicht getestet.

Gruß Tommy

Da ist wohl speziell vom Mega die Rede. Vielleicht hab ichs auch noch nicht überblickt.

Ein externer Baustein wäre schon eher was, ich müsste ja wiederum nur den Basisstrom meines BD911 genauer einstellen können, als mit dem Arduino-internen PWM. Wobei ich noch nicht weiß, wie das dann funktionieren soll.

Danke auch Gregorss für die Anregung, müsste mal sehen wie lange meine loop braucht, vor allem wenn ich die Infrarot-LED auslese (hab dafür ne Lib eingebunden, also keine Ahnung davon).

Mahimus:
... ich müsste ja wiederum nur den Basisstrom meines BD911 genauer einstellen können, als mit dem Arduino-internen PWM.

Du hast 2 Sachen nicht verstanden:
Einen Transistor steuert man nicht durch die Größe des Basisstroms weil die Verstärkung des Transistors nicht konstant ist (hängt vom Collektorstrom und Exemplarstreuungen ab).
PWM ist eine gepulste Spannung keine Analogspannung. Es wird das Verhältnis von ON zu OFF-Zeit varriert. Der Ansteuertransitor wird immer voll durchgeschaltet bzw gesperrt.

Grüße Uwe

Mahimus:
Da ist wohl speziell vom Mega die Rede. Vielleicht hab ichs auch noch nicht überblickt.

Ja, es geht um den Mega.

Mahimus:
Ein externer Baustein wäre schon eher was, ich müsste ja wiederum nur den Basisstrom meines BD911 genauer einstellen können, als mit dem Arduino-internen PWM. Wobei ich noch nicht weiß, wie das dann funktionieren soll.

Die Aufgabe ist letztendlich nicht so irre kompliziert, dass man es nicht in stinknormalem Code erledigen könnte. IMO.

Gruß

Gregor

uwefed:
Du hast 2 Sachen nicht verstanden:

Ich dachte eigentlich schon. Meine Formulierung ist allerdings falsch, da hast du Recht. Ich möchte dann eben das Tastverhältnis meines PWM Signals genauer einstellen, mit dem ich meinen Basisstrom ganz schnell an und ausschalte, mit dem ich meinen Kollektorstrom ganz schnell ein und ausschalte, mit dem ich meine LEDs...ihr wisst schon.

uwefed:
Einen Transistor steuert man nicht durch die Größe des Basisstroms weil die Verstärkung des Transistors nicht konstant ist (hängt vom Collektorstrom und Exemplarstreuungen ab).

Grüße Uwe

Diese beiden Aussagen widersprechen sich gar nicht. Nur weil der Zusammenhang nicht linear ist, heißt es nicht, dass ich nicht steuern kann. Ich habe, wenn mich nicht alles täuscht, sehr wohl gelernt, dass ein Bipolarer Transistor mit der Größe des Basisstroms gesteuert werden kann. Würde mich jetzt ehrlich gesagt wundern, wenn wir da nicht zusammen kommen.

gregorss:
Die Aufgabe ist letztendlich nicht so irre kompliziert, dass man es nicht in stinknormalem Code erledigen könnte. IMO.

Gruß
Gregor

Du meinst einfach in der Loop an und ausschalten? Wie gesagt ich müsste eben schauen wie lang die Loop maximal braucht, wenn sie schnell genug ist geht das natürlich und ist auch nicht irre kompliziert, hast Recht!

Mahimus:
... Du meinst einfach in der Loop an und ausschalten? Wie gesagt ich müsste eben schauen wie lang die Loop maximal braucht, wenn sie schnell genug ist geht das natürlich und ist auch nicht irre kompliziert, hast Recht!

Ja, genau. In einem meiner Gebastel mache ich das.

Wenn Du loop() halbwegs schlau programmierst, sind mehrere kHz PWM-Frequenz drin. Ich mache das, um einen mit 38 kHz modulierten IR-Puls zu erzeugen. Die 38 kHz lassen sich ziemlich einfach erzeugen - ganz ohne Bauteile, Interrupts und Magie.

Gruß

Gregor

Dass nur 8 Bit gehen liegt nur an der Arduino Software. Der Prozessor hat 8 Bit und 16 Bit Timer und man wollte da konsistent sein. Das ist von der Software her keine schlechte Idee, aber andererseits nicht so schön, das es die Funktion einschränkt.

Mit den 16 Bit Timern sind auch 9-16 Bit PWM möglich. Völlig in Hardware. Man muss es aber per Hand programmieren

Oder wie gesagt mit millis() nachbilden. Das kostet natürlich mehr Rechenleistung, aber je nach Anwendung ist das völlig ok

Diese beiden Aussagen widersprechen sich gar nicht. Nur weil der Zusammenhang nicht linear ist, heißt es nicht, dass ich nicht steuern kann. Ich habe, wenn mich nicht alles täuscht, sehr wohl gelernt, dass ein Bipolarer Transistor mit der Größe des Basisstroms gesteuert werden kann. Würde mich jetzt ehrlich gesagt wundern, wenn wir da nicht zusammen kommen.

Wenn man mit einem Transistor einen Verstärker bauen will dann muß man eine Schaltung benutzen wo die Verstärkung durch andere Maßnahmen definiert wird; nicht durch den Transistor selbst zB Arbeitspunkteinstellung oder Rückkopplung.

Aber zurück zu PWM. Ein PWM gesteuerter Transistor muß so angesteuert werden (genügend Basisstrom) damit er ganz durchsteuert. Nur so bleibt die Verlustleistung klein. Darum ist mehr Basisstrom besser als genau der mit dem ungenauen Wert Hfe errechnete. Wenn Du den Collektorstrom durch zu wenig Basisstrom begrenzt dann hat der Transistor genügend Verlustleistung daß er durchbrennt.

Grüße Uwe

uwefed:
Wenn man mit einem Transistor einen Verstärker bauen will dann muß man eine Schaltung benutzen wo die Verstärkung durch andere Maßnahmen definiert wird; nicht durch den Transistor selbst zB Arbeitspunkteinstellung oder Rückkopplung.

Eine Signalverstärkung ist aber jetzt nochmal eine ganz andere Aufgabe mit ganz anderen Systemanforderungen im Gegensatz zum Steuern von Leistungen.

uwefed:
Wenn Du den Collektorstrom durch zu wenig Basisstrom begrenzt dann hat der Transistor genügend Verlustleistung daß er durchbrennt.

Grüße Uwe

Und das gilt für jeden Transistor an jeder Last? Als Beispiel: Mein System oben hat eine maximale Leistung (konservativ) von 12W. Der Transistor kann (konservativ) 40W Verlustleistung ab. Meines Erachtens nach gibt es keinen Betriebszustand in dem ich das Teil kaputt bekommen würde. (Datenblatt BD911)
Korrigier mich bitte wenn ich mich irgendwo irre.

Mahimus:
Und das gilt für jeden Transistor an jeder Last? Als Beispiel: Mein System oben hat eine maximale Leistung (konservativ) von 12W. Der Transistor kann (konservativ) 40W Verlustleistung ab. Meines Erachtens nach gibt es keinen Betriebszustand in dem ich das Teil kaputt bekommen würde. (Datenblatt BD911)

Ja, oft vergißt man daß ein 40W Transistor zwar 40 W in Wärme umsetzen kann aber dazu einen Kühlkörper braucht, um diese Verlustleistung abzuführen und das Siliziumstück (Sperrschicht) im Transistor auf vertägliche Temperaturen halten kann.
Am Beispiel BD911:
Max Leistung: Ptot 90W
Max Temperatur der Sperrschicht: Tj 150°C
Gehäuse: TO-220 mit Wärmewiderstand gegen Luft (ohne Kühlkörper) 62,5 K/W

Bei Umgebungstemperatur von 35°C (es ist Sommer und ich lebe südlich von Deutschland) ohne Gehäuse (das staut die Wärme) ist die max zulässige Erwärmung 150°C -35°C = 115°C
Die max Leistung um den Transistor um 115°C zu erwärmen ist 115/62,5 = 1,84W. Diese Verlustleistung ist kleiner wenn im Gehäuse ein Hitzesteu einstellt.

Dein 90W Transistor kann also ohne Kühlkörper nicht mal 2W Verlustleitung vertragen.

Grüße Uwe

uwefed:
Ja, oft vergißt man daß ein 40W Transistor zwar 40 W in Wärme umsetzen kann aber dazu einen Kühlkörper braucht...

Danke erstmal an der Stelle für deine Hilfe.
Vergessen war hier wohl weniger das Problem, als dass ich die Daten einfach falsch interpretiert habe. Als ich Power Dissipation (P_D) gelesen habe und das abhängig von einer Temperatur, da dachte ich einfach das wäre die Gesamtleistung, die das gesamte Teil an die Umgebung abgeben kann.

Wo hast du denn den Wärmewiderstand des Gehäuses abgelesen? Im von mir verlinkten Datenblatt finde ich den Wert nicht.

Die 90W geben also eher an Maximum an, welches unabhängig vom Kühlkörper gilt?
Auch die Grafik "Active Region safe operating Area" (Datenblatt ganz unten rechts) ist ja so ziemlich irreführend. Da schau ich rein und bekomme ohne weiteren Hinweis suggeriert, dass ich das Produkt entsprechend der Angaben nutzen kann...

Die maximal mögliche Verlustleistung bezieht sich immer auf ein ausreichend gekühltes Bauteil. Das heißt Die Temperatur im Bauteil, wo die Abwärme entsteht, muß unter dem maximal zulässigen Wert bleiben.
Die Wärmeabgabe über einen Kühlkörper hängt vom Gehäuse des Bauteils, wie das Gehäuse am Kühlkörper montiert ist, vom Material, Form und Größe des Kühlkörpers, von der Belüftung bzw Luftfluß durch einen Ventillator, und von der Lufttemperatur ab.

Wo hast du denn den Wärmewiderstand des Gehäuses abgelesen? Im von mir verlinkten Datenblatt finde ich den Wert nicht.

gegoogelt

Die 90W geben also eher an Maximum an, welches unabhängig vom Kühlkörper gilt?

Wäre schön, ist aber nicht so. Das ist das Maximum bei ausreichender Kühlung sprich die max Sperrschichttemperatur wird nicht überschritten.

Also gib dem Transistor genügend Basisstrom sprich benutze den BD911 in Darlingtonschaltung oder besser nimm einen anderen Transistor oder einen MOSFET.

Der Transistor muß zwischen Sperren (es fließt kein Strom) und voll durchgeschaltet (Collektor -emittospannung ist klein) wechseln damit die Verlustleistung klein ist.

Grüße Uwe

uwefed:
Wäre schön, ist aber nicht so. Das ist das Maximum bei ausreichender Kühlung sprich die max Sperrschichttemperatur wird nicht überschritten.

Grüße Uwe

Hier hab ich undeutlich formuliert. Wollte fragen: Wenn man einen riesigen Kühlkörper hätte, der 200W bei delta_T von 10K abgibt, sind dann die 90W aus dem Datenblatt dennoch ein Maximum?

Wie ich den als Schalter betreibe weiß ich schon.
Hatte halt mit dem Gedanken gespielt mein PWM zu echtem analog zu glätten und zu sehen wie sich das System dann verhält hinsichtlich wahrgenommener Helligkeit abhängig von meiner jetzigen Stellgröße.