http://www.easy-soft.tsnet.pl/
Programowanie zoptymalizowane pod względem
wykorzystania czasu procesora
1. Wstęp.
W artykule chcę przedstawić moje sposoby na programowanie mikrokontrolerów
jednoukladowych (8051, PIC i podobne) w języku asemblera - ze szczególnym
zwróceniem uwagi na jak najmniejsze obciążenie procesora przez dużą liczbę
podprogramów (współdzielenie czasu procesora). Takie podejście sprawia, że układ
mikroprocesorowy staje się jakby układem wielozadaniowym, a przy okazji zostaje
zminimalizowane niebezpieczeństwo zawieszania się programów i utknięcia całości w
martwym punkcie. Z pewnością wiele z przedstawionych poniżej sposobów i sposobików
będzie Wam znanych, część być może bedzie nowych - generalnie chodzi jednak o
całościowe uświadomienie sobie takiej koncepcji programowania tak, aby później można
ją było świadomie stosować w praktyce (programowanie: od koncepcji do realizacji).
Opisane tu sposoby będą przydatne zwłaszcza przy pisaniu większych programów, gdzie
się "wiecej dzieje jednocześnie", ale mogą być również stosowane przy prostszych
programach. Tyle tytułem wstępu - chciałbym jednocześnie zachęcić wszystkich
programujących do opisywania własnych sposobów na programowanie - zwłaszcza o
opisy koncepcji, podejścia do problemu i jego rozwiązania, bo dostępne jest mnóstwo
przykładów, fragmentów procedur, etc ,etc, ale brak jest bardziej ogólnych zasad
programowania, a wydaje mi się że zapotrzebowanie na tego rodzaju literaturę jest
bardzo duże.
2. Uświadomienie sobie problemu to już połowa sukcesu.
Przeważnie piszący programy skupiają się na sposobie rozwiązania problemu - jak
najprościej (jeśli nie "jak w ogóle") napisać programik, który będzie realizował
przewidziane przez nich zadanie. Jest to podejście słuszne, aczkolwiek "mikroskopowe".
Trochę na zasadzie: " bierze się procedurę obsługi I2C, do tego dodaje obsługę
klawiatury, wyświetlacza LCD, łączy sie razem i mamy programik". Potrzebne jest
również myślenie "makroskopowe", które będzie nadrzędne, będzie obejmowało całość
koncepcji programu. Jednym słowem - planowanie. Oczywiście ma to szczególnie sens
przy większych programach, gdzie łatwo się jest pogubić w gąszczu procedurek, etykiet,
skoków, itp...
Częstokroć skupiając się na problemie bieżącym zapominamy o całości programu i o tym
jaki wpływ na pozostałe procedury i programy bedzie miala opracowywana przez nas
procedura. W skrajnym przypadku może to prowadzić do powstania sytuacji, kiedy
napisane przez nas programy - każdy oddzielnie, działają prawidłowo, ale kiedy
połączymy je wszystkie razem, to okazuje się, ze wzajemnie zakłócaja sobie pracę. W
rezultacie program nie działa albo działa "skokowo", gubiąc po drodze dane. Jak temu
przeciwdziałać - poniżej.
- strona 1/11 -
http://www.easy-soft.tsnet.pl/
3. Wizualizacja koncepcji działania procesora. Martwe pętle.
Dla ułatwienia zrozumienia działania procesora przygotowałem przykład opisowy - tzw.
"przedstawienie łopatologiczne".
Jednym z głównych zadań jakie wykonuje procesor jest tzw. pętla główna. Wyobraźmy ją
sobie jako dośc duży, kwadratowy pokój. Pośrodku pokoju stoi człowieczek, który jest
odpowiednikiem bieżącego procesu wykonywanego przez procesor.
Na początku naszych rozważań nasza pętla głowna jest pusta:
petla_glowna:
nop
goto petla_glowna
Wlączamy zasilanie procesora - teraz nasz czlowieczek zaczyna chodzic sobie wokól
pokoju zupelnie nic nie wykonując - po prostu chodzi w kólko.
Aby dać mu jakieś zajęcie wprowadzamy do akcji podprogramy. Wyobraźmy je sobie
jako pokoje umieszczone wokól naszego głownego pomieszczenia. Nasz czlowieczek
idąc wokół scian, gdy napotyka pokój, wchodzi do niego i wykonuje znalezione tam przez
niego zadanie. Po czym wraca do pokoju głownego, idzie dalej wzdłóż sciany, do
nastepnego pokoju i wykonuje następne zadanie....I tak dalej w kółko...
petla_glowna:
podprogram_1
podprogram_2
podprogram_3
podprogram_4
...
podprogram_n
goto petla_glowna
Na razie sytuacja jest prosta, ale jak to w życiu bywa, zaczynaja sie utrudnienia.:-))
Okazuje się bowiem, że nie zawsze jest możliwa natychmiastowa realizacja zadania
przez procesor (czyli naszego czlowieczka). Nie dowieźli danych i nasz człowieczek musi
czekać na nie ;-). Siada więc sobie gdzieś na krzeselku albo chodzi w kólko po pokoiku
podprogamu.
Wyglada to tak:
petla_glowna:
podprogram_1
podprogram_2
podprogram_3
podprogram_4
...
podprogram_n
goto petla_glowna
podprogram_1:
sprawdz_czy_sa_dane
jesli_nie - goto podprogram_1
jesli_tak - wykonaj podprogram
- strona 2/11 -
http://www.easy-soft.tsnet.pl/
i powroc z podprogramu
Jak widzimy nasz czlowieczek uzależniony jest od przeplywu danych, musi siedzieć w
pokoiku i czekać na nie.
A przecież ma do wykonania prace takze w innych pokojach. Może akurat w ktorymś z
nich są juz wszystkie potrzebne dane, a on siedząc tutaj marnuje tylko czas, podczas gdy
moglby wykonać inne zadania.
I tu dochodzimy do własciwego problemu. Sa to wlaśnie tzw. martwe petle podczas
ktorych procesor testuje jakis warunek i czekajac na niego nie wykonuje nic wiecej.
Wprowadzmy wiec usprawnienie. Nasz czlowieczek po wejsciu do kazdego pokoju
bedzie sprawdzal czy sa dla niego dane - jesli beda - wykona zadanie, jesli zas nie -
opusci on pokoj i pojdzie sprawdzac nastepne pokoje. Jesli w ktorymś z nich bedą dane,
bedzie wykonywał swoje tam zadanie.
Tak to wyglada w praktyce:
petla_glowna:
podprogram_1
podprogram_2
podprogram_3
podprogram_4
...
podprogram_n
goto petla_glowna
podprogram_1:
sprawdz_czy_sa_dane
jesli_nie - wyskocz z podprogramu
jesli_tak - wykonaj podprogram
i powroc z podprogramu
W ten sposób nie mamy blokady podprogamów w oczekiwaniu na dane... Nawet gdyby
z jakiś powodów dana ta nie nadeszła nigdy, to program nie zawiesi się w martwej pętli -
pozostałe podprogramy będą się dalej wykonywać bez zakłoceń.
Oczywiście - bywają pewne krytyczne czasowo operacje, które muszą zangażowac całą
moc obliczeniowa procesora, gdyż obsługa w tym czasie innych zadań spowodowałaby
utratę części danych. W tej sytuacji rozsądnym rozwiązaniem tego problemu jest
wykorzystanie przerwań - zwłaszcza gdy dane przychodzą zupelnie asynchroniczne.
- strona 3/11 -
http://www.easy-soft.tsnet.pl/
4.
Przekazywanie informacji przez
flagi.
Najprostszym sposobem na sygnalizacje czy wazne dane nadeszly sa flagi. Flaga jest
bitem, ktorego ustawienie lub wyzerowanie informuje procesor o stanie danych. W
postaci programu wyglada to nastepująco:
podprogram_1:
sprawdz_czy_flaga_ustawiona
jesli_nie - wyskocz z podprogramu
jesli_tak - wykonaj podprogram
i powroc z podprogramu
Flaga moze byc ustawiana przez inny podprogram (ten ktory zbiera dla nas dane), jak i
przez nasz wykonawczy podprogram (gdy np. chcemy zasygnalizowac innym
podprogramom, ze wykorzystalismy juz dane i moga one ladowac następną porcję)
podprogram_1:
sprawdz_czy_flaga_ustawiona
jesli_nie - wyskocz z podprogramu
jesli_tak - wykonaj podprogram
zeruj_flage
i powroc z podprogramu
podprogram_2:
sprawdz_czy_flaga_wyzerowana
jesli_nie - wyskocz z podprogramu
jesli_tak - pobierz_dane_i_umiesc_je_w_zmiennych
ustaw_flage
i powroc z podprogramu
W ten sposob jak widzimy następuje dwustronna wymiana informacji pomiedzy
podprogramami - jednocześnie są one na tyle niezależne, że nawet w przypadku blędu
(brak w doplywie nowych danych do programu 2) nie następuje zawieszenie pracy
programu głównego (tak czy tak podprogram zawsze wyskakuje). To pozwala na np.
stworzenie dodatkowego podprogramu monitorujacego, ktory w przypadku wystąpienia
błedu - przywraca normalną pracę systemu (reset programowy - czyszczenie zmiennych,
ponowna inicjalizacja otoczenia, itp)
Flagi mogą być sprawdzane, ustawiane, bądź zerowane przez kilka programów jeśli
korzystają one z tych samych danych.
Czasami zdarza się, że chcielibyśmy mieć kontrolę nad wykonywaniem sie pewnych
podprogramów w odniesieniu do innych
podprogramów. Np. taka sytuacja: jeśli wykonuje sie poprogram nr 1, to w tym czasie
nie powinien sie wykonywać podprogram nr 2, bo oba korzystaja ze wspolnych
zmiennych i moga wystąpic błedy. Taka sytuacja zdarza sie w momencie kiedy jeden z
podprogramów wykonuje się w pętli głównej, a drugi w przerwaniu. Podczas obsługi
przerwania, program z pętli głownej zostaje przerwany "w trakcie" i jest wykonywany
program z przerwania. Jesli istnieje niebezpieczenstwo zamazania danych przez
program z przerwania, nalezy również uzyć flag dla zasygnalizowania tego faktu.
- strona 4/11 -
http://www.easy-soft.tsnet.pl/
podprogram_2: ; wykonywany w pętli głównej
ustaw_flage_wykonywania_programu
wykonaj_podprogram
wyzeruj_flage_wykonywania_programu
i powroc z podprogramu
podprogram_3: ; wykonywany w przerwaniu
sprawdz_czy_flaga_wykonywania_podprogramu_2_ustawiona
jesli_tak - wyskocz z podprogramu
jesli_nie - wykonaj podprogram
i powroc z podprogramu
W ten sposób jeden podprogram jest zsynchronizowany z drugim - sa wzajemnie
powiazane i nie nastapi nigdy "zderzenie danych".
Za pomoca flag mozna też przekazywac programom informacje o upływie czasu - są to
tzw. znaczniki czasowe.
Zasada jest prosta - jeden z programów (najczęściej jest to program obsługi przerwania,
wywoływany cyklicznie co określony odcinek czasu) po obliczeniu zadanego odcinka czasu
ustawia flage informującą pozostałe programy, ze "czas upłynął".
Podprogram z pętli głównej, testuje cały czas tą flagę i gdy wykryje jej ustawienie -
wykonuje określoną sekwencję działan.
Oto przykład:
podprogram_1: ; wykonywany w przerwaniu
sprawdz_czy_uplynal_zadany_czas
jesli_nie - wyskocz z podprogramu
jesli_tak - ustaw flage TimeOn
i powroc z podprogramu
podprogram_2: ; wykonywany w pętli głównej
sprawdz_flage_TimeOn
jesli nie ustawiona - wyskocz z podprogramu
jesli ustawiona - wykonaj_podprogram i wyzeruj_flage
i powroc z podprogramu
W ten sposob nasz podprogram z pętli głownej wykonuje się np. co 1s, zamiast
wielokrotnie szybciej - nie obciąża to nadmiernie procesora i pozwala na wykonywanie
innych podprogramów w tym czasie. Tego typu zwolnione wykonywanie się programów
ma sens w przypadku np.odczytu pomiarow, gdy nie zalezy nam na duzej ilosci próbek/s
- zwlaszcza gdy do tego dochodzą opóźnienia związane z czasem konwersji wyników
przez urządzenia zewnętrzne.
- strona 5/11 -
http://www.easy-soft.tsnet.pl/
5.
Przekazywanie informacji przez zmienne.
Podział podprogramów na stany.
Flagi ze wzgledu na swoja jednobitowość nadaja się do sygnalizacji sytuacji
dwustanowych - na zasadzie "jest albo nie ma".
Czasami potrzebujemy zasygnalizowac więcej stanów - wowczas z pomocą przyjdą nam
zmienne.
W jednym bajcie mozemy przekazać do 256 stanów, a więc w wiekszości przypadków to
wystarcza ;-))
Tu trzeba zrobić rozróżnienie tzw. zmiennych sterujacych procesami, od zmienych
przekazujacych bezposrednie dane - np. o temperaturze otoczenia.
Odpowiednia budowa podprogramów pozwala na zbudowanie pewnych zespołów
decyzyjnych - sterujących wykonaniem podprogramu w zależności od wartości zmiennej
sterujacej. Innymi słowy podprogram składa się z kilku-kilkunastu ścieżek wykonania a
wybór konkretnej z nich zalezy od wartości naszej zmiennej sterujacej.
Taki podzial podprogramów na kilka-kilkanascie sekcji ułatwia programowanie ze wzgledu
na pewną modularność programu, przyporzadkowanie sciezek programu róznym
sytuacjom w systemie i wreszczie - co najwazniejsze - pozwala na dokonywanie
wstrzyman dzialania podprogramu wewnatrz niego bez stosowania martwych petli.
Nasz podprogram zostaje jakby "zamrozony" w jednym ze stanów (wskazywanym przez
nasza zmienna sterującą) na dowolnie dlugi czas - w tym czasie moze on czekać na
pojawienie sie danych, skonczenie wykonywania innego programu, czy inna sytuacje ...
Jednoczesnie nie jest wstrzymywana praca procesora - caly czas przeskakuje on od
jednego podprogramu do drugiego - wykonujac powierzone mu zadania.
Ale oto przyklad programu:
podprogram_1:
sprawdz_wartosc_zmiennej_sterujacej
jesli_wartosc =0 skocz do etykiety 'sciezka_0'
jesli_wartosc =1 skocz do etykiety 'sciezka_1'
jesli_wartosc =2 skocz do etykiety 'sciezka_2'
jesli_wartosc =n skocz do etykiety 'sciezka_n'
sciezka_0:
sprawdz_czy sa_dane
jesli nie - wyskocz z podprogramu (ew. skok do etykiety wspolne)
jesli tak - pobierz dane
ustaw zmienna_sterujaca_na_1
wyskocz_z_podprogramu
sciezka_1:
wykonaj_dzialanie_zwiazane_z_obrobka_danych
ustaw zmienna_sterujaca_na_2
wyskocz_z_podprogramu (ew. skok do etykiety wspolne)
sciezka_2:
sprawdz_czy_mozna_dokonywać_opracji_zapisu
jesli nie - wyskocz z podprogramu (ew. skok do etykiety wspolne)
jesli tak - zapisz dane
ustaw zmienna_sterujaca_na_n
wyskocz_z_podprogramu
...
sciezka_n:
- strona 6/11 -
http://www.easy-soft.tsnet.pl/
wykonaj_dzialanie_tej_sciezki
...
ustaw zmienna_sterujaca_na_0 ; tu wracamy do stanu poczatkowego podprogramu
wyskocz_z_podprogramu
wspolne:
i powroc z podprogramu
Napisany w ten sposób podprogram wywolywany cyklicznie w pętli, bedzie kolejno
przechodzil wszystkie swoje stany w kazdym bedąc tak dlugo jak to potrzebne, a
jednocześnie nie bedzie martwego oczekiwania na dane.
Jak widzimy, podprogram w kazdym ze swoich stanow dokonuje modyfikacji zmiennej
sterujacej - moze to byc inkrementacja,
dekrementacja, wpisanie bezposredniej wartosci, skoki po kilka stanów naraz. To
pozwala na duza elastycznosc naszego podprogramu. W pewnym sensie jest to
samomodyfikacja programu, choc wykonana w sposob dozwolony (w przeciwienstwie do
modyfikacji kodu programu w pamieci).
W przerwach na oczekiwanie na dane procesor moze wykonywac obsluge innych
programow nie tracac przy tym kontroli nad bieząco wykonywanym podprogramem.
Jednoczesnie w kadej chwili mozemy stwierdzic na jakim etapie wykonywania jest nasz
podprogram - wystarczy sprawdzic zmienna sterujaca.
Ale pojdzmy jeszcze dalej - ten popdprogram bedzie sie wykonywal cyklicznie, po kolei
przechodzac swoje stany od 0 do n i tak w kółko. Czasami jednak chcemy, aby
podprogram wykonywal sie tylko raz (podobnie jak to ma miejsce w przypadku zwyklych
podprogramów). Dodajmy wiec zatem jeszcze jeden stan - stan spoczynkowy w ktorym
nasz podprogram mimo, ze wywolywany cyklicznie - nie wykonuje nic. Bedzie to
wygladac nastepująco:
podprogram_1:
sprawdz_wartosc_zmiennej_sterujacej
jesli_wartosc =0 skocz do etykiety 'sciezka_0'
jesli_wartosc =1 skocz do etykiety 'sciezka_1'
jesli_wartosc =2 skocz do etykiety 'sciezka_2'
jesli_wartosc =3 skocz do etykiety 'sciezka_3'
jesli_wartosc =n skocz do etykiety 'sciezka_n'
sciezka_0:
wyskocz z podprogramu (ew. skok do etykiety wspolne)
sciezka_1:
sprawdz_czy sa_dane
jesli nie - wyskocz z podprogramu (ew. skok do etykiety wspolne)
jesli tak - pobierz dane
ustaw zmienna_sterujaca_na_1
wyskocz_z_podprogramu
sciezka_2:
wykonaj_dzialanie_zwiazane_z_obrobka_danych
ustaw zmienna_sterujaca_na_2
wyskocz_z_podprogramu (ew. skok do etykiety wspolne)
- strona 7/11 -
http://www.easy-soft.tsnet.pl/
sciezka_3:
sprawdz_czy_mozna_dokonywać_opracji_zapisu
jesli nie - wyskocz z podprogramu (ew. skok do etykiety wspolne)
jesli tak - zapisz dane
ustaw zmienna_sterujaca_na_n
wyskocz_z_podprogramu
...
sciezka_n:
wykonaj_dzialanie_tej_sciezki
...
ustaw zmienna_sterujaca_na_0 ; tu wracamy do stanu poczatkowego podprogramu
wyskocz_z_podprogramu
wspolne:
i powroc z podprogramu
W tej sytuacji - po jednorazowym przejsciu przez wszystkie stany nasz podprogram
zatrzymuje sie w stanie 0, gdzie praktycznie nic nie robi (obciazajac przy tym minimalnie
procesor). Jedynym sposobem wyrwania go z tego stanu jest ustawienie naszej
zmiennej sterujacej na wartosc 1 (albo tez i inna różną od 0, bo mozemy skakac do
dowolnego fragmentu naszego podprogramu). W ten sposob dowolny inny program moze
wyzwalac nasz podprogram ustawiajac tylko wartosc zmienej sterujacej. Po
jednorazowym wyzwoleniu naszego podprogramu zaczyna on juz jakby samodzielnie
"żyć", az do jego pelnego wykonania - mozna to porównac do lawiny kamieni poruszonej
rzutem jednego kamienia.
Jest to bardzo wygodne np. przy obsludze klawiatury i wyzwalanych przez nacisnięcie
klawiszy działaniach - we fragmencie odpowiadajacym danemu klawiszowi wpisujemy
tylko wartośc jaka ma przyjąć nasz zmienna sterująca i gotowe :-))
Dodatkową korzyscia (i czesto koniecznoscia) bedzie fakt możności sprawdzenia czy nasz
podprogram nie jest juz w stanie "wyzwolonym". Wystarczy sprawdzić znienną sterującą
- jesli jest rózna od zera, nasz podprogram jest w trakcie wykonywania się.
6. Zasady dzielenia podprogramów na stany
Każdy z fragmentów podprogramu odpowiadający jednemu ze stanów na jakie dzielimy
nasz program powinien spełniać pewne kryteria - tak żeby ten podział mial sens.
Po pierwsze - jeśli w podprogramie ma występować odwoływanie się do sprzętu, układów
zewnętrznych (np. zewnętrzne pamięci EEPROM) itp, to kolejne stany powinny być tak
zorganizowane, aby podczas każdego z nich następowało tylko jedno odwołanie do
układów zewnętrznych - tak aby okres oczekiwania na dane przypadał na czas pomiędzy
stanami (inicjalizacja transmisji w jednym stanie/.wyskok ze stanu /odczyt danych w
następnym stanie).
Po drugie - jeśli nasz program jest dość skomplikowany, zawiera wiele instrukcji, etc ,etc
- należy starać się aby zostały one podzielone na kilka fragmentów o zbliżonym czasie
wykonania, tak aby uniknąć spiętrzania się obciążenia procesora w jednym ze stanów
podczas gdy w innych obciążenie to będzie bardzo małe. Generalnie należy dążyć, aby
- strona 8/11 -
http://www.easy-soft.tsnet.pl/
stany były dość krótkie czasowo - to umożliwi dostęp do procesora innym podprogramom
(ich stanom).
Przy odwoływaniu się do układów zewnętrznych, przy różnego rodzaju transmisjach
występują pewne sekcje krytyczne, które muszą sie wykonywać w pewnym, ściśle
określonym czasie - zakłócenie tego czasu spowoduje błąd transmisji.
Natomiast pomiędzy tymi sekcjami mogą występować przerwy - zazwyczaj o dowolnie
długim czasie trwania, bez obaw o zakłócenie transmisji.. W zależności od rodzaju
protokolu przerwy te mogą występować pomiedzy pojedyńczymi bitami danych,
pomiędzy bajtami lub większymi zespołami bitów. To doskonale nadaje się do
wykorzystania w naszych programach dzielonych na stany - w jednym ze stanów
wykonujemy sekcje krytyczną dostępu do danych (1bit, bajt lub wiecej bitów), pomiedzy
nimi - wyskakujemy ze stanu i mogą się wykonywać inne podprogramy, a następnie
wchodzimy w następny stan, gdzie wykonuje się następna sekcja krytyczna... itd, itd...
W ten sposób transmisja danych, mimo, że stosunkowo wolna w porównaniu z szybkością
wykonywania instrukcji procesora nie zwalnia działania procesora, nie ma zadnych
wstrzymań na czas transmisji danych (z wyjątkiem sekcji krytycznych) - dzieje się ona
jakby "w tle", "w wolnych chwilach procesora"...:-))
W ten sposób mozna w jednym układzie połączyć np. obsługę transmisji I2C, RS232, 1-
wire, obsługe przetwarzania a/c, klawiatury, wyświetlacza, beeper'a i jeszcze kilku innych
urządzeń i transmisji, a mimo to nie następuje żadne zazębianie się tych programów - z
punktu widzenia obserwatora wykonują się one jakby równocześnie.
7. Praktyczne przykłady programów
Oto przykłady praktyczne realizacji w/w sposobu programowania - w języku ASM dla
procesorów 8051 i PIC
Prosze zwrócić uwagę na jedną rzecz - jesli procedurę naszą wywolujemy poleceniem
CALL, to wówczas każda ze ścieżek musi się kończyc poleceniem RET (RETURN). Jeśli
natomiast naszą procedure wywołujemy poleceniem GOTO (SJMP), to każda ze scieżek
musi się kończyć również GOTO (SJMP).
Procedury te mozemy stosowac zarówno w pętli głownej jak i w przerwaniach - przy
przerwaniach powrotem z procedury bedzie oczywiscie rozkaz RETI :-))
a) Dla procesorów z rodziny MCS51
;----------------------------------------------------------------------------
TimeKeeper
; przykładowa nazwa naszej procedury
;----------------------------------------------------------------------------
mov dptr,#poczatek_tablicy_skokow
; ładujemy adres tablicy
mov a,zmienna_sterujaca
; pobieramy naszą zmienna sterującą do ACC
clr c
; zerowanie Carry
rlc a
; mnozymy zmienna sterujaca przez 2 bo rozkazy sjmp sa dwubajtowe
jmp @a+dptr
; skok do adresu w tablicy wskazywanego przez zmienna
sterującą
;----------------------------------------------------------------------------
poczatek_tablicy_skokow:
; tablica rozkazów koków
sjmp sciezka_1
; rozkazy skoków do różnych ścieżek działań
- strona 9/11 -
http://www.easy-soft.tsnet.pl/
sjmp sciezka_2
sjmp sciezka_3
sjmp sciezka_n
;----------------------------------------------------------------------------
sciezka_1:
; sciezka dzialania nr 1
nop
; tutaj mamy ciało procedury
inc zmienna_sterujaca
; następna ścieżka w następnym przebiegu procedury
ret lub sjmp ciag_dalszy
;----------------------------------------------------------------------------
sciezka_2:
; sciezka dzialania nr 2
nop
; tutaj mamy ciało procedury
inc zmienna_sterujaca
; następna ścieżka w następnym przebiegu procedury
ret lub sjmp ciag_dalszy
;----------------------------------------------------------------------------
sciezka_3:
; sciezka dzialania nr 3
nop
; tutaj mamy ciało procedury
inc zmienna_sterujaca
; następna ścieżka w następnym przebiegu procedury
ret lub sjmp ciag_dalszy
;----------------------------------------------------------------------------
sciezka_n:
; sciezka dzialania nr n
nop
; tutaj mamy ciało procedury
mov zmienna_sterujaca,#0
; wracamy do pierwszej scieżki w następnym
przebiegu procedury
ret lub sjmp ciag_dalszy
;----------------------------------------------------------------------------
ciag_dalszy:
; częsc wspólna - jeśli scieżki mają sie znowu polączyć
ret, reti lub sjmp
b) Dla procesorów rodziny PIC (konkretnie PIC 16F877)
;----------------------------------------------------------------------------
TimeKeeper
; obsluga zegara czasu rzeczywistego - Maszyna stanow
;----------------------------------------------------------------------------
movlw HIGH(TKZwrotn)
; pobranie PCH adresu spod labelu TKZwrotn
movwf PCLATH
; zaladowanie PCLATH (=PCH) ta wartoscia
movfw TimeState
; pobranie zmiennej sterującej do W
andlw B'00000111'
; zabezp indeksu przed przekroczeniem 8
addlw D'3'
; dodanie przeskoku o 3 rozkazy (do poczatku tablicy)
addwf PCL,W
; suma PCL i rej indeksowego w W
TKZwrotn
skpnc
; spr. czy nie przekroczylismy FF
incf PCLATH,F
; jesli tak, zwieksz PCH (za pomoca PCLATCH) o 1
movwf PCL
; zaladowanie PCL suma (skok)
;---------------------------------------------------------------------
-----
goto TK_idle
; State=0
- strona 10/11 -
http://www.easy-soft.tsnet.pl/
;-------inicjalizacja ukladu zegara
goto DS1307_init
; State=1
;-------odczyt czasu z zegara
goto DS1307_set_adres
; State=2
goto DS1307_read_time
; State=3
;-------zapis czasu do zegara
goto DS1307_write_time
; State=4
;---------------------------------------------------------------------------
goto TK_idle
; State=5
goto TK_idle
; State=6
goto TK_idle
; State=7
;**********************************************************************
******
TK_idle ; stan idle procedury
;---------------------------------------------------------------------------
clrf TimeState
; zerowanie zmiennej sterującej
return
itd....dalsze podprogramy :-))
8. Zakończenie.
Programy tego typu mozna dosyć rozbudowywać, chociaż w praktyce nie zdarzyło mi się
wyjść poza kilkanascie stanów zmiennej sterującej dla jednego podprogramu.
Dzieląc w ten sposob kilka, czy kilkanaście swoich podprogramów mamy pewność, że
będą się one wykonywać niemalże równolegle, wzajemnie przeplatąjac swoje stany i nie
nastąpi blokowanie jednego podprogramu przez drugi.
Oczywiście - nie wszystkie programy dadzą się zaprogramować w w/w sposób - to już
wymaga indywidualnego rozpatrzenia sytuacji.
Jacek Domański
(Jado)
- strona 11/11 -