niedziela, 2 grudnia 2018

[11] Atmega - Obsługa diod WS2812B

W tym poście chciałbym opisać sposób obsługi diod WS2812B za pomocą mikrokontrolera Atmega328P. 

Znalezione obrazy dla zapytania ws2812

[Źródło: https://www.sparkfun.com/products/11821]

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:

  1. void WS2812_InitSingleLedRGB(struct WS2812_RGB *rgbColor, uint16_t diodeNumber, uint8_t refreshDiodes)
  2. {
  3.     diodeNumber++;
  4.    
  5.     if(refreshDiodes == 1) {
  6.         WS2812_SetLedsRGBColor(rgbColor, diodeNumber);
  7.     }
  8. }

Uruchomienie zadanej ilości diod w konkretnym kolorze na podstawie zadanej wartości kolorów, lub zadanie koloru poprzez strukturę:

  1. void WS2812_InitAllLedRGB_WithOneColor(uint16_t maxDiodeSize, uint8_t red, uint8_t green, uint8_t blue)
  2. {
  3.     struct WS2812_RGB ledsRGB[maxDiodeSize];
  4.    
  5.     for(uint16_t i = 0; i < maxDiodeSize; i++)
  6.     {
  7.         ledsRGB[i].red = red;
  8.         ledsRGB[i].green = green;
  9.         ledsRGB[i].blue = blue;
  10.     }
  11.    
  12.     WS2812_SetLedsRGBColor(ledsRGB, maxDiodeSize);
  13. }
  14. void WS2812_InitAllLedRGB_WithOneColor_FromStru(uint16_t maxDiodeSize, struct WS2812_RGB colorToDisplay)
  15. {
  16.     struct WS2812_RGB ledsRGB[maxDiodeSize];
  17.        
  18.     for(uint16_t i = 0; i < maxDiodeSize; i++)
  19.     {
  20.         ledsRGB[i].red = colorToDisplay.red;
  21.         ledsRGB[i].green = colorToDisplay.green;
  22.         ledsRGB[i].blue = colorToDisplay.blue;
  23.     }
  24.        
  25.     WS2812_SetLedsRGBColor(ledsRGB, maxDiodeSize);
  26. }

Zamiana kolorów z HSV na RGB:

  1. static void WS2812ConvertHSVtoRGB(WS2812_HSV_Color_t hsv_color, struct WS2812_RGB *rgb_color)
  2. {
  3.     if(hsv_color.hue>359) { hsv_color.hue=359; }
  4.     if(hsv_color.saturation>100) { hsv_color.saturation=100; }
  5.     if(hsv_color.value>100) { hsv_color.value=100; }
  6.    
  7.     if(hsv_color.hue <= HSV_YELLOW){
  8.         rgb_color->red = 255;
  9.         rgb_color->green = (425 * hsv_color.hue) / 100;
  10.         rgb_color->blue = 0;
  11.     }
  12.     else if(hsv_color.hue <= HSV_GREEN) {
  13.         rgb_color->red = 255 - ((425 * (hsv_color.hue-60))/100);
  14.         rgb_color->green = 255;
  15.         rgb_color->blue = 0;
  16.     }
  17.     else if(hsv_color.hue <= HSV_CYJAN){
  18.         rgb_color->red = 0;
  19.         rgb_color->green = 255;
  20.         rgb_color->blue = (425 * (hsv_color.hue-120))/100;
  21.     }
  22.     else if(hsv_color.hue <= HSV_BLUE){
  23.         rgb_color->red = 0;
  24.         rgb_color->green = 255 - ((425 * (hsv_color.hue-180))/100);
  25.         rgb_color->blue = 255;
  26.     }
  27.     else if(hsv_color.hue <= HSV_MAGENTA){
  28.         rgb_color->red = (425 * (hsv_color.hue-240))/100;
  29.         rgb_color->green = 0;
  30.         rgb_color->blue = 255;
  31.     }
  32.     else {
  33.         rgb_color->red = 255;
  34.         rgb_color->green = 0;
  35.         rgb_color->blue = 255 - ((425 * (hsv_color.hue-300))/100);
  36.     }
  37.     hsv_color.saturation = 100 - hsv_color.saturation;
  38.     rgb_color->red += ((255 - rgb_color->red) * hsv_color.saturation)/100;
  39.     rgb_color->green += ((255 - rgb_color->green) * hsv_color.saturation)/100;
  40.     rgb_color->blue += ((255 - rgb_color->blue) * hsv_color.saturation)/100;
  41.     rgb_color->red *= (hsv_color.value)/100;
  42.     rgb_color->green *= (hsv_color.value)/100;
  43.     rgb_color->blue *= (hsv_color.value)/100;
  44. }

Ustawienie zadanego koloru na diodzie na podstawie danych HSV:

  1. void WS2812_InitSingleLedHSV(struct WS2812_RGB *rgbColor, WS2812_HSV_Color_t hsvStruct, uint16_t diodeNumber, uint8_t refreshDiodes)
  2. {
  3.     if(diodeNumber < MAX_DIODE_COUNT) {
  4.         WS2812ConvertHSVtoRGB(hsvStruct, rgbColor + diodeNumber);
  5.         diodeNumber++;
  6.     }
  7.    
  8.     if(refreshDiodes == 1) {
  9.         WS2812_SetLedsRGBColor(rgbColor, diodeNumber);
  10.     }                                  
  11. }

Uruchomienie wszystkich diod z kolorów HSV:

  1. void WS2812_InitAllLedHSV_WithOneColor(uint16_t hue, uint8_t saturation, uint8_t value, uint16_t numberOfDiodes)
  2. {
  3.     struct WS2812_RGB ledsRGB[numberOfDiodes];
  4.     WS2812_HSV_Color_t hsvStructWithData;
  5.    
  6.     hsvStructWithData.hue = hue;
  7.     hsvStructWithData.saturation = saturation;
  8.     hsvStructWithData.value = value;
  9.    
  10.     if(numberOfDiodes < MAX_DIODE_COUNT) {
  11.         WS2812ConvertHSVtoRGB(hsvStructWithData, ledsRGB);
  12.     }
  13.    
  14.     for(uint16_t i = 1; i < numberOfDiodes; i++)
  15.     {
  16.         ledsRGB[i].red = ledsRGB[0].red;
  17.         ledsRGB[i].blue = ledsRGB[0].blue;
  18.         ledsRGB[i].green = ledsRGB[0].green;
  19.     }
  20.        
  21.     WS2812_SetLedsRGBColor(ledsRGB, numberOfDiodes);
  22. }
  23. void WS2812_InitAllLedHSV_WithOneColor_FromStru(WS2812_HSV_Color_t hsvStruct, uint16_t numberOfDiodes)
  24. {
  25.     struct WS2812_RGB ledsRGB[numberOfDiodes];
  26.    
  27.     if(numberOfDiodes < MAX_DIODE_COUNT) {
  28.         WS2812ConvertHSVtoRGB(hsvStruct, ledsRGB);
  29.     }
  30.    
  31.     for(uint16_t i = 1; i < numberOfDiodes; i++)
  32.     {
  33.         ledsRGB[i].red = ledsRGB[0].red;
  34.         ledsRGB[i].blue = ledsRGB[0].blue;
  35.         ledsRGB[i].green = ledsRGB[0].green;
  36.     }
  37.    
  38.     WS2812_SetLedsRGBColor(ledsRGB, numberOfDiodes);
  39. }

Przesunięcie diod świecących w lewo:

  1. void WS2812_MoveDiodeToLeft(struct WS2812_RGB *rgbColor)
  2. {
  3.     uint16_t i = 0;
  4.     struct WS2812_RGB *ledPtr = rgbColor;
  5.    
  6.     for(= 1; i<MAX_DIODE_COUNT; i++)
  7.     {
  8.         ledPtr[- 1].blue = ledPtr[i].blue;
  9.         ledPtr[- 1].red = ledPtr[i].red;
  10.         ledPtr[- 1].green = ledPtr[i].green;
  11.     }
  12.    
  13.     ledPtr[- 1].blue = 0x00;
  14.     ledPtr[- 1].red = 0x00;
  15.     ledPtr[- 1].green = 0x00;
  16.    
  17.     WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
  18. }

Przesunięcie diod świecących w prawo:

  1. void WS2812_MoveDiodeToRight(struct WS2812_RGB *rgbColor)
  2. {
  3.     uint16_t i = 0;
  4.     struct WS2812_RGB *ledPtr = rgbColor;
  5.    
  6.     for(uint16_t i = MAX_DIODE_COUNT-1; i<MAX_DIODE_COUNT; i++)
  7.     {
  8.         ledPtr[i].blue = ledPtr[- 1].blue;
  9.         ledPtr[i].red = ledPtr[- 1].red;
  10.         ledPtr[i].green = ledPtr[- 1].green;
  11.     }
  12.    
  13.     ledPtr[i].blue = 0x00;
  14.     ledPtr[i].red = 0x00;
  15.     ledPtr[i].green = 0x00;
  16.     WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
  17. }

Obrócenie diod w lewo:

  1. void WS2812_RotateDiodeToLeft(struct WS2812_RGB *rgbColor)
  2. {
  3.     uint16_t i = 0;
  4.     struct WS2812_RGB *ledPtr = rgbColor;
  5.     struct WS2812_RGB tmpValue;
  6.    
  7.     tmpValue.blue = rgbColor->blue;
  8.     tmpValue.red = rgbColor->green;
  9.     tmpValue.green = rgbColor->red;
  10.        
  11.     for(= 1; i<MAX_DIODE_COUNT; i++)
  12.     {
  13.         ledPtr[- 1].blue = ledPtr[i].blue;
  14.         ledPtr[- 1].red = ledPtr[i].red;
  15.         ledPtr[- 1].green = ledPtr[i].green;
  16.     }
  17.        
  18.     ledPtr[- 1].blue = tmpValue.blue;
  19.     ledPtr[- 1].red = tmpValue.red;
  20.     ledPtr[- 1].green = tmpValue.green;
  21.        
  22.     WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT);
  23. }

