Kurs programowania PIC.doc

(110 KB) Pobierz

Kurs programowania PIC

Posts from the ‘Kurs’ Category

·                      Lekcja 3, programujemy linijke LED on 9 listopada, 2011

·                      Lekcja 2, tworzymy pętle opóźniające on 5 listopada, 2011

·                      Lekcja1, piszemy pierwszy program on 30 października, 2011

 

Lekcja1, Piszemy pierwszy program

30 października, 2011

Czas na programowanie

Ostatnio udało nam się uruchomić programator, przetestować jego działanie i w końcu zaprogramować PIC-a. Teraz nadszedł czas na poznanie tajników działania tego złożonego tworu jakim jest mikroprocesor. Zacznijmy jednak od opisu działania podstawowych komend asemblera użytych w poprzednim programie testowy.

Architektura RISC

Należy wspomnieć, że rodzina PIC-ów, którą jak mniemam postanowiłeś się zająć posiada jedynie 35 komend w języku asembler. Znakomicie ułatwia to rozpoczęcie programowania. Nie musimy uczyć się wielu komend, których zadaniem jest wykonanie pojedynczego działania mikroprocesora. Mamy za to 35 komend, które zostały uznane za najpraktyczniejsze. Część z nich powoduje wykonanie jednej operacji, a część jest złożeniem kilku. Tak małą ilość komend zawdzięczamy architekturze RISC w jakiej został wykonany mikroprocesor. Charakteryzuje się ona właśnie zniwelowaniem ilość komend. Zostało to osiągnięte poprzez połączenie kilku podstawowych komend w jedną.

Opis programu

Teorią dotyczącą działania mikroprocesora i subtelnościami PIC-ów zajmiemy się możliwie w najbliższym czasie. Przy nauce programowania w asemblerze i tak siłą rzeczy poznamy jego działanie, a czytanie suchej niezrozumiałej teorii na pewno dla nikogo nie jest przyjemnością.

Blok konfiguracji

Nazwałem tak część programu, która musi wystąpić przy każdym tworzonym projekcie. Składa się na nią:

– Zdeklarowanie używanego mikroprocesora. Realizuje tą funkcję komenda, która jest wysyłana tylko do kompilatora. Ma to na celu zapoznanie kompilator z procesorem, który programujemy. Dzięki temu wie on niejako jakie adresy mogą wystąpić w danym programie. Oznacza to tyle, że dzięki temu kompilator wyrzuci nam błąd, kiedy odwołamy się do rejestru, który nie istnieje w programowanym mikrokontrolerze.

Procesor 16f84 ;to jest właśnie ta komenda

– Powiązanie pliku nagłówkowego nagłówkowego z pisanym programem. Jak już doszliśmy do wniosku w poprzednim artykule, (a raczej ja napisałem) plik nagłówkowy znakomicie ułatwia pisanie programu i skraca i tak już długi czas poświęcony na programowanie. Taki plik nagłówkowy, dany nam przez twórców kompilatora asemblera, składa się praktycznie w całości z bloku procedur mających następujący wygląd, zasadę działania:

nazwa rejestru equ adres rejestru np.

PORTA equ xxx

Tłumacząc, rejestr o adresie xxx nazwij PORTA. Dzięki temu zamiast przy każdym kolejnym odwołaniu do adresu rejestru PORTA, zamiast pisać niezrozumiały i mylący adres piszemy po prostu PORTA.

W pliku nagłówkowym znajdują się również adresy procedur bitów konfiguracyjnych. Chodzi mi o bity odpowiedzialne za wyłączenie/włączenie watchdoga, typ kwarcu z jakim pracujemy itp.

– Ustawienie bitów konfiguracyjnych, fusebitów. Wspominałem już wcześniej o swoim problemie z watchdogiem. Takich problemów unikniemy świadomie ustawiając bity konfiguracyjne. Najważniejsze z nich to właśnie watchdog, oscylator z jakim pracujemy i kilka innych, które na moim etapie nauki po prostu zostawiam w spokoju.

