Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
NOVATEK-ELECTRO POLSKA SP. Z O.O.NOVATEK-ELECTRO POLSKA SP. Z O.O.
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Arduino, WS2812, zmiana efektu w przerwaniu.

12 Dec 2018 07:11 1176 22
  • Level 23  
    Witam.

    Mam program z kilkoma efektami na diody WS2812, przycisk obsługiwany w przerwaniu do zmiany efektu. Problem w tym że efekt przełącza się dopiero po zakończeniu poprzedniego. Nie wiem jak zrobić aby w chwili naciśnięcia przycisku działający efekt się zakończył i wskoczył mi do pętli głównej w celu przełączenia efektu.

    Code: c
    Log in, to see the code
  • NOVATEK-ELECTRO POLSKA SP. Z O.O.NOVATEK-ELECTRO POLSKA SP. Z O.O.
  • Level 31  
    Aby to zrobić zgodnie ze sztuką obsłuż LED na przerwaniach od USART. Trochę zabawy z tym jest, bo przerwania 300 tysięcy razy na sekundę dla AVR to duże obciążenie (wstawki ASM wskazane). Na 16MHz nie pójdzie, więc kwarc min 18MHz. Prosta sztuczka pozwoli zmniejszyć częstotliwość przerwań dwukrotnie. Aby uniknąć dodatkowych "efektów" wszystkie inne przerwania musisz deklarować jako nieblokowane.
    Tak w skrócie, żegnajcie biblioteki Arduino. Piszemy porządnie w C. Ze względu na ok 90% obciążenie AVR przez WS2812, proponuję wybrać coś z DMA. Ze względu na cenę i możliwości lepiej ARM niż Xmega.


    PS
    Niestety, 99% bibliotek Arduino jest pisanych bez głowy. Nie używają przerwań, zużywają dużo RAM (C++, klasa String), niepotrzebnie używają liczb zmiennoprzecinkowych (DS18B20, BMP280).
  • Level 23  
    LChucki wrote:
    Aby to zrobić zgodnie ze sztuką obsłuż LED na przerwaniach od USART. Trochę zabawy z tym jest, bo przerwania 300 tysięcy razy na sekundę dla AVR to duże obciążenie (wstawki ASM wskazane). Na 16MHz nie pójdzie, więc kwarc min 18MHz. Prosta sztuczka pozwoli zmniejszyć częstotliwość przerwań dwukrotnie. Aby uniknąć dodatkowych "efektów" wszystkie inne przerwania musisz deklarować jako nieblokowane.
    Tak w skrócie, żegnajcie biblioteki Arduino. Piszemy porządnie w C. Ze względu na ok 90% obciążenie AVR przez WS2812, proponuję wybrać coś z DMA. Ze względu na cenę i możliwości lepiej ARM niż Xmega.


    PS
    Niestety, 99% bibliotek Arduino jest pisanych bez głowy. Nie używają przerwań, zużywają dużo RAM (C++, klasa String), niepotrzebnie używają liczb zmiennoprzecinkowych (DS18B20, BMP280).


    Czyli z tymi bibliotekami absolutnie nie da się zrobić tak żeby efekty zmieniały się od razu po wciśnięciu przycisku?
  • Level 31  
    pier wrote:
    Czyli z tymi bibliotekami absolutnie nie da się zrobić tak żeby efekty zmieniały się od razu po wciśnięciu przycisku?

    Da,prawie zawsze się da,tyle, że nakład pracy i uzyskane efekty będą kiepskie.

    Można bibliotekę Arduino zmodyfikować tak aby nie blokowała przerwań co robi rozkazem "cli();". Następnie trzeba zablokować wszystkie przerwania poza EXTI do którego podepnie się przycisk. Teraz zabawa aby on-line podmienić efekt. Roboty dużo efekt połowiczny.

    Można napisać własna obsługę LED, wysyłać dane do LED pojedynczo, w pętli sprawdzając stan GPIO (ale nie przez digitalRead, to wysypie komunikację do LEDy), po wysyłaniu danych do wszystkich LED wygenerować reset do LED (torchę kiepsko to nazwali, bo reset przepisuje dane w zatrzaskach LED).

    Teraz zastawów się. Powyższe dwie opcje są równie, jak nie bardziej pracochłonne co z przerwaniami od USART a efekty połowiczne. Warto się w to bawić?
    Jak nie jesteś w stanie tego zrobić, to nie ma sprawy, podejmę się.
  • Level 23  
    LChucki wrote:

    Można bibliotekę Arduino zmodyfikować tak aby nie blokowała przerwań


    Ale gdzie ja napisałem że biblioteka blokuje przerwania?
    Podczas "wykonywania" efektu przerwanie od przycisku jest wykonywane bo zwiększa się wartość licznika "count". Tylko żeby ta zmiana przyniosła skutek efekt musi się zakończyć program skoczy wtedy do pętli głównej i wykona się instrukcja "switch case" która przełączy na następny efekt.
    Kombinowałem aby instrukcję "switch case" wsadzić do przerwania ale pojawia się problem podczas uruchomienia urządzenia bo przerwanie choć raz musiało by się wykonać samo aby włączyć pierwszy efekt.
    Chodzi generalnie o to aby powrót z przerwania nie był do "efektu" tylko do pętli głównej.
    Da się tak?
  • Level 31  
    pier wrote:
    Ale gdzie ja napisałem że biblioteka blokuje przerwania?

    Biblioteka WS2812 blokuje przerwania.
    pier wrote:
    Podczas "wykonywania" efektu przerwanie od przycisku jest wykonywane

    Nie. Zapamiętywana jest flaga, gdy biblioteka WS2812 odblokuje przerwania, wykona się przerwanie od przycisku. Arduino na ma sprzętowego debugera, gdyby miało zobaczyłbyś, że tak właśnie jest.
    pier wrote:
    Kombinowałem aby instrukcję "switch case" wsadzić do przerwania ale pojawia się problem podczas uruchomienia urządzenia bo przerwanie choć raz musiało by się wykonać samo aby włączyć pierwszy efekt.

    Chyba już teraz wiesz dlaczego tak się dzieje.

    pier wrote:
    Chodzi generalnie o to aby powrót z przerwania nie był do "efektu" tylko do pętli głównej.
    Da się tak?

    Da, jak pisałem WS2812 na USART, transmisja do LED w przerwaniach, program główny bada GPIO i odpowiednio reaguje. Proste jak kilometr sznurka w kieszeni.
  • NOVATEK-ELECTRO POLSKA SP. Z O.O.NOVATEK-ELECTRO POLSKA SP. Z O.O.
  • Level 26  
    Lib z założenia musi blokować przerwania. A raczej sam driver tj. procedura transmitująca dane do LEDów. Jednakże przypuszczam, że poza funkcją drivera przerwania są odblokowywane, więc to nie problem. Problemem leży w tym, że funkcje efektów są napisane w ten sposób, że nie można ich przerwać w połowie. Trzeba by je napisać od nowa. Na przykład raibowCycle pięc razy transmituje dane do LEDów a między transmisjami czeka w pętli busy wait. Ażeby przerwać tę funkcję w połowie, trzeba by ją zmodyfikować np. w ten sposób, że każde wywołanie realizowało by transfer danych po którym funkcja by kończyła działanie. Kolejne fazy efektu były by realizowane przez kolejne wywołania funkcji z pętli glównej w zadanym interwale czasowym. Gdzieś należało by pamiętać fazę w której efekt się w danej chwili znajduje. Należalo by także przewidzieć reset efektu aby powracając do efektu gdzieś w dalszej części programu nie zaczynać go w połowie.
    Nie jest to samo w sobie trudnym zadaniem ale wymaga ZASADNICZEJ modyfikacji całej architektury liba.
    Na chybcika można by spróbować tam gdzie są delaye w funkcjach efektów wstawić testowanie globalnej flagi ustawianej przez button i w razie potrzeby wykonać return.
  • Level 39  
    Witam,
    nie mam czasu na dokładną analizę kodu ale problem raczej nie jest zabójczo skomplikowany.
    Powinieneś zastosować jakiś znacznik "właśnie wciśnięto przycisk zmiany efektu".
    W funkcjach poszczególnych efektów (dużo pętli for) analizujesz wspomniany znacznik i ewentualnie "wyskakujesz" z pętli (czasami chyba kilku zagnieżdżonych), na wyjściu z funkcji kasujesz znacznik i tyle.
    Musisz również zrezygnować z "delay".

    Pozdrawiam
  • Level 31  
    ex-or wrote:
    Na przykład raibowCycle pięc razy transmituje dane do LEDów a między transmisjami czeka w pętli busy wait.

    To nie problem.
    ex-or wrote:
    Gdzieś należało by pamiętać fazę w której efekt się w danej chwili znajduje. Należalo by także przewidzieć reset efektu aby powracając do efektu gdzieś w dalszej części programu nie zaczynać go w połowie.

    I zaczynają się schody. Pomiędzy transmisjami, czas nieaktywności, zależnie od typu led, nie może być dłuższy niż 15..25us bo zostanie uznany za reset. Z jednej strony 25us to dużo z drugiej mało. Zacznie się jazda z liczeniem cykli,co raczej dla niezaznajomionych z z asseblerem łatwe nie będzie. Wymaga ręcznego wygenerowania z ELF pliku LSS. Jak mam z tymwalczyc, to prościej zrobić na UART w przerwaniach.

    Dodano po 1 [minuty]:

    ex-or wrote:
    Na chybcika można by spróbować tam gdzie są delaye w funkcjach efektów wstawić testowanie globalnej flagi ustawianej przez button i w razie potrzeby wykonać return.

    Ta ma sens.Wydaje się w miarę proste w realizacji ale nadal to proteza.

    Dodano po 1 [minuty]:

    krzysiek_krm wrote:
    nie mam czasu na dokładną analizę kodu ale problem raczej nie jest zabójczo skomplikowany.
    Powinieneś zastosować jakiś znacznik "właśnie wciśnięto przycisk zmiany efektu".

    To proszę znaleźć czas bo ta wypowiedź nic nie wnosi poza +4 pkt na konto.

    Dodano po 2 [minuty]:

    krzysiek_krm wrote:
    Musisz również zrezygnować z "delay".

    A konkretnie, które delay powodują problem?
  • Level 39  
    LChucki wrote:
    A konkretnie, które delay powodują problem?

    Generalnie to wszystkie . To poszczególne "efekty" powinny być wywoływane w konkretnych interwałach czasowych, jako tzw. zdarzenia i niekoniecznie na przerwaniach, a nie same efekty decydować, jak długo mają trwać i blokować wykonywanie pozostałej części kodu.
    Kod jest do przeprojektowania w taki sposób, aby to główna pętla generowała konkretne zdarzenia na wywołanie poszczególnych efektów w konkretnym czasie, wtedy też może obsługiwać zdarzenie obsługi przycisku, a co za to idzie decydować. który z efektów ma być wywołany/przerwany i kiedy.

    LChucki wrote:
    Niestety, 99% bibliotek Arduino jest pisanych bez głowy.

    Niestety 99% takich opinii jest pisanych bez znajomości tych bibliotek.
  • Level 31  
    khoam wrote:
    Generalnie to wszystkie . To poszczególne "efekty" powinny być wywoływane w konkretnych interwałach czasowych, jako tzw. zdarzenia i niekoniecznie na przerwaniach, a nie same efekty decydować, jak długo mają trwać i blokować wykonywanie pozostałej części kodu.

    Zgodzę się z tym ale ten kod nie robi nic innego jak generuje efekty. W tej sytuacji wielowatkowość to przerost formy nad treścią.

    khoam wrote:
    Niestety 99% takich opinii jest pisanych bez znajomości tych bibliotek.

    Nie muszę znać biblioteki aby stwierdzić, że w przypadku DS18B20 czy BM280 liczby zmiennoprzecikowe to głupota.
    Jak już poznałem lib do DS18B20 to przeraziłem się, gdy okazało się, że ignoruje CRC. Takich co obsługują EEPROM DS18B20 mozna policzyć na palcach jednej ręki drwala po przejściach z piłą łańcuchową.
    Bibliotek Arduino widziałem może 20..30 i wszystkie są beznadziejne. Nie będę teraz analizował setki kolejnych libs aby znaleźć ta jedna dobrą. Nawet jak znajdę, to co? Mam odtrąbić sukces, że 1% bibliotek jest ok?
  • Level 39  
    LChucki wrote:
    Zgodzę się z tym ale ten kod nie robi nic innego jak generuje efekty. W tej sytuacji wielowatkowość to przerost formy nad treścią.

    Nie, ma też dodatkowo obsługiwać zdarzenie przycisku i nie wiadomo, czy Autor coś jeszcze nie będzie chciał dodać - architektura kodu musi takie sytuacje przewidzieć. Nie pisałem nic o wielowątkowości - w tej aplikacji aż się prosi o scheduling.

    LChucki wrote:
    Jak już poznałem lib do DS18B20 to przeraziłem się, gdy okazało się, że ignoruje CRC.

    I tak się rodzą plotki w necie, a wszystko przez brak wiedzy o bibliotece OneWire, na które bazuje biblioteka DS18B20. Ale to już jest off-topic.
  • Level 31  
    khoam wrote:
    LChucki wrote:
    Jak już poznałem lib do DS18B20 to przeraziłem się, gdy okazało się, że ignoruje CRC.

    I tak się rodzą plotki w necie, a wszystko przez brak wiedzy o bibliotece OneWire, na które bazuje biblioteka DS18B20. Ale to już jest off-topic.

    Co ma wspólnego lib OneWire do CRC? OneWire nie liczy CRC! Tak samo OneWire nie obsługuje bądź nie EEPROM w DS18B20!
    OneWire jest spie.. zła. Zwarcie magistrali traktuje jak odczyt zera a powinien byc sygnalizowany błąd.
    Jeśli już ktoś pisze obsługę DS18B20 i wie, że OneWire jest do du... do niczego, to jaki problem napisać własną OneWire a właściwie zmodyfikować bubel OneWire? Przy okazji zrobić "po ludzku" wersję na UART.

    Dodano po 12 [minuty]:

    Wracając do tematu.
    Tanio nie będzie, jakieś 10zł, ale do obsługi WS2812 można zastosować SC16IS7xx. Oferuje on przepływność 5MB/s (do WS potrzeba 2,4) ale co ważne, ma FIFO 64 bajty. Oznacza to, że można przesłać w ok 66us (dla Arduino 16MHz, AVR 20MHz będą to 52us, ARM 15us - tylko po co, tam UART i DMA dostatek) dane dla 8 led. Transmisja do 8 led trwa 2,4ms. Można więc wysyłać paczki po 8led i jest dużoooo czasu na inne zadania. Paczki mogą być wysyłane w przerwaniu. Program główny co prawda wisi aż jakieś 70..80us ale to nic podług zawieszania programu na "wieczność" przez Arduinową bibliotekę dla WS2812. Napisałem "aż 70..80us", bo typowe przerwania wykonują się w 15..20us.
  • Level 26  
    A więc co można zrobić na dziś, żeby to jakoś działało ale się nie narobić:
    1. utworzyć zmienną globalną
    Code: c
    Log in, to see the code

    2. wszystkie wywołania funkcji delay(cośtamcośtam) w efektach przerobić na
    Code: c
    Log in, to see the code

    3. Zamiast przerwania generowanego przez buton zrobić porządną funkcję testowania przycisku z debouncingiem i wykrywaniem zmiany stanu. Do tego potrzebny będzie timer. Timer0 jest w arduino zajęty więc pozostaje timer1 lub 2. Jak skonfigurować timer i przerwanie od przepełnienienia w Arduino nie wiem i pozostawiam do zrobienia Autorowi. Timer ma co 10 do 30 ms wywołać przerwanie od przepełnienia albo kompare match z mniej więcej taką treścią:
    Code: c
    Log in, to see the code

    Jeśli "tutaj_wstawić_coś_co_da_stan_pinu_buttona" będzie dawać wyłącznie stan pinu buttona to OK. Jeśli stan całego portu to do powyższego kodu trzeba wstawić jeszcze odpowiednią maskę.

    Dodano po 4 [minuty]:

    LChucki wrote:
    to nic podług zawieszania programu na "wieczność" przez Arduinową bibliotekę dla WS2812.

    Przesada. Jedna dioda to 30µs, a więc typowy 5m łańcuch 60/m to 9ms. Nie jest to czas który by powodował jakieś zakłócenia obsługi buttona. Tak więc nie ma co wyciągać armat na muchę.
  • Level 31  
    ex-or wrote:
    Przesada. Jedna dioda to 30µs, a więc typowy 5m łańcuch 60/m to 9ms

    9ms dla mnie to wieczność. W 9ms, to nawet przy 9600 gubię 9 znaków, przy 115200 ponad 100. W ARM, przerwania systick są co 1ms,więc gubię 9ms. Milisekunda to ogrom czasu, co dopiero 9ms. Może autor na razie nie używa UART ale
    khoam wrote:
    nie wiadomo, czy Autor coś jeszcze nie będzie chciał dodać - architektura kodu musi takie sytuacje przewidzieć.

    Gdy led będzie 1000? 30ms to już czas drżenia styków. Czyżby to był sposób na likwidację ich drżenia? Zawieszanie programu na 30ms?

    ex-or wrote:
    Do tego potrzebny będzie timer. Timer0 jest w arduino zajęty

    Nie do końca. Tylko przerwanie od przepełnienia. Przerwania od porównania można użyć do własnych celów. Podobnie można używać PWM, tyle, że taktowanie jest z góry narzucone.

    ex-or wrote:
    Jak skonfigurować timer i przerwanie od przepełnienienia w Arduino nie wiem i pozostawiam do zrobienia Autorowi.

    Rada w stylu, podobno się da. Albo, mój znajomy widział Elvisa.
    Skoro nie potrafisz, to skąd wiesz, czy da się to w tym przypadku zrobić?
  • Level 33  
    Można też return wstawić na początku funkcji showStrip() po if od wykryciu wciśnięcia przycisku, w przerwaniu od przycisku to przerwanie wyłączać i włączać z powrotem na początku funkcji case. Jeśli obieg pętli jest za krótki to można tego delay(80) przenieść przed włączenie przerwania od przycisku (pewnie wystarczy mniejszy delay). I tak jak napisał @ex-or nie ma co przeżywać jak jak mrówka okres, co najwyżej mając już działający kod można go dalej ulepszać. Choć do tego co ma robić pewnie i tak już będzie ok.
    Edit: no po przebiegnięciu za około domu to jednak zaświtało, że trzeba wstawić w każdą funkcję zamiast w showStrip(), bo przerwiemy tylko jedno wyświetlenie.
  • Level 31  
    Analizując różne rozwiązania i potrzeby wymagane w zadaniu, aby nie uciekać się do RTOS, który to w AVR nie ma za bardzo sensu zrobiłbym tak:
    1. zadanie transmisji do WS2812
    2. zadanie generujące efekty
    3. zadanie przełączania efektów

    Realizacja 1, wiadomo, USART i przerwania
    2 - maszyna stanów w pętli głównej
    3 - obsługa przycisku w pętli głównej

    Niestety, aby to zrobić, nie mogą być obce terminy takie jak ISR_NOBLOCK, ISR_NAKED, ASM.

    Inne sposoby, o ile osiągnie się cel, też będą dobre ale wymagają większego nakładu pracy jak np obsługa przycisku w kilkunastu miejscach programu. Niestety, te "proste" rozwiązania, to także odcięcie się od obsługi USART i innych peryferii wymagających szybkiej obsługi przerwań. Kod wynikowy także będzie zdecydowanie dłuższy.

    Należy mieć świadomość ograniczeń AVR. Obsługa WS2812 w AVR jest zrobiona na siłę, tak samo jak LCD 320x240 kolor 16-bit lub większych, dekodowanie plików JPEG, obsługa dużych plików na SD. Co z tego, że to działa skoro wyświetlanie obrazka 320x240 16-bit z karty SD trwa blisko 3 sekundy?
    W przypadku AVR, zadania graniczące z jego możliwościami trzeba dzielić na kilka mikrokontrolerów. Tylko jaki to ma sens, skoro można użyć jednego taniego ARM? To, że jeden ARM (i to najsłabszy z rodziny) będzie tańszy niż trzy AVR a nawet jeden duży, nie muszę chyba udowadniać.

    Podsumuję może tak.
    Gdybym miał realizować projekt z WS2812 i należałoby obsługiwać przerwania to w przypadku narzucenia AVR, koszt robocizny przemnożyłbym przez 3 albo cztery. Sam projekt sprzętu prawdopodobnie byłby droższy niż na ARM.
  • Level 23  
    Bardzo dziękuję za tak szeroki odzew.
    Muszę się przyznać że mało rozumiem z tego co Radzicie, pewnie gdybym to pojmował to nie było by tematu i sam bym podołał.
    Za dużo roboty a efekt to tylko szybsze przełączanie trybów. Daruje to sobie...

    A Powiedzcie jeszcze bo mnie to nurtuje. Po co biblioteka obsługująca WS-y blokuje przerwania? Czy nie można w prosty sposób po prostu ich odblokować?
  • Level 31  
    pier wrote:
    Po co biblioteka obsługująca WS-y blokuje przerwania?

    Generuje krótkie czasy (1,25us) i pojawienie sie przerwania (typowo 10..20us) skutecznie wszystko psuje. Niby można by pozwolic na przerwania w czasie gdy ustawiony jest poziom niski. Reset WS to 50us ale w praktyce reset interpretują przy 15..20us. Typowo przerwanie tyle trwa więc wszystko na styk i wymagałoby zaangażowania timera sprzętowego do odliczania czasu. AVR sa ubogie w timery więc jest problem. Ten sam problem występuje w 1-Wire. Panaceum na to jest transmisja przez UART. Tyle, ze jak w 1-Wire pomiędzy bitami może być dowolnie długi odstęp, tak w WS nie.W praktyce, na AVR trzeba trochę pokombinować aby obsłużyć WS na UART w przerwaniach bo tylko taka opcja zapewnia że pomiędzy bitami nie będzie przerw. Niestety AVR jest ubogi w UART. Cztery uart maja tylko układy w obudowach 100pin.

    Nie potrafię zrozumieć dlaczego tak kombinować skoro sa inne sprawdzone metody dające czasy zgodne z dokumentacją?
    Jeśli jeden UART w Mega328 to za mało można użyć Mega328PB.
    Jakiś sensowny argument przeciwko mojemu?

    <ciach>
    Moderated By Marek_Skalski:

    Posty scaliłem. Proszę trzymać się tematu. Osobiste preferencje i narzekania Kolegi, mało nas interesują.
    3.1.11. Nie wysyłaj wiadomości, które nic nie wnoszą do dyskusji. Wprowadzają w błąd, są niebezpieczne czy nie rozwiązują problemu użytkownika.

  • Level 11  
    Sprawdź czy takie rozwiązanie zadziała.
    Code: c
    Log in, to see the code
  • Level 23  
    Jeśli ktoś potrzebuje działających przerwań w czasie obsługi WS2812 to tutaj gość ugryzł problem.
    Sam jeszcze nie testowałem.
  • Level 14  
    Panowie, dlaczego nie zrobić sterowania diodami porządnie, tylko bawicie się w jakieś opóźnienia..?

    Macie w mikrokontrolerze timer z trybem porównania.
    1. Robimy przerwanie od przepełnienia timera co czas wysłania na szyne danych diod pojedynczego bitu (ok 1,2 ms)

    2. Ustawiamy wartość rejestru porównania na wartość "większą" lub "mniejszą" (trzeba policzyć wartości) w zależności czy chcemy wysłać na szynę 1 lub 0.

    3. konfigurujemy odpowiedni (dla tego timera) pin który zmienia stan po zrównaniu/przekroczeniu wartości porównania.

    Tym sposobem, mamy zrobiony sprzętowy nadajnik bitów dla diod.
    W takt przerwań od timera wkładamy do rejestru porównania wartość "małą" lub "dużą" i już. Cieszymy się obsługą przerwaniowo-sprzętową obsługą diod.

    Czasem warto wejść głębiej niż muskać temat ślizgając się po falach arduino..
    ------------------------------

    Ponadto, po wykryciu naciśnięcia przycisku zaczynamy zliczać przerwania od timera (co ok 1,2 ms) i po np 10 sprawdzamy czy przycisk jest nadal wciśnięty, jeśli tak - uznajemy przycisk za wciśnięty.
    W efekcie mamy też porządnie załatwione drganie styków na przycisku.

    Kurczę, ale bym się pobawił w takie projekciki :) tylko tak mało czasu... :(