Programowanie
pewien dobry zwyczaj. Zanim jeszcze zaczniemy zastanawiać się nad tym, jak w ogóle nasz program ma działać, od razu wpiszemy do pliku źródłowego kilka elementów', które powinny pojawić się tutaj zawsze. Przedstawia to listing 4.
Dobrym zwyczajem jest umieszczenie na początku komentarza, w którym zawieramy informację na temat przeznaczenia pliku, autora oraz wykorzystanego kompilatora. Ułatwi to orientację nie tylko innym, ale przede wszystkim nam, gdy zechcemy wrócić na przykład do jakiegoś programu pisanego kilka miesięcy wcześniej.
Zauważ, że ja stosuję tutaj nietypowe dla C komentarze gdzie znak // oznacza komentarz do końca linii. Jak już wspomniałem - większość kompilatorów na to pozwala. Dla mnie jest to kwestia nawyku. Jeśli chcesz, możesz stosować komentarze zaczynające się od I* i kończące na */ - uzyskasz wtedy większą przenośność kodu.
Każdy program pisany dla kompilatora WinAVR powinien zawierać dołączenie pliku nagłówkowego avr\io.h. Umożliwi to stosowanie symbolicznych nazw rejestrów czy wektorów przerwań. Kompilator sam zadecyduje z jakiego pliku dokładnie ma skorzystać. Zrobi to na podstawie informacji o wskazanym w pliku makefile typie procesora.
Dalej, chociażby w ciemno, możemy napisać szkielet funkcji main. Stąd zawsze rozpocznie się wykonywanie nowego programu. Nieważne, czy będziemy pisać nowe funkcje przed, czy za funkcją main. Jest to pewna różnica w stosunku do BASCOM-a... Moim zdaniem, bardzo wygodna różnica.
Zauważ, że poza kilkoma liniami, które zdołałem już omówić w pliku, jest jeszcze... dużo wolnego miejsca. Dokładnie tak to powinno wyglądać. Wygodnie jest zostawić miejsce między dołączanymi nagłówkami a funkcją main oraz zaraz za otwartą klamerką wyznaczającą ciało naszej funkcji. W tym miejscu pojawią się już elementy, które zadecydują o tym, jak ma zachowywać się nasz procesor. Przypomnę jeszcze tylko o pewnym istotnym drobiazgu: Na koniec pliku klikamy ENTER. Inaczej program będzie się kompilował, ale kompilator zgłosi niepotrzebne nam ostrzeżenia.
ABC... C
Komendy #defme preprocesora możemy użyć w celu przypisania wygodnych dla nas nazw dla dowolnych elementów procesora. Najczęściej będą to rejestry wejścia-wyjścia. W zastosowaniu takim komenda łłdefme znakomicie zastępuje znaną z BASCOMA lub asemblera komendę alias. W takim zastosowaniu jej składnię przedstawia zawarty w ramce obrazek.
Teraz proponuję Ci coś, co może w pierwszej chwili wydać się dziwne. Zapisz nasz szablon, a następnie - skompiluj go. W tej chwili chcemy przede wszystkim zaspokoić naszą ciekawość. Zobaczyć, co jest w takim „pustym” kodzie. Czy jeśli procesor nic nie robi, to nie ma w nim jakiegokolwiek kodu... Jednak w późniejszym czasie znaczenie takiego działania jest nieco inne. Na tym etapie możemy sprawdzić, czy wszystko zostało prawidłowo skonfigurowane. Jeśli podczas kompilacji otrzymamy informację o błędzie, nie musimy się długo zastanawiać, czy przyczyna leży w konfiguracji, czy w napisanym właśnie kodzie.
Aktualnie jednak z czystej ciekawości przeprowadzamy kompilację. Jeśli wszystkie narzędzia skonfigurowałeś tak jak proponowałem w poprzedniej części - zrobisz to najprościej za pomocą przycisku F7. Kompilacja powinna przebiec bez trudności. Końcową część opisu jej przebiegu pokazuje rysunek 18.
Dutput
§§AVR Memory Usage:
|jDevi ce: at90s2313
jlProgram: 90 bytes (1.1* Fuli)
>p(.text -i- .data + .bootloader)
IlData: 0 bytes (0.0* Fuli)
8(.data + . bss + .noinit)
i--------end--------
Process Exit Codę: 0 .
Na rysunku widać wyraźnie, że kompilator wygenerował kod zajmujący dokładnie 90 bajtów pamięci programu. Osoby lubiące język asemblera mogą za pomocą AVRStudia podejrzeć, co pojawia się w pliku wyjściowym. Okaże się wtedy, że 22 bajty zajmują wektory przerwań. Mimo tego, że my świadomie przerwań nic wykorzystujemy, kompilator utworzył tutaj domyślne procedury ich obsługi. Procedury tc działają w taki sposób.
że wystąpienie któregoś z nieprzewidzianych przerwań spowoduje praktycznie programowy reset programu. Warto o tym wiedzieć, aby w przyszłości nie zaskoczył nas ten fakt.
Reszta kodu zajmuje się inicjacją pamięci, stosu... Ogółem przygotowaniem programu do pracy, podobnie jak robił to kompilator BASCOM’a. Znajdziemy tutaj także pętlę nieskończoną, jaką kończy się funkcja main. Dzięki temu mamy pewność, że procesor po inicjacji zatrzyma się i na tym skończy swoje działanie.
Pisząc nasz program sterowania wyświetlaczem, moglibyśmy posługiwać się bezpośrednio nazwami odpowiednich portów. Takie rozwiązanie jest jednak zarówno niewygodne, jak i niepraktyczne. Zwykle więc nadajemy wyprowadzeniom jakieś własne, kojarzące się z funkcją nazwy. Zmniejsza to prawdopodobieństwo popełnienia błędu oraz upraszcza dostosowanie programu do ewentualnych zmian układowych.
W BASCOM-ie posługiwaliśmy się w tym celu komendą Alias. W C istnieje tylko jedna dyrektywa służąca do tworzenia stałych liczbowych, symbolicznych, znakowych, a nawet całych makr. Dyrektywa ta to #define. Jest ona przetwarzana przez preprocesor (czyli jeszcze zanim rozpocznie się kompilacja). Jeśli gdzieś w kodzie pojawi się nazwa, która została za jej pomocą zdefiniowana, zostanie ona automatycznie zastąpiona przez odpowiedni ciąg znaków. To ważne: nie będzie to liczba, nie będzie to tekst. Użycie słowa kluczowego, które wcześniej zdefiniowaliśmy, jest praktycznie równoważne z wpisaniem w edytorze odpowiedniego ciągu znaków (z pominięciem komentarzy). Do skutków, jakie się z tym wiążą, jeszcze powrócimy. W tej chwili nie sprawi nam to przykrych niespodzianek. Składnię interesującej nas dyrektywy przy tworzeniu prostych przypisań omawia odpowiednia ramka.
Tekst, który po znalezieniu w kodzie, będzie zamieniany. Musi być to identyfikator w znaczeniu C - nie może zaczynać się od cyfry, może składać się z liter, cyfr oraz znaku podkreślenia. Niemożliwe jest stosowanie znaków takich jak:!, + - - # % A () [ ] będących operatorami. Wielkość liter ma znaczenie. | |
#define, s SPACJA |
^EDPORT^PORTB SI>ACJA |
Ciąg znaków, na który zostanie zamieniony odnaleziony tekst. Może pojawić się tutaj dowolna ilość spacji. |
// Definicje wyprowadzeń Sdefine LED_A 0 Sdefine LED_B 1 Sdefine LED_C 2 Sdefine LED_D 3 Sdefine LED_E 4 Sdefine ŁED_F 5 Sdefine LEB_G 6 Sdefine LED_DP 7 Sdefine LEDPORT PORTB Sdefine ŁEDDDR DDRB Sdefine COM1 6 Sdefine COM2 5 Sdefine COM3 4 Sdefine COM4 3 Sdefine COMPORT PORTD Sdefine COMDDR DDRD
Elektronika dla Wszystkich Czerwiec 2005 39