Sửa lỗi cho độ sáng phi tuyến tính trong đèn LED khi sử dụng PWM


33

Khi lái một đèn LED với PWM, độ sáng (theo tôi cảm nhận được) không mở rộng tuyến tính theo chu kỳ nhiệm vụ. Độ sáng chậm tăng lên, sau đó tăng theo cấp số nhân với chu kỳ nhiệm vụ.

Bất cứ ai cũng có thể đề xuất một quy tắc của ngón tay cái để sử dụng như một yếu tố điều chỉnh, hoặc cách giải quyết khác?


Khi tôi tạo một cặp liên kết cuff Knight Rider, tôi đã phải sử dụng x ^ 10 để làm mờ dần đi trông rất đẹp!
Rocketmagnet

3
Bạn có chắc chắn đó không phải là "độ sáng ban đầu tăng theo cấp số nhân, và sau đó chậm tăng lên"?
Dmitry Grigoryev

1
Tôi tin rằng đôi mắt của chúng ta phản ứng logarit với độ sáng.
DKNguyen

Câu trả lời:


13

Đối với 16 cấp độ, thật dễ dàng để thực hiện một bảng tra cứu đơn giản "bằng tay" và chuyển đổi giá trị 4 bit thành giá trị 8 bit để chuyển sang bộ điều khiển PWM: đây là thành phần tôi đã sử dụng trong trình điều khiển mảng led của tôi. Đối với bộ điều khiển mức 8 bit, bạn sẽ cần ít nhất 11-12 bit đầu ra từ bảng tra cứu.

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;

Tôi đang cố gắng tìm ra chính xác công thức của bạn là gì. Nó khá gần với f (x) = x ^ 2, nhưng đường cong không đủ sâu. f (x) = x ^ 3/13 giúp tôi gần gũi hơn rất nhiều.
ajs410

Đó không phải là công thức (không cố ý) ... Tôi đã tìm hiểu các giá trị ban đầu của bộ tuyến tính hóa chỉ là đoán :-). Sau đó, tôi đã cấp nguồn cho mảng, điều khiển các cột được dẫn theo thứ tự độ sáng và điều chỉnh các giá trị để có được một đoạn chẵn. Nó thực sự dễ dàng chỉ với 16 cấp độ.
Axeman

1
2n1

17

Về lý thuyết, nó phải theo cấp số nhân, nhưng tôi đã có kết quả tốt nhất để làm mờ dần bằng cách sử dụng hàm bậc hai.

Tôi cũng nghĩ rằng bạn đã nhận nó ngược. Ở chu kỳ nhiệm vụ thấp, mức tăng nhận thức về độ sáng lớn hơn nhiều so với chu kỳ nhiệm vụ gần như toàn phần, trong đó mức tăng độ sáng gần như không thể đo được.



17

Tôi đã tìm hiểu về chủ đề này trong vài ngày qua vì tôi có cùng một vấn đề ... cố gắng làm mờ đèn LED bằng cách sử dụng PWM theo cách tuyến tính rõ ràng, nhưng tôi muốn độ phân giải 256 bước đầy đủ. Cố gắng đoán 256 số để tự tạo đường cong không phải là một nhiệm vụ dễ dàng!

Tôi không phải là một nhà toán học chuyên gia, nhưng tôi biết đủ để tạo ra một số đường cong cơ bản bằng cách kết hợp một vài hàm và công thức mà không thực sự biết chúng hoạt động như thế nào. Tôi thấy rằng bằng cách sử dụng bảng tính (tôi đã sử dụng Excel), bạn có thể chơi xung quanh với một bộ số từ 0 đến 255, đặt một vài công thức vào ô tiếp theo và vẽ biểu đồ cho chúng.

Tôi đang sử dụng trình biên dịch pic để làm mờ dần và do đó, bạn thậm chí có thể lấy bảng tính để tạo mã trình biên dịch với công thức ( ="retlw 0x" & DEC2HEX(A2)). Điều này làm cho nó rất nhanh chóng và dễ dàng để thử một đường cong mới.

Sau một chút chơi xung quanh với các hàm LOG và SIN, trung bình của cả hai và một vài thứ khác, tôi thực sự không thể có được đường cong phù hợp. Điều đang xảy ra là phần giữa của sự mờ dần diễn ra chậm hơn so với các cấp thấp hơn và cao hơn. Ngoài ra, nếu một sự mờ dần ngay lập tức theo sau một sự mờ dần, có một sự tăng đột biến rõ rệt về cường độ. Những gì cần thiết (theo tôi) là một đường cong S.

Một tìm kiếm nhanh trên Wikipedia đã đưa ra công thức cần thiết cho một đường cong S. Tôi đã cắm nó vào bảng tính của mình và thực hiện một vài điều chỉnh để làm cho nó nhân lên trên phạm vi giá trị của tôi và đưa ra điều này:

