Zápisník experimentátora
Hierarchy: WS2812
NeoPixel Ring obsahuje v každom bode tri LED diódy, ktoré tvoria pixel. Výsledná farba sa nastavuje pomocou troch zložiek RGB. Takéto vytváranie farieb je pre človeka komplikované, pretože nie každý si vie predstaviť výslednú farbu, ktorá sa skladá z troch zložiek. Na uľahčenie nastavovania farieb sa používa HSV (hue, saturation, value) model, ktorý tieto farby mieša pomocou:
V tomto článku sa pokúsime pomocou HSV modelu nastavovať NeoPixel Ring. Pre každú zložku HSV modelu si napíšeme vzorový program, ktorý ju bude zobrazovať.
Ochranné súčiastky sú nutné preto, aby sa NeoPixel Ring nepoškodil. Podrobnosti ochrany sú uvedené v článku o napájaní týchto diód.
Na obrázku je Ring s 12 diódami. Ring s 24 diódami sa nijako mimoriadne od neho nelíši a je preto možné použiť aj menší ring po príslušnej úprave kódu.
V tomto príklade sa po obvode NeoPixel Ring zobrazia jednotlivé farebné odtiene. Farebný odtieň sa nastavuje v intervale 0-MAXHUE
. Sýtosť je nastavená na maximálnu hodnotu a jas má minimálnu hodnotu, aby boli podmienky pre fotografovanie čo najlepšie. Rozdelenie farieb na jednotlivé pixely je urobené pomocou jednoduchého vzorca i * (MAXHUE / CNT)
. Funkcia getPixelColorHsv
je vysvetlená na konci tohto č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 príklady. V jednom nastavíme po obvode NeoPixel Ring rôznu sýtosť červenej farby. V druhom príklade sýtosť nastavíme pomocou gama korekcie.
Sýtosť farby bez gama korekcie sa prejavuje po obvode NeoPixel Ring tak, že sa červená farba veľmi rýchlo zmení na bielu a ľudské oko nevie rozoznať žiadne rozdiely na zvyšných pixeloch.
#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 korekciou je výsledok výrazne lepší a ľudské oko vidí postupnú zmenu z červenej farby na bielu farbu po celom obvode 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 príklady. V jednom nastavíme po obvode NeoPixel Ring rôznu hodnotu jasu červenej farby. V druhom príklade hodnotu jasu nastavíme pomocou gama korekcie.
Aj v tomto prípade má ľudské oko problém rozoznať zmeny jasu. Farba na NeoPixel Ring sa veľmi rýchlo zmení z čiernej farby na červenú farbu a na väčšine pixelov nevidí žiadnu zmenu.
#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 korekciou dosiahneme optimálne rozloženie jasu po celom obvode 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() {
}
Táto funkcia slúži na prevod farby medzi modelom HSV a RGB. Našťastie som ju nemusel napísať, pretože som ju našiel v Pull request
pre knižnicu NeoPixel. V čase písania príkladov nebola táto funkcia zahrnutá v knižnici. Túto funkciu som preto mierne upravil, aby bola použiteľná s mojimi príkladmi. Kód funkcie nie je ľahko pochopiteľný, ale autor v nej urobil veľký kus práce. Funkcia je napísaná tak, aby boli všetky výpočty s celými číslami. Treba k nej pristupovať ako k čiernej skrinke, ktorá robí to, na čo je určená.
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 sa nachádza na serveri GitHub.
15.04.2018