PWM kullanırken LED'lerde doğrusal olmayan parlaklığın düzeltilmesi


33

PWM'li bir LED kullanırken, parlaklık (algıladığım gibi) görev döngüsü ile doğrusal olarak ölçeklenmez. Parlaklığın artması yavaş, sonra görev döngüsü ile katlanarak artar.

Herhangi biri bir düzeltme faktörü ya da başka bir geçici çözüm olarak kullanmak için bir kural belirleyebilir mi?


Bir çift Knight Rider manşet bağlantısı yaptığımda, solgunluğun güzel görünmesi için x ^ 10 kullanmak zorunda kaldım!
Rocketmagnet

3
"Parlaklığın başlangıçta katlanarak arttığından ve sonra yavaşlaması yavaş" olmadığından emin misiniz?
Dmitry Grigoryev

1
Gözlerimizin logaritmik olarak parlaklığa tepki verdiğine inanıyorum.
DKNguyen

Yanıtlar:


13

16 seviye için "elle" basit bir arama tablosu yapmak ve PWM kontrol cihazına geçmek için 8 bitlik bir değerde 4 bitlik değeri değiştirmek kolaydır: FPGA led dizilim sürücümde kullandığım bileşen budur. 8 bitlik bir seviye kontrol cihazı için, arama tablosundan en az 11-12 bit çıkış gerekir.

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;

Formülün tam olarak ne olduğunu bulmaya çalışıyorum. F (x) = x ^ 2 değerine oldukça yakın, ancak eğri yeterince derin değil. f (x) = x ^ 3/13 beni daha da yakınlaştırır.
ajs410

Formül yok (kasıtlı değil) ... Doğrusallaştırıcı başlangıç ​​değerlerinde sadece tahmin ediyorum :-). Daha sonra diziyi açtım, led sütunlarını parlaklık sırasına soktum ve eşit bir rampa elde etmek için değerleri çimdikledim. Sadece 16 seviye ile gerçekten kolay.
Axeman

1
@ ajs410 - bana daha çok benziyor : ilk bit az ya da çok, her adımda 1 konumu sola kaydırıyor. 2n1
stevenvh

17

Teoride üstel olmalı, ancak ikinci dereceden bir işlev kullanarak solma için en iyi sonuçları aldım.

Ben de onu geri aldığını düşünüyorum. Düşük görev döngüsünde, parlaklıktaki algılanan artış, neredeyse tam görev döngüsünden çok daha büyüktür; burada parlaklıktaki artış neredeyse algılanamaz.


Ayrıca bakınız Gamma düzeltmesi .
starblue

17

Son birkaç gündür bu konuya bakıyordum çünkü aynı problemim var ... PWM kullanarak LED'leri görünür şekilde doğrusal bir şekilde azaltmaya çalışıyorum ama 256 adımlık tam çözünürlük istiyorum. El ile bir eğri oluşturmak için 256 sayı tahmin etmeye çalışmak kolay bir iş değil!

Uzman bir matematikçi değilim, ancak gerçekten nasıl çalıştıklarını bilmeden birkaç işlevi ve formülü birleştirerek bazı temel eğriler oluşturacak kadar bilgim var. Bir elektronik tablo kullanarak (Excel kullandım) 0 ile 255 arasında bir sayı kümesiyle oynayabilir, bir sonraki hücreye birkaç formül koyabilir ve bunları grafik haline getirebilirim.

Solma yapmak için pic assembler kullanıyorum ve böylece bir formül ( ="retlw 0x" & DEC2HEX(A2)) ile assembler kodunu oluşturmak için elektronik tabloyu bile alabilirsiniz . Bu, yeni bir eğri denemeyi çok hızlı ve kolaydır.

LOG ve SIN işlevleriyle biraz uğraştıktan sonra, ikisinin ortalaması ve diğer bazı şeylerden sonra doğru eğriyi alamadım. Olan şey, solgunluğun orta kısmının daha düşük ve daha yüksek seviyelerden daha yavaş gerçekleşmesiydi. Ayrıca, hemen solmaya bir solmanın ardından solmaya bağlı olarak, yoğunlukta keskin bir şekilde gözle görülür bir yükselme oldu. Gerekli olan (benim görüşüme göre) bir S eğrisi.

Vikipedi'de hızlı bir arama, bir S eğrisi için gereken formülü buldu. Bunu elektronik tabloma ekledim ve değer aralığımla çarpmak için birkaç ayar yaptım ve şunu gördüm:

S eğrisi

Teçhizatımda test ettim ve çok iyi çalıştı.

Kullandığım Excel formülü şuydu:

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

buradaki A2, her değer için A3, A4, ..., A256'yı artıran A sütunundaki ilk değerdir.

Bunun matematiksel olarak doğru olup olmadığı hakkında hiçbir fikrim yok, ancak istenen sonuçları veriyor.

İşte kullandığım 256 seviye kümesi:

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

Bu denklem benim için mükemmel çalıştı.
Ignacio Vazquez-Abrams


4

Güvertemi aydınlatmak için bir ATtiny kullanıyordum. Parlaklık, ADC pimine bağlı bir kap kullanılarak kontrol edilir.

Denenmiş üstel fonksiyon ve buna bağlı PWM çıkışı algılanan parlaklıkta doğrusal bir artış sağlıyor gibi görünüyor.

Bu formülleri kullanıyordum:

out = pow(out_max, in/in_max)