Đường cong S

Tôi đã thử nó trên giàn khoan của tôi, và nó hoạt động rất đẹp.

Công thức Excel tôi đã sử dụng là:

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

Trong đó A2 là giá trị đầu tiên trong cột A, làm tăng A3, A4, ..., A256 cho mỗi giá trị.

Tôi không biết điều này có đúng về mặt toán học hay không, nhưng nó tạo ra kết quả mong muốn.

Dưới đây là bộ 256 cấp độ đầy đủ mà tôi đã sử dụng:

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

Phương trình này làm việc hoàn hảo cho tôi.
Ignacio Vazquez-Abrams


4

Tôi đã sử dụng một ATtiny để thắp sáng bộ bài của mình. Độ sáng được kiểm soát bằng cách sử dụng nồi được kết nối với chân ADC.

Đã thử chức năng hàm mũ và đầu ra PWM dựa trên điều đó dường như làm tăng tuyến tính độ sáng nhận biết.

Tôi đã sử dụng công thức này:

out = pow(out_max, in/in_max)

Attiny85 @ 8MHz đã mất khoảng 210us để thực hiện phép tính trên. Để cải thiện hiệu suất, hãy lập một bảng tra cứu. Vì đầu vào là từ ADC 10 bit và bộ nhớ ATtiny bị hạn chế, tôi cũng muốn tạo một bảng ngắn hơn.

Thay vì tạo bảng tra cứu với 1024 mục, hãy tạo bảng tra cứu ngược với 256 mục (512 byte) trong bộ nhớ chương trình (PGMEM). Một hàm được viết để thực hiện tìm kiếm nhị phân trên bảng đó. Phương pháp này chỉ mất 28uS cho mỗi lần tra cứu. Nếu tôi sử dụng bảng tra cứu trực tiếp, nó sẽ cần bộ nhớ 2kb, nhưng việc tra cứu sẽ chỉ mất 4uS hoặc hơn.

Các giá trị được tính toán trong bảng tra cứu chỉ sử dụng phạm vi đầu vào 32-991, loại bỏ phạm vi thấp hơn / trên của ADC, trong trường hợp có vấn đề với mạch.

Dưới đây là những gì tôi có bây giờ.

// chương trình kiểm tra chống_log

/ * Đèn LED được kết nối với PIN6 (PB1) * /
#define LED 1 

// Bảng tra cứu chống nhật ký (đảo ngược) 
// y = 0-255 (đầu ra pwm), y_range = 256
// x = 0-1023 (đầu vào ADC 10 bit); 
// giả sử không thể sử dụng các giá trị ADC thấp hơn / cao hơn
// loại bỏ 32 giá trị đầu tiên và 32 giá trị cuối cùng.
// 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)
// đã cho một giá trị là x, thực hiện tra cứu nhị phân trên bảng bên dưới
// mất khoảng 28uS cho đồng hồ Attiny85 @ 8 MHz
CHƯƠNG TRÌNH prog_uint16_t ANTI_LOG [] = {
  0x0000, 0x0020, 0x0098, 0x00de, 0x0110, 0x0137, 0x0156, 0x0171, 0x0188, 0x019c, 0x01af, 0x01bf, 0x01ce, 0x01dc, 0x01d
  0x0200, 0x020a, 0x0214, 0x021e, 0x0227, 0x022f, 0x0237, 0x023f, 0x0246, 0x024d, 0x0254, 0x025b, 0x0261, 0x026
  0x0278, 0x027d, 0x0282, 0x0288, 0x028c, 0x0291, 0x0296, 0x029a, 0x029f, 0x02a3, 0x02a7, 0x02ab, 0x02b, 0x02
  0x02be, 0x02c2, 0x02c5, 0x02c9, 0x02cc, 0x02cf, 0x02d3, 0x02d6, 0x02d9, 0x02dc, 0x02df, 0x02e2, 0x02e, 0x02e
  0x02f0, 0x02f3, 0x02f5, 0x02f8, 0x02fa, 0x02fd, 0x0300, 0x0302, 0x0304, 0x0307, ​​0x0309, 0x030b, 0x030e, 0x030e, 0x030
  0x0317, 0x0319, 0x031b, 0x031d, 0x031f, 0x0321, 0x0323, 0x0325, 0x0327, 0x0329, 0x032b, 0x032d, 0x032f, 0
  0x0336, 0x0338, 0x033a, 0x033c, 0x033d, 0x033f, 0x0341, 0x0342, 0x0344, 0x0346, 0x0347, 0x0349, 0x034b, 0x03434
  0x0351, 0x0352, 0x0354, 0x0355, 0x0357, 0x0353, 0x035a, 0x035b, 0x035d, 0x035e, 0x0360, 0x0361, 0x0363, 0x036
  0x0368, 0x0369, 0x036b, 0x036c, 0x036d, 0x036f, 0x0370, 0x0371, 0x0372, 0x0374, 0x0375, 0x0376, 0x0378, 0x0378
  0x037c, 0x037e, 0x037f, 0x0380, 0x0381, 0x0382, 0x0383, 0x0385, 0x0386, 0x0387, 0x0388, 0x0389, 0x038a, 0x038
  0x038f, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0394, 0x0394, 0x0398, 0x0399, 0x039a, 0x039b, 0x039b, 0x039
  0x039f, 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03a
  0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03bb
  0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03bf, 0x03c0, 0x03c1, 0x3
  0x03c9, 0x03ca, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03cd, 0x03ce, 0x03cf, 0x03d0, 0x03d0, 0x03d1, 0x03d2, 0x03d2, 0
  0x03d5, 0x03d6, 0x03d6, 0x03d7, 0x03d8, 0x03d8, 0x03d9, 0x03da, 0x03db, 0x03db, 0x03dc, 0x03dd, 0x03dd, 0x03dd
};

