Korrektur der nichtlinearen Helligkeit in LEDs bei Verwendung von PWM

Beim Ansteuern einer LED mit PWM skaliert die Helligkeit (wie ich sie wahrnehme) nicht linear mit dem Arbeitszyklus. Die Helligkeit steigt langsam an und steigt dann exponentiell mit dem Arbeitszyklus an.

Kann jemand eine Faustregel zur Verwendung als Korrekturfaktor oder eine andere Problemumgehung vorschlagen?

Als ich ein Paar Knight Rider-Manschettenknöpfe gemacht habe, musste ich x^10 verwenden, damit das Ausbleichen schön aussieht!
Sind Sie sicher, dass es nicht "die Helligkeit zunächst exponentiell zunimmt und dann langsam hochfährt"?
Ich glaube, unsere Augen reagieren logarithmisch auf die Helligkeit.

Antworten (12)

Für 16 Stufen ist es einfach, eine einfache Nachschlagetabelle "von Hand" zu erstellen und den 4-Bit-Wert in einen 8-Bit-Wert umzuwandeln, um ihn an den PWM-Controller weiterzuleiten: Dies ist die Komponente, die ich in meinem FPGA-LED-Array-Treiber verwendet habe. Für einen 8-Bit-Pegelregler benötigen Sie mindestens 11-12-Bit-Ausgang aus der Nachschlagetabelle.

library IEEE;
use IEEE.Std_logic_1164.all;

entity Linearize is
port ( reqlev : in std_logic_vector (3 downto 0) ;
    pwmdrive : out std_logic_vector (7 downto 0) );
    end Linearize;

architecture LUT of Linearize is
    begin
    with reqlev select
        pwmdrive <= "00000000" when "0000",
                    "00000010" when "0001",
                    "00000100" when "0010",
                    "00000111" when "0011",
                    "00001011" when "0100",
                    "00010010" when "0101",
                    "00011110" when "0110",
                    "00101000" when "0111",
                    "00110010" when "1000",
                    "01000001" when "1001",
                    "01010000" when "1010",
                    "01100100" when "1011",
                    "01111101" when "1100",
                    "10100000" when "1101",
                    "11001000" when "1110",
                    "11111111" when "1111",
                    "00000000" when others;
    end LUT;
Ich versuche herauszufinden, was Ihre Formel genau ist. Es ist bemerkenswert nah an f(x)=x^2, aber die Kurve ist nicht tief genug. f(x)=x^3/13 bringt mich viel näher.
Es ist keine Formel (nicht absichtlich) ... Ich habe die Anfangswerte des Linearisierers nur geschätzt :-). Ich habe dann das Array mit Strom versorgt, LED-Spalten in Helligkeitsreihenfolge angesteuert und die Werte optimiert, um eine gleichmäßige Rampe zu erhalten. Es ist wirklich einfach mit nur 16 Levels.
@ ajs410 - sieht eher so aus 2 n für mich: das erste 1Bit verschiebt sich mit jedem Schritt mehr oder weniger um 1 Position nach links.

Theoretisch sollte es exponentiell sein, aber ich habe die besten Ergebnisse für das Fading, indem ich eine quadratische Funktion verwende.

Ich denke auch, dass du es falsch verstanden hast. Bei niedriger Einschaltdauer ist die wahrgenommene Helligkeitszunahme viel größer als bei fast voller Einschaltdauer, wo die Helligkeitszunahme fast nicht wahrnehmbar ist.

Siehe auch Gamma-Korrektur .

Ich habe mich in den letzten Tagen mit diesem Thema beschäftigt, da ich das gleiche Problem habe ... ich versuche, LEDs mit PWM sichtbar linear zu dimmen, aber ich möchte die volle Auflösung von 256 Schritten. Der Versuch, 256 Zahlen zu erraten, um manuell eine Kurve zu erstellen, ist keine leichte Aufgabe!

