Pulsweitenmodulation

Programmieren

Wie kann man die Helligkeit einer LED verändern? Die Lösung nennt sich Pulsweitenmodulation.

Pulsweitenmodulation (PWM)

Letztes Mal haben wir gelernt, analoge Eingänge zu verwenden und einen Dämmerungsschalter zu erstellen. Dieses Mal werden wir einen Blick auf die analogen Ausgänge werfen - zumindest in gewisser Weise. Es gibt echte Digital-Analog-Wandler (DACs) für die analoge Ausgabe, aber der Arduino Uno hat keinen. Stattdessen werden wir uns mit Pulsbreitenmodulation oder kurz PWM befassen.

Wenn wir PWM verwenden, ändern wir nicht die Spannung, sondern stattdessen die Einschaltdauer der LED. Indem wir einen digitalen Ausgang schnell ein- und ausschalten, können wir eine LED dimmen. Wenn dies schnell genug geschieht, bemerkt man das Flackern nicht, stattdessen nehmen wir die LED als weniger hell wahr. Diese Technik eignet sich perfekt für LEDs, da deren Helligkeit nicht einfach durch Änderung der Spannung eingestellt werden kann. Dies könnte dazu führen, dass die LED die Farbe leicht verändert und unterhalb einer bestimmten Spannung erlischt die LED einfach. Theoretisch kann das PWM-Signal mit einem Tiefpassfilter in ein echtes analoges Signal umgewandelt werden, aber das ist eindeutig keine Aufgabe für Anfänger und wird uns mit unserer LED sowieso nicht weiterhelfen.

Das Arduino kann an den mit ~ gekennzeichneten Pins ein schnelles PWM-Signal erzeugen. Üblicherweise wird dabei eine Frequenz von 490 Hz verwendet. Das bedeutet, dass die LED 490 Mal pro Sekunde ein- und ausgeschaltet wird. Die an den Pins 5 und 6 verwendete Frequenz von 980 Hz ist noch höher. Im normalen Arduino-Code können wir diese Frequenz nicht ändern, aber wir können den Zeitanteil, für den die LED in jeder Periode eingeschaltet ist, ändern. Dies wird auch als Tastgrad (engl. duty cycle) bezeichnet. Ich habe den Signalverlauf für drei verschiedene Tastgrade einmal aufgezeichnet. Wie du sehen kannst, ist die LED bei 50 % Tastgrad für die Hälfte der Zeit an und für die andere Hälfte der Zeit ausgeschaltet. Ein Tastgrad von 0 % entspricht einer dauerhaft ausgeschalteten LED und ein Tastgrad von 100 % bedeutet, dass die LED permanent leuchtet. Statt einer Angabe in Prozent verwendet der Arduino Werte zwischen 0 und 255, wobei 255 einem Tastgrad von 100 % entspricht. PWM-Signal mit unterschiedlichem Tastgrad

Aufbau des Schaltkreises

Schaltkreis mit Potentiometer und LED auf dem Breadboard

Wie üblich verbinden wir die LED über einen Vorwiderstand von 220 Ω. Dieses Mal ist sie an einen PWM-fähigen Pin angeschlossen. Ich habe für diesen Zweck Pin 9 gewählt. Um die Helligkeit unserer LED zu steuern, verwenden wir ein Potentiometer. Die beiden Seiten des Potentiometers sind mit GND und 5V verbunden, während der Schleifer in der Mitte mit dem analogen Pin A0 verbunden ist. Ich habe ein lineares Potentiometer mit einem Gesamtwiderstand von 10 kΩ verwendet. Der Schleifer gleitet über das Widerstandsmaterial. Genau in der Mitte haben wir die Hälfte des Widerstandsmaterials rechts und die Hälfte links von unserem Schleifer. Dies entspricht einem Spannungsteiler mit zwei 5 kΩ Widerständen. In der Mitte messen wir 2,5V. Durch Drehen des Potentiometers können wir Spannungen von 0 V bis 5 V erhalten. Wir messen diese Spannung mithilfe des analogen Eingangs A0, wo sie vom Analog-Digital-Wandler (ADC) in einen Wert zwischen 0 und 1023 umgewandelt wird. Auf Grundlage dieses Messwertes können wir den Tastgrad und damit die Helligkeit unserer LED einstellen.