_config – mówimy procesorowi o tym, że zaraz nastąpi przesłanie danych dotyczących ustawienia bitów konfiguracyjnych.

_XT_OSC – oscylatorem jest kwarc o normalnej prędkości (do 4 MHz)

_WTD_OFF – wyłączamy watchdoga

Wszystkie procedury konfiguracji łączymy ze sobą spójnikiem &.

Mamy już za sobą konfigurację. Jeszcze raz zwracam uwagę na to, że są to bardzo ważne ustawienia. Często rozpoczynając programowanie  chcemy jak najszybciej skompilować program i przetestować czy wszystkie komponenty działają. Jeżeli mamy trochę szczęścia to pierwszy prosty program zadziała (np. ze względu na to, że użyjemy oscylatora RC, który jest automatycznie ustawiany). Niestety po wymianie przytoczonego oscylatora na kwarc wszystko zacznie się sypać, my stracimy chęci i skończymy z nauką uważając, że jest to jakieś magiczne. W końcu skoro nie działa wpisanie 1 na wyjście RB.0, które wcześniej zostało zainicjowane jako wyjście, to albo coś jest nie tak ze sprzętem, albo z człowiekiem. Zamiast skazywać się na takie rozmyślania sumiennie przestudiujmy działanie bitów konfiguracyjnych i dobierzmy je wedle swoich potrzeb.

Program właściwy, ustawiamy porty

PIC, którym się zajmujemy czyli pic16f84 posiada dwa porty wyjściowe. PORTA o adresie 05h i PORTB o adresie 06h. PORTA posiada cztery wyjścia RA.0 (nóżka 17), RA.1 (nóżka 18), RA.2 (nóżka 1), RA.3 (nóżka 2) i RA.4 (nóżka3). Dla nas oznacza to możliwość zaadresowania takiego portu przy użyciu 5 bitów (kod dwójkowy). Tłumacząc to prościej wpisanie liczby 00001 do PORTA spowoduje pojawienie się jedynki na wyjściu RA.0. Wpisanie 11111 spowoduje pojawienie się jedynek na wszystkich wyjściach tego portu. Oczywiście wcześniej musimy ustalić czy mają to być wejścia czy wyjścia, ale o tym za chwilę. PORTB ustawiamy analogicznie jak PORTA. Mamy tutaj do dyspozycji aż 8 wyprowadzeń. Należy wziąć pod uwagę to, że dwa z nich wykorzystujemy do programowania (RB.6 i RB.7). Nie oznacza to, że nie możemy ich używać, ale wpisanie jedynki na RB.7 w momencie gdy mamy tam podłączoną linie programatora nie spowoduje pojawienia się tam 5V. Dopiero po odłączeniu tej linii mikroprocesor zachowa się tak jak chcieliśmy.

Podsumowując: PORTA umożliwia obsługę 5 wyjść, PORTB natomiast 8. W tym drugim uważamy na wyjścia RB.7 i RB.6. Programujemy je normalnie, ale żeby działały tak jak chcemy odłączamy po prostu linie programatora. Na razie nie zajmujemy się specyfiką wyjść tych portów. W niedalekim czasie zajmiemy się tym, a na razie nie zaprzątaj sobie głowy tym co jest tam  w środku i co oznacza TOCKI przy RA.4.

Omówienie programu

Zajmijmy się teraz opisem poszczególnych procedur użytych w programie. Na pierwszy ogień idzie ustawianie portów jako wejściowe i wyjściowe. Wejściowe oznacza, że będą one odczytywały sygnał zewnętrzny, natomiast wyjściowe będą przekazywać sygnał na zewnątrz.

Jak już, jako wielbiciel teorii się dowiedziałeś, bohater tego cyklu (pic16f84) ma pamięć podzieloną na dwa banki. Czemu i w jakim celu spróbujemy dojść później. Sygnały na podawane na porty, czy procedury odczytywania znajdują się w BANK0, a bity odpowiedzialne za ustawienie portów jako wyjścia i wejścia w BANK1. Dla programisty, czyli nas oznacza to przymus przełączenia się między tymi bankami w celu ustawienia portów.

