PWMを使用してLEDを駆動する場合、明るさ(私が認識しているように)はデューティサイクルに比例してスケーリングしません。輝度の上昇は遅く、デューティサイクルとともに指数関数的に増加します。
誰かが補正係数として使用する経験則、または他の回避策を提案できますか?
PWMを使用してLEDを駆動する場合、明るさ(私が認識しているように)はデューティサイクルに比例してスケーリングしません。輝度の上昇は遅く、デューティサイクルとともに指数関数的に増加します。
誰かが補正係数として使用する経験則、または他の回避策を提案できますか?
回答:
16レベルの場合、単純なルックアップテーブルを「手動」で実行し、4ビット値を8ビット値に変換して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;
私は同じ問題を抱えているため、過去数日間このテーマを調査していました... PWMを目に見える直線的に使用してLEDを暗くしようとしていますが、256ステップのフル解像度が必要です。256個の数字を推測して曲線を手動で作成するのは簡単なことではありません。
私は数学の専門家ではありませんが、それらがどのように機能するかを実際に知らなくても、いくつかの関数と数式を組み合わせていくつかの基本曲線を生成するのに十分なことを知っています。スプレッドシート(Excelを使用)を使用すると、0から255までの一連の数値をいじり、次のセルにいくつかの数式を入力し、グラフ化することができます。
私はpicアセンブラーを使用してフェードを行っているため、スプレッドシートを取得して式(="retlw 0x" & DEC2HEX(A2)
)でアセンブラーコードを生成することさえできます。これにより、新しい曲線を簡単に試すことができます。
LOG関数とSIN関数、2つの関数の平均、その他いくつかのことをいじってみたところ、適切な曲線を得ることができませんでした。何が起こっているのかは、フェードの中央部分が低レベルと高レベルよりもゆっくりと起こっていたことです。また、フェードアップの直後にフェードダウンが続く場合、強度に顕著な顕著なスパイクがありました。(私の意見では)必要なのはSカーブです。
ウィキペディアで簡単に検索すると、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
「アンチログドライブ」と呼ばれる方法を使用するこの男を見つけました。こちらが彼の情報の直接ダウンロードリンクです。
私はデッキを照らすために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の下位/上位範囲を破棄します。
以下は私が今持っているものです。
// anti_logテストプログラム / * PIN6(PB1)に接続されたLED * / #define LED 1 //アンチログ(逆)ルックアップテーブル // y = 0-255(pwm出力)、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; } else if(av <x) { y | = i; } 他に { yを返す; } } if(pgm_read_word_near(ANTI_LOG + y)> x) { y-= 1; } yを返す; } void setup() { pinMode(LED、OUTPUT); digitalWrite(LED、LOW); } #define MIN_X 0 #define MAX_X 1024 void loop() { int i; // antilog_drive for(i = MIN_X; i <MAX_X; i ++) { analogWrite(LED、antilog(i)); delay(2); } for(--i; i> = MIN_X; i--) { analogWrite(LED、antilog(i)); delay(2); } 遅延(1000); //リニア駆動 for(i = MIN_X; i <MAX_X; i ++) { analogWrite(LED、i >> 2); delay(2); } for(--i; i> = MIN_X; i--) { analogWrite(LED、i >> 2); delay(2); } delay(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-769(つまり1023-255)の非線形値を生成しsin()
、それを入力レベルに追加して、重複なしで範囲0-1023の出力を取得します。1023のカウンターを使用するようにタイマーを構成し、PWM出力のコンパレーターを、必要な照明レベル(0〜255)に基づいたルックアップテーブルの値に設定します。
ルックアップテーブルの生成に使用した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