Schreiben des Codes

Der benötigte Code ist wiedereinmal recht einfach. Wir kennen bereits die Funktion analogRead und brauchen nur eine neue Funktion: analogWrite.

  • analogWrite(pin, value)
    • pin: Nummer eines PWM-fähigen Pins
    • value: Tastgrad als Zahl zwischen 0 und 255

In unserem Code müssen wir nur den von analogRead zurückgegebenen Wert in den Bereich zwischen 0 und 255 skalieren und ihn an die Funktion analogWrite übergeben. Dazu dividieren wir den Wert einfach durch vier und schon kann es losgehen.
Hier ist der Code:

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {
  int dutyCycle = analogRead(A0)/4;
  analogWrite(9, dutyCycle);
}

Das Ergebnis

Wenn du am Potentiometer drehst, kannst du sehen, wie sich die Helligkeit der LED ändert. Vielleicht bemerkst du jedoch, dass die LED bei geringer Helligkeit sehr empfindlich reagiert und bei hoher Helligkeit fast keine Veränderung feststellbar ist. Liegt es am Code? Oder ist es die Art und Weise, wie wir das Potentiometer angeschlossen haben? Nein, alles ist in Ordnung, aber es scheint trotzdem nicht ganz korrekt zu funktionieren. Ich habe dir ein Video beigefügt, und wie du sehen kannst, leidet auch meine Lösung unter dem gleichen Problem. Was geht hier vor?

Das Problem liegt nicht im Code oder in der Schaltung, es liegt daran, wie wir Menschen Helligkeit wahrnehmen. Wir nehmen Helligkeitsänderungen nicht linear wahr, sondern bemerken bei geringerer Helligkeit viel kleinere Veränderungen als bei höher Helligkeit. Dies ist ein Problem, mit dem alle dimmbaren Lampen und auch Monitore zu kämpfen haben. Die Lösung nennt sich Gammakorrektur.

Gammakorrektur

Bei der Gammakorrektur kompensieren wir die Nichtlinearität des menschlichen Sehens mithilfe der Mathematik. Wir benötigen kleinere Schritte bei geringerer Helligkeit und größere Schritte bei hoher Helligkeit, um die Helligkeit der LED gleichmäßig zu verändern. Zu diesem Zweck kann die folgende Formel verwendet werden:

\[ I_{kompensiert} = I_{in}^\gamma \]

Der Wert für \(\gamma\) liegt normalerweise irgendwo zwischen 1,5 und 3. Ein üblicher Wert, der für Monitore verwendet wird, ist 2,2. Dies ist auch der Wert, den ich verwenden werde. Für die Potenz werden wir die Funktion pow(base, exponent) verwenden. Ich transferiere den Wert für die Potentiometerstellung in den Bereich zwischen 0 und 1 und benutze den Datentyp float, um Probleme bei der späteren Skalierung des Wertes zu vermeiden. Wenn wir den Wert zwischen 0 und 1023 als Basis verwenden, müssten wir anschließend durch irgendeinen seltsamen Wert dividieren, um das Ergebnis wieder in den Bereich zwischen 0 und 255 zu bekommen. Wegen des Exponenten 2,2 müssen wir zudem ohnehin Gleitkommazahlen verwenden. Und hier ist der Code:

void setup() {
  pinMode(9, OUTPUT);
}

void loop() {
  float input = analogRead(A0)/1023.0;
  float corrected = pow(input, 2.2);
  int dutyCycle = corrected*255; 
  analogWrite(9, dutyCycle);
}

Im Video kannst du sehen, dass wir jetzt einen gleichmäßige Helligkeitsverlauf erhalten. Mathe ist super, nicht wahr?

Vorheriger Beitrag Nächster Beitrag