W tym poście chciałbym opisać sposób obsługi diod WS2812B za pomocą mikrokontrolera Atmega328P.
Program:
Jako płytkę testową wykorzystałem Arduino Uno z kwarcem 16 MHz.
Poniżej przejdę przez funkcje zintegrowane w bibliotece:
Ustawienie koloru w formacie RGB dla pojedynczej diody:
Uruchomienie zadanej ilości diod w konkretnym kolorze na podstawie zadanej wartości kolorów, lub zadanie koloru poprzez strukturę:
Zamiana kolorów z HSV na RGB:
Ustawienie zadanego koloru na diodzie na podstawie danych HSV:
Uruchomienie wszystkich diod z kolorów HSV:
Przesunięcie diod świecących w lewo:
Przesunięcie diod świecących w prawo:
Obrócenie diod w lewo:
Obrócenie diod w prawo:
Czyszczenie danych w strukturze HSV:
Czyszczenie danych w strukturze RGB:
Funkcja niskopoziomowa odpowiedzialna za przesyłanie danych do diody zawiera wstawki asemblerowe. Jest to funkcja udostępniona w serwisie Github, z drobnymi zmianami.
Testy jednostkowe powyższych funkcji mogą polegać tylko na sprawdzeniu czy ustawiliśmy odpowiednie dane w bufforze wyjściowym.
Diody WS2812B potrafią pobrać od 18.5mA do 55.AmA w zależności od tego ile sekwencji diody wykorzystujemy. Wobec tego im większy pasek ledowy tym lepiej należy przemyśleć zasilanie i w przypadku sterowania bardzo dużej ilości diod należy umieszczać dodatkowe zasilanie.
Z powodu braku DMA w układzie Atmega328p można diody aktualizować co jakiś czas pod względem wgrywanych kolorów bądź trybów świecenia, natomiast wykonywanie ciągłych operacji np. komunikacyjnych w trakcie wyświetlania danych na diodach wpłynie negatywnie na wyświetlanie zadanych trybów, chyba, że interesuje nas . Ponieważ aby ciągle odświeżać diody należy ciągle zajmować mikrokontroler wgrywaniem danych.
Poniżej przejdę przez funkcje zintegrowane w bibliotece:
Ustawienie koloru w formacie RGB dla pojedynczej diody:
- void WS2812_InitSingleLedRGB(struct WS2812_RGB *rgbColor, uint16_t diodeNumber, uint8_t refreshDiodes)
- {
- diodeNumber++;
- if(refreshDiodes == 1) {
- WS2812_SetLedsRGBColor(rgbColor, diodeNumber);
- }
- }
Uruchomienie zadanej ilości diod w konkretnym kolorze na podstawie zadanej wartości kolorów, lub zadanie koloru poprzez strukturę:
- void WS2812_InitAllLedRGB_WithOneColor(uint16_t maxDiodeSize, uint8_t red, uint8_t green, uint8_t blue)
- {
- struct WS2812_RGB ledsRGB[maxDiodeSize];
- for(uint16_t i = 0; i < maxDiodeSize; i++)
- {
- ledsRGB[i].red = red;
- ledsRGB[i].green = green;
- ledsRGB[i].blue = blue;
- }
- WS2812_SetLedsRGBColor(ledsRGB, maxDiodeSize);
- }
- void WS2812_InitAllLedRGB_WithOneColor_FromStru(uint16_t maxDiodeSize, struct WS2812_RGB colorToDisplay)
- {
- struct WS2812_RGB ledsRGB[maxDiodeSize];
- for(uint16_t i = 0; i < maxDiodeSize; i++)
- {
- ledsRGB[i].red = colorToDisplay.red;
- ledsRGB[i].green = colorToDisplay.green;
- ledsRGB[i].blue = colorToDisplay.blue;
- }
- WS2812_SetLedsRGBColor(ledsRGB, maxDiodeSize);
- }
Zamiana kolorów z HSV na RGB:
- static void WS2812ConvertHSVtoRGB(WS2812_HSV_Color_t hsv_color, struct WS2812_RGB *rgb_color)
- {
- if(hsv_color.hue>359) { hsv_color.hue=359; }
- if(hsv_color.saturation>100) { hsv_color.saturation=100; }
- if(hsv_color.value>100) { hsv_color.value=100; }
- if(hsv_color.hue <= HSV_YELLOW){
- rgb_color->red = 255;
- rgb_color->green = (425 * hsv_color.hue) / 100;
- rgb_color->blue = 0;
- }
- else if(hsv_color.hue <= HSV_GREEN) {
- rgb_color->red = 255 - ((425 * (hsv_color.hue-60))/100);
- rgb_color->green = 255;
- rgb_color->blue = 0;
- }
- else if(hsv_color.hue <= HSV_CYJAN){
- rgb_color->red = 0;
- rgb_color->green = 255;
- rgb_color->blue = (425 * (hsv_color.hue-120))/100;
- }
- else if(hsv_color.hue <= HSV_BLUE){
- rgb_color->red = 0;
- rgb_color->green = 255 - ((425 * (hsv_color.hue-180))/100);
- rgb_color->blue = 255;
- }
- else if(hsv_color.hue <= HSV_MAGENTA){
- rgb_color->red = (425 * (hsv_color.hue-240))/100;
- rgb_color->green = 0;
- rgb_color->blue = 255;
- }
- else {
- rgb_color->red = 255;
- rgb_color->green = 0;
- rgb_color->blue = 255 - ((425 * (hsv_color.hue-300))/100);
- }
- hsv_color.saturation = 100 - hsv_color.saturation;
- rgb_color->red += ((255 - rgb_color->red) * hsv_color.saturation)/100;
- rgb_color->green += ((255 - rgb_color->green) * hsv_color.saturation)/100;
- rgb_color->blue += ((255 - rgb_color->blue) * hsv_color.saturation)/100;
- rgb_color->red *= (hsv_color.value)/100;
- rgb_color->green *= (hsv_color.value)/100;
- rgb_color->blue *= (hsv_color.value)/100;
- }
Ustawienie zadanego koloru na diodzie na podstawie danych HSV:
- void WS2812_InitSingleLedHSV(struct WS2812_RGB *rgbColor, WS2812_HSV_Color_t hsvStruct, uint16_t diodeNumber, uint8_t refreshDiodes)
- {
- if(diodeNumber < MAX_DIODE_COUNT) {
- WS2812ConvertHSVtoRGB(hsvStruct, rgbColor + diodeNumber);
- diodeNumber++;
- }
- if(refreshDiodes == 1) {
- WS2812_SetLedsRGBColor(rgbColor, diodeNumber);
- }
- }
Uruchomienie wszystkich diod z kolorów HSV:
- void WS2812_InitAllLedHSV_WithOneColor(uint16_t hue, uint8_t saturation, uint8_t value, uint16_t numberOfDiodes)
- {
- struct WS2812_RGB ledsRGB[numberOfDiodes];
- WS2812_HSV_Color_t hsvStructWithData;
- hsvStructWithData.hue = hue;
- hsvStructWithData.saturation = saturation;
- hsvStructWithData.value = value;
- if(numberOfDiodes < MAX_DIODE_COUNT) {
- WS2812ConvertHSVtoRGB(hsvStructWithData, ledsRGB);
- }
- for(uint16_t i = 1; i < numberOfDiodes; i++)
- {
- ledsRGB[i].red = ledsRGB[0].red;
- ledsRGB[i].blue = ledsRGB[0].blue;
- ledsRGB[i].green = ledsRGB[0].green;
- }
- WS2812_SetLedsRGBColor(ledsRGB, numberOfDiodes);
- }
- void WS2812_InitAllLedHSV_WithOneColor_FromStru(WS2812_HSV_Color_t hsvStruct, uint16_t numberOfDiodes)
- {
- struct WS2812_RGB ledsRGB[numberOfDiodes];
- if(numberOfDiodes < MAX_DIODE_COUNT) {
- WS2812ConvertHSVtoRGB(hsvStruct, ledsRGB);
- }
- for(uint16_t i = 1; i < numberOfDiodes; i++)
- {
- ledsRGB[i].red = ledsRGB[0].red;
- ledsRGB[i].blue = ledsRGB[0].blue;
- ledsRGB[i].green = ledsRGB[0].green;
- }
- WS2812_SetLedsRGBColor(ledsRGB, numberOfDiodes);
- }
Przesunięcie diod świecących w lewo:
- void WS2812_MoveDiodeToLeft(struct WS2812_RGB *rgbColor)
- {
- uint16_t i = 0;
- struct WS2812_RGB *ledPtr = rgbColor;
- for(i = 1; i<MAX_DIODE_COUNT; i++)
- {
- ledPtr[i - 1].blue = ledPtr[i].blue;
- ledPtr[i - 1].red = ledPtr[i].red;
- ledPtr[i - 1].green = ledPtr[i].green;
- }
- ledPtr[i - 1].blue = 0x00;
- ledPtr[i - 1].red = 0x00;
- ledPtr[i - 1].green = 0x00;
- WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
- }
Przesunięcie diod świecących w prawo:
- void WS2812_MoveDiodeToRight(struct WS2812_RGB *rgbColor)
- {
- uint16_t i = 0;
- struct WS2812_RGB *ledPtr = rgbColor;
- for(uint16_t i = MAX_DIODE_COUNT-1; i<MAX_DIODE_COUNT; i++)
- {
- ledPtr[i].blue = ledPtr[i - 1].blue;
- ledPtr[i].red = ledPtr[i - 1].red;
- ledPtr[i].green = ledPtr[i - 1].green;
- }
- ledPtr[i].blue = 0x00;
- ledPtr[i].red = 0x00;
- ledPtr[i].green = 0x00;
- WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
- }
Obrócenie diod w lewo:
- void WS2812_RotateDiodeToLeft(struct WS2812_RGB *rgbColor)
- {
- uint16_t i = 0;
- struct WS2812_RGB *ledPtr = rgbColor;
- struct WS2812_RGB tmpValue;
- tmpValue.blue = rgbColor->blue;
- tmpValue.red = rgbColor->green;
- tmpValue.green = rgbColor->red;
- for(i = 1; i<MAX_DIODE_COUNT; i++)
- {
- ledPtr[i - 1].blue = ledPtr[i].blue;
- ledPtr[i - 1].red = ledPtr[i].red;
- ledPtr[i - 1].green = ledPtr[i].green;
- }
- ledPtr[i - 1].blue = tmpValue.blue;
- ledPtr[i - 1].red = tmpValue.red;
- ledPtr[i - 1].green = tmpValue.green;
- WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
- }
Obrócenie diod w prawo:
- void WS2812_RotateDiodeToRight(struct WS2812_RGB *rgbColor)
- {
- uint16_t i = 0;
- struct WS2812_RGB *ledPtr = rgbColor;
- struct WS2812_RGB *ledPtrLastDiode = rgbColor + (MAX_DIODE_COUNT - 2);
- for(i = MAX_DIODE_COUNT-1; i>0; i--)
- {
- ledPtr[i].blue = ledPtr[i - 1].blue;
- ledPtr[i].red = ledPtr[i - 1].red;
- ledPtr[i].green = ledPtr[i - 1].green;
- }
- ledPtr[i].blue = ledPtrLastDiode->blue;
- ledPtr[i].red = ledPtrLastDiode->red;
- ledPtr[i].green = ledPtrLastDiode->green;
- WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
- }
Czyszczenie danych w strukturze HSV:
- void WS2812_ClearDataInHSVStruct(WS2812_HSV_Color_t * hsv_color, uint16_t tableSize)
- {
- for(uint16_t i = 0; i < tableSize; i++)
- {
- hsv_color->hue = 0x00;
- hsv_color->saturation = 0x00;
- hsv_color->value = 0x00;
- hsv_color++;
- }
- }
Czyszczenie danych w strukturze RGB:
- void WS2812_ClearDataInStruct(struct WS2812_RGB *rgbColor, uint16_t tableSize)
- {
- for(uint16_t i = 0; i < tableSize; i++)
- {
- rgbColor->blue = 0x00;
- rgbColor->green = 0x00;
- rgbColor->red = 0x00;
- rgbColor++;
- }
- }
Funkcja niskopoziomowa odpowiedzialna za przesyłanie danych do diody zawiera wstawki asemblerowe. Jest to funkcja udostępniona w serwisie Github, z drobnymi zmianami.
Testy jednostkowe powyższych funkcji mogą polegać tylko na sprawdzeniu czy ustawiliśmy odpowiednie dane w bufforze wyjściowym.
Diody WS2812B potrafią pobrać od 18.5mA do 55.AmA w zależności od tego ile sekwencji diody wykorzystujemy. Wobec tego im większy pasek ledowy tym lepiej należy przemyśleć zasilanie i w przypadku sterowania bardzo dużej ilości diod należy umieszczać dodatkowe zasilanie.
Z powodu braku DMA w układzie Atmega328p można diody aktualizować co jakiś czas pod względem wgrywanych kolorów bądź trybów świecenia, natomiast wykonywanie ciągłych operacji np. komunikacyjnych w trakcie wyświetlania danych na diodach wpłynie negatywnie na wyświetlanie zadanych trybów, chyba, że interesuje nas . Ponieważ aby ciągle odświeżać diody należy ciągle zajmować mikrokontroler wgrywaniem danych.