Ich bin kein erfahrener Mathematiker, aber ich weiß genug, um einige grundlegende Kurven zu generieren, indem ich ein paar Funktionen und Formeln kombiniere, ohne wirklich zu wissen, wie sie funktionieren. Ich finde, dass Sie mit einer Tabelle (ich habe Excel verwendet) mit einer Reihe von Zahlen von 0 bis 255 herumspielen, ein paar Formeln in die nächste Zelle einfügen und sie grafisch darstellen können.

Ich benutze pic Assembler, um das Fading zu machen, und so können Sie sogar die Tabelle bekommen, um den Assembler-Code mit einer Formel ( ="retlw 0x" & DEC2HEX(A2)) zu generieren. Dadurch ist es sehr schnell und einfach, eine neue Kurve auszuprobieren.

Nach ein bisschen Herumspielen mit LOG- und SIN-Funktionen, dem Durchschnitt der beiden und ein paar anderen Dingen konnte ich nicht wirklich die richtige Kurve bekommen. Was passiert ist, dass der mittlere Teil des Fades langsamer ablief als die unteren und höheren Pegel. Auch wenn auf ein Aufblenden unmittelbar ein Abblenden folgt, gab es eine scharfe, wahrnehmbare Spitze in der Intensität. Was (meiner Meinung nach) benötigt wird, ist eine S-Kurve.

Eine schnelle Suche auf Wikipedia ergab die Formel, die für eine S-Kurve benötigt wird. Ich habe dies in meine Tabelle eingefügt und ein paar Anpassungen vorgenommen, damit es sich über meinen Wertebereich multipliziert, und kam auf Folgendes:

S-Kurve

Ich habe es auf meiner Anlage getestet und es hat wunderbar funktioniert.

Die Excel-Formel, die ich verwendet habe, war diese:

=1/(1+EXP(((A2/21)-6)*-1))*255

wobei A2 der erste Wert in Spalte A ist, wodurch A3, A4, ..., A256 für jeden Wert erhöht wird.

Ich habe keine Ahnung, ob dies mathematisch korrekt ist oder nicht, aber es führt zu den gewünschten Ergebnissen.

Hier ist der vollständige Satz von 256 Ebenen, die ich verwendet habe:

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05,
0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B,
0x0C, 0x0C, 0x0D, 0x0D, 0x0E, 0x0F, 0x0F, 0x10, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x26, 0x27, 0x29, 0x2B, 0x2C,
0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x43, 0x45, 0x47, 0x4A, 0x4C, 0x4F,
0x51, 0x54, 0x57, 0x59, 0x5C, 0x5F, 0x62, 0x64, 0x67, 0x6A, 0x6D, 0x70, 0x73, 0x76, 0x79, 0x7C,
0x7F, 0x82, 0x85, 0x88, 0x8B, 0x8E, 0x91, 0x94, 0x97, 0x9A, 0x9C, 0x9F, 0xA2, 0xA5, 0xA7, 0xAA,
0xAD, 0xAF, 0xB2, 0xB4, 0xB7, 0xB9, 0xBB, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
0xD0, 0xD2, 0xD3, 0xD5, 0xD7, 0xD8, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5,
0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2,
0xF2, 0xF3, 0xF3, 0xF4, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF8,
0xF9, 0xF9, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFC,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF
Diese Gleichung funktionierte perfekt für mich.

Ich habe ein ATtiny zum Beleuchten meines Decks verwendet. Die Helligkeit wird über einen Topf gesteuert, der mit dem ADC-Pin verbunden ist.

Die erprobte Exponentialfunktion und die darauf basierende PWM-Ausgabe scheinen die wahrgenommene Helligkeit linear zu erhöhen.

Ich habe diese Formeln verwendet:

out = pow(out_max, in/in_max)

Attiny85 @8MHz benötigte etwa 210 us, um die obige Berechnung durchzuführen. Um die Leistung zu verbessern, wurde eine Nachschlagetabelle erstellt. Da die Eingabe von 10-Bit-ADC erfolgte und der ATtiny-Speicher begrenzt ist, wollte ich auch eine kürzere Tabelle erstellen.

