background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -

background image

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 -