Za to, który bank jest aktualnie włączony, odpowiada bit piąty rejestru STATUS, który znajduję się pod adresem 03h. Jeżeli bit ten jest wyzerowany to jesteśmy w obszarze BANK0 jeżeli ustawiony (1) to w obszarze BANK1. Zerowanie czyli ustawienie bitu w stan zera logicznego wykonywany jest za pomocą funkcji bcf rejestr,numer bitu .

BCF czyli bit clear, wyczyść bit.

Natomiast jedynkę logiczną ustawiamy komendą bsf rejestr,numer bitu.

BSF czyli bit set, ustaw bit.

Przechodzimy więc do przykładu. Jako, że na początku programu zawsze znajdujemy się w BANK0, przejdziemy teraz do BANK1. Dokonamy tego przy pomocy następującej procedury:

BCF STATUS,5 ;bit 5 rejestru STATUS jest teraz zerem, co oznacza, że jesteśmy jesteśmy obszarze BANK1.

Kiedy już znajdujemy się w pożądanym banku, musimy dokonać ustawienia portów. Wpisując do portu jedynkę ustawiamy go jako wyjście, natomiast wpisując zero jako wejście. W celu wpisania jakiejkolwiek liczby do rejestru musimy wykonać dwie czynności. Wpisać żądaną liczbę do rejestru w, a następnie przepisać zawartość rejestru w do docelowego rejestru (w naszym przypadku TRISA lub TRISB). W to główny rejestr mikroprocesora PIC, w którym przechowywane są dane liczbowe, które mogą być potem przesuwane do innych rejestrów, dodawane itp. Generalizując w to rejestr zapewniający przestrzeń do wykonywania działań na liczbach.

W celu wpisania czegokolwiek do rejestru w wykonujemy komendę movlw liczba. Gdzie wpisywana liczba musi mieć wyraźnie zaznaczony kod np.

MOVLW d’10’ – wpisuje do rejestru w liczbę 10 dziesięteni

MOVLW 10h    – wpisuje do rejestru w liczbę 10 heksalnie

MOVLW b’1010’ – wpisuje do rejestru w liczbę 10 binarnie

Jest to ważne, bo w przypadku nie określenia kodu w jakim zapisana jest dana liczba kompilator wyrzuci błąd.

Kolejny krok to wpisanie liczby z rejestru w do innego rejestru. Jest to zadanie komendy movwf adres rejestru (przesuń z rejestru w do f). Jak widać dla znających język angielski chociażby w najmniejszym stopniu komendy asemblera są same w sobie zrozumiałe i czytelne. Jedyne co jest tu do zapamiętania to składnia jaka występuję po komendzie.

Podajmy dalszy przykład dla naszej liczby dziesięć. Załóżmy, że jesteśmy w BANK0 i chcemy tą dziesiątkę wpisać do PORTA, który wcześniej został zdefiniowany jako wyjściowy.

MOVLW 10h ;wpisujemy dziesięć do rejestru ogólnego przeznaczenia w

MOVWF PORTA ;przepisujemy zawartość z rejestru w do PORTA

Po wykonaniu tych dwóch komend na wyjściach PORTA panują następujące stany 01010. Oczywiście jest to włożona do rejestru w liczba dziesięć tyle, że zapisana dwójkowo.

Wiemy już jak ustawiać i zerować poszczególne bit. Oczywiście nie odnosi się to nie tylko do rejestru STATUS i bitu odpowiedzialnego za wybór banku. Poznanymi poleceniami jesteśmy w stanie wyzerować i ustawić każdy bit dowolnego rejestru. Jak wyzerować wszystkie bity danego rejestru? Mamy kilka możliwości. Mianowicie możemy zerować każdy bit po kolei poznaną już komendą, ale zużyjemy tym samym masę pamięci na powtarzanie tej samej komendy. Możemy wpisać zero do rejestru w, a następnie do zerowanego rejestru (przypuśćmy, że zerujemy PORTA).

