Co I Jak umieścić w nagłówku
Pliki nagłówkowe możemy, dla własnych potrzeb, podzielić na dwa rodzaje: te, które mają przyporządkowany plik kodu źródłowego, oraz takie, które istnieją samodzielnie. Jest to podział tworzony tylko przez nas - kompilator nie interesuje się tym, czy plik nagłówkowy posiada jakiś plik źródłowy o identycznej nazwie.
W pliku nagłówka umieszcza- m/$^Ę/////f///ll/l
my: // skrótowy opis pliku
• Definicje stałych.
Jednak jeśli są one // Autor, kompilator
przeznaczone tylko Ill/IIIIIIIIIIIIIWiWIIIIWI
dla danego modułu (lifndcf IDENTYFIKATOR g§
i reszta programu //dcfinc IDENTYFIKATOR |£
nie potrzebuje ich
znać, stale takie // makra, stale, deklaracje...
można umieszczać
bezpośrednio w ffendif //IDENTYFIKATOR
pliku źródłowym,
Funkcja itoa nie należy do standardu języka C. Jest jednak na tyle popularna, żc wiele kompilatorów ją udostępnia. W AVR-GCC wymaga ona dołączenia nagłówka <stdlib.h>. itoa zamienia podaną liczbę (pierwszy argument) na napis, który wpisuje do bufora (drugi argument). Liczba jest przekształcana z bazą
• Makra, z identycznym zastrzeżeniem jak wyżej.
• Funkcje statyczne, traktowane jako makra i z takim samym, jak dotyczące ich, zastrzeżeniem. Teraz może nie jest to jasne, ale wyjaśni się gdy tylko dowiesz Się, czym są funkcje statyczne.
Dodatkowo, tylko w przypadku plików skojarzonych z plikami źródłowymi:
• Deklaracje funkcji, które chcemy udostępnić do wywoływania z zewnątrz. Deklaracja taka teoretyćz-nie może pojawić się w dowolnym pliku nagłówkowym. Można nawet powtarzać ją bezpośrednio w pliku źródłowym, tam, gdzie jest ona potrzebna. Jednak ze względu na czytelność kodu nie robi się tego. Deklaracje takie umieszczaj zawsze w pliku nagłówkowym o nazwie identycznej z nazwą pliku źródłowego, W:którym znajduje się definicję funkcji.
Szkielet pliku nagłówkowego
Na listingu w tej ramce przedstawiam, w jaki sposób prawidłowo utworzyć plik nagłówkowy. Bardzo ważne są pojawiające się tutaj instrukcje preprocesora.
podaną jako trzeci argument (na listingu 39 jest to baza dziesiętna). Jeśli baza przekształcanej liczby jest równa 10, a przekształcana liczba ujemna - przed wynikiem zostanie umieszczony znak Maksymalna baza, na jaką zezwala nasz kompilator, to 36 (jednak nic jest to ujęte w żaden standard). Jeśli baza jest większa niż 10, po cyfrze ‘9’ wyświetlana jest litera
Są to instrukcje kompilacji warunkowej. W takim zastosowaniu jak przedstawia listing zapewniają one, że zawartość pliku Zostanie przetworzona tylko raz. Jest to cenna właściwość w dużych projektach, gdzie czasami w samym pliku nagłówkowym musimy dołączyć plik, który sam dołącza plik, w którym właśnie jesteśmy. W takim przypadku zostałaby zgłoszona masa błędów oznaczających, że stałe zostały już zdefiniowane, a funkcje zdeklarowane. (.,;.);;W praktyce powiązania takie mogą być dużo dalsze i irudmeisze do wykrycia - wykorzystanie kompilacji warunkowej rozwiązuje problem w sposób uniwersalny i elegancki.
Oczywiście aby całość działała prawidłowo, każdy plik musi posługiwać się innym identyfikatorem. Zwykło się wykorzystywać w nim nazwę pliku oraz słowo INCLUDED (ang. „dołączony”). Na przykład dla pliku delay.h będzie to: DELAY_H_1NCLUDED,
Poleceniu #ifndef równoważny jest zapis: #if Idefincd.
Powyższe informacje pozwalają nam obliczyć niezbędną wielkość bufora. Dla sześnastobitowych liczb ze znakiem, wynikiem o maksymalnej długości jest: -32768. Daje to 6 znaków plus znak zerowy.
Funkcja zwraca zawsze swój drugi argument (wskaźnik do łańcucha). Umożliwia to jej wstawienie bezpośrednio w miejsce odpowiedniej zmiennej (tak jak zostało to zrobione na listingu 39).
oraz *.elf i *.sym - zawierające informacje ułatwiające symulacje programu.
Jeśli chcesz, zobacz, jak wyglądają poszczególne pliki - otwórz je w notatniku albo w naszym Programmers Notepadzie. Zobacz, jaka jest różnica między plikami *.lst a *.lss.
Zanim zaczniesz
Zanim zaczniesz pisać podawane dalej kody, koniecznie utwórz nowy katalog. W moim przypadku nazywał się on PCF8591. Możesz skopiować do niego wzorzec pliku makefile i wykonać w nim standardowe zmiany - jednak aby całość działała prawidłowo, konieczna będzie jego dalsza edycja - napiszę o tym po przedstawieniu kodów programu. Stwórz w Programmers Notepadzie nowy projekt i zapisz go w utworzonym katalogu. Pisane pliki dodawaj od razu do projektu.
Listing 35 - plik delay.h
ne delayus8{t)\ ism volatile( \
„delayus8_loopSfl=: \n\t„\
„nop \n\t„\
„dec %[ticks] \n\t„\
„brnę delayus8_loop%= \n\t„\
: [ticks],,r„(t) );)
' DEC - 1 cykl, BRNĘ 2 cykle, +■ lxnop.
3 najdłuższym opóźnię
di f //DELAY_H_INCLUDED
Tworzenie modułów proponuję rozpocząć od elementu najprostszego - funkcji opóźnień. Konieczne jest utworzenie pliku nagłówkowego oraz pliku kodu źródłowego. Aby lepiej zrozumieć dalej przedstawiane listingi, zajrzyj do ramki „Co i jak umieścić w nagłówku”.
Oba pliki, jakie musisz teraz napisać, pokazują listingi 35 i 36. Zauważ, że nie ma tutaj nic nowego. Dokładnie takie funkcje opóźnieni były już przez nas napisane. Może zainteresuje Cię nowe makro delay500ns. Zapis taki powoduje wykonanie skoku do następnej instrukcji. Co nam to daje? Opóźnienie dwóch cykli, przy zajęciu tylko jednego słowa programu. Prosta sztuczka poprawiająca optymalność kodu.
Główną różnicą między tym, co robiliśmy do tej pory, jest podzielenie pliku. Zauważ, że definicja funkcji została umieszczona w pliku kodu źródłowego.
Do pliku źródłowego dołączam nagłówek tego samego modułu. Regułą jest, aby robić to zawsze, chociaż czasami
Listing 36 - plik,letay.c
#include <avr\io.h> iHnclude „delay.h,,
V0id delayl00us8(uint8_t C>
delayusBClOO);
nic nam to nie daje. W tym przypadku jednak potrzebujemy zawartych w delay.h makr.
Pierwszy moduł mógł być napisany bez wgłębiania się w to, co siedzi w plikach makra.h oraz harddef.h, ponieważ, jak to zostało już wspomniane - moduł ten z nich nie korzysta. Jednak przed pójściem dalej powinniśmy utworzyć wspomniane pliki. Jedyna nowość na listingu 37 to sposób zapisu. Z tego powodu nie będę się wgłębiał w jego opis. Nie przejmuj się pojawiającą się na listingu 38 tajemniczą stałą I2CJSPEED. Wszystko stanie się jasne przy okazji omawiania modułu i2c.
Ze względu na wygodę, kody przedstawiam w całości. Chcę jednak, abyś wiedział, że o ile plik z makrami został stworzony „od ręki”, to jednak plik z definicją sprzętu był rozwijany, w ramach potrzeb, podczas pisania kolejnych modułów. Oba pliki należy traktować właśnie w taki sposób - dodawać do nich nowe wpisy w razie potrzeby.
■dr:
Ponieważ ostatnio napisaliśmy już procedury obsługi wyświetlacza LCD, wykorzystajmy je teraz w celu stworzenia interfejsu użytkownika.
Poza plikiem głównym, dobrym zwyczajem jest zaznaczanie w nazwie funkcji z jakiego pliku ona pochodzi. Znakomicie upraszcza to orientację w programie, zwłaszcza jeśli przysiadamy do niego po dłuższym czasie. Nie ma jakiejś sztywnej reguły, jak takiego oznaczenia dokonywać - kompilator zupełnie się tym nie interesuje. Polecam Ci
38 Grudzień 2005 Elektronika dla Wszystkich