
Co to jest komputer? /
Pamięć i rejestry /
Budowa 89C2051 /
Programujemy
Zaczynamy programować
Rozpoczynamy programowanie na poważnie. Wiesz już jakie są pamięci, znasz rejestry do których możesz się odwoływać, więc możesz zacząć programować.
Jak już wspominałem wcześniej użyjemy do tego celu Windows'owego notatnika (przypominam: może to być jakikolwiek edytor tekstu zapisujący znaki w standardzie ASCII, nawet dosowy edytor EDIT). Pamiętaj by nie używać takich edytorów tekstu jak: Word, Ami Pro (pamiętasz jeszcze ten edytor? Używałem go na 486SX 50MHz 8MB RAM) czy tym podobnych. Edytory te bowiem zapisują oprócz samego kodu źródłowego również wiele innych informacji dotyczących czcionki, koloru, formatowania tekstu itp. Asembler wszystkie te znaki będzie próbował przetłumaczyć na kod wynikowy dla naszego procesorka. W efekcie zamiast gotowego programu uzyskamy piękne komunikaty o błędach podczas asemblacji.
Oto szablon naszego programu:
CPU 8052.def
ORG 00h
etykieta:
rozkaz1 ;komentarz po średniku
rozkaz2 ;komentarz
end
A teraz parę wyjaśnień (przypominam, że informacje te dotyczą asemblera którego nabyć możemy w AVT). W pierwszej linijce znajduje się dyrektywa CPU. Jest to informacja dla asemblera, że ma korzystać ze zbioru rejestrów przeznaczonych dla procesora 8052. Zbioru tego możemy użyć również dla 89C2051.
Następna dyrektywa, od nowej linijki po znaku tabulacji to ORG. Informuje ona asembler od jakiego adresu ma zaczynać się dalsza część programu. Nie ma przeciwwskazań aby nasz program zawierał kilka dyrektyw ORG.
Tekst pisany od nowej linijki w pierwszej kolumnie zostanie potraktowany przez kompilator jako nazwa etykiety. Etykiety są bardzo pomocne przy wykonywaniu skoków względnych lub bezwzględnych w programie. Nie musimy wówczas sami obliczać adresu do którego ma wskoczyć program. My posłużymy się nazwą etykiety, a kompilator sam wyliczy sobie adres. Dokładniej zrozumiesz to na konkretnym przykładzie poniżej.
Wszelkie napisy umieszczony po przynajmniej 1 znaku "spacji" (lub tabulacji) zostaną zinterpretowane jako mnemoniki (mnemonik to nazwa instrukcji np. mov, acall). Wszystko co znajduje się w danej linijce po znaku średnika jest ignorowane przez kompilator. Są to tzw. komentarze. Używaj ich w programie jak najwięcej. Pisz w nich co robi program i dlaczego. Sam przekonasz się, że nawet prosty program który sam napisałeś będzie ci trudno analizować już po tygodniu jeśli brakuje w nim komentarzy. Np. rozkaz: mov P1,R5 raz będzie znaczył ustaw wszystkie bity P1 na 1, innym razem ustaw wszystkie bity P1 na 0, a jeszcze innym wpisz do P1 liczbę 135. Dzięki komentarzom od razu będziesz wiedział jaka dana znajduje się w R5 i poprzez to jaka dana zostanie wystawiona na P1.
Ostatnim elementem każdego programu jest dyrektywa END. Jak łatwo się domyślić znaczy ona KONIEC. Wszystko co jest poniżej END zostanie zignorowane przez kompilator. Przy okazji ktoś inny kto przegląda taki program wie, że programista zakończył swoje dzieło, a nie np. zasnął podczas swej pracy.
Ostatnim krokiem przed pisaniem wielkich i małych programów jest poznanie listy rozkazów naszego mikroprocesora.
Kiedyś myślałem, że procesor to wspaniałe narzędzie które rozróżnia słowa tak jak ludzie i potrafi wykonywać przeróżne wspaniałe rzeczy. Jak się jednak okazuje, procesor to bezmyślne urządzenie potrafiące wykonywać jedynie najłatwiejsze czynności związane z przepisywaniem danych, ustawianiem, kasowaniem bitów itp. On nawet nie potrafi odejmować. Niektórzy czytelnicy mogą się oburzyć, bo nasz procesor posiada rozkazy dodawania, odejmowania, mnożenia i dzielenia. Tak to prawda, ale wszystkie te operacje to w rzeczywistości i tak dodawanie. Już w szkole podstawowej uczą, że 5-3=2 jest to to samo, co 5+(-3)=2. Procesor automatycznie zamienia odjemną na liczbę ujemną i wykonuje zwykłe dodawanie. Sposób reprezentacji liczb ujemnych w kodzie U2 (uzupełnienia do 2) nie jest tu poruszany. W czym zatem tkwi potęga procesorów? W tym, że w ciągu jednej sekundy potrafią one wykonać takich operacji bardzo dużo.
Oto lista rozkazów mikrokontrolera 8051. Przygotowana jest ona w formacie Word'a, zajmuje 63KB i jest do pobrania jako samorozpakowywujące się archiwum Rar'a. Zapoznaj się z nią, bo w dalszej części będę się do niej odwoływał. Lista rozkazów 89C2051 jest podzbiorem rozkazów procesora 8051. Nie musisz się jednak tym martwić. Dotyczą one np. komunikacji z zewnętrzną pamięcią. Ponieważ my z tej pamięci korzystać nie będziemy, więc i rozkaz MOVX mamy z głowy, no nie?
Uwaga, zaczynamy swój pierwszy program
Na samym początku zastanówmy się co ma robić ten nasz program. Może tak: niech po prostu migają sobie diody przyłączone do portu P1. Częstotliwość migania nie jest określona. No dobra, założenia już są więc do roboty. Załączamy np. Notatnik, piszemy program, zapisujemy go w katalogu asemblera np. pod nazwą diody.s03 (Rozszerzenie s03 jest bardzo ważne). Wykorzystamy w tym celu 3 rejestry: A, R0 i R1. Rejestr A będzie przechowywał naszą daną, a rejestry R0 i R1 będą wyznaczały opóźnienie. Oto listing tego programu:
CPU 8052.def
;******************
;* Migające diody *
;* *
;* by creth/7be *
;******************
ORG 00h
mov a,#ffh ;wszystkie bity ustaw w stan 1
cykl mov p1,a ;wyślij zawartość akumulatora na port P1
czekaj:
djnz r0,czekaj ;opóźnienie - policz do 65536
djnz r1,czekaj
cpl a ;zaneguj wszystkie bity akumulatora
ajmp cykl ;skocz w miejsce etykiety "cykl"
end of file ;koniec
Przeanalizujmy go. Dyrektywy CPU i ORG są już ci znane. Następnie wpisujemy do rejestru A liczbę FFh (mov a,#ffh). Dziesiętnie jest to 255, a binarnie (dwójkowo) 11111111b. Po co tam jest znaczek # ? Dzięki niemu do A zostanie wpisana liczba FFh, a nie liczba która znajduje się pod adresem FFh. Więcej informacji na ten temat znajdziesz analizując listę rozkazów.
(Zauważyłeś pewnie literki po liczbach. Przekazują one informacje asemblerowi jak ma interpretować liczbę. Litera "h" znaczy, że dana liczba jest zapisana w notacji "heksadecymalnej" (czyli szesnastkowej). Literka "b" oznacza zapis binarny (dwójkowy). Jeśli po liczbie nie będzie żadnego symbolu lub jest symbol "d", to kompilator zinterpretuje to jako liczba dziesiętna. O tym, że ma to ogromne znaczenie świadczy następujący przykład. Jaką liczbę reprezentuje dany zapis: 11? W notacji dziesiętnej jest to liczba 11. W notacji szesnastkowej 11h=17d. W zapisie dwójkowym natomiast 11b=3d. Żeby było śmieszniej, to 11h=10001b, a 11d=1011b. No i skąd ten asembler ma wiedzieć o co nam chodzi? Właśnie poprzez ten znaczek (d b h). Dokładnie nie będę tu opisywał systemów liczbowych. Gdybyś miał z tym jakieś trudności to napisz do mnie, a stosowny materiał na pewno ukaże się na tej stronie)
Następny rozkaz mov p1,a powoduje, że dana z akumulatora zostaje przekopiowana do rejestru z obszaru SFR odpowiedzialnego za port P1. Mówiąc po ludzku to co było w akumulatorze pojawia się teraz na końcówkach portu P1. Wszystkie one w tym momencie przyjmują stan logicznej jedynki (1). Od pierwszej kolumny w tej linijce znajduje się etykieta cykl. Wrócimy do niej za chwilę.
Następna dwa rozkazy (djnz r0,czekaj i djnz r1,czekaj) mają na celu wprowadzenie opóźnienia. Okazuje się bowiem, że procesor nie może nic nie robić. Musimy go zatem czymś zająć. A nich sobie tak po prosu policzy: 1,2,3... Zajmie mu to jakiś czas, a oto nam właśnie chodzi. Robimy zatem pustą pętlę w której procesor będzie zliczał sobie od 256 do 0. Rozkaz djnz r0,czekaj zmniejsza R0, a następnie porównuje czy R0=0. Jeśli nie, to skacze w miejsce etykiety czekaj. Znowu sprawdza R0 i tak aż do skutku. Jeśli na samym początku R0=0, to po zmniejszeniu R0=FFh! W ten oto sposób zmusiliśmy procesorek by policzył sobie do 256. To opóźnienie jest jednak za krótkie. Procesor wykona je w ciągu jakiejś tysięcznej części sekundy. Trzeba więc je wydłużyć. W tym celu posłużymy się następnym rejestrem R1. Po "przekręceniu" się R0 zmniejszymy R1. Sprawdzamy, czy R1=0. Jeśli nie, to znów "przekręcamy" R0, zmniejszamy R1. W ten oto sposób na dwóch 8-bitowych rejestrach zrobiliśmy 16-bitową pętlę. W tym czasie nasz procesor policzy sobie do 65536 (256*256), czyli do FFFFh. Na tym właśnie polega sztuka programowania (czyli efektywnego okłamywania odbiorcy). Tobie wydaje się, że procesor patrzy na zegarek i w odpowiednim momencie cos przełącza, a on w gruncie rzeczy wykonuje jakieś bzdurne czynności
Rozkaz cpl a neguje wszystkie bity akumulatora. Negować to znaczy zamienić wszystkie jedynki na zera, a wszystkie zera na jedynki. na samym początku wpisaliśmy do A liczbę 11111111b=FFh. Teraz, po zanegowaniu bitów akumulatora, znajduje się w nim liczba 00000000b=00h.
Ostatni rozkaz ajmp cykl powoduje skok pod wskazany adres. Musimy jakoś więc ten adres wpisać. W tym krótkim programie możemy sami go sobie bez trudu wyliczyć, ale przy dłuższych programach mogą z tym być pewne trudności. Zlecamy więc kompilatorowi by sam sobie wyliczył ten adres. My posługujemy się w tym celu etykietą. Podczas kompilacji w rozkazie "ajmp cykl" w miejsce słowa cykl asembler wstawi adres rozkazu który rozpoczyna się etykietą cykl. W naszym przypadku asembler wstawi adres rozkazu "mov p1,a". Procesor zatem napotkawszy w naszym programie rozkaz ajmp cykl wskoczy w miejsce gdzie znajduje się etykieta cykl i zacznie wykonywać znajdujący się tam program.
Teraz znów zawartość A zostaje wysłana do P1, ale teraz w A znajdują się same zera. Na końcówkach portu P1 pojawi się zatem stan niski (logiczne 0). Znowu pętla opóźniająca a następnie negacja bitów akumulatora. Znów w A pojawią się same jedynki. Skok i znów wysyłamy zawartość A na port P1 (teraz na końcówkach pojawi się stan wysoki)
Jeśli zatem podłączymy diody do portu P1 będą one raz zapalane, raz gaszone i tak aż do znudzenia. Jeśli mamy napisany program, to zapiszmy go na dysk. Pamiętaj aby nazwy programów zawierały max. 8 znaków + rozszerzenie .s03 Czas więc przystąpić do asemblacji. Wchodzimy do DOS'a, a następnie do katalogu z naszym asemblerem (przypominam: w DOS'ie cd.. znaczy tyle samo co w Windows'ie "do góry", natomiast cd nazwa_katalogu to wejście do tego katalogu).
Jeśli wszystko to już zrobiłeś, to wywołaj asemblera. Napisz: do diody. Nazwę programu podajesz bez rozszerzenia. Asembler sam poszuka pliku o podanej nazwie i rozszerzeniu .s03. Jeśli nie popełniłeś błędów przy programowaniu to twoim oczom ukaże się nam napis:
Kompilacja zakończona pomyślnie!
Jeśli popełniłeś jakiś błąd, to asembler poinformuje cię o tym podając czego nie rozumie i w której linii występuje błąd. Jeśli chcesz bawić się dalej, to pomyśl jak napisać program aby diody nie tylko migały sobie, ale żeby tworzyły jakieś fajne efekty. Jeśli Ci się to uda, to niechcący stworzysz prawdziwy sterownik efektów dyskotekowych. Rozwiązanie tego zadania zamieszczone zostanie już wkrótce na łamach tej strony.
Na samym początku wspomniałem o programie zegara. Jakimś sposobem musimy obliczyć czas dokładnie 1 sekundy. Sposób opóźnienia zastosowany w migających diodach jest niedokładny. Jak więc to zrobić? Z pomocą przyjdą nam tzw. przerwania. Co to są i jak z nich korzystać dowiesz się już wkrótce. Pamiętaj: ta strona cały czas się tworzy...

|