Anstatt eine Nachschlagetabelle mit 1024 Einträgen zu erstellen, wurde eine umgekehrte Nachschlagetabelle mit 256 Einträgen (512 Bytes) im Programmspeicher (PGMEM) erstellt. Es wurde eine Funktion geschrieben, um eine binäre Suche in dieser Tabelle durchzuführen. Diese Methode benötigt nur 28 uS für jede Suche. Wenn ich eine direkte Nachschlagetabelle verwende, würde dies 2 KB Speicher erfordern, aber die Suche würde nur etwa 4 uS dauern.

Berechnete Werte in der Nachschlagetabelle verwenden nur den Eingangsbereich 32–991, wobei der untere/obere Bereich des ADC verworfen wird, falls es ein Problem mit der Schaltung gibt.

Unten ist, was ich jetzt habe.

// Anti_log-Testprogramm

/*LED an PIN6(PB1) angeschlossen*/
#definiere LED 1

// Anti-Log (umgekehrte) Lookup-Tabelle
// y = 0-255 (PWM-Ausgabe), y_range=256
// x = 0-1023 (10-Bit-ADC-Eingang);
// Unter der Annahme, dass das untere/höhere Ende der ADC-Ausgangswerte nicht verwendet werden kann
// Verwerfen der ersten 32 und letzten 32 Werte.
// min_x = 32, max_x = 1023-min_x, x_range=1024-2*min_x
// ANTI_LOG[y] = round( x_range*log(y, base=y_range) + min_x )
// Bei einem Wert von x führe eine binäre Suche in der folgenden Tabelle durch
// dauert etwa 28uS für Attiny85 @8MHz Takt
PROGMEM prog_uint16_t ANTI_LOG[] = {
  0x0000, 0x0020, 0x0098, 0x00de, 0x0110, 0x0137, 0x0156, 0x0171, 0x0188, 0x019c, 0x01af, 0x01bf, 0x01ce, 0x01dc, 0x01e9, 0x01f5,
  0x0200, 0x020a, 0x0214, 0x021e, 0x0227, 0x022f, 0x0237, 0x023f, 0x0246, 0x024d, 0x0254, 0x025b, 0x0261, 0x0267, 0x026d, 0x0273,
  0x0278, 0x027d, 0x0282, 0x0288, 0x028c, 0x0291, 0x0296, 0x029a, 0x029f, 0x02a3, 0x02a7, 0x02ab, 0x02af, 0x02b3, 0x02b7, 0x02bb,
  0x02be, 0x02c2, 0x02c5, 0x02c9, 0x02cc, 0x02cf, 0x02d3, 0x02d6, 0x02d9, 0x02dc, 0x02df, 0x02e2, 0x02e5, 0x02e8, 0x02eb, 0x02ed,
  0x02f0, 0x02f3, 0x02f5, 0x02f8, 0x02fa, 0x02fd, 0x0300, 0x0302, 0x0304, 0x0307, ​​0x0309, 0x030b, 0x030e, 0x0310, 0x0312, 0x0314,
  0x0317, 0x0319, 0x031b, 0x031d, 0x031f, 0x0321, 0x0323, 0x0325, 0x0327, 0x0329, 0x032b, 0x032d, 0x032f, 0x0331, 0x0333, 0x0334,
  0x0336, 0x0338, 0x033a, 0x033c, 0x033d, 0x033f, 0x0341, 0x0342, 0x0344, 0x0346, 0x0347, 0x0349, 0x034b, 0x034c, 0x034e, 0x034f,
  0x0351, 0x0352, 0x0354, 0x0355, 0x0357, 0x0358, 0x035a, 0x035b, 0x035d, 0x035e, 0x0360, 0x0361, 0x0363, 0x0364, 0x0365, 0x0367,
  0x0368, 0x0369, 0x036b, 0x036c, 0x036d, 0x036f, 0x0370, 0x0371, 0x0372, 0x0374, 0x0375, 0x0376, 0x0378, 0x0379, 0x037a, 0x037b,
  0x037c, 0x037e, 0x037f, 0x0380, 0x0381, 0x0382, 0x0383, 0x0385, 0x0386, 0x0387, 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038e,
  0x038f, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e,
  0x039f, 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ab, 0x03ac, 0x03ad,
  0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03ba, 0x03bb,
  0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03bf, 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c7, 0x03c8,
  0x03c9, 0x03ca, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03cd, 0x03ce, 0x03cf, 0x03d0, 0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d3, 0x03d4,
  0x03d5, 0x03d6, 0x03d6, 0x03d7, 0x03d8, 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03db, 0x03dc, 0x03dd, 0x03dd, 0x03de, 0x03df, 0x03df
};