MOVLW 00h ;wpisujemy zero do rejestru w

MOVWF PORTA ;wpisujemy zawartość w (czyli 0) do rejestru PORTA

Jak widać tracimy na to, tylko dwie komendy. My jednak znając wszystkie dostępne komendy skorzystamy z procedur clrf adres rejestru, która w jednej komendzie wyczyści nam całą zawartość wskazanego rejestru.

CLRF PORTA ;zerujemy cały PORTA

Piszemy program

Na początek obrazek ze schematem układu, pod który napisany był program i pod którym na pewno on zadziała:

Mamy już wystarczająco dużo wiadomości by napisać program, który zapali nam diodę dajmy na to na wyjściu RA.3 , a pozostawi zgaszoną na wyjściu RA.4.

Zaczynamy oczywiście od najważniejszego, czyli procedur konfiguracyjnych:

;***PLIKI KONFIGURACYJNE***

processor 16f84

;wpisujemy typ naszego PIC-a

__config _XT_OSC & _PWRTE_ON & _CP_OFF & _WDT_OFF

;konfigurujemy oscylator kwarcowy i wyłączamy watchdoga

include <p16f84a.inc>

;dołączamy plik p16f84a.inc , w którym znajdują się nazwy wszystkich rejestrów PIC-a z ;przypisanymi adresami

Dodam, że po znaku ; umieszczamy komentarze, które później naprawdę się przydają. Nawet te najprostsze operacje warto obarczyć krótkim opisem. Najlepiej krótkim i zrozumiałym dla każdego, a najbardziej dla nas samych.

Procedura konfiguracyjna została już omówiona wcześniej, więc przejdźmy już do głównego programu. Naszym celem jest podanie 01000 na PORTA. Czwarty bit ma być jedynka co znaczy, że spowoduje włączenie odpowiednio dołączonej diody. 01000 binarnie to liczba 8 heksalnie. Tyle wprowadzenie teraz zajmijmy się programem. Cały PORTA ma tutaj pracować jako wyjścia danych czyli musimy go ustawić jako wyjście. Na ustawianie portów mamy dwa sposobu. Możemy to zrobić tak jak już wcześniej zaczęliśmy czyli:

;***USTAWIENIE PORTÓW I/O***

BSF STATUS,5 ;idziemy do BANK1

MOVLW 00h ;wpisujemy zero do rejestru w

MOVWF TRISA ;przepisujemy zawartość w do rejestru TRISA odpowiedzialnego za ;ustawienie portów (PORTA)

BCF STATUS,5 ;powracamy do BANK0, gdzie wykonuje się większość operacji

Drugi sposób jest krótszy i zachęcam Ci do jego stosowania:

;***USTAWIENIE PORTÓW I/O***

MOVLW 00h ;wpisujemy zero do w

TRIS PORTA ;procedura wpisująca zawartość w do rejestrów PORTA odpowiedzialnych za ;ustawienie portów, czyli do TRISA

Polecenie TRIS wykonuje trzy czynności. Najpierw zmienia obszar na BANK1. Następnie wpisuje zawartość w do rejestru TRIS podanego portu, a na końcu znowu wraca do BANK0. Znakomite ułatwienie, nie sądzisz?

Główna część programu jest w tym przypadku najkrótsza. Oczywiście musimy wpisać tą wspomnianą wcześniej ósemkę heksanie lub 01000 binarnie do PORTA. Mam nadzieję, że sam poradzisz sobie z tym zadaniem. Ja wspomnę tylko o przymusie zapętlenia programu. Procesor jeżeli jest włączony to ciągle pracują (na razie pomijamy różne tryby wstrzymania czy watchdogi), dlatego musimy go czymś zająć w większości przypadków wystarczy zakończyć program komendą end, a kompilator się wszystkim zajmie. My jednak lubimy być zabezpieczeni przed nie lubianymi wpadkami i sami zadbamy oto by procesor miał co robić. Skorzystamy tutaj z funkcji goto, czyli idź do.

