Sterowanie diod WS2812 poprzez DMX

Sterowanie diod WS2812 poprzez DMX
Pobierz PDF Download icon

Sterowanie diodami WS2812 za pośrednictwem DMX nie jest pomysłem wyjątkowym, jednak zrealizowanie tego zadania z użyciem interfejsu UART i systemu przerwań na bazie mikrokontrolera AVR nie jest zadaniem łatwym. Natomiast praca w w tym samym czasie jako urządzenie DMX oraz odbiór sygnału z USB z prędkością 1Mb/s wydaje się niemożliwe. Opracowanie takiego systemu wymagało zastosowania nieszablonowych rozwiązań.

Spis treści

Sterowanie diodami przy pomocy UART

Diody sterowane są impulsami o okresie 1,25 µs ±600 ns (rysunek 6), co daje przepływność na poziomie 800 kb/s.

Rysunek 6. Fragment dokumentacji diod LED typu WS2812 pokazujący czasy impulsów sterujących

Transmisja każdego bitu jest podzielona na trzy odcinki czasowe, z których pierwszy zawsze jest stanem wysokim, drugi decyduje o wartości przesyłanej informacji a trzeci zawsze ma stan niski (rysunek 7).

Rysunek 7. Fragment dokumentacji diod LED typu WS2812 pokazujący podstawowe przebiegi sterujące

Zatem przepływność UART powinna wynosić 2,4 Mb/s (1,6...4,6 Mb). Ramka transmisyjna UART 7N1 składa się z dziewięciu odcinków czasowych – bit startu, 7 bitów danych i 1 bit stopu. W jednym bajcie można przesłać 3 bity sterujące diodą typu WS2812. Bit startu i stopu ma z góry ustaloną wartość. Niestety bit startu to zero, a bit stopu jeden. Po zanegowaniu sygnału UART otrzymujemy sygnał wymagany przez sterownik diod LED typu WS2812. Aby wysłać trzy bity o wartości 0 poprzez UART należy wpisać wartość $26 (binarnie 100110). Działanie tego rozwiązania zostało zobrazowane przy pomocy tabeli 1.

Na pomarańczowo zaznaczono bity startu i stopu, na żółto stałą wartość jaka musi być wysyłana poprzez UART, litery A, B, C ustalają wartości bitów 1, 2, 3 przesyłanych do diody LED.
Jaką maksymalną przepływność można uzyskać przy pomocy UART w AVR? Według noty katalogowej: Fosc/8·UBRR+1. Dla 20 MHz i UBRR=1 otrzymamy 1,25 Mb/s czyli dwa razy za wolno. Okazało się, że wpisując zero dla UBRR otrzymujemy przepływność 2,5 Mb/s (rysunek 8).

Rysunek 8. Zmierzona maksymalna przepływność interfejsu UART

Transmisję da się zrealizować na przerwaniach nawet bez wstawki w asemblerze, co pokazuje listing 1.

Listing 1. Kod realizujący transmisję przy pomocy przerwania

SIGNAL(USART1_TX_vect){
if (pozScr){
UDR1 = *(scr+pozScr++);
pozScr &=SCRLEN-1;
}
if (pozScr >= SCRLEN) FL_TransEnd=true;
}
Rysunek 9. Efekt działania transmisji z kodem bez optymalizacji

Wynik działania programu obsługi przerwania został pokazany na rysunku 9. Czas obsługi przerwania wynosi 4,44 µs więc na pewno nie będzie zinterpretowany przez diody jako reset. Po optymalizacji kodu, której wynik pokazano na listingu 2 (niepotrzebne operacje zakomentowano) czas przerwania zmniejszył się do 4,08 µs. Łączna oszczędność 7 rozkazów/cykli ma dość duże znaczenie z racji bardzo częstego wywoływania przerwania.

Listing 2. Kod obsługi przerwania po optymalizacji

ISR(USART1_TX_vect, ISR_NAKED){
asm volatile (
// usunięta operacja na nieużywanym R1 i R0
// "push r1 \n\t"
// "push r0 \n\t"
// "in r0, 0x3f \n\t" // SREG
// "push r0 \n\t"
// "eor r1, r1 \n\t"
"push r18 \n\t"
//SREG na stos przy użyciu używanego później R18 (+3)
"in r18, 0x3f \n\t" // SREG
"push r18 \n\t"
//Dalej tak jak zrobił kopilator
"push r24 \n\t"
"push r25 \n\t"
"push r30 \n\t"
"push r31 \n\t"
);
//I dotychczasowy kod:
if ( pozScr ){
UDR1 = *(scr+pozScr++);
pozScr &=SCRLEN-1;
}
if (pozScr >= SCRLEN) FL_TransEnd=true;
asm volatile (
"pop r31 \n\t"
"pop r30 \n\t"
"pop r25 \n\t"
"pop r24 \n\t"
//Dodane SREG przy użyciu R18
"pop r18 \n\t"
"out 0x3f, r18\n\t"
//Dalej normalnie R18
"pop r18 \n\t"
//Usunięte operacje na R0 i R1
// "pop r0 \n\t"
// "out 0x3f, r0\n\t"
// "pop r0 \n\t"
// "pop r1 \n\t"
);
reti();
}
Rysunek 10. Efekt działania transmisji ze zoptymalizowanym kodem

Efekt działania zoptymalizowanej procedury został pokazany na rysunku 10. Niebieski przebieg obrazuje działanie pętli głównej programu, która po skompilowaniu wykonuje się w czterech cyklach maszynowych (listing 3).

Listing 3. Kod testowej pętli głównej

loop:
wdg();
PORTA |= _BV(PA3);
PORTA &= ~_BV(PA3);

Jak łatwo zauważyć pomiędzy przerwaniami program główny wykonuje około 16 rozkazów. Dość mało i niewiele da się zrobić w takim czasie. Jeśli jednak weźmiemy pod uwagę to, że transmisja nie musi trwać bez przerwy i pomiędzy paczkami wstawimy pauzę o długości 20 ms, co da odświeżanie zawartości LED na poziomie 50 Hz, to przy 60 diodach LED otrzymamy: 60·1,2 µs·24 bity ok. 1,7 ms, czyli zajętość CPU na poziomie 8,5%. Przykładowe obliczenia obciążenia CPU dla innych parametrów pokazano w tabeli 2.

Artykuł ukazał się w
Elektronika Praktyczna
kwiecień 2021
DO POBRANIA
Pobierz PDF Download icon
Materiały dodatkowe
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik grudzień 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio listopad - grudzień 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje listopad - grudzień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna grudzień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich styczeń 2025

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów