Zápisník experimentátora
Hierarchy: WS2812
NeoPixel Ring obsahuje v každém bodě tři LED diody, které tvoří pixel. Výsledná barva se nastavuje pomocí tří složek RGB. Takové vytváření barev je pro člověka komplikované, protože ne každý si umí představit výslednou barvu, která se skládá ze tří složek. Pro usnadnění nastavování barev se používá HSV (hue, saturation, value) model, který tyto barvy míchá pomocí:
V tomto článku se pokusíme pomocí HSV modelu nastavovat NeoPixel Ring. Pro každou složku HSV modelu si napíšeme vzorový program, který ji bude zobrazovat.
Ochranné součástky jsou nutné proto, aby se NeoPixel Ring nepoškodil. Podrobnosti ochrany jsou uvedeny v článku o napájení těchto diod.
Na obrázku je Ring s 12 diodami. Ring s 24 diodami se nijak mimořádně od něj neliší a je proto možné použít i menší ring po příslušné úpravě kódu.
V tomto příkladu se po obvodu NeoPixel Ring zobrazí jednotlivé barevné odstíny. Barevný odstín se nastavuje v intervalu 0-MAXHUE
. Sytost je nastavena na maximální hodnotu a jas má minimální hodnotu, aby byly podmínky pro fotografování co nejlépe. Rozdělení barev na jednotlivé pixely je uděláno pomocí jednoduchého vzorce i * (MAXHUE / CNT)
. Funkce getPixelColorHsv
je vysvětlena na konci tohoto článku.
#include <Adafruit_NeoPixel.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
// hue - whole circle
// saturation - maximum
// value - very low
for (int i = 0; i < CNT; i++)
strip.setPixelColor(i, getPixelColorHsv(i, i * (MAXHUE / CNT), 255, 10));
strip.show();
}
void loop() {
}
Použijeme dva příklady. V jednom nastavíme po obvodu NeoPixel Ring různou sytost červené barvy. Ve druhém příkladu sytost nastavíme pomocí gama korekce.
Sytost barvy bez gama korekce se projevuje po obvodu NeoPixel Ring tak, že se červená barva velmi rychle změní na bílou a lidské oko neví rozeznat žádné rozdíly na zbývajících pixelech.
#include <Adafruit_NeoPixel.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
// hue - red
// saturation - 255-0
// value - low
for (int i = 0; i < CNT; i++)
strip.setPixelColor(i, getPixelColorHsv(i, 0, 255-i*(255/CNT), 100));
strip.show();
}
void loop() {
}
S gama korekcí je výsledek výrazně lepší a lidské oko vidí postupnou změnu z červené barvy na bílou barvu po celém obvodu NeoPixel Ring.
#include <Adafruit_NeoPixel.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
// hue - red
// saturation - 255-0
// value - low
for (int i = 0; i < CNT; i++)
strip.setPixelColor(i, getPixelColorHsv(i, 0, 255-strip.gamma8(i*(255/CNT)), 100));
strip.show();
}
void loop() {
}
Použijeme dva příklady. V jednom nastavíme po obvodu NeoPixel Ring různou hodnotu jasu červené barvy. Ve druhém příkladu hodnotu jasu nastavíme pomocí gama korekce.
I v tomto případě má lidské oko problém rozeznat změny jasu. Barva na NeoPixel Ring se velmi rychle změní z černé barvy na červenou barvu a na většině pixelů nevidí žádnou změnu.
#include <Adafruit_NeoPixel.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
// hue - red
// saturation - max
// value - 0-255
for (int i = 0; i < CNT; i++)
strip.setPixelColor(i, getPixelColorHsv(i, 0, 255, i*(255/CNT)));
strip.show();
}
void loop() {
}
S gama korekcí dosáhneme optimální rozložení jasu po celém obvodu NeoPixel Ring.
#include <Adafruit_NeoPixel.h>
#include "hsv.h"
// data pin
#define PIN 6
// led count
#define CNT 24
// max Hue
#define MAXHUE 256*6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(CNT, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
// hue - red
// saturation - max
// value - 0-255
for (int i = 0; i < CNT; i++)
strip.setPixelColor(i, getPixelColorHsv(i, 0, 255, strip.gamma8(i*(255/CNT))));
strip.show();
}
void loop() {
}
Tato funkce slouží pro převod barvy mezi modelem HSV a RGB. Naštěstí jsem ji nemusel napsat, protože jsem ji našel v Pull request
pro knihovnu NeoPixel. V době psaní příkladů nebyla tato funkce zahrnuta v knihovně. Tuto funkci jsem proto mírně upravil, aby byla použitelná s mými příklady. Kód funkce není snadno pochopitelný, ale autor v ní udělal velký kus práce. Funkce je napsána tak, aby byly všechny výpočty s celými čísly. Třeba k ní přistupovat jako k černé skříňce, která dělá to, na co je určena.
uint32_t getPixelColorHsv(
uint16_t n, uint16_t h, uint8_t s, uint8_t v) {
uint8_t r, g, b;
if (!s) {
// Monochromatic, all components are V
r = g = b = v;
} else {
uint8_t sextant = h >> 8;
if (sextant > 5)
sextant = 5; // Limit hue sextants to defined space
g = v; // Top level
// Perform actual calculations
/*
Bottom level:
--> (v * (255 - s) + error_corr + 1) / 256
*/
uint16_t ww; // Intermediate result
ww = v * (uint8_t)(~s);
ww += 1; // Error correction
ww += ww >> 8; // Error correction
b = ww >> 8;
uint8_t h_fraction = h & 0xff; // Position within sextant
uint32_t d; // Intermediate result
if (!(sextant & 1)) {
// r = ...slope_up...
// --> r = (v * ((255 << 8) - s * (256 - h)) + error_corr1 + error_corr2) / 65536
d = v * (uint32_t)(0xff00 - (uint16_t)(s * (256 - h_fraction)));
d += d >> 8; // Error correction
d += v; // Error correction
r = d >> 16;
} else {
// r = ...slope_down...
// --> r = (v * ((255 << 8) - s * h) + error_corr1 + error_corr2) / 65536
d = v * (uint32_t)(0xff00 - (uint16_t)(s * h_fraction));
d += d >> 8; // Error correction
d += v; // Error correction
r = d >> 16;
}
// Swap RGB values according to sextant. This is done in reverse order with
// respect to the original because the swaps are done after the
// assignments.
if (!(sextant & 6)) {
if (!(sextant & 1)) {
uint8_t tmp = r;
r = g;
g = tmp;
}
} else {
if (sextant & 1) {
uint8_t tmp = r;
r = g;
g = tmp;
}
}
if (sextant & 4) {
uint8_t tmp = g;
g = b;
b = tmp;
}
if (sextant & 2) {
uint8_t tmp = r;
r = b;
b = tmp;
}
}
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
Zdrojový kód se nachází na serveru GitHub.
16.04.2018