Przystąpmy teraz do przypisania własnych oznaczeń dla wyprowadzeń naszego ukiadu. Umieść kursor w wolnej przestrzeni za dołączonymi nagłówkami. W tym miejscu powinien pojawić się kod widoczny na listingu 5. Nadajemy tutaj nazwy zarówno samym wyprowadzeniom, jak i odpowiednim portom. Dodatkowo pojawia się przypisanie dla odpowiednich rejestrów DDRx. Zgodnie z dokumentacją naszego procesora, wpisanie tutaj logicznego 1 spowoduje ustawienie odpowiedniego wyprowadzenia jako wyjścia. Wpisanie logicznego 0 natomiast, ustawi odpowiednie wyprowadzenie jako wejście. Ze względu na konfigurację musimy więc mieć dostęp do wspomnianych rejestrów.
Jeśli teraz gdziekolwiek w programie, za wpisanym właśnie ciągiem definicji wykorzystamy dowolne z utworzonych oznaczeń, zostanie ono zamienione na oznaczenie rozumiane przez kompilator, odnoszące się do interesującego nas portu lub wyprowadzenia.
Teraz, kiedy mamy już zdefiniowane wszystkie potrzebne nam aktualnie wyprowadzenia, napiszmy coś, co zacznie działać. Stworzymy coś prostego, co pozwoli zweryfikować naszą ideę sterowania. Pomysł jest taki, aby na pierwszym z wyświetlaczy wyświetlić cyfrę 1.1 tyle. Brak obsługi wszystkich wyświetlaczy, zmiany wyświetlanej wartości... praktycznie chodzi tylko o to, aby odpowiednio wysterować wszystkie porty i zakończyć program. Zauważ, że aktualna idea, chociaż nie przedstawia dużych możliwości, jest w zasadzie małym elementem programu w pełni obsługującego wyświetlacz, Będziemy musieli wtedy kolejno włączać odpowiednie wyświetlacze. Spróbujmy napisać taki maleńki frag-
Przenicś kursor zaraz za klamerką wyznaczającą początek naszej funkcji, jeszcze b przed komendą return. Propozycję całej funkcji main przedstawia listing 6. Dodatkowo C w zrozumieniu kodu pomocny może być rysunek 19 - przedstawiający przyporządkowanie nazw segmentów — dla sterowanego wyświetlacza. Warto zerknąć do ramki opisującej zasadę przeprowadzania obliczeń w C oraz do wkładki zawierającej opis najpotrzebniejszych nam operatorów.
Spróbujmy zanalizować, jak powinien działać przedstawiony kod. Na początku, w części oznaczonej jako inicjacja, następuje ustawienie odpowiednich portów jako wyjścia. Zgodnie z dokumentacją procesora AVR wyprowadzenie portu jest traktowane jako wyjście w chwili, gdy na odpowiadającej mu pozycji w rejestrze DDRx znajduje się jedynka, Pierwsza linia nie powinna już budzić wątpliwości. Jedyną nowością jest to, że zamiast odwoływać się do odpowiednich rejestrów bezpośrednio, korzystamy ze zdefiniowanych wcześniej nazw. Bardziej skomplikowana wydawać się może linia następna. Aby ją zrozumieć, konieczne jest poznanie występujących tutaj operatorów. Operator « daje w wyniku liczbę, która znajduje się po jego lewej stronie, przesuniętą logicznie w lewo o podaną liczbę bitów, która musi znaleźć się po stronie prawej operatora. Zapis taki, jak widzisz, jest często stosowany tam, gdzie chcemy mieć kontrolę nad poszczególnymi bitami.
ABC... C
Przeprowadzanie obliczeń
Jak w (chyba) każdym języku wysokiego poziomu, obliczenia w C przeprowadza się, korzystając ze zbioru operatorów. Nie ma tutaj jednak ograniczenia mówiącego na przykład, że można wykonywać tylko jedno działanie naraz. Tylko jeśli napiszemy bardzo złożone wyrażenie arytmetyczne, zostaniemy poproszeni przez kompilator o rozbicie go na prostsze. Osobiście jednak nie pamiętam, aby taka sytuacja w ogóle kiedykolwiek mi się przytrafiła.
Równania zapisujemy, powiedziałbym, w sposób naturalny. Na przykład czysto teoretycznie wyobraźmy sobie, że chcemy wartość jakiejś zmiennej a pomnożyć przez dwa i dodać do niej 1. Kod w takim przypadku wyglądałby następująco:
a=2*a+ 1;
Całkowicie równoważny jest także kod: a=l + 2*a;
Okazuje się, że w C każdy operator ma swoją wagę. Operatory są wykonywane w określonej przez nią kolejności. Gdy stosujemy typowe operacje znane jeszcze ze szkoły podstawowej — możemy śmiało założyć, że działania zostaną wykonane dokładnie tak jak byliśmy tego nauczeni. Problemy zaczynają się dla operatorów, które praktycznie poza programowaniem nie mają swoich odpowiedników.
W dodatku do tego odcinka znajdziesz tabelę skrótowo omawiającą najpotrzebniejsze nam operatory. Znajdziesz także zestawienie pokazujące kolejność wykonywania operatorów. Jeśli jakieś operatory znajdują się na tym samym poziomie, obliczenia są przeprowadzane od lewej do prawej - znowu zgodnie z intuicją.
Co jednak, jeśli chcemy na przykład najpierw dodawać a później mnożyć? Znów moż.emy zastosować szkolną wiedzę: służą do tego zwykle nawiasy. Przykładowo:
a = 2 * (a + J) ;
spowoduje przemnożenie przez 2 poprzedniej wartości zmiennej, a powiększonej o 1.
Warto tutaj zauważyć, żc operator przypisania, taki jak znak równości, ma najniższy piorytet. Oznacza to, że zawsze najpierw wartość będzie obliczona, a dopiero później zapisana we wskazane miejsce (także i tutaj można stosować nawiasy w celu zakłócenia tej kolejności... jednak to już zupełnie inna historia).
Nie bój się obliczanych stałych
Warto wiedzieć, że nawet jeśli optymalizacja kodu zostanie wyłączona, standard ANSI C nakazuje, aby wszystkie stale zostały wyliczone przez kompilator.
Oznacza to, że jeśli to wygodne lub bardziej czytelne, możesz podać kompilatorowi cate długie równanie ze stałych. Ostatecznie w kodzie asemblerowym pojawi się tylko jedna liczba.
40 Czerwiec 2005 Elektronika dla Wszystkich