Kolejka zdarzeń o uwarunkowaniach czasowych

Pozostałe układy mikrokontrolerów, układy peryferyjne i inne, nie mieszczące się w powyższych kategoriach.
Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » wtorek 23 maja 2017, 15:53

Inspiracje

Tasza, wyzwalacz, inspiratorka wielu moich [i nie tylko moich] poczynań, można wręcz rzec, muza :mrgreen:
Wielokrotnie dawałem się jej wciągać, z własnej nieprzymuszonej woli, do pewnych badań, eksperymentów, pisania o elektronice i programowaniu. Przez tych kilkanaście lat wymieniliśmy ze sobą tysiące e-maili. Niektóre z nich zostawiły większy lub mniejszy ślad w otoczeniu...
Fragmenty z jej korespondencji:
  • <styczeń 2005> jakieś rozważania dotyczące Z80
    "Dzisiaj to z tego w szufladzie wala się ileś procków Z80 i nie wiadomo co z tym zrobić."

    zwierasz linie danych D0...D7 do masy (rozkaz NOP) na wejście zegarowe podajesz impulsy i masz LICZNIK 16-bitowy!!! jako wyjścia licznika pracują linie adresowe A0...A15 to na wypadek jakby Ci zabrakło odpowiednich ttl-i... no i jak dostojnie wygląda, prawda? a jak to jeszcze oryginalne Zilog-i a nie podróbki Nec-a to już w ogóle rewelacja!
:arrow: i z tego wyszedł żartobliwy tekst o tym, jak zbudować z C51 licznik (ten tekst ma naście lat, teraz po remake trafił na microgeek jak forma żartu w dziale Hyde Parku), no i później przemieścił się do innego działu. Ptaszki ćwierkały, że sprężyną w przeprowadzce była... nie będę pokazywał palcem, bo jakie to ma znaczenie.
:arrow: a tak przy okazji, co ma wspólnego Z80 z królikiem? Ano wiele, odziedziczył najlepsze geny po ojcu.
IMG_5577.JPG
IMG_5578.JPG
IMG_5579.JPG
IMG_5580.JPG

  • <styczeń 2005>
    "Ale zdarzyły mi się przypadki zejścia procków z tego świata."

    tak, to boli, wiem... ale on Ci poświęcił tyle swojego czasu wykonał tyle miliardów cykli maszynowych... doceń to! nie pozwól wylądować mu w koszu! tak nie wolno!
:arrow: zbudowałem system wieloprocesorowy, potrzebnych było ileś PC-towych procków (mój miał 6 procków typu 486 i jeden pentium), po remake może trafi do microgeek...

  • <styczeń 2005> (dotyczy materiłów o AVR)
    Andrzejku!
    te Twoje materiały są bardzo dobre! ja bym na Twoim miejscu jednak uparcie walczyła, aby się ukazały światu - na sieci, w czasopiśmie, jakkolwiek, warto!
:arrow: napisałem ogrom komiksów o AVR-ach (komiks to opowiadanie słowno-rysunkowe). Nie od dziś wiadomo, że wiedza, to podstawa. A żeby wiedzieć, to w pierwszej kolejności trzeba zrozumieć i wtedy spada zasłona całej iluzji z tym związanej, okazuje się, że nie ma w tym żadnej magii, tylko manipulacja czystą fizyką kwantową. Tylko czy to ważne w jaki sposób poginają elektrony i dziury?

  • <sierpień, 2005>
    (…) ciekawe ile razy jeszcze będę musiała marudzić o tych Naszych Stronach aby coś drgnęło, ale planuję do skutku...
:arrow: dałem się wciągnąć do pisania do Naszych Stron (serwis w obrębie elportal.pl). No jak nie patrzeć, czysta manipulacja.

  • <listopad, 2016> (w kwestii przykładów ilustrujących obsługę przycisków klawiatury).
    „chociaż to będzie trudne, bo co tu można jeszcze wymyślić nowego.”
    a można, kwestia fantazji - sterowanie gestami, czyli jak upchnąć jak największą ilość poleceń w jak najmniejszą ilość guzików, może dam przykład: mój lampowy wzmacniaczyk i jego sterowanie: ma gałkę, która jest de facto zestawem dwóch push buttonów na skrajnych położeniach jak przekręcisz i trzymasz chwilę: max w lewo - cichnie, jak max w prawo – pogłaśnia jak szybko szarpniesz lewo - prawo to włącza tryb timer (po 30 min. wyłącza lampy i idzie spać) jak szybko szarpniesz prawo - lewo - wyłącza tryb timer (działanie odwrotne do powyższego) jak jest już w trybie timer - ponowne szarpnięcie lewo-prawo daje dodatkowe 30 minut zanim się wyłączy to powstało, ponieważ ja często zasypiałam ze słuchawkami na uszach i lampy się świeciły bez sensu, a poza tym to niebezpieczne się robiło w podsumowaniu: jedno naciśnięcie - jakaś funkcja, dwa / trzy szybkie naciśnięcia (tupnięcia) druga lub trzecia funkcja, rozpoznawana