// Binäre Suche mit obiger Tabelle.
Byte-Antilog ( int x )
{
  Byte y = 0x80;
  int av;
  for( int i=0x40; i>0; i>>=1 )
  {
    av = pgm_read_word_near(ANTI_LOG+y);
    wenn ( v > x )
    {
      y -= ich;
    }
    Sonst wenn ( av < x )
    {
      y |= ich;
    }
    anders
    {
      gib y zurück;
    }
  }
  if (pgm_read_word_near(ANTI_LOG+y) > x)
  {
    y-= 1;
  }
  gib y zurück;
}


ungültige Einrichtung ()
{
  PinMode (LED, AUSGANG);
  digitalWrite (LED, NIEDRIG);
}

#define MIN_X 0
#define MAX_X 1024

Leere Schleife ()
{
  int ich;
  // antilog_drive
  für( i=MIN_X; i<MAX_X; i++ )
  {
    AnalogWrite (LED, Antilog (i));
    Verzögerung (2);
  }
  for( --i; i>=MIN_X; i-- )
  {
    AnalogWrite (LED, Antilog (i));
    Verzögerung (2);
  }
  Verzögerung (1000);
  // Linearantrieb
  für( i=MIN_X; i<MAX_X; i++ )
  {
    analogWrite (LED, i>>2);
    Verzögerung (2);
  }
  for( --i; i>=MIN_X; i-- )
  {
    analogWrite (LED, i>>2);
    Verzögerung (2);
  }
  Verzögerung (2000);
}

Ich habe diesen Typen gefunden , der eine Methode verwendet, die er "Anti-Log Drive" nennt. Hier ist der direkte Download-Link für seine Informationen.

Dieses PDF erklärt die benötigte Kurve, anscheinend eine logarithmische. Wenn Sie einen linearen Dimmer haben (Ihren PWM-Wert), sollte die Funktion logarithmisch sein.

Hier finden Sie eine Nachschlagetabelle für 32 Helligkeitsstufen für 8-Bit-PWM.

Hier für 16 Schritte.

Hier ist, was ich basierend auf dieser Antwort im Arduino-Forum getan habe . Ich habe die Werte von 0 bis 255 berechnet, damit es mit pwm auf Arduino einfach zu verwenden ist

byte ledLookupTable[] = {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30,30,31,32,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,46,47,47,48,49,50,51,52,53,54,55,56,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,73,74,75,76,77,78,79,80,81,82,84,85,86,87,88,89,91,92,93,94,95,97,98,99,100,102,103,104,105,107,108,109,111,112,113,115,116,117,119,120,121,123,124,126,127,128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151,152,154,155,157,158,160,162,163,165,166,168,170,171,173,175,176,178,180,181,183,185,186,188,190,192,193,195,197,199,200,202,204,206,207,209,211,213,215,217,218,220,222,224,226,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255};

Dann zur Verwendung auf Arduino einfach so:

analogWrite(ledPin, ledLookupTable[brightness]); //with brighness between 0 and 255

Hoffe es ist für einige Leute hilfreich ;)