Twój kod wpisujący 8 do PORTA

Program

GOTO Program ;idź do procedury Program

END ;kompilator tego wymaga

Procesor natrafia na procedurę (podprogram, jak zwał tak zwał) o nazwie Program, przechodzi dalej i trafia na komendę wysyłającą go do wcześniej zdefiniowanej procedury Program. Procesor posłusznie wykonuje rozkaz i jest zapętlony w nieskończoność. Oczywiście często zdarzy się, że będziemy musieli wyprowadzać program z takich pętli. Tym jednak zajmiemy się później. Twój program równie dobrze mógłby wyglądać tak:

Program

Twój kod wpisujący 8 do PORTA

GOTO Program ;idź do program i kolejny raz wpis 8 do PORTA

END ;kompilator tego wymaga

Jak widać programy wykonujące te samą funkcję możemy napisać na kilka różnych sposobów. Najczęściej jest tak, że wybieramy ten najoptymalniejszy lub ten, który najbardziej nam odpowiada. W przyszłości jeszcze nie raz przekonasz się o wielorakich możliwościach rozwiązania tego samego problemu.

Co już umiesz, powinieneś umieć?

Zapoznaliśmy się ze sposobem ustawiania konfiguracji nieodłącznym dla każdego programu. Umiemy wpisywać liczby do dowolnych rejestrów, a także zerować i ustawiać zarówno całe rejestry jaki i ich pojedyncze bity. Potrafimy zapętlić program i tworzyć własne procedury. Wiedza ta doprowadziła Cię mam nadzieję do stworzenia pierwszego programu. Nic wielkiego ledwie potrafisz zapalić diodę. Ona nawet nie mruga. Na większe projekty przyjdzie jednak czas. Te pierwsze programy, które wykonujemy samodzielnie są bardzo ważne i wymagają dużo cierpliwości, dlatego gratuluję Ci pierwszego własnoręcznie napisanego programu i do zobaczenia w kolejnym wpisie. Jeżeli masz chwilę czasu zostaw tutaj jakiś komentarz odnośnie jakości tekstu, problemów, czy chociażby pochwal się pierwszym programem.

 

Lekcja 2, tworzymy pętle opóźniające

5 listopada, 2011

Koncepcja programu

W pierwszym ćwiczeniu nauczyliśmy się obsługiwać porty wejścia i wyjścia mikrokontrolera. Znamy również kilka przydatnych instrukcji asemblera pod PIC16. Dzisiaj spróbujemy napisać program, którego celem będzie mruganie diodą podłączoną załóżmy do portu RA.3 (pin 2).

Standardowo jak to bywa w programowaniu możemy to wykonać na kilka zgoła różnych sposobów, które dadzą jednak ten sam efekt. Teoretycznie moglibyśmy po prostu dodać do poprzedniego programu taką komendę:

Program ;główny program

BSF PORTA,3 ;ustaw RA.3 (RA.3 = 1)

BCF PORTA,3 ;zeruj RA.3 (RA.3 = 0)

GOTO Program ;powrót do programu głównego

Częstotliwość pracy procesora

Jak myślisz co się stanie jeżeli skompilujemy taki plik i wyślemy do mikrokontrolera? Dioda będzie mrugać, ale z tak dużą prędkością, że my z naszym niedoskonałym okiem będziemy widzieć ciągłe świecenie. Wynika to oczywiście z tego, że mikrokontroler na wykonanie jednej operacji (np. BSF, czy BCF) potrzebuje bardzo mało czasu. W przypadku pic16f84, którego używamy czas ten wynosi jak podaje katalog 200ns. Dla ścisłości te 200ns to czas pojedynczej instrukcji wykonywanej przez mikroprocesor. Składa się na niego kilka stałych czynności. Dla przykładu polecenie BSF będzie wykonywane mniej więcej  tak:

– zaadresuje pamięć

– odczyta rozkaz BSF

– zwiększy licznik programu, o którym później, 0 jeden