:arrow: napisałem kilka komiksów na temat obsługi prostych klawiatur, a szukając jakiegoś fajnego klawiaturowego pomysłu do rozwiązania spadło na mnie coś, co wymagało pomyślenia. Efektem tego jest moduł obsługi zakolejkowanych czasowo zdarzeń.
No i tak ulegając urokowi czarnookiej i brązowookiej czarodziejki, piszę dalej przeróżne komiksy, a im dalej, to robi się ciekawiej. Tak jak powiedział starożytny filozof Sokrates „Weź tyle, ile potrzebujesz, daj tyle, ile możesz”. Wyzwania rosną i droga prowadzi pod górę, ale... chodźmy zdobywać szczyty.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony wtorek 23 maja 2017, 17:45 przez gaweł, łącznie zmieniany 1 raz.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » wtorek 23 maja 2017, 16:20

Koncepcja

Obecnie praktycznie nie występują mikrokontrolery, które w swojej strukturze nie integrują podzespołów o różnorodnej funkcjonalności. Wśród całej gamy tzw. peryferali występują układy licznikowo-zegarowe. Na bazie takiego zespołu możliwe jest, by program uzyskał „świadomość upływającego czasu”. Wystarczy, że układ licznikowo-czasowy zostanie skonfigurowany do zliczania impulsów o określonej częstotliwości, gdzie w wyniku przepełnienia tego licznika zostanie wygenerowany sygnał przerwania. Od przerwania do przerwania upływa stały interwał czasu. Zliczając przerwania pośrednio mierzony jest upływ czasu.
W oprogramowaniu czasami zachodzi potrzeba pomiaru czasu. Ot choćby wygenerować sygnał dźwiękowy. W takim przypadku należy włączyć buzerek i po określonym czasie go wyłączyć. Niby prosta rzecz, ale...
Właśnie ale, zachodzi potrzeba obmierzenia jakiegoś interwału czasu. W prostym przypadku można wykonać na pusto ileś milionów instrukcji pustych, których realizacja zajmie określony czas. Ileś milionów pustych instrukcji... co za marnotrawstwo. Jednak w dobie powszechnego marnotrawstwa takie mogło by jeszcze ujść, ale... co zrobić, jak są dwie akcje wymagające pomiaru czasu. Trochę sprawa się komplikuje. W przypadku wystąpienia większej liczby zdarzeń z określonym interwałem czasowym ta metoda trafia na ścianę nie do pokonania. Jak się coś zaczyna komplikować, to trzeba coś zmienić. Co by tu zrobić żeby się nie narobić: najlepiej włączyć myślenie.
Warto by stworzyć moduł dołączany do programu jako coś w formie biblioteki funkcji, który będzie regularnie pobudzany w wyniku obsługi przerwań od upływu czasu. Każde pobudzenie obleci sobie nagromadzoną kolejkę akcji uwarunkowanych czasowo i w przypadku, gdy coś się doczekało, wykona określoną akcję. Jednak powstaje tu pewien problem, nie jest zalecane, by w obsłudze przerwań realizować bardziej złożone algorytmy. W przypadku wyłączenia buzerka po upływie określonego czasu to jeszcze można zastanawiać się, czy jest to złożony algorytm. Jednak, przykładowo w sytuacji, gdy co ileś czasu należy dokonać wielopunktowego pomiaru poprzez przetwornik ADC i później przykładowo obliczyć transformatę FFT, to takie rozwiązanie jest do… kubła. Sygnał przerwania od upływu czasu może ustawić flagę, że takie zdarzenie zaistniało oraz poza obsługą przerwań w sytuacji, gdy flaga jest ustawiona, to oblecieć istniejącą kolejkę czasowych uwarunkowań. Z punktu widzenia algorytmu realizowanym przez program, taka flaga będzie ustawiać się niejako w sposób magiczny, w ciągu instrukcji składających się na realizację algorytmu nie ma jawnego jej ustawienia. Obsługa przerwań jest asynchroniczna i odbywa się niejako poza świadomością algorytmu programu wykonywanego przez mikrokontroler.
Rozwiązanie oparte o kolejkę, w ramach sprzedaży wiązanej uzyskuje ciekawą cechę: skoro można coś włożyć do kolejki, to również można coś z kolejki wyjąć. W przypadku, gdy w takiej kolejce znajdują się akcje odłożone w czasie, to usunięcie elementu z kolejki implikuje, że dana akcja nie zaistnieje.
No więc powstaje maksymalnie niezależny moduł z własną instancją. Wystarczy go napędzać z określonym taktem zegarowym. Ten napęd to zgłoszenie eventu, że upłynął czas. No i oczywiście drugi poolingowy napęd do przeglądania kolejek. Aby uzyskać jako taką równomierność, to poolingowy napęd aktywuje się zgłoszonymi eventami czasowymi. Jak się okaże, że dla jakiegoś zdarzenia upłynął czas oczekiwania, to zostanie ono wywołane i usunie się z kolejki. Warto by może utworzyć możliwość autopowielania kolejkowania zdarzeń (taka permanentnie powtarzana akcja z określonym interwałem czasowym). Może do czegoś się przyda.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » wtorek 23 maja 2017, 17:44