Ich beschäftige mich jetzt damit, und ich verfolge einen etwas anderen Ansatz. Ich möchte 256 Helligkeitsstufen, aber die Zuordnung eines linearen 0-255-Bereichs zu einem nichtlinearen 0-255-Bereich endet, wie Sie in einigen der anderen Antworten sehen können, mit vielen doppelten Einträgen. (Dh mehrere Ihrer Eingabewerte ergeben die gleiche Helligkeitsstufe.)

Ich habe versucht, den Algorithmus so zu modifizieren, dass er einen Eingangsbereich von 0 bis 256 einem Ausgangsbereich von 0 bis 1023 zuordnet, aber selbst das hatte mehrere Werte, die auf 0 abgebildet wurden. Also versuche ich etwas ein bisschen anderes - ich verwende den Pegel von 0 bis 255 um nichtlineare Werte im Bereich 0-769 (das sind 1023 minus 255) mit zu generieren sin(), fügen Sie diese dann zum Eingangspegel hinzu, um eine Ausgabe im Bereich 0-1023 ohne Duplikate zu erhalten. Ich werde einen Timer konfigurieren, um einen Zähler von 1023 zu verwenden, und den Komparator für den PWM-Ausgang auf Werte aus der Nachschlagetabelle setzen, basierend auf dem gewünschten Beleuchtungsniveau (0-255).

Hier ist das C-Programm, mit dem ich meine Nachschlagetabelle generiert habe:

#include <stdio.h>
#include <math.h>

int main() {
    int i;
    double j;
    int k;

    printf( "int brightness[] = {\n" );
    for( i=0; i<256; i++ ) {
        // 0 - 255 => 1.0 - 0.0, multiply by 90 degrees (in radians)
        j = (1 - (i / 255.0)) * M_PI / 2;
        j = sin( j );
        k = (1023-255) - j * (1023-255);
        printf( "%s%d%s%s",
                (((i % 8) == 0) ? "    " : " "), // leading space at start of line
                k+i,
                ((i < 255) ? "," : ""),          // comma after all but last value
                (((i % 8) == 7) ? "\n" : "")     // line break every 8 items
              );
    }
    printf( "  };\n" );
}

Und hier ist die Tabelle:

int brightness[] = {
    0, 1, 2, 3, 4, 5, 6, 7,
    8, 10, 11, 12, 14, 15, 16, 18,
    19, 21, 22, 24, 25, 27, 29, 30,
    32, 34, 35, 37, 39, 41, 43, 44,
    46, 48, 50, 52, 54, 56, 58, 61,
    63, 65, 67, 69, 72, 74, 76, 78,
    81, 83, 86, 88, 91, 93, 96, 98,
    101, 103, 106, 109, 111, 114, 117, 120,
    122, 125, 128, 131, 134, 137, 140, 143,
    146, 149, 152, 155, 158, 161, 164, 168,
    171, 174, 177, 181, 184, 187, 191, 194,
    198, 201, 205, 208, 212, 215, 219, 222,
    226, 230, 233, 237, 241, 244, 248, 252,
    256, 260, 263, 267, 271, 275, 279, 283,
    287, 291, 295, 299, 303, 307, 312, 316,
    320, 324, 328, 333, 337, 341, 345, 350,
    354, 358, 363, 367, 372, 376, 381, 385,
    390, 394, 399, 403, 408, 412, 417, 422,
    426, 431, 436, 440, 445, 450, 455, 459,
    464, 469, 474, 479, 484, 489, 493, 498,
    503, 508, 513, 518, 523, 528, 533, 538,
    543, 548, 554, 559, 564, 569, 574, 579,
    584, 590, 595, 600, 605, 610, 616, 621,
    626, 632, 637, 642, 647, 653, 658, 664,
    669, 674, 680, 685, 690, 696, 701, 707,
    712, 718, 723, 729, 734, 740, 745, 751,
    756, 762, 767, 773, 778, 784, 790, 795,
    801, 806, 812, 818, 823, 829, 834, 840,
    846, 851, 857, 863, 868, 874, 880, 885,
    891, 897, 902, 908, 914, 920, 925, 931,
    937, 942, 948, 954, 960, 965, 971, 977,
    982, 988, 994, 1000, 1005, 1011, 1017, 1023
};