– wykona rozkaz, w tym przypadku ustawi dany bit

Dla nas praktycznie nie ma to większego znaczenia. Dioda mruga nam z częstotliwością zależną od użytego kwarcu i nie satysfakcjonuje nas ten stan rzeczy. Musimy obniżyć częstotliwość mrugania, czyli przedłużyć czas wyłączenia diody i jej włączenia. Zrobimy to za pomocą programowego opóźnienia. W innych językach programowania mielibyśmy pewnie gotowe procedury odmierzające czas, ale niestety w asemblerze wszystko musimy robić samodzielnie.

 Pętla opóźniająca, czyli odmierzamy czas

Pętle opóźniającą wykonamy w następujący sposób. Zainicjujemy w mikrokontrolerze dwie zmienne np. liczba1 i liczba2. Następnie wpiszemy do nich odpowiednie wartości. Od tych wartości będzie zależała częstotliwość mrugania diody. Pierwszą zmienną z wpisaną wartością będziemy w każdym cyklu zmniejszać, a kiedy osiągnie ona zero przejdziemy do zmniejszania drugiej zmiennej. Po pojedynczym zmniejszeniu  tej drugiej program znowu zacznie zmniejszać pierwszą i tak, aż wartość drugiej nie będzie się równać zeru. Jeżeli tak się stanie program opuści pętle opóźniającą i przejdzie do wykonywania dalszych komend.

Opóźnienie: ;tu wskakujemy po przejściu do opóźnienia

MOVLW .200 ;wpisujemy do rejestru w 200 dziesiętnie

MOVWF liczba2 ;wpisujemy 200 do naszej drugiej zmiennej

Petla 1 ;tu wskakujemy, gdy liczba1 = 0 i liczba2 została zmniejszona o jeden

MOVWF liczba1 ;wpisujemy 200 do naszej pierwszej liczby

Petla2 ;tu wskakujemy po każdym zmniejszeniu zmkiennej liczba1

DECFSZ liczba1,f ;zmniejszamy liczba1 o jeden i jeżeli liczba1 = 0 to przeskakuje jedną ;komendę

GOTO Petla2 ;tu wskoczy jeżeli liczba1 nie będzie zerem

DECFSZ liczba2,f ;tu wskoczy, gdy liczba1 będzie zerem i dodatkowo zmniejszy o jeden ;liczba2 i podobnie jak wcześniej, jeżeli jest zerem przeskoczy komendę jeżeli nie przejdzie ;do następnej

GOTO Petla1 ;jeżeli nie jest zerem idzie do Petla1, gdzie wpisuje 200 do licza1 i proces się ;powtarza

RETURN ;powrót do miejsca, z którego został wywołany podprogram opóźnienie, w momencie gdy liczba2 = 0.

Może to się wydać trochę skomplikowane, ale po krótkiej analizie działania dojdziesz do wniosku, że wcale tak nie jest. Wyjaśnijmy teraz działanie nowych komend. Pojawiła się tutaj komenda DECFSZ f,d ,gdzie f to nazwa zmiennej, która jest poddawana operacji, a d to miejsce gdzie zapisywany jest wynik operacji. Dla d = 0 (lub gdy dodaliśmy plik nagłówkowy po prostu w) wynik jest zapisywany w rejestrze w. Natomiast gdy d = 1 (lub f w programie z plikiem nagłówkowym) wynik jest zapisywany w rejestrze f.

DECFSZ to komenda, która jest złożeniem dwóch operacji. Mianowicie zmniejszenia zmiennej o jeden, a następnie sprawdzeniu czy zmienna równa się zeru. W naszym przypadku zmniejszaniu ulegały zmienne liczba1 i liczba2, a ich wartości zmniejszone wpisywane były jak gdyby do ich samych dzięki wstawieniu po zmiennej f (zapis wyniku do rejestru f). Po tej komendzie program ma możliwość wyboru. Jeżeli zmienna nie równa się zeru program wykonuje się dalej normalnie komenda po komendzie. W przypadku, gdy zmienna równa się zeru program przeskakuje, pomija jedną komendę i idzie do następnej

