Dioda i PWM, czemu świeci nierówno?

Sterując diody PWM, łatwo dojść do wniosku, „że coś jest nie tak”. Chodzi o jasność diody. Otóż zmieniając stopień wypełnienia, nietrudno odnieść wrażenie, że zmianę jasności widać tylko przy pierwszych krokach, potem dioda świeci już niemal bez zmian.

Czy tak jest naprawdę?

Nie do końca. Problem nie leży w diodzie ale w ludzkim oku. A dokładniej to w mózgu. Nasze mózgi są nieliniowe, i taka sama zmiana jasności przy niemal nie świecącej diodzie jest o wiele bardziej przez nas zauważana niż gdy poziom jasności jest wysoki.

Niech za ilustrację posłuży film. Dioda z prawej jest sterowana „od zera” do maksimum i z powrotem za pomocą prostej pętli:

  for (int = 0; i < 256; i++) {
    analogWrite(9, i);
    delay(25);
  }
  for (int = 255; i >= 0; i-) {
    analogWrite(9, i);
    delay(25);
  }

Po początkowym rozjaśnieniu, w zasadzie nie widzimy zmian w jasności. Natomiast doda z lewej zachowuje się inaczej. Film trochę to przekłamuje ale patrząc bezpośrednio, prawie cały czas widać stopniową zmianę jej jasności.

Jak to jest możliwe, czy to jakiś inny PWM?

PWM jest ten sam, jednak z korekcją. Wiedząc, że czułość naszego oka ma taki kształt:

odbierana jasność

to można skorygować trochę PWM. Jak? Tak by skok wypełnienia o jedną jednostkę skutkował mniej więcej takim samym skokiem odbieranej jasności. Co to znaczy? Że rozdzielczość nam spadnie. Przy skoku z 1 na 2 rzeczywistego wypełniania PWM na Arduino na drugim końcu musimy skoczyć z 235 do 255 by zachować jednakowy skok odbieranej przez oko jasności. Dlatego nasz nowy analogWrite bedzie miał tylko 32 kroki zamiast 256.

Jak to dokładnie skorygować? No tutaj już nie będę ukrywał – posłużyłem się gotowcem znalezionym w sieci (https://ledshield.wordpress.com/2012/11/13/led-brightness-to-your-eye-gamma-correction-no/), tam znalazłem gotową tablicę, która służy za źródło korekty. W załączeniu użyty kod programu: https://gist.github.com/netmaniac/8be83f2e66ae25e949f1

Kluczowa jest tablica CIEL8, która zawiera poziomy PWM jakich trzeba użyć by z każdym krokiem dostać względną jasność zmieniała się równo. Jest to kod przeniesiony ze Arduino IDE 1.0 – podejrzewam, że można zrezygnować z makra CIELPWM na rzecz zwykłego indeksu w tablicy, ale skoro działa ;)