// Tra cứu nhị phân bằng bảng trên.
antimon byte (int x)
{
  byte y = 0x80;
  int av;
  cho (int i = 0x40; i> 0; i >> = 1)
  {
    av = pgm_read_word_near (ANTI_LOG + y);
    nếu (av> x)
    {
      y - = i;
    }
    khác nếu (av <x) 
    {
      y | = i;
    }
    khác
    {
      trả lại y;
    }
  }
  if (pgm_read_word_near (ANTI_LOG + y)> x)
  {
    y - = 1;
  }
  trả lại y;
}


thiết lập void ()
{
  pinMode (LED, OUTPUT);
  kỹ thuật sốWrite (LED, THẤP);
}

#define MIN_X 0
#define MAX_X 1024

vòng lặp void ()
{
  int i;
  // antilog_drive
  cho (i = MIN_X; i <MAX_X; i ++)
  {
    analogWrite (LED, antimon (i));
    trì hoãn (2);
  }
  cho (--i; i> = MIN_X; i--)
  {
    analogWrite (LED, antimon (i));
    trì hoãn (2);
  }
  độ trễ (1000);
  // Ổ đĩa tuyến tính
  cho (i = MIN_X; i <MAX_X; i ++)
  {
    analogWrite (LED, i >> 2);
    trì hoãn (2);
  }
  cho (--i; i> = MIN_X; i--)
  {
    analogWrite (LED, i >> 2);
    trì hoãn (2);
  }
  trì hoãn (2000);
}

1

PDF này giải thích đường cong cần thiết, rõ ràng là một đường logarit. Nếu bạn có một bộ điều chỉnh độ sáng tuyến tính (giá trị PWM của bạn) thì hàm sẽ là logarit.

Tại đây bạn có thể tìm thấy một bảng tra cứu cho 32 bước độ sáng cho 8 bit PWM.

Ở đây cho 16 bước.


1

Đây là những gì tôi đã làm dựa trên phản hồi của diễn đàn arduino . Tôi đã tính các giá trị từ 0 đến 255 để dễ sử dụng với pwm trên arduino

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

Sau đó, để sử dụng trên Arduino chỉ cần làm như vậy:

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

Hy vọng nó hữu ích cho một số người;)


1

Bây giờ tôi đang giải quyết vấn đề này và tôi đang thực hiện một cách tiếp cận hơi khác. Tôi muốn 256 mức độ sáng, nhưng ánh xạ phạm vi 0-255 tuyến tính sang phạm vi 0-255 phi tuyến tính, như bạn có thể thấy trong một số câu trả lời khác, với rất nhiều mục trùng lặp. (Tức là, một số giá trị đầu vào của bạn có cùng mức độ sáng.)

Tôi đã thử sửa đổi thuật toán để ánh xạ phạm vi đầu vào 0-256 thành phạm vi đầu ra 0-1023, nhưng thậm chí nó có một vài giá trị ánh xạ thành 0. Vì vậy, tôi đang thử một cái gì đó hơi khác một chút - Tôi đang sử dụng mức 0-255 để tạo các giá trị phi tuyến tính trong phạm vi 0-769 (tức là 1023 trừ 255) bằng cách sử dụng sin(), sau đó thêm giá trị đó vào mức đầu vào để có đầu ra trong phạm vi 0-1023 không có trùng lặp. Tôi sẽ định cấu hình bộ hẹn giờ để sử dụng bộ đếm 1023 và đặt bộ so sánh cho đầu ra PWM thành các giá trị từ bảng tra cứu dựa trên mức độ chiếu sáng tôi muốn (0-255).

Đây là chương trình C tôi đã sử dụng để tạo bảng tra cứu của mình:

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

Và đây là bảng:

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

Có lẽ tôi sẽ điều tra các chức năng khác (như log()) một khi tôi đã cài đặt nó và chạy.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.