Instancja

Jest moduł zawierający w sobie kilka funkcji pozwalających na dostęp do wewnętrznej instancji modułu. Istotnym towarem eksportowym modułu jest typ do uchwytu do akcji, która ma być wykonana po określonym czasie. Typ ten, jako struktura, jest reprezentowany jako wskaźnik do funkcji, jaka ma być wywołana po upływie określonego interwału czasowego oraz wartość tego interwału. Całość jest napędzana zdarzeniami czasowymi. Podstawowy napęd to jest wywołanie bezparametrowej funkcji void SysTimerPool ( void ), którą należy wywoływać odpowiednio często. Typowe rozwiązanie to wywoływanie jej w ciasnej pętli. Generalnie w tej funkcji nic się nie dzieje z wyjątkiem sytuacji, gdzie nastąpiło wyzwolenie (jak w przerzutnikach cyfrowych w automatach). Wyzwalaczem dla SysTimerPoll jest wywołanie SysTimerTickEvent. Funkcja ta eksportowana z modułu jako void SysTimerTickEvent ( void ) ustawia jedynie flagę określającą, że zaistniało zdarzenie (by nie wnosić w „koszt” obsługi przerwania realizację akcji odłożonej w czasie, całkowity koszt obsługi zostanie poniesiony później przez SysTimerPoll poza obsługą przerwania). Zalecane jest by ta funkcja (SysTimerTickEvent) była wywoływana w regularnych odstępach czasu, czyli przykładowo była zawarta w obsłudze przerwań od czasu (w obsłudze przerwań wystąpiło jej wywołanie). Częstotliwość wywoływania tej funkcji odpowiada gradacji pomiaru czasu. Im wywoływana jest częściej, tym uzyskuje się większą rozdzielczość czasową, ale nie należy również zbytnio przesadzać, bo w ogromnej większości przypadków gradacja czasowa na poziomie dziesiątych części sekundy jest wystarczająca.
Oczywiście cała instancja, jako obszar danych lokalnych modułu obsługi kolejki czasowej, musi być jednorazowo zainicjowana, poprzez jednorazowe wywołanie funkcji void SoftTimeEventInit ( void ).
Dodanie zdarzenia czasowego do kolejki realizowane jest poprzez wywołanie funkcji:
  • AttachTimer ( <funkcja obsługi> , <interwał czasowy> ) – dodanie jednorazowej akcji, jako wywołanie funkcji wskazanej w parametrach <funkcja obsługi>, która będzie wywołana za <interwał czasowy> ticków wygenerowanych przez SysTimerTickEvent zliczanych od tej chwili.
  • AttachPermanentTimer ( <funkcja obsługi> , <interwał czasowy> ) – dodanie autopowielarnej akcji, jako wywołanie funkcji wskazanej w parametrach <funkcja obsługi>, która będzie wywołana po każdym <interwał czasowy> ticków wygenerowanych przez SysTimerTickEvent zliczanych od tej chwili.
<Funkcja obsługi> jest dowolną bezparametrową funkcją nie zwracającą wyniku. Obie powyższe funkcje zwracają uchwyt, który może być użyty w funkcji DetachTimer w celu usunięcia akcji z kolejki zdarzeń (po uchwycie DetachTimer odszuka w kolejce właściwy element, który zostanie zwolniony). Akcja usunięta przez DetachTimer nie zostanie wykonana (jest to forma rezygnacji z wykonania czynności odłożonej w czasie).
W sytuacji, gdy dojdzie do realizacji czynności odłożonych w czasie (aktywowanej pośrednio przez któreś wywołanie SysTimerPool), automatycznie jest wykonane w domyśle DetachTimer dla akcji pojedynczej (włożonej do kolejki poprzez wywołanie AttachTimer). W przypadku użycia funkcji AttachPermanentTimer, w przeciwieństwie do AttachTimer, realizacja odłożonej akcji powoduje automatycznie reaktywację z identycznym interwałem czasowym. W tym przypadku konieczne jest użycie DetachTimer w celu przerwania łańcucha wywołań.
Plik nagłówkowy modułu:

Kod: Zaznacz cały

typedef void ( * TimeEventServiceProcT ) ( void ) ;

typedef struct {
                 unsigned short        TimeInterval ;
                 TimeEventServiceProcT TimeService ;
               } TimeEventElementType ;
typedef TimeEventElementType * PtrTimeEventElementType ;

extern void SysTimerTickEvent ( void ) ;

extern PtrTimeEventElementType AttachTimer ( TimeEventServiceProcT /* Service   */ ,
                                             unsigned short        /* DelayTime */ ) ;