Ich werde wahrscheinlich andere Funktionen (wie log()) untersuchen, sobald ich das zum Laufen gebracht habe.

Für mich scheint dieses Gesetz ziemlich gut zu funktionieren: http://www.pyroelectro.com/tutorials/fading_led_pwm/theory2.html

Ok, damit musste ich mich einfach abfinden. Laut diesem Link ist das emittierte Licht zwar linear mit der Erhöhung des PWM-Tastverhältnisses, unsere Wahrnehmung ist jedoch nicht linear, sondern logarithmisch.

Wir können es so transformieren, dass es als linear wahrgenommen wird, indem wir eine Exponentialfunktion darauf anwenden. Mit einem Python-Beispiel:

exp_scaler = 100/(math.exp(1)-1)

def change(v):
    pwm.ChangeDutyCycle(int((math.exp(v)-1)*exp_scaler))

wo vzwischen 0-1 liegt.

Dies sollte einfach in jede andere Sprache zu übersetzen sein ( ChangeDutyCycleerwartet Werte zwischen 0-100, also entsprechend skalieren).

Vor einiger Zeit habe ich einen PWM-Controller für einen LED-Streifen entworfen. In ersten Tests verifizierte ich, dass die wahrgenommene Helligkeit nicht linear mit dem PWM-Tastverhältnis war. Beispielsweise war die wahrgenommene Helligkeit bei 50 % DC-PWM viel höher als die 50 % ihres Maximalwerts bei 100 % DC-PWM. Tatsächlich nimmt das menschliche Auge die Lichtintensität logarithmisch wahr, denn nur so kann es sich an starke Schwankungen wie Sonnenlicht und Schatten anpassen.

Sie können von folgendem Zusammenhang ausgehen:

L p e r c e ich v e d l Ö g ( d . c . )

Um dieses Problem zu lösen, können Sie eine LUT aufbauen, um die gewünschte Helligkeit in PWM-Werte umzuwandeln. Bei der gewünschten empfundenen Helligkeit L im Bereich 0-1 sollten Sie mit dieser Faustformel gute Ergebnisse erzielen:

d . c . % = 10 L 1 9 100 %

Wenn das Ziel nur ein sanfter Anstieg der Helligkeitsstufe über die gesamte Bandbreite möglicher Helligkeitsstufen ist:

Der Zeitschritt, der linear abnimmt, wenn die Helligkeit mit einem konstanten Schritt zunimmt, führt dazu, dass der Helligkeitspegel mit der Zeit exponentiell ansteigt.

Beispiel:

#define MAX_DUTY 255
#define MIN_DUTY 0

#define RAMP_DOWN 0
#define RAMP_UP 1
    
#define MIN_DELAY 100UL
#define MAX_DELAY 1000UL

void loop(void)
{   
    uint32_t delay;
    uint8_t duty;
    int8_t ramp_dir;

    duty = MIN_DUTY;
    ramp_dir = RAMP_UP;

    for (;;) {
            // Offset by +1 to avoid division by zero
        for (delay = MIN_DELAY + ((MAX_DELAY - MIN_DELAY) * (MAX_DUTY + 1)) / (duty + 1); delay > 0; delay--) {
            ; // Wait
        }

        if (ramp_dir) {
            if (duty < MAX_DUTY) {
                duty++;
            } else {
                duty--;
                ramp_dir = RAMP_DOWN;
            }
        } else {
            if (duty > MIN_DUTY) {
                duty--;
            } else {
                duty++;
                ramp_dir = RAMP_UP;
            }
        }
        set_led_duty(duty);
    }
}
```