<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl-pl">
<link rel="self" type="application/atom+xml" href="https://forum.atnel.pl/feed.php?f=4&amp;t=12732&amp;mode" />

<title>ATNEL tech-forum</title>
<link href="https://forum.atnel.pl/index.php" />
<updated>2015-08-22T23:13:57+01:00</updated>

<author><name><![CDATA[ATNEL tech-forum]]></name></author>
<id>https://forum.atnel.pl/feed.php?f=4&amp;t=12732&amp;mode</id>
<entry>
<author><name><![CDATA[xor]]></name></author>
<updated>2015-08-22T23:13:57+01:00</updated>
<published>2015-08-22T23:13:57+01:00</published>
<id>https://forum.atnel.pl/viewtopic.php?t=12732&amp;p=138107#p138107</id>
<link href="https://forum.atnel.pl/viewtopic.php?t=12732&amp;p=138107#p138107"/>
<title type="html"><![CDATA[Znów miganie diodami]]></title>

<content type="html" xml:base="https://forum.atnel.pl/viewtopic.php?t=12732&amp;p=138107#p138107"><![CDATA[
W ramach rozruszania komórek mózgowych i utrwalenia wiedzy nabytej z ostatnio czytanej książki (<a href="http://helion.pl/ksiazki/wyklady-z-informatyki-z-przykladami-w-jezyku-c-alfred-v-aho-jeffrey-d-ullman,jezc.htm"  class="postlink">http://helion.pl/ksiazki/wyklady-z-informatyki-z-przykladami-w-jezyku-c-alfred-v-aho-jeffrey-d-ullman,jezc.htm</a> - gorąco polecam każdemu kto lubi różne algorytmiczne fiku-miku) postanowiłem napisać programik, coś jakby framework, do uruchamiania zadań w określonym przedziale czasowym oparty na timerze programowym. Sporą inspiracją był też ten wątek: <a href="http://forum.atnel.pl/topic3265.html"  class="postlink">http://forum.atnel.pl/topic3265.html</a>, a konkretnie wynurzenia kolegi rolba.<br /><br /><br />Jak to działa? Wszystko co µC ma wykonać podzielone jest na niezależne zadania, niezależne od siebie i od pętli głównej (pętla główna nic &quot;nie wie&quot; o zadaniach, ile ich jest, czy w ogóle jakieś są). Ta część jest definiowalna przez użytkowanika frameworka. Zadania są umieszczone w kolejce według pewnego porządku (o tym później) i uruchamiane kolejno z pętli głównej - po upływie ustalonego dla danego zadania czasu od ostatniego uruchomienia. W międzyczasie (jeśli nie ma zadań do wykonania) wykonywane jest zadanie idle (czyli np. usypianie µC). Ta część jest zasadniczo niemodyfikowalna przez użytkownika.<br /><br />Kod uruchamiający zadania jest bardzo prosty:<br /><br />[syntax=c]/*  scheduler.c   */<br />void do_job()<br />{<br />//zaktualizuj kolejkę zadań na podstawie timera programowego<br />if(timer_tick)<br />aktualizuj_kolejkę_zadań;<br /><br />//pobierz id następnego zadania do wykonania<br />if(priorytet_pierwszego_zdania != 0)<br />{<br />//nie ma nic do roboty, wykonaj coś: posprzątaj, uśpij procka albo jeszcze co tam chcesz<br />idle();<br />return;<br />}<br /><br />//zdejmij zadanie z początku kolejki<br />callback = get_job();<br /><br />//ponownie wstaw zadanie do kolejki w odpowiednie miejsce<br />insert_job(callback);<br /><br />//i w końcu wykonanie zadania<br />(*callback)();<br />}<br /><br />...<br /><br />/* main.c */<br /><br />int main()<br />{<br />/* jakieś inicjalizacje */<br /><br />while(1)<br />{<br />do_job();<br />}<br />}[/syntax]<br /> <br />Cała &quot;inteligencja&quot; zaszyta jest w kolejce zadań. Do zadań przypisany jest atrybut nazwany priorytetem, który w rzeczywistości jest interwałem czasowym między wykonaniami zadania (liczonym w tyknięciach timera programowego). Im niższa liczba, tym krótszy czas - priortytet równy zero to wykonanie zadania przy każdym wywołaniu funkcji do_job (z powodu nadmiernej prostoty algorytmu obsługi kolejki takie zadanie może być najwyżej jedno). Zadania są uszeregowane w kolejce wg tego priorytetu, na początku kolejki jest zawsze zadanie o najwyższym priorytecie (czyli to którego wykonanie przypadnie w najbliższym czasie). Po każdym tyknięciu timera priorytety wszystkich zadań są pomniejszane o jeden (linie 5 i 6 listingu wyżej). W momencie gdy priorytet pierwszego zadania w kolejce osiągnie wartość zero (linia 9) zadanie zostanie zdjęte z kolejki (linia 17) i wykonane (linia 23). Przed wykonaniem zostanie ponownie wstawione do kolejki z pierwotnym priorytetem (linia 20). I tak w kółko.<br /><br />Sama kolejka jest oparta na tzw. drzewie częściowo uporządkowanym. Proste wyjaśnienie cóż to jest można znaleźć w podanej wyżej książce :-&gt; Ja powiem tylko, że ta struktura danych wpływa korzystnie na szybkość operacji wyszukiwania, wstawiania i kasowania elementów. Jej zalety widać zwłaszcza przy dużej ilości elementów bowiem średni czas operacji rośnie wraz z ilością w tempie logarytmicznym. <br /><br />W załączniku program w formie archiwum Eclipse, jakby się kto chciał pobawić. Import do Eclipse: plik ściągnąć na dysk, potem <em>File/Import...</em> rozwinąć gałązkę <em>General</em>, wybrać <em>Existing Projects into Workspace</em>, baton <em>Next&gt;</em> wybrać <em>Select archive file:</em>, baton <em>Browse</em>, znaleźć i wybrać <em>mrugalka.zip</em>, baton <em>Finish</em>.<br />Program wymaga avr-gcc w wersji co najmniej 4.7 (np. Atmel toolchain). Program jest na m168/m328. <br /><br />Struktura projektu:<br /><strong>katalog SCHED</strong> - główna część. Definicje funkcji wstawiających i usuwających zadania z kolejki oraz wykonujących zadania. <br /><strong>katalog QU</strong> - implementacja kolejki priorytetowej. Pliki wykorzystywane przez funkcje z SCHED. Stała PQ_HEAPSIZE w pliku nagłówkowym definiuje wielkość kolejki.<br /><strong>Katalog TIMER</strong> - timer programowy. Stała ST_INTERVAL_MS w pliku nagłówkowym określa tick timera. Przeniesienie programu na inny µC może wymagać zmian nazw rejestrów w funkcji init_time oraz w procedurze obsługi przerwania ISR(TIMER0_COMPA_vect)<br /><strong>Katalog POWER</strong> - przykładowa implementacja zadania idle (funkcja sch_idle)- usypianie procka. Katalog można całkowicie usunąć, wtedy wykonywana jest defaultowa funkcja sch_idle (która nic nie robi <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";-)" title="Puszcza oko" />).<br /><strong>Katalog JOBS</strong> - definicje zadań oraz definicja struktury opisu zadań na potrzeby funkcji zarządzających zadaniami. Tutaj zdefiniowanych jest 10 zadań migających diodami na pinach PB1-PB5, PD2-PD5 oraz PD7, jedno zadanie wykorzystane przy testowaniu oraz jedno zadanie dekodujące komendy odbierane przez odbiornik IR.<br /><strong>katalog IR</strong> - driver czujnika IR w standardzie NEC wykorzystywany przez zadanie <em>ir_handler</em>. Dostosowanie do swojego pilota IR wymaga zmiany stałej IR_ID w pliku <em>JOBS/ir_handlin.h</em> (id pilota) oraz funkcji <em>is_valid</em> i <em>get_id</em> w pliku <em>JOBS/ir_handlin.c</em> (kody wykorzystanych klawiszy). Przeniesienie programu na inny µC może wymagać zmian nazw rejestrów w funkcji <em>ir_init</em> oraz <em>ISR(TIMER1_CAPT_vect)</em>. Katalog ten oraz pliki <em>ir_handlin.*</em> w katalogu JOBS (oraz zadanie <em>ir_handler</em> z tablicy <em>jobstable</em>) można całkowicie wykasować, jeżeli nie korzysta się ze sterowania IR<br /><strong>katalog UART</strong> - wykorzystywane przez zadanie <em>ir_handler</em>.<br /><br /><br />Efekt działania: <br /><div style="width: 583px; height: 471px; margin: 0 auto; padding-left: 26px; padding-top: 48px; background: url('https://www.atnel.pl/download/atnel_tv.png') no-repeat;"> <strong>iframe</strong> </div><br /><br />Mając w miarę zręczne palce można wydusić jakieś efekty <img src="https://forum.atnel.pl/images/smilies/icon_e_wink.gif" alt=";-)" title="Puszcza oko" /><br /><div style="width: 583px; height: 471px; margin: 0 auto; padding-left: 26px; padding-top: 48px; background: url('https://www.atnel.pl/download/atnel_tv.png') no-repeat;"> <strong>iframe</strong> </div><p>Statystyki: Napisane przez <a href="https://forum.atnel.pl/memberlist.php?mode=viewprofile&amp;u=1774">xor</a> — 22 sie 2015, o 23:13</p><hr />
]]></content>
</entry>
</feed>