PWM으로 LED를 구동 할 때 밝기는 (인식 한대로) 듀티 사이클에 따라 선형으로 스케일되지 않습니다. 밝기가 느리게 증가한 다음 듀티 사이클에 따라 기하 급수적으로 증가합니다.
누구나 교정 요소 또는 다른 해결 방법으로 사용할 규칙을 제안 할 수 있습니까?
PWM으로 LED를 구동 할 때 밝기는 (인식 한대로) 듀티 사이클에 따라 선형으로 스케일되지 않습니다. 밝기가 느리게 증가한 다음 듀티 사이클에 따라 기하 급수적으로 증가합니다.
누구나 교정 요소 또는 다른 해결 방법으로 사용할 규칙을 제안 할 수 있습니까?
답변:
16 레벨의 경우 "손으로 직접"간단한 룩업 테이블을 수행하고 8 비트 값의 4 비트 값을 PWM 컨트롤러로 전달하기가 쉽습니다. 이것이 FPGA LED 어레이 드라이버에서 사용한 구성 요소입니다. 8 비트 레벨 컨트롤러의 경우 룩업 테이블에서 최소 11-12 비트 출력이 필요합니다.
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;
1
지난 며칠 동안 동일한 문제가 발생 하여이 주제를 살펴 보았습니다. 가상 선형 방식으로 PWM을 사용하여 LED를 어둡게하려고하지만 전체 256 단계 해상도가 필요합니다. 곡선을 수동으로 생성하기 위해 256 개의 숫자를 추측하는 것은 쉬운 일이 아닙니다!
나는 전문 수학자가 아니지만, 작동 방식을 실제로 몰라도 몇 가지 함수와 수식을 결합하여 기본 곡선을 생성 할만큼 충분히 알고 있습니다. 스프레드 시트 (Excel을 사용)를 사용하면 0에서 255까지의 숫자 집합으로 놀 수 있고 다음 셀에 몇 가지 수식을 넣고 그래프로 만들 수 있습니다.
그림 어셈블러를 사용하여 페이딩을 수행하므로 스프레드 시트에서 수식 ( ="retlw 0x" & DEC2HEX(A2)
)으로 어셈블러 코드를 생성 할 수도 있습니다 . 이를 통해 새로운 곡선을 매우 빠르고 쉽게 시험해볼 수 있습니다.
LOG 및 SIN 함수, 두 가지의 평균 및 기타 몇 가지를 사용하여 약간의 장난을 한 후에 실제로 올바른 곡선을 얻을 수 없었습니다. 일어나고있는 일은 페이드의 중간 부분이 낮은 레벨과 높은 레벨보다 느리게 일어나고 있다는 것입니다. 또한 페이드 업 직후에 페이드 다운이 발생하면 강도가 급격히 상승했습니다. 필자의 의견으로는 S 곡선이 필요합니다.
Wikipedia 에 대한 빠른 검색 은 S 곡선에 필요한 공식을 제시했습니다. 나는 이것을 스프레드 시트에 꽂고 내 값의 범위에 곱할 수 있도록 몇 가지 조정을 수행했으며 다음을 생각해 냈습니다.
나는 그것을 내 장비에서 테스트했으며 아름답게 작동했습니다.
내가 사용한 Excel 수식은 다음과 같습니다.
=1/(1+EXP(((A2/21)-6)*-1))*255
여기서 A2는 열 A의 첫 번째 값이며 각 값에 대해 A3, A4, ..., A256이 증가합니다.
이것이 수학적으로 올바른지 아닌지 모르겠지만 원하는 결과를 얻습니다.
내가 사용한 256 레벨의 전체 세트는 다음과 같습니다.
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
"Anti-Log Drive"라고하는 방법을 사용하는 사람 을 찾았습니다 . 그의 정보에 대한 직접 다운로드 링크 는 다음과 같습니다 .
갑판을 밝히기 위해 ATtiny를 사용하고있었습니다. ADC 핀에 연결된 포트를 사용하여 밝기를 제어합니다.
시도한 지수 함수와이를 기반으로 한 PWM 출력은인지되는 밝기를 선형으로 증가시키는 것으로 보입니다.
나는이 공식을 사용하고 있었다 :
out = pow(out_max, in/in_max)
Attiny85 @ 8MHz는 위의 계산을 수행하는 데 약 210us를 사용했습니다. 성능을 향상시키기 위해 찾아보기 테이블을 작성하십시오. 입력은 10 비트 ADC에서 왔으며 ATtiny 메모리는 제한되어 있기 때문에 더 짧은 테이블을 만들고 싶었습니다.
1024 개의 항목으로 룩업 테이블을 만드는 대신 프로그램 메모리 (PGMEM)에 256 개의 항목 (512 바이트)으로 역방향 룩업 테이블을 만들었습니다. 해당 테이블에서 이진 검색을 수행하는 함수가 작성되었습니다. 이 방법은 각 조회마다 28uS 만 사용합니다. 직접 조회 테이블을 사용하는 경우 2kb 메모리가 필요하지만 조회에는 4uS 정도 걸립니다.
룩업 테이블의 계산 된 값은 회로에 문제가있는 경우 입력 범위 32-991 만 사용하여 ADC의 하한 / 상한 범위를 버립니다.
아래는 내가 가진 것입니다.
// 안티 로그 테스트 프로그램 / * PIN6 (PB1)에 연결된 LED * / #define LED 1 // 안티 로그 (역) 룩업 테이블 // y = 0-255 (펌웨어 출력), y_range = 256 // x = 0-1023 (10 비트 ADC 입력); // ADC 출력 값의 하한 / 상한을 사용할 수 없다고 가정 // 처음 32 및 마지막 32 값을 버립니다. // 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) // x 값이 주어지면 아래 표에서 이진 조회를 수행하십시오. // Attiny85 @ 8MHz 클럭에 약 28uS 소요 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 }; // 위의 표를 사용한 이진 조회. 바이트 안티 로그 (int x) { 바이트 y = 0x80; int av; for (int i = 0x40; i> 0; i >> = 1) { av = pgm_read_word_near (ANTI_LOG + y); if (av> x) { y-= i; } 그렇지 않으면 (av <x) { y | = i; } 그밖에 { y를 반환; } } if (pgm_read_word_near (ANTI_LOG + y)> x) { y-= 1; } y를 반환; } 무효 설정 () { pinMode (LED, 출력); digitalWrite (LED, 낮음); } # 정의 MIN_X 0 #define MAX_X 1024 무효 루프 () { int i; // antilog_drive for (i = MIN_X; i <MAX_X; i ++) { analogWrite (LED, 안티 로그 (i)); 지연 (2); } for (--i; i> = MIN_X; i--) { analogWrite (LED, 안티 로그 (i)); 지연 (2); } 지연 (1000); // 리니어 드라이브 for (i = MIN_X; i <MAX_X; i ++) { analogWrite (LED, i >> 2); 지연 (2); } for (--i; i> = MIN_X; i--) { analogWrite (LED, i >> 2); 지연 (2); } 지연 (2000); }
이 arduino 포럼 응답을 기반으로 수행 한 작업은 다음과 같습니다 . 나는 0에서 255까지의 값을 계산 했으므로 arduino에서 pwm과 함께 사용하기 쉽습니다.
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};
그런 다음 Arduino에서 사용하려면 다음과 같이하십시오.
analogWrite(ledPin, ledLookupTable[brightness]); //with brighness between 0 and 255
그것이 일부 사람들에게 도움이되기를 바랍니다.)
나는 지금 이것을 다루고 있으며 약간 다른 접근법을 취하고 있습니다. 256 레벨의 밝기를 원하지만 많은 중복 항목이있는 다른 답변에서 볼 수 있듯이 선형 0-255 범위를 비선형 0-255 범위로 매핑하는 것이 좋습니다. (즉, 여러 입력 값의 밝기 수준이 동일합니다.)
0-256 입력 범위를 0-1023 출력 범위에 매핑하도록 알고리즘을 수정하려고했지만 몇 가지 값이 0에 매핑되었습니다. 그래서 약간 다른 것을 시도하고 있습니다-0-255 수준을 사용하고 있습니다 를 사용하여 0-769 범위 (1023-255) 범위의 비선형 값을 생성 sin()
한 다음 입력 레벨에 추가 하여 중복없이 0-1023 범위의 출력을 얻습니다. 1023 카운터를 사용하도록 타이머를 구성하고 원하는 조명 레벨 (0-255)에 따라 PWM 출력의 비교기를 룩업 테이블의 값으로 설정합니다.
조회 테이블을 생성하는 데 사용한 C 프로그램은 다음과 같습니다.
#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" );
}
그리고 여기 테이블이 있습니다 :
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
};
이 기능을 사용하고 log()
나면 다른 기능 (예 :)을 조사 할 것입니다 .
나를 위해이 법은 꽤 잘 작동하는 것 같습니다 : http://www.pyroelectro.com/tutorials/fading_led_pwm/theory2.html