extern PtrTimeEventElementType AttachPermanentTimer ( TimeEventServiceProcT /* Service   */ ,
                                                      unsigned short        /* DelayTime */ ) ;

extern void DetachTimer ( PtrTimeEventElementType /* ServiceHandle */ ) ;

extern void SysTimerPool ( void ) ;

extern unsigned char NumberOfEvents ( void ) ;

extern void SoftTimeEventInit ( void ) ;

Moduł jest tak napisany, by pasował do środowiska różnych mikrokontrolerów, nie jest przywiązany do architektury (harwardzka, von Neumanna) nie posiłkuje się powiązaniami z jakimkolwiek innym plikiem nagłówkowym).

Załącznik, plik implementacji i plik nagłówkowy:
timeevent.zip
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: tasza » wtorek 23 maja 2017, 18:20

ja...zaniemówiła normalnie, no nie sądziłam, że cokolwiek mnie jeszcze zaskoczy, do teraz właśnie
stare mejle straciłam sromotnie, a teraz nagle przychodzi mi czytać swoje własne słowa, po tylu latach
ty cytujesz wielkich,
to dobrze umieć korzystać z ich mądrości
ale pozwól Andrzeju, że ja zacytuję starszą, skromną kobietkę, mogą gospodynię kochaną gdy ja mieszkała w Galway w te ostatnie lata

``Maireann croi eadrom i bhfad`` radosne serca źyją dłużej

powtarzała mi to celtyckie porzekadło w koło, nieustannie, tym częściej im było mi trudniej
możliwe, że dzięki temu w ogóle to czytasz; poźniej sie odezwę
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » wtorek 23 maja 2017, 18:40

tasza pisze:ja...zaniemówiła normalnie, no nie sądziłam, że cokolwiek mnie jeszcze zaskoczy, do teraz właśnie
stare mejle straciłam sromotnie, a teraz nagle przychodzi mi czytać swoje własne słowa, po tylu latach

Bo u mnie nic nie zginęło, z jednym wyjątkiem. Niechcący skasowałem nieprzeczytanego emaila od ciebie. Jak poprosiłem cię o powtórkę, to napisałaś, że ... widocznie tak miało być i rypleja nie będzie.
tasza pisze:nieustannie, tym częściej im było mi trudniej możliwe, że dzięki temu w ogóle to czytasz

Wiem o czym piszesz :mrgreen: , znam ten ból, sam byłem matką :mrgreen:

tasza pisze:radosne serca żyją dłużej

Alchemia słowa w najczystszej postaci.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » wtorek 23 maja 2017, 23:36

Przykład dla mikrokontrolera ATMEGA1284

Przykład użycia jest programem, w którym po naciśnięciu przycisku na klawiaturze generowany jest sygnał dźwiękowy. Królik doświadczalny odpowiada środowisku pokazanym na ilustracjach:
tevnt_ex1-i01.png
tevnt_ex1-i02.png
i w realu:
tevnt_ex1-i03.jpg

Do mikrokontrolera przyłączony jest typowy, tekstowy wyświetlacz LCD (8-bitowa szyna danych zajmuje PORTA i wszystkie trzy sygnały sterujące są przyłączone do określonych wyprowadzeń PORTB), czteroprzyciskowa klawiatura (pozostałe wyprowadzenia PORTB) oraz mały głośniczek sterowany z określonego wyjścia PORTB.
W wyniku naciśnięcia określonego przycisku klawiatury (obsługa przycisków jest opisana w Obsługa prostej klawiatury) program generuje sygnał dźwiękowy o określonym czasie trwania i określonym tonie. Generacja dźwięku odbywa się w oparciu o przerwania generowane przepełnieniem licznika 2. W przypadku naciśnięcia przycisku Key0 realizowana jest akcja, której schemat przedstawia ilustracja.
tevnt_ex1-i04.png

„Konsumpcja” naciśnięcia przycisku Key0 następuje w funkcji OptionService, gdzie po wyświetleniu odpowiedniego komunikatu uruchamiana jest akcja wygenerowania sygnały dźwiękowego. Sygnał akustyczny jest generowany w funkcji obsługi przerwania od zegara/licznika 2, więc będzie wytwarzał się sam bez specjalnych zabiegów w oprogramowaniu. Dodatkowo zostaje zakolejkowane w czasie (ma się wywołać po upłynięciu pewnego interwału czasu) wywołanie funkcji, której zadaniem jest wyłączenie generowania sygnału akustycznego.

Kod: Zaznacz cały