Attiny85 @ 8MHz, yukarıdaki hesaplamayı yapmak için yaklaşık 210us alıyordu. Performansı artırmak için bir arama tablosu yaptı. Girdi 10 bit ADC'den olduğundan ve ATtiny belleği sınırlı olduğundan, daha kısa bir tablo oluşturmak istedim.

1024 girişli arama tablosu yapmak yerine, program belleğinde (PGMEM) 256 girişli (512 bayt) geriye doğru arama tablosu yapıldı. Bu masada ikili arama yapmak için bir fonksiyon yazılmıştır. Bu yöntem her arama için sadece 28uS alır. Doğrudan bir arama tablosu kullanırsam, 2kb bellek gerektirir, ancak arama yalnızca 4uS kadar sürer.

Arama tablosundaki hesaplanan değerler, devre ile ilgili bir sorun olduğunda, alt / üst ADC aralığını atarak, yalnızca 32-991 giriş aralığını kullanır.

Aşağıda şimdi sahip olduğum şey bu.

// anti_log test programı

/ * PIN6'ya bağlı LED (PB1) * /
#define LED 1 

// Anti-Log (geriye doğru) arama tablosu 
// y = 0-255 (pwm çıkışı), y_range = 256
// x = 0-1023 (10 bitlik ADC girişi); 
// alt / üst ADC çıkış değerlerinin sonunun varsayılmadığını varsayarsak
// ilk 32 ve son 32 değerin atılması.
// min_x = 32, maks_x = 1023-min_x, x_range = 1024-2 * min_x
// ANTI_LOG [y] = yuvarlak (x_range * log (y, base = y_range) + min_x)
// x değeri verildiğinde aşağıdaki tabloda ikili arama yapın
// Attiny85 @ 8MHz saat için yaklaşık 28uS alır
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, 0x0367, 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, 0x03b7, 0x03b, 0x03b,
  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
};

// Yukarıdaki tabloyu kullanarak ikili arama.
bayt antilogu (int x)
{
  bayt y = 0x80;
  int av;
  (int i = 0x40; i> 0; i >> = 1)
  {
    av = pgm_read_word_near (ANTI_LOG + y);
    eğer (av> x)
    {
      y - = i;
    }
    aksi takdirde (av <x) 
    {
      y | = i;
    }
    Başka
    {
      y dönüşü;
    }
  }
  if (pgm_read_word_near (ANTI_LOG + y)> x)
  {
    y - = 1;
  }
  y dönüşü;
}


geçersiz kurulum ()
{
  pinMode (LED, ÇIKIŞ);
  digitalWrite (LED, DÜŞÜK);
}

#define MIN_X 0
#define MAX_X 1024

geçersiz döngü ()
{
  int i;
  // antilog_drive
  (i = MIN_X; i <MAX_X; i ++) için
  {
    analogWrite (LED, antilog (i));
    gecikme (2);
  }
  (--i; i> = MIN_X; ı--) için
  {
    analogWrite (LED, antilog (i));
    gecikme (2);
  }
  gecikme (1000);
  // Doğrusal sürücü
  (i = MIN_X; i <MAX_X; i ++) için
  {
    analog yazma (LED, i >> 2);
    gecikme (2);
  }
  (--i; i> = MIN_X; ı--) için
  {
    analog yazma (LED, i >> 2);
    gecikme (2);
  }
  gecikme (2000);
}

1

Bu PDF , görünüşe göre logaritmik olan gerekli eğriyi açıklar. Doğrusal bir karartıcıya sahipseniz (PWM değeriniz), fonksiyon logaritmik olmalıdır.

Burada 8 bitlik PWM için 32 adımlık parlaklık için bir tablo bulabilirsiniz.

İşte 16 adım için.


1

İşte o arduino forum cevabını temel alarak ne yaptım . 0 - 255 arasındaki değerleri hesapladım, bu yüzden arduino'da pwm ile kullanımı kolay

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};

Sonra Arduino'da kullanmak için sadece böyle yapın:

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

Bazı insanlar için yararlı olur umarım;)


1

Şimdi bununla ilgileniyorum ve biraz farklı bir yaklaşım izliyorum. 256 parlaklık seviyesi istiyorum, ancak doğrusal bir 0-255 aralığını doğrusal olmayan bir 0-255 aralığına eşlemek, bazı yanıtlarda birçok yinelenen girişle görebileceğiniz gibi, yukarı doğru rüzgarlar. (Yani, giriş değerlerinden birkaçı aynı parlaklık seviyesine neden olur.)

Bir 0-256 giriş aralığını 0-1023 çıkış aralığına eşlemek için algoritmayı değiştirmeye çalıştım, ancak bu bile 0 değer eşlemesine sahip. Bu yüzden biraz farklı bir şey deniyorum - 0-255 seviyesini kullanıyorum kullanarak 0-769 aralığında (bu, 1023 eksi 255) doğrusal olmayan değerler üretmek sin()için, çoğaltmadan 0-1023 aralığında bir çıktı elde etmek için bunu girdi seviyesine ekleyin. Bir zamanlayıcıyı 1023 sayacını kullanacak şekilde ayarlayacağım ve PWM çıkışı için karşılaştırıcıyı, istediğim aydınlatma seviyesine göre arama tablosundaki değerlere ayarlayacağım (0-255).

İşte arama tablomu oluşturmak için kullandığım C programı:

#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" );
}

Ve işte tablo:

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
};

Muhtemelen log()bunu çalıştırıp çalıştırdıktan sonra diğer fonksiyonları (gibi ) araştıracağım .


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.