Witam,
Próbuję wysterować z STM32F4 (Kamami KA-NUCLEO-F411CE) pasek "adresowalnych" diod LED z chipami WS2812B.
Diody te wymagają wysyłania szeregowo po 24 bity informacji (RGB TrueColor) dla każdej diody bez przerwy oraz przerwy min. 50 us po wysłaniu informacji o kolorach dla wszystkich diod, aby diody zmieniły kolory. Jedynka kodowana jest jako dłuższy okres stanu wysokiego za którym następuje krótki okres stanu niskiego, a zero jako krótki okres stanu wysokiego i dłuższy stanu niskiego. Czas trwania pojedynczego bitu to 1.25 us, więc jest to dość szybkie. Dokładniej jest to opisane tutaj: https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
Ustawianie odpowiednich stanów na pinach i odczekiwanie (np. while(delay--)) mogło by nie być zbyt optymalne. Widziałem w Internecie podobne rozwiązania wykorzystujące UART do generowania "waveforma" z bitów zapisanych w tablicy poprzez DMA. Z pewnym trikiem jest to możliwe na XMedze - link.
Ja chciałbym wykonać to przy pomocy SPI. Wyliczyłem że aby pojedynczy bajt nadawany przez SPI (traktowany jako pojedynczy bit sygnału dla diod - "waveform" jedynki albo zera) trwał 1.25 us, potrzebuję ustawić taktowanie procesora na 51.2 MHz i podzielić tą częstotliwość przez 8 (w ustawieniach SPI). Wynika to stąd że pojedynczy bit SPI kodujący 1/8 bit dla diod musi trwać przez 1.25/8 us = 0.15625 us, a to daje częstotliwość 6.4 MHz. Następnie tworzę odpowiedni bufor wartości odpowiadających bitom sygnału dla diod i transmituję go przez SPI.
Mój kod z wyciętymi niepotrzebnymi fragmentami:
http://pastebin.com/fg0sdCuC
Wrzuciłem go też jako załącznik.
Sama funkcja generująca bufor do transmisji przez SPI:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
while (1) {
uint8_t zeroes = 0;
uint8_t zero = 0b11100000;
uint8_t one = 0b11111000;
uint8_t buffer[744];
for (int i = 0; i < 24; i++)
buffer[i] = zeroes;
uint8_t color = 0;
for (int i = 24; i < 744; i += 24) {
for (int j = 0; j < 24; j++)
buffer[i+j] = zero;
if (color == 0) {
buffer[i] = one;
color++;
} else if (color == 1) {
buffer[i + 8] = one;
color++;
} else if (color == 2) {
buffer[i + 16] = one;
color = 0;
}
}
HAL_SPI_Transmit(&hspi1, &buffer, 744, 1000);
HAL_Delay(10000);
}
}
Problem w tym że mój kod czasem działa, a czasem nie. Na początku bardziej działał - wyświetlał naprzemiennie kolory GRB, ale czasem traciły one na jasności (w kolejnych cyklach odświeżania co 10 sec) i ponownie stawały się jasne. Obecnie bardziej nie działa - cały pasek jest biały, czasem widać że na ten biały kolor składają się naprzemiennie bardziej zielone, czerwone i niebieskie piksele (kolejne diody).
Co może być przyczyną takiego zachowania? Czy między ramkami SPI następuje jakaś przerwa? Czy do ramek jest coś doklejane jak w UART? Niestety nie mam oscyloskopu ani nawet analizatora stanów logicznych. To ostatnie planuję kupić.
Nie chciał bym przerabiać gotowych rozwiązać, ponieważ dopiero uczę się STM32 z HAL-em. Nie korzystałem jeszcze z DMA itp.
ws2812b.c