DECFSZ zmienna,f

Wykona komendę wpisaną tutaj, gdy zmienna nie jest równa zeru

Przeskoczy tutaj nie wykonując komendy poprzedniej, gdy zmienna = 0

 Licznik programu PC

W celu zrozumienia skąd PIC wie gdzie i o ile ma przeskakiwać należy wprowadzić pojęcie PC (program counter) czyli licznika programu. Przy przejściu do każdej następnej komendy licznik ten jest zwiększany o jeden. Z wartości tego licznika PIC wie, którą komendę ma aktualnie wykonać

1 Opoznienie:

2 MOVLW .200

3 MOVWF liczba1

4 Petla1

5 DECFSZ liczba1,f

6 GOTO Petla1

7 RETURN

W przypadku natrafienia na komendę DECFSZ  w PC znajduję się liczba 5. Gdy zmienna liczba1 = 0, do PC jest dodawana liczba 2 i w następnym cyklu instrukcji przechodzi on do lini 7 (5+2=7). Jeżeli liczba1 jest różna od zera do PC jest dodawana standardowo jak przy każdej „zwykłej komendzie” liczba jeden. W tym przypadku PC = 6 i program wykonuje komendę GOTO Petla1.

Trafiła nam się kolejna zagadka. Skąd PIC wie, gdzie jest procedura Petla1, do której ma iść. Znowu jest tu wykorzystany licznik programu. Po natrafieniu na jakąś procedurę (np. Petla1) mikroprocesor zapisuje sobie informacje o tym przy jakim stanie PC (w tym przypadku PC = 4) została ona wywołana i dzięki temu przy instrukcji GOTO ta informacja jest wydobywana i program powraca tam, gdzie ma wrócić.

Praktycznie nie obchodzi nas zupełnie jaką wartość ma PC i jakie liczby są do niego wypisywane. Warto jednak wiedzieć, że takie cudo istnieje i to dzięki niemu PIC wie gdzie w aktualnym momencie ma skoczyć i jaką komendę wykonać.

Przy okazji skróciłem trochę program opóźnienia. Jednak najprawdopodobniej taki krótki będzie wytwarzał za małe opóźnienie i dioda będzie świeciła ciągle. Zależy to od tego jakiego rezonatora używasz.

Ten krótki programie da nam 200 cyklów opóźnienia, natomiast ten pierwszy dłuższy 200*200, czyli 40000. Mam nadzieję, że wiesz z czego wynikają te liczby. W pierwszym programie liczba1 jest zmniejszana w każdym cyklu o jeden, aż nie osiągnie wartości 0. Gdy osiągnie tą wartość liczba2 jest zmniejszana o jeden, program wraca do procedury Petla1, wpisuje do liczba1 kolejne 200 i żeby zmniejszyć liczba2 o kolejną jedynkę liczba1 znowu musi zostać zmniejszona do zera.

Słowo o podprogramach

W powyższych przykładach komendy były pisanie w podprogramie. Podprogram to taka część kodu, która może zostać wywołana z dowolnego miejsca w programie poprzez instrukcję CALL podprogram. Działa to podobnie jak z instrukcją GOTO. Do PC jest wpisywana aktualna wartość, procesor skacze do podprogramu, wykonuje go i gdy natrafi na RETURN dodaje do poprzednio zapisanej wartości PC jeden przez co wraca do komendy bezpośrednio po CALL.

Dzięki podprogramom zmniejszamy wagę programu wynikowego. Zamiast przed i po zapaleniu diody dawać dwie kolumny opóźnień dodajemy tam jedynie CALL opóźnienie.

Zilustrujmy to na przykładzie

Program

BSF PORTA,3 ;włączamy diodę

;Tutaj wklejamy kod opóźnienia czyli 5 linijek z poprzedniego programu

MOVLW .200

MOVWF liczba1

Petla1

...

Zgłoś jeśli naruszono regulamin