Obrócenie diod w prawo:

  1. void WS2812_RotateDiodeToRight(struct WS2812_RGB *rgbColor)
  2. {
  3.     uint16_t i = 0;
  4.     struct WS2812_RGB *ledPtr = rgbColor;
  5.     struct WS2812_RGB *ledPtrLastDiode = rgbColor + (MAX_DIODE_COUNT - 2);
  6.    
  7.     for(= MAX_DIODE_COUNT-1; i>0; i--)
  8.     {
  9.         ledPtr[i].blue = ledPtr[- 1].blue;
  10.         ledPtr[i].red = ledPtr[- 1].red;
  11.         ledPtr[i].green = ledPtr[- 1].green;
  12.     }
  13.    
  14.     ledPtr[i].blue = ledPtrLastDiode->blue;
  15.     ledPtr[i].red = ledPtrLastDiode->red;
  16.     ledPtr[i].green = ledPtrLastDiode->green;
  17.    
  18.     WS2812_SetLedsRGBColor(rgbColor, MAX_DIODE_COUNT); 
  19. }

Czyszczenie danych w strukturze HSV:

  1. void WS2812_ClearDataInHSVStruct(WS2812_HSV_Color_t * hsv_color, uint16_t tableSize)
  2. {
  3.     for(uint16_t i = 0; i < tableSize; i++)
  4.     {
  5.         hsv_color->hue = 0x00;
  6.         hsv_color->saturation = 0x00;
  7.         hsv_color->value = 0x00;
  8.            
  9.         hsv_color++;
  10.     }
  11. }

Czyszczenie danych w strukturze RGB:

  1. void WS2812_ClearDataInStruct(struct WS2812_RGB *rgbColor, uint16_t tableSize)
  2. {
  3.     for(uint16_t i = 0; i < tableSize; i++)
  4.     {
  5.         rgbColor->blue = 0x00;
  6.         rgbColor->green = 0x00;
  7.         rgbColor->red = 0x00;
  8.        
  9.         rgbColor++;
  10.     }
  11. }

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.

Cały projekt można pobrać z dysku Google pod tym linkiem.