static void OptionService ( uint8_t KeyCode )
{
(...)
  switch ( KeyCode )
  {
    case StandKey0Code                  :
      WriteTextLCDFlash ( MessageKey0Text ) ;
      SingleToneStartBeep ( BeepTimeInterval , 21 ) ;
      break ;
(...)
} /* OptionService */

gdzie:

Kod: Zaznacz cały

static void SingleToneStartBeep ( uint16_t BeepTime ,
                                  uint8_t  BeepFreq )
{
(...)
  BeepFreqReq = BeepFreq ;
  BeepEnable = TRUE ;
  BeepHandler = AttachTimer ( StopBeepService , BeepTime ) ;
} /* SingleToneStartBeep */

gdzie po upływie <BeepTime> ticków zgłoszonych poprzez wywołanie SysTimerTickEvent ( ) zostanie wywołana bezparametrowa funkcja StopBeepService ( ) ;
Z kolei funkcja StopBeepService ( ) jest następująca:

Kod: Zaznacz cały

void StopBeepService ( void )
{
  /*----------------------------------------------------------------*/
  BeepOff ( ) ;
  DetachTimer ( BeepHandler ) ;
  BeepHandler = NULL ;
} /* StopBeepService */

gdzie wystąpi wyłączenie generowania sygnału akustycznego.
Wygenerowanie sygnału wielotonowego, przykładowo w wyniku naciśnięcia przycisku Key3, odbywa się na podobnej zasadzie.
tevnt_ex1-i05.png
Jedynie łańcuszek wywołań jest dłuższy.
Po zaprogramowaniu mikrokontrolera:
tevnt_ex1-i06.jpg
tevnt_ex1-i07.jpg
tevnt_ex1-i08.jpg


Załącznik: projekt w AVRSTUDIO:
tevent1.zip


Prezentacja działania:
tevent1.mov
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » środa 24 maja 2017, 23:36

Przykład 2 dla mikrokontrolera ATMEGA1284

Cyt: "a można, kwestia fantazji - sterowanie gestami, czyli jak upchnąć jak największą ilość poleceń w jak najmniejszą ilość guzików, może dam przykład: mój lampowy wzmacniaczyk i jego sterowanie: ma gałkę, która jest de facto zestawem dwóch push buttonów na skrajnych położeniach jak przekręcisz i trzymasz chwilę: max w lewo - cichnie, jak max w prawo – pogłaśnia jak szybko szarpniesz lewo - prawo to włącza tryb timer (po 30 min. wyłącza lampy i idzie spać) jak szybko szarpniesz prawo - lewo - wyłącza tryb timer (działanie odwrotne do powyższego) jak jest już w trybie timer - ponowne szarpnięcie lewo-prawo daje dodatkowe 30 minut zanim się wyłączy to powstało, ponieważ ja często zasypiałam ze słuchawkami na uszach i lampy się świeciły bez sensu, a poza tym to niebezpieczne się robiło w podsumowaniu: jedno naciśnięcie - jakaś funkcja, dwa / trzy szybkie naciśnięcia (tupnięcia) druga lub trzecia funkcja,"

Inny przykład, który jest głównym powodem do powstania tego komiksu. Prezentuje on przykładowe rozwiązanie zagadnienia kliku i dwukliku. Środowisko do eksperymentu pokazują ilustracje:
tevnt_ex2-i01.PNG
tevnt_ex2-i02.png
Do mikrokontrolera przyłączony jest typowy, tekstowy wyświetlacz LCD (8-bitowa szyna danych zajmuje PORTA i wszystkie trzy sygnały sterujące są przyłączone do określonych wyprowadzeń PORTB), czteroprzyciskowa klawiatura do wybranych ponów PORTB.
Program, w sensie obsługi rzeczywistej klawiatury, rozpoznaje normalne naciśnięcia pojedynczych klawiszy (wystarczająco szczegółowo przedstawione w Obsługa prostej klawiatury). Naciśnięcie dowolnego przycisku prowadzi do następującego algorytmu (opis dotyczy przycisku Key0, ale każdy inny jest obsługiwany identycznie).

Kod: Zaznacz cały

static void OptionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  switch ( KeyCode )
  {
    case StandKey0Code                  :
      if ( KeyActionInProgress ( ) )
      {
        DecodeKeySecondStep ( KeyCode ) ;
      } /* if ... */
      else
      {
        Key0Handle = AttachTimer ( DoubleClickSenseKey0 , DoubleClickTimeInterval ) ;
      } /* if ... else */ ;
      break ;
    case StandKey1Code                  :
(…)
  } /* switch */ ;
} /* OptionService */
Czyli: jeżeli jest aktywowana jakakolwiek akcja odłożona w czasie związana z obsługą klawisza, to znaczy, że jest już po „pierwszym kliku” → następuje druki krok w dekodowaniu klawisza wirtualnego. W przeciwnym razie, nie było jeszcze żadnego kliku, jest aktywowana akcja (DoubleClickSenseKey0) odłożona w czasie z zapamiętaniem uchwytu (Key0Handle). Jeżeli przed upłynięciem odpowiedniego interwału czasowego nie nastąpi drugi klik, to dojdzie do wykonania funkcji odłożonej w czasie. Funkcja ta (DoubleClickSenseKey0) włoży do kolejki klawiaturowej odpowiedni znak (oznaczający pojedynczy klik na Key0).

Kod: Zaznacz cały

static uint8_t KeyActionInProgress ( void )
{
  /*-------------------------------------------------------------------------*/
  if ( Key0Handle || Key1Handle || Key2Handle || Key3Handle )
    return ( TRUE ) ;
  else
    return ( FALSE ) ;
} /* KeyActionInProgress */
Rozpoznanie, czy jest aktywowana jakakolwiek akcja sprowadza się do zbadania, czy którykolwiek uchwyt do akcji odłożonej w czasie skojarzony z właściwym przyciskiem wskazuje na cokolwiek (jest inny niż NULL).

Kod: Zaznacz cały

static void DoubleClickSenseKey0 ( void )
{
  /*-------------------------------------------------------------------------*/
  StoreKey ( VirtualKey0Code ) ;
  Key0Handle = NULL ;
} /* DoubleClickSenseKey0 */
Jeżeli nie nastąpi drugi klik, to po upłynięciu odpowiedniego interwału czasu zostanie wykonana akcja odłożona w czasie. Sprowadza się to do dodania do kolejki klawiaturowej kodu odpowiadającego pojedynczemu klikowi. Już samo wywołanie spowoduje, że akcja ta usunie się z kolejki wszystkich akcji (→ SysTimerPool w timeeventmodule.c), jednak uchwyt będzie nadal gdzieś wskazywał, należy go wyzerować, gdyż jest używany do rozpoznania, czy jest aktywowana jakakolwiek akcja.

Kod: Zaznacz cały

static void DecodeKeySecondStep ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  if ( Key0Handle )
  {
    DetachTimer ( Key0Handle ) ;
    Key0Handle = NULL ;
    StoreVirtualKey ( KeyCode , VirtualKey0Key0Code , VirtualKey0Key1Code ,
                                VirtualKey0Key2Code , VirtualKey0Key3Code ) ;
  } /* if */ ;
  if ( Key1Handle )
  {
    DetachTimer ( Key1Handle ) ;
    Key1Handle = NULL ;
    StoreVirtualKey ( KeyCode , VirtualKey1Key0Code , VirtualKey1Key1Code ,
                                VirtualKey1Key2Code , VirtualKey1Key3Code ) ;
  } /* if */ ;
  if ( Key2Handle )
  {
    DetachTimer ( Key2Handle ) ;
    Key2Handle = NULL ;
    StoreVirtualKey ( KeyCode , VirtualKey2Key0Code , VirtualKey2Key1Code ,
                                VirtualKey2Key2Code , VirtualKey2Key3Code ) ;
  } /* if */ ;
  if ( Key3Handle )
  {
    DetachTimer ( Key3Handle ) ;
    Key3Handle = NULL ;
    StoreVirtualKey ( KeyCode , VirtualKey3Key0Code , VirtualKey3Key1Code ,
                                VirtualKey3Key2Code , VirtualKey3Key3Code ) ;
  } /* if */ ;
} /* DecodeKeySecondStep */
W zależności od tego, który aktualnie naciśnięty jest przycisk (parametr KeyCode) i od tego, który uchwyt do akcji odroczonej nie jest pusty, do bufora klawiatury trafia odpowiedni kod. Właściwie trudno go nazwać kodem przycisku/klawisza, ale można go nazwać kodem akcji do wykonania, takim wirtualnym kodem znaku.

Kod: Zaznacz cały

static void OptionService ( uint8_t KeyCode )
{
  /*-------------------------------------------------------------------------*/
  switch ( KeyCode )
  {
(...)
    case VirtualKey0Code                :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey0Text ) ;
      break ;
    case VirtualKey1Code                :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey1Text ) ;
      break ;
    case VirtualKey2Code                :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey2Text ) ;
      break ;
    case VirtualKey3Code                :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey3Text ) ;
      break ;
    case VirtualKey0Key0Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey0Key0Text ) ;
      break ;
    case VirtualKey0Key1Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey0Key1Text ) ;
      break ;
    case VirtualKey0Key2Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey0Key2Text ) ;
      break ;
    case VirtualKey0Key3Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey0Key3Text ) ;
      break ;
    case VirtualKey1Key0Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey1Key0Text ) ;
      break ;
    case VirtualKey1Key1Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey1Key1Text ) ;
      break ;
    case VirtualKey1Key2Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey1Key2Text ) ;
      break ;
    case VirtualKey1Key3Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey1Key3Text ) ;
      break ;
    case VirtualKey2Key0Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey2Key0Text ) ;
      break ;
    case VirtualKey2Key1Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey2Key1Text ) ;
      break ;
    case VirtualKey2Key2Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey2Key2Text ) ;
      break ;
    case VirtualKey2Key3Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey2Key3Text ) ;
      break ;
    case VirtualKey3Key0Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey3Key0Text ) ;
      break ;
    case VirtualKey3Key1Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey3Key1Text ) ;
      break ;
    case VirtualKey3Key2Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey3Key2Text ) ;
      break ;
    case VirtualKey3Key3Code            :
      ClrScrLCD ( ) ;
      WriteTextLCDFlash ( VirtualKey3Key3Text ) ;
      break ;
  } /* switch */ ;
} /* OptionService */
Reakcja na wirtualny kod znaku sprowadza się do napisu na wyświetlaczu LCD. Napisy są następujące:

Kod: Zaznacz cały

static uint8_t VirtualKey0Text [ ] PROGMEM     = "Klik:Key 0" ;
static uint8_t VirtualKey1Text [ ] PROGMEM     = "Klik:Key 1" ;
static uint8_t VirtualKey2Text [ ] PROGMEM     = "Klik:Key 2" ;
static uint8_t VirtualKey3Text [ ] PROGMEM     = "Klik:Key 3" ;
static uint8_t VirtualKey0Key0Text [ ] PROGMEM = "Dwuklik:Key 0+0" ;
static uint8_t VirtualKey0Key1Text [ ] PROGMEM = "Dwuklik:Key 0+1" ;
static uint8_t VirtualKey0Key2Text [ ] PROGMEM = "Dwuklik:Key 0+2" ;
static uint8_t VirtualKey0Key3Text [ ] PROGMEM = "Dwuklik:Key 0+3" ;
static uint8_t VirtualKey1Key0Text [ ] PROGMEM = "Dwuklik:Key 1+0" ;
static uint8_t VirtualKey1Key1Text [ ] PROGMEM = "Dwuklik:Key 1+1" ;
static uint8_t VirtualKey1Key2Text [ ] PROGMEM = "Dwuklik:Key 1+2" ;
static uint8_t VirtualKey1Key3Text [ ] PROGMEM = "Dwuklik:Key 1+3" ;
static uint8_t VirtualKey2Key0Text [ ] PROGMEM = "Dwuklik:Key 2+0" ;
static uint8_t VirtualKey2Key1Text [ ] PROGMEM = "Dwuklik:Key 2+1" ;
static uint8_t VirtualKey2Key2Text [ ] PROGMEM = "Dwuklik:Key 2+2" ;
static uint8_t VirtualKey2Key3Text [ ] PROGMEM = "Dwuklik:Key 2+3" ;
static uint8_t VirtualKey3Key0Text [ ] PROGMEM = "Dwuklik:Key 3+0" ;
static uint8_t VirtualKey3Key1Text [ ] PROGMEM = "Dwuklik:Key 3+1" ;
static uint8_t VirtualKey3Key2Text [ ] PROGMEM = "Dwuklik:Key 3+2" ;
static uint8_t VirtualKey3Key3Text [ ] PROGMEM = "Dwuklik:Key 3+3" ;


Po zaprogramowaniu proca … działa zgodnie z założeniami (przynajmniej moimi). Tasza, tak miało być?

Załącznik: projekt AVRSTUDIO
tevent2.zip


Prezentacja działania:
tevent2.mov
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: tasza » czwartek 25 maja 2017, 06:11

kapitalne opracowanie tematu, pewnie że tak!
i dziękuje za to
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » czwartek 25 maja 2017, 08:32

tasza pisze:i dziękuje za to

Dziękuję za inspiracje.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1321
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Kolejka zdarzeń o uwarunkowaniach czasowych

Postautor: gaweł » czwartek 25 maja 2017, 13:26

Przykład 3 dla mikrokontrolera ATMEGA1284
Dotyczący synergii dwóch elementów:
obsługi wyświetlacza LCD i kolejek czasowych


Środowisko sprzętowe jest identyczne jak pokazane w poprzednim przykładzie. Zagadnienie dotyczy "autonomicznego" przewijania tekstu na wyświetlaczy LCD jeżeli długość tekstu przekracza "szerokość" ekranu.
Wiadomo, że maksymalna długość jednego wiersza w standardzie HD44780 wynosi 40 znaków. Więc... wyświetlany tekst w rzeczywistości jest przybuforowany w pamięci RAM a jedynie w ramach obsługi eventów czasowych jest wyświetlany od inkrementowanej w każdym wyświetleniu pozycji początkowej. Fizyczne wyświetlenie tekstu odbywa się na okrętkę: jak zostanie napotkany koniec wiersza w "wirtualnym ekranie" to na "fizycznym ekranie" wyświetlanie zaczyna się od początku tekstu. Daje to efekty płynącego, zapętlonego tekstu.
Akcja przewijanie tekstu na ekranie aktywowana jest jedynie w sytuacji, gdy tekst do wyświetlenia "wykracza" poza ekran. W sytuacji, gdy zaistnieje taki przypadek, aktywowana jest akcje odłożona w czasie z jakimś interwałem czasowym (pozwalającym użytkownikowi na pierwsze przeczytanie informacji), po czym każdorazowo jest aktywowana z mniejszym interwałem czasowym przeznaczonym już jedynie do przewijania ekranu. Drugi wiersz jest obsługiwany niezależnie w identyczny sposób.

Kod: Zaznacz cały

static uint8_t HelloText [ ] PROGMEM           = "* Obsluga LCD *" ;
//                                                1234567890123456
static uint8_t ShortLineText [ ] PROGMEM       = "Krotki tekst" ;
static uint8_t LongLineText [ ] PROGMEM        = "*** Dlugi wyswietlany test ***" ;
Wyświetlane teksty.

Kod: Zaznacz cały

void WriteTextLCDFlash ( uint8_t * String )
{
  uint8_t Data ;
  /*-------------------------------------------------------------------------*/
  for ( ; ; )
  {
    Data = pgm_read_byte ( String ++ ) ;
    if ( ! Data )
      break ;
    VirtualWriteChLCD ( Data ) ;
  } /* for */ ;
  RefreshDisplay ( ) ;
} /* WriteTextLCDFlash */
Wyświetlenie tekstu odbywa się w ten sposób, że tekst jest zapisany do wirtualnego ekranu i w dalszej kolejności jest wykonana operacja odświeżenia ekranu.

Kod: Zaznacz cały

static void RfshFirstLine ( void )
{
  uint8_t Control ;
  uint8_t Loop ;
  uint8_t Index ;
  uint8_t Ch ;
  uint8_t LineWidth ;
  /*-------------------------------------------------------------------------*/
  DetachTimer ( FirstLineTimerHandle ) ;
  FirstLineTimerHandle = NULL ;
  Control = 0x80 ;
  LCDOutC ( Control ) ;
  WaitAccept ( ) ;
  Index = 0 ;
  for ( Loop = 0 ; Loop < ScreenWidth ; Loop ++ )
  {
    Ch = BufferedLCDScreen . BufferedFLCDLine [ Index ] ;
    if ( Ch )
    {
      Index ++ ;
    } /* if ... */
    else
    {
      Ch = ' ' ;
    } /* if ... else */ ;
    OutChOnDisplay ( Ch ) ;
  } /* for */ ;
  LineWidth = 0 ;
  for ( Loop = 0 ; Loop < BufferSize ; Loop ++ )
  {
    Ch = BufferedLCDScreen . BufferedFLCDLine [ Loop ] ;
    if ( ! Ch )
      break ;
    LineWidth ++ ;
  } /* for */ ;
  if ( LineWidth > ScreenWidth )
  {
    FirstLineTimerHandle = AttachTimer ( ScrollFirstLine , StartDelayForLine ) ;
  } /* if */ ;
} /* RfshFirstLine */
Odświeżenie ekranu polega na fizycznym wyświetleniu informacji na ekranie w obrębie jego fizycznej szerokości. Jeżeli nie wszystkie znaki zostały wyświetlone, to aktywowana jest akcja przewijania ekranu, która uruchomi się na określony (pierwszy, większy) interwał czasu.

Kod: Zaznacz cały

static void ScrollFirstLine ( void )
{
  uint8_t Ch ;
  uint8_t Loop ;
  uint8_t Index ;
  /*-------------------------------------------------------------------------*/
  LCDOutC ( 0x02 ) ;
  WaitAccept ( ) ;
  Index = FirstLineBaseIndex ;
  for ( Loop = 0 ; Loop < ScreenWidth ; Loop ++ )
  {
    Ch = BufferedLCDScreen . BufferedFLCDLine [ Index ] ;
    if ( ! Ch )
    {
      OutChOnDisplay ( ' ' ) ;
      OutChOnDisplay ( ' ' ) ;
      OutChOnDisplay ( ' ' ) ;
      Index = 0 ;
      Ch = BufferedLCDScreen . BufferedFLCDLine [ Index ] ;
    } /* if */ ;
    OutChOnDisplay ( Ch ) ;
    Index ++ ;
  } /* for */ ;
  FirstLineBaseIndex ++ ;
  Ch = BufferedLCDScreen . BufferedFLCDLine [ FirstLineBaseIndex ] ;
  if ( ! Ch )
  {
    FirstLineBaseIndex = 0 ;
  } /* if */ ;
  DetachTimer ( FirstLineTimerHandle ) ;
  FirstLineTimerHandle = AttachTimer ( ScrollFirstLine , ContinueDelayForLine ) ;
} /* ScrollFirstLine */
Odłożona w czasie akcja przewijania wyświetlanego wiersza, robi co trzeba (jeżeli napis się skończył, do wstawia 3 spacje, by koniec napisu nie zlewał się z jego początkiem), przestawia położenie początku "okienka" w buforze do kolejnego wywołania i aktywuje samą siebie z drugim, mniejszym interwałem czasowym.

Jak to często bywa, pierwsze śliwki robaczywki ;) . Ten przykład obnażył subtelną niedoróbkę w module obsługi kolejek czasowych, więc leciutko różni się od poprzednich.

Załącznik: projekt w AVRSTUDIO
tevent3.zip

Filmowa prezentacja działania:
tevent3.mov
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse


Wróć do „Inne mikroklocki, również peryferyjne”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 2 gości