kursC czesc010

background image

38

Programowanie

Elektronika dla Wszystkich

Kontynuujemy dziœ rozwijanie naszej

zaawansowanej obs³ugi wyœwietlacza alfanu-

merycznego. Stworzymy interfejs umo¿liwia-

j¹cy obs³ugê wielu jêzyków jednoczeœnie.

Przy tej okazji poznamy tablicê wielowymia-

row¹ oraz typ wyliczeniowy. Poka¿ê, w jaki

sposób umieœciæ w pamiêci programu tablicê

wskazuj¹ca na napisy w pamiêci programu –

ta konstrukcja wbrew pozorom nie jest tak

bardzo oczywista.

Projekt multilang

Dzisiejszy program bêdzie korzysta³ z wiêk-

szoœci plików, które zosta³y utworzone w po-

przedniej czêœci. Utwórz nowy katalog i sko-

piuj do niego pliki lcd.h, lcd.c, makra.h oraz

harddef.h z czêœci 9. Mo¿esz skopiowaæ tak¿e

dotychczasowy makefile, jednak z plików

Ÿród³owych usuwamy local.c, a polu TAR-

GET przypisujemy multilang (nazwê nowego

projektu).

W katalogu projektu utwórz nowy folder

o nazwie lang. Umieœcimy w nim pliki defi-

niuj¹ce wszystkie nasze jêzyki. Proponujê

dodanie utworzonego katalogu do projektu

Programmers Notepada jako „Magic Folder”.

Folder taki odwzorowuje rzeczywist¹ zawar-

toϾ katalogu. Klikamy prawym klawiszem na

ikonie projektu w oknie Projects i z pojawia-

j¹cego siê menu wybieramy Add Magic

Folder. Uruchomiony zostanie kreator, który

PP

PP

rr

rr

oo

oo

gg

gg

rr

rr

aa

aa

m

m

m

m

oo

oo

w

w

w

w

aa

aa

nn

nn

ii

ii

ee

ee

pp

pp

rr

rr

oo

oo

cc

cc

ee

ee

ss

ss

oo

oo

rr

rr

óó

óó

w

w

w

w

w

w

w

w

jj

jj

êê

êê

zz

zz

yy

yy

kk

kk

uu

uu

CC

CC

czêœæ 10

Idea rozwi¹zania.

Obs³uga wielu jêzyków

Myœl¹ przewodni¹ tworzonego dziœ wielojêzycznego

interfejsu jest oddzielenie wszystkich napisów od

reszty aplikacji. Interfejs powinien byæ napisany

w taki sposób, aby wybór jêzyka nastêpowa³ tylko

w jednym miejscu (na przyk³ad odpowiednia pozycja

menu) i tylko w tym miejscu musimy zainteresowaæ

siê tym, ile jêzyków zosta³o „zainstalo-

wanych”, jakie to s¹ jêzyki i jakie s¹ ich

nazwy. Ca³a reszta programu powinna

dzia³aæ niezale¿nie od wybranego jêzy-

ka i nie bêdzie nawet „wiedzia³a”, jaki

jêzyk zosta³ wybrany.

Ideê rozwi¹zania pokazuje rysunek w

ramce. Dla czytelnoœci nie umieœci³em

tutaj tablicy zawieraj¹cej informacje o

znakach specjalnych. Napisy nadal

umieszczamy w pamiêci programu.

Jednak teraz, w stosunku do prostej

wersji jednojêzycznej, stworzymy tab-

licê dwuwymiarow¹, która bêdzie

zawieraæ wskaŸniki na wszystkie ³añcu-

chy. Takie rozwi¹zanie zajmuje dodat-

kowo po 2 bajty na ka¿dy napis (szes-

nastobitowy wskaŸnik). Z jednej strony

zwiêksza to zajêtoœæ pamiêci, jednak z drugiej umo¿-

liwia szybkie wyszukanie interesuj¹cego nas tekstu.

Nie mo¿emy umieszczaæ tekstów bezpoœrednio w tab-

licy, poniewa¿ ka¿dy napis mo¿e mieæ inn¹ d³ugoœæ.

Funkcje korzystaj¹ce z napisów bêd¹ pos³ugiwaæ

siê ich identyfikatorem, czyli indeksem w tablicy.

Naszym zadaniem bêdzie tak¿e takie zdefiniowanie

identyfikatorów, aby ich numeracja by³a tworzona

automatycznie.

ABC... C

Tablice wielowymiarowe

W jêzyku C tablice wielowymiarowe s¹ tworzone

w pewien specyficzny sposób. Od strony logicznej

taka tablica jest tablic¹ sk³adaj¹c¹ siê z tablic. Wi¹¿e

siê z tym okreœlony sposób tworzenia oraz obs³ugi.

Przyk³ad utworzenia dwuwymiarowej tablicy:

cchhaarr

tablica[[

2

]][[

3

]];;

Do poszczególnych elementów dostajemy siê

nastêpuj¹co:

tablica[[a]][[b]] ==

0

;;

Nie tak jak ma to miejsce na przyk³ad w Paskalu:

tablica[[a,, b]] ==

0

;;

//le!

Fizycznie, w pamiêci, elementy roz³o¿one s¹ tak

jak pokazuje rysunek w ramce. Ta wiedza pozwala

nam obliczyæ pozycjê w pamiêci danego elementu.

Jest to wa¿ne, jeœli chcemy dostaæ siê do tablicy za

pomoc¹ wskaŸnika. Dla naszej tablicy:

Przesuniêcie = a*3 + b

Tablicê do funkcji mo¿emy przekazaæ na dwa

sposoby. Pierwszy z nich to poprzez wskaŸnik:

vvooiidd

ZerujP((

cchhaarr

** pt,, uint16_t ilosc))

{

ffoorr

((;; ilosc>>

0

;; ilosc——))

**((pt++++)) ==

0

;;

}}

Przyk³ad wywo³ania takiej funkcji dla naszej tablicy:

ZerujP((((

cchhaarr

**))tablica,,

6

));;

Drugi sposób wymaga jawnego podania rozmiaru

tablicy:

vvooiidd

Zeruj((

cchhaarr

t[[

2

]][[

3

]]))

{

uint16_t a,,b;;

ffoorr

((a==

0

;; a<<

2

;; a++++))

ffoorr

((b==

0

;; b<<

3

;; b++++))

t[[a]][[b]] ==

0

;;

}}

Zauwa¿, ¿e aby obliczyæ pozycjê elementu w pa-

miêci, kompilator nie potrzebuje informacji o rozmia-

rze najwy¿szego wymiaru (2). Rzeczywiœcie okazuje

siê, ¿e mo¿na go pomin¹æ przy podawaniu listy argu-

mentów funkcji:

vvooiidd

Zeruj((

cchhaarr

t[[]][[

3

]]))......

Wywo³anie naszej funkcji zeruj¹cej przebiega³oby

teraz nastêpuj¹co:

Zeruj((tablica));;

Co bardzo wa¿ne, do funkcji zostanie przes³any

jedynie wskaŸnik – nie kopia ca³ej tablicy.

Aby zainicjowaæ wstêpnie tablicê wielowymiaro-

w¹, poszczególne wiersze grupujemy za pomoc¹

nawiasów klamrowych. Powstaje doœæ intuicyjny zapis:

cchhaarr

tablica[[

2

]][[

3

]] ==

{

{

‘a’

,,

‘b’

,,

‘c’

},,

{

‘A’

,,

‘B’

,,

‘C’

}

};;

Ponownie naj³atwiej powy¿szy zapis zrozumieæ,

odnosz¹c siê do naszego stwierdzenia, ¿e mamy

do czynienia z tablic¹ tablic. Tablica dwuelementowa

zawiera dane inicjuj¹ce ka¿d¹ z jej sk³adowych, tróje-

lementowych tablic. Rozumuj¹c w identyczny sposób,

mo¿na inicjowaæ tablice o dowolnej liczbie wymiarów.

Na koniec warto wspomnieæ o pewnym drobiazgu.

Korzystaliœmy ju¿ z tablic, których rozmiar by³ wyli-

czany automatycznie przez kompilator. W przypadku

tablic wielowymiarowych automatycznie obliczony

mo¿e byæ jedynie wymiar najwy¿szy:

cchhaarr

tablica[[]][[]] ==

//le

((......))

cchhaarr

tablica[[]][[

3

]] ==

//Dobrze

((......))

Przyk³ad: listing 97

background image

zapyta nas o wskazanie folderu, który zosta-

nie odwzorowany. W nastêpnym kroku zosta-

niemy poproszeni o podanie typu plików,

jakie maj¹ byæ brane pod uwagê – wpisujemy

*.h. Reszt¹ opcji mo¿emy siê nie przejmowaæ

i zostawiæ ich wartoœci domyœlne.

Stwórzmy od razu pliki jêzyków. Bêd¹ to

doœæ nietypowe pliki nag³ówkowe, poniewa¿

bêdziemy tworzyæ w nich zmienne napisowe.

Wa¿ne jest, aby nag³ówki te do³¹czaæ tylko

w jednym miejscu ca³ego programu. Z tego

powodu te¿ mo¿emy pomin¹æ pisanie sek-

wencji #ifndef #endif.

Plik lang/polski.h powsta³ poprzez skopio-

wanie zawartoœci pliku local.c z poprzedniej

czêœci. Zmienimy jedynie tworzone napisy

a nazwy zmiennych zmodyfikujemy tak, aby

zaczyna³y siê od liter PL_. Pokazuje to listing

94. Plik jêzyka angielskiego bêdzie jeszcze

prostszy, poniewa¿ jêzyk ten nie wymaga

definicji znaków specjalnych – zobacz

listing 95.

Zarz¹dzanie jêzykami

Stwórzmy teraz modu³ odpowiedzialny za

zarz¹dzanie jêzykami. Utwórz dwa nowe

pliki: langsys.c oraz langsys.h i do-

daj je do projektu. Przypominam

o wpisaniu pliku langsys.c do pliku

makefile.

Listing 96 pokazuje niezbêdne

nam nag³ówki. Zauwa¿, ¿e nie do³¹-

czamy tutaj pliku <avr/io.h>. Modu³

langsys nie zajmuje siê sprzêtem, dla-

tego te¿ plik ten nie jest potrzebny. Do

pliku langsys.c do³¹czamy pliki

wszystkich jêzyków i co bardzo wa¿ne

– robimy to tylko tutaj.

Zaraz po do³¹czeniu nag³ówków

tworzymy wszystkie potrzebne zmien-

ne oraz tablice zawieraj¹ce dane –

pokazuje to listing 97. Pojawia siê

w tym miejscu po raz pierwszy tablica

wielowymiarowa, o której mo¿esz

przeczytaæ w odpowiedniej ramce.

Zwracam Twoja uwagê na wybrane

przypisanie wymiarów: wy-

miar najwy¿szy to wymiar

jêzyka, wymiar ni¿szy to

kolejne teksty. Gdybyœmy

wybrali odwrotn¹ konwen-

cjê, rozmiar tablicy móg³by

dostosowywaæ siê automa-

tycznie do iloœci napisów.

Jednak inicjowanie takiej tablicy wymaga³o-

by wiêcej pisania: w naszym przypadku

nawiasy klamrowe obejmuj¹ wszystkie teksty

danego jêzyka; w przypadku odwrócenia

indeksów, obok siebie pisalibyœmy te same

napisy dla ró¿nych jêzyków i konieczne by³o-

by ka¿dorazowe ich objêcie klamrami. Rze-

czywiœcie dla tabeli 2x2 nie widaæ ró¿nicy,

jednak zwykle w programie ró¿nych tekstów

39

Programowanie

Elektronika dla Wszystkich

ABC... C

Tablica wskaŸników

w pamiêci programu

na napisy w pamiêci programu

Dziœ mamy zamiar korzystaæ z tablicy zawieraj¹cej

wskaŸniki na wszystkie wykorzystywane napisy.

ANSI-C daje nam proste narzêdzie do tworzenia

takich tablic:

cchhaarr

** strTab[[]] ==

{

„Napis 1”

,,

„Napis 2”

,,

}};;

Kompilator umieszcza w pamiêci napisy, a w tab-

licy wskaŸniki na nie. Pytanie jednak: jaka to bêdzie

pamiêæ? Mamy do czynienia z problemem, na który

natknêliœmy siê kilkukrotnie – ANSI-C zak³ada jed-

nolit¹ przestrzeñ adresow¹. W naszym przypadku

dane zostan¹ umieszczone w pamiêci RAM. Trafi tam

zarówno tablica wskaŸników, jak i same ³añcuchy.

Niewiele pomo¿e poni¿sza sztuczka:

prog_char** strTab[[]] PROGMEM ==

((......))

Tablica co prawda znajdzie siê w pamiêci progra-

mu, jednak napisy umieszczone zostan¹ w pamiêci

danych. Pomijaj¹c wszystkie mo¿liwoœci które nie

dzia³aj¹ (plik z propozycj¹ eksperymentów znajdzie

siê w materia³ach dodatkowych), przedstawiam

rozwi¹zanie przyjmowane przez AVR-GCC:

prog_char napis1[[]]==

„Napis 1”

;;

prog_char napis2[[]]==

„Napis 2”

;;

ccoonnsstt

prog_char** strTab[[]]

PROGMEM ==

{

napis1,, napis2

};;

Sztuczka polega na utworzeniu oddzielnie napi-

sów w pamiêci programu, a nastêpnie wprowadzenie

do tablicy wskaŸników na odpowiednie „zmienne”.

Dziêki operatorowi const pozbêdziemy siê ostrze-

¿enia podczas kompilacji, mówi¹cego, ¿e inicjujemy

tabelê niekompatybilnymi elementami. Elementy

w pamiêci programu domyœlnie s¹ typu const. Opis,

co to oznacza, znajduje siê w oddzielnej ramce.

Przyk³ad: listing 97

Listing 95 – plik lang/english.h

// Teksty

prog_char EN_strDisplay[[]] ==

„English”

;;

prog_char EN_strStart[[]] ==

„Welcome in

English Version”

;;

Listing 98 – funkcje manipuluj¹ce jêzykiem w local.c

// Iloœæ „zainstalowanych“ jêzyków

iinnlliinnee

uint8_t langsys_GetNumOfLangs((

vvooiidd

))

{{

rreettuurrnn

ELEMS((langsys_strTable));;

}}

// Wybór jêzyka

iinnlliinnee vvooiidd

langsys_Select((uint8_t index))

{{

langsys_sel == index;;

}}

// Pobranie informacji o wybranym jêzyku

iinnlliinnee

uint8_t langsys_GetSelected((

vvooiidd

))

{{

rreettuurrnn

langsys_sel;;

}}

// Pobranie nazwy jêzyka o podanym indeksie

prog_char** langsys_GetLangName((uint8_t index))

{{

iiff

((index >> ELEMS((langsys_strTable))))

rreettuurrnn

NULL;;

rreettuurrnn

((prog_char**))pgm_read_word_near((

&&langsys_strTable[[index]][[IDS_LANGNAME]]));;

}}

Listing 96 – nag³ówki pliku langsys.c

#include <inttypes.h>

#include <avr/pgmspace.h>

#include „makra.h”

#include „lcd.h”

#include „langsys.h”

// Tutaj nag³ówki jêzyków

#include „lang/english.h”

#include „lang/polski.h”

Listing 99 – funkcje podaj¹ce dane dla wybranego jêzyka (langsys.c)

// Pobieranie wskaŸnika na informacjê o znakach specjalnych

LCD_LOCAL_PGM** langsys_GetSpec((

vvooiidd

))

{{

rreettuurrnn

((LCD_LOCAL_PGM**))pgm_read_word_near((&&langsys_lcdspec[[langsys_sel]]));;

}}

// Pobieranie wybranego napisu

prog_char** langsys_GetText((uint8_t index))

{{

rreettuurrnn

((prog_char**))pgm_read_word_near((&&langsys_strTable[[langsys_sel]][[index]]));;

}}

Listing 97 – zmienne i tablice w local.c

ssttaattiicc

uint8_t langsys_sel ==

0

;;

// wybrany jêzyk

// Definicja informacji o znakach specjalnych

ccoonnsstt

LCD_LOCAL_PGM** langsys_lcdspec[[]] ==

{

NULL,,

PL_lcdspec

};;

// Tablica wszystkich posiadanych napisów

// [jêzyk][tekst]

ccoonnsstt

prog_char** langsys_strTable[[

2

]][[

2

]] PROGMEM ==

{

{

EN_strDisplay,,

EN_strStart

},,

{

PL_strDisplay,,

PL_strStart

}

};;

Listing 94 – plik lang/polski.h

LCD_LOCAL_PGM PL_lcdspec[[

18

]] ==

{

((......))

}};;

// Teksty

prog_char PL_strDisplay[[]] ==

„Polski”

;;

prog_char PL_strStart[[]] ==

„Witaj w wersji Polskiej”

;;

background image

40

Programowanie

Elektronika dla Wszystkich

ABC... C

Typ wyliczeniowy – enum

Przyk³adowa sk³adnia przy tworzeniu typu wylicze-

niowego jest nastêpuj¹ca:

eennuumm

DniTygodnia

{

PONIEDZIALEK,, WTOREK,,

SRODA,, CZWARTEK,, PIATEK,,

SOBOTA==

0x10

,, NIEDZIELA

}dt;;

W tym przypadku tworzymy nowe wyliczenie

o nazwie DniTygodnia oraz tworzymy now¹ zmienn¹

o nazwie dt. Jeœli pominiemy tworzenie zmiennej,

mo¿emy zrobiæ to póŸniej:

eennuumm

DniTygodnia dt;;

Zmienna typu wyliczeniowego to najmniejsza

zmienna typu ca³kowitego, która jest w stanie pomieœ-

ciæ wszystkie wyliczone wartoœci.

Nazwy wystêpuj¹ce w poszczególnych wylicze-

niach musz¹ byæ identyfikatorami w rozumieniu C,

a wiêc nie mog¹ zawieraæ znaków specjalnych i nie

mog¹ zaczynaæ siê od cyfry.

W ró¿nych wyliczeniach nazwy nie mog¹ siê po-

wtarzaæ. Przypisanie identyfikatora do liczby ma taki

sam zasiêg, jakby by³o tworzone za pomoc¹ #define.

Jeœli w wyliczeniu nie podamy jawnie ¿adnych

wartoœci, zostan¹ im przypisane kolejne liczby ca³ko-

wite poczynaj¹c od 0. Jeœli natomiast w którymœ

momencie wartoœæ zostanie podana, nastêpny, niepo-

dany jawnie identyfikator otrzymuje wartoϾ o 1

wiêksz¹.

Wartoœci w wyliczeniu mog¹ siê powtarzaæ:

eennuumm

LICZBY

{

JEDEN==

1

,,

JEDYNKA==

1

,,

PIERWSZY==

1

};;

Tworz¹c typ wyliczeniowy, nie musimy ani tworzyæ

zmiennej, ani umo¿liwiaæ jej przysz³ego utworzenia:

eennuumm

{ bn1,, bn2 };;

W powy¿szym przypadku jedynie identyfikato-

rom bn1 oraz bn2 przyporz¹dkowywane s¹ wartoœci

0 oraz 1.

Przewag¹ takiego zapisu w stosunku do wyko-

rzystania s³owa #define jest automatyczne generowa-

nie kolejnych wartoœci przez kompilator.

Uwaga: AVR-GCC nie sprawdza, czy do zmiennej

wyliczeniowej wpisywana jest odpowiednia wartoϾ

(z zakresu wyliczonego w klamrach). Mo¿na powie-

dzieæ, ¿e odpowiednia zmienna oraz identyfikatory

tworzone s¹ oddzielnie.

Dodatkow¹ zalet¹ zmiennych wyliczeniowych

jest to, ¿e AVRStudio pokazuje wartoœci takiej zmien-

nej symbolicznie.

Przyk³ad: listing 100

ABC... C

Kwalifikator typu const

Kwalifikator const (sta³y) zastosowany podczas dek-

laracji zmiennej mówi nam, ¿e jej wartoœæ nie bêdzie

nigdy zmieniana. Kompilator zg³osi b³¹d, jeœli

bêdziemy próbowali zmieniæ jej wartoœæ.

Oznaczenie tablicy kwalifikatorem const ozna-

cza, ¿e ¿aden z jej elementów nie bêdzie modyfiko-

wany.

S³owo const u¿yte do wskaŸnika w liœcie para-

metrów funkcji oznacza, ¿e funkcja nie bêdzie

modyfikowaæ zmiennej przez niego wskazywanej.

Czasami jest to wa¿ne, poniewa¿ chcemy mieæ pew-

noœæ, ¿e tak jest w istocie. Pomaga to tak¿e kompila-

torowi w optymalizacji kodu.

Uwaga: jeœli zmienna oznaczona jako const

fizycznie znajduje siê w pamiêci RAM, tak napraw-

dê mo¿e byæ zmieniona. Mo¿na to uczyniæ na przy-

k³ad poprzez wskaŸnik oraz jego rzutowanie na typ

bez kwalifikatora const:

ccoonnsstt iinntt

wartosc ==

100

;;

((......))

**((((

iinntt

**))&&wartosc)) ==

1

;;

O ile w AVR-GCC to dzia³a, jednak rzeczywisty

efekt takiego zagrania nie jest okreœlony przez normê

ANSI. Takich sztuczek nie powinno siê stosowaæ –

w za³o¿eniu oznaczenie, ¿e zmienna nie powinna

byæ modyfikowana, ma swój sens.

Przyk³ad: listing 97

Ci¹g dalszy ze strony 25.

W sk³ad podstaw wchodz¹ równie¿ zagad-

nienia zwi¹zane z uk³adami analogowymi.

Prezentowane s¹ podstawowe uk³ady elektro-

niczne, takie jak wzmacniacze, generatory,

scalone uk³ady analogowe, zasilacze, stabili-

zatory, przetworniki A/C oraz C/A, modulato-

ry i demodulatory. Wymienione zagadnienia

omawiane s¹ od strony zastosowañ, zasady

dzia³ania oraz zasad projektowania. T³uma-

czone s¹ równie¿ zagadnienia zwi¹zane z tech-

nik¹ cyfrow¹ z uwzglêdnieniem budowy, dzia-

³ania oraz zastosowania uk³adów cyfrowych

(materia³ obejmuje bramki, liczniki, rejestry,

uk³ady GAL, pamiêci pó³przewodnikowe itd.).

Urz¹dzenia elektroniczne to drugi stopieñ

wtajemniczenia, na lekcjach zwi¹zanych

z tym blokiem przedmiotowym przedstawia-

ne s¹ zasady dzia³ania i obs³ugi: urz¹dzeñ

elektroakustycznych, odbiorników radiowych

i telewizyjnych, urz¹dzeñ s³u¿¹cych do

odczytu i zapisu informacji, telewizji kablo-

wej i satelitarnej itd. Ale to nie wszystko.

Uczniowie w ramach zajêæ poznaj¹ równie¿

budowê i zasadê dzia³ania systemów pomia-

rowych oraz nowoczesnych przyrz¹dów

pomiarowych. Uzupe³nienie wiedzy stanowi¹

zagadnienia zwi¹zane z urz¹dzeniami auto-

matyki (uk³ady wykonawcze, sygnalizatory,

regulatory, czujniki itp.)

Pomiary elektroniczne stanowi¹ najwa¿-

niejszy element kszta³cenia elektronika,

na tych zajêciach nastêpuje prze³o¿enie wie-

dzy teoretycznej na praktyczne umiejêtnoœci.

W sk³ad pomiarów wchodz¹ cztery laboratoria

(elektryczne, elektroniki analogowej i cyfro-

wej, uk³adów mikroprocesorowych, urz¹dzeñ

elektronicznych) poznawane w kolejnych

latach nauki. Zajêcia obejmuj¹ projektowanie

i badanie elementów, uk³adów i urz¹dzeñ

elektronicznych. Wyposa¿enie obejmuje

m.in. zasilacze napiêcia sta³ego, generatory

funkcyjne, mierniki analogowe i cyfrowe,

mostki RLC, dydaktyczne systemy mikropro-

cesorowe oraz ca³¹ masê modeli, makiet oraz

elementów przeznaczonych do diagnozowa-

nia i badania. Obecnie standardem staje siê

równie¿ komputer pomagaj¹cy w interpretacji

otrzymanych wyników pomiarowych. Do-

k³adny wykaz wszystkich nauczanych zagad-

nieñ, umiejêtnoœci oraz wymaganego wyposa-

¿enia dostêpny jest na stronie MENiS

(www.menis.gov.pl/ksztzaw/strategia/strateg-

ia.php

)

. W technikum mamy równie¿ prakty-

kê zawodow¹, ale na ten temat by³a ju¿ mowa.

Przedmiotów zawodowych, jakie obo-

wi¹zuj¹ ucznia technikum, jest sporo, jedne

³atwiejsze, drugie trudniejsze. Czêœæ osób

preferuje przedmioty teoretyczne, czêœæ prak-

tyczne, wszystko jest kwesti¹ indywidualnych

predyspozycji. Czy mo¿na te wszystkie

zagadnienia i umiejêtnoœci mo¿na opanowaæ

samodzielnie w zaciszu domowym w rozs¹d-

nym czasie? OdpowiedŸ na to pytanie pozo-

stawiam pod os¹d czytelnika.

Co wybraæ?

Nie by³o moim zamiarem wbrew pozorom

gloryfikowaæ technikum elektronicznego, sta-

ra³em siê jedynie w sposób doœæ obiektywny

przedstawiæ jego cele, zadania i umiejêtnoœci,

jakie przekazuje swoim absolwentom. Wybór,

jak¹ drogê rozwoju zawodowego wybraæ,

przygotowuj¹c siê do studiowania elektronik

poprzez technikum czy liceum, musi nale¿eæ

do samych zainteresowanych – w tym

momencie absolwentów gimnazjum.

Na zakoñczenie ju¿ ca³kiem subiektyw-

nie. Swoj¹ edukacjê techniczn¹ rozpocz¹³em

w technikum elektrycznym, kontynuuj¹c j¹ na

studiach technicznych (kierunek elektronika

i telekomunikacja). Przedmioty zawodowe

z technikum bardzo siê przyda³y i brak³o do

pe³ni szczêœcia niestety kilku z zakresu elek-

troniki. Z matematyk¹ by³o trudno, ale mo¿na

siê by³o nauczyæ, natomiast bardzo du¿o

absolwentów liceum odpad³o na przedmio-

tach zawodowych, choæ byli i tacy, którzy

radzili sobie doskonale. Obecnie prowadzê

zajêcia m.in. w technikum elektronicznym

i uwa¿am, ¿e pomimo ca³ej masy problemów

zwi¹zanych z podrêcznikami, wyposa¿eniem

oraz ogromem ciê¿kiej pracy, jak¹ musi siê

w³o¿yæ w naukê, oraz trudnego egzaminu

zawodowego jest to szko³a warta polecenia.

Szko³y techniczne maj¹ w sobie jeszcze „to

coœ”, bli¿ej nieokreœlone, ale ucz¹ce wspó³-

pracy i zaufania do kolegów, posiadaj¹ swois-

ty klimat niedostêpny w innych szko³ach.

Z perspektywy czasu i pomimo ci¹gle istnie-

j¹cych niedoskona³oœci jeszcze raz polecam

technikum jako w³aœciwy wybór dla osób

zajmuj¹cych siê elektronik¹.

Piotr Brzózka

pbrzozka@elportal.pl

background image

jest wiêcej ni¿ jêzyków. Problemem automa-

tycznej inicjacji rozmiaru tablicy jeszcze siê

zajmiemy.

Zgodnie z ramk¹ o idei naszego programu,

utworzymy teraz funkcje manipuluj¹ce jêzy-

kiem. Spójrz na listing 98. Zwróæ szczególn¹

uwagê na sposób odczytu danych z naszej tab-

licy w funkcji langsys_GetLangName. Dostêp

wygl¹da doœæ skomplikowanie – niestety nie

mo¿emy czytaæ bezpoœrednio danych z tabli-

cy umieszczonej w pamiêci programu. Makro

pgm_read_word_near odczytuje szesnastobi-

tow¹ liczbê z pamiêci programu. Poniewa¿

my wiemy, ¿e w tym miejscu znajduje siê

wskaŸnik i wskaŸnika oczekujemy, musimy

wykonaæ rzutowanie, aby odczytana wartoœæ

mog³a byæ prawid³owo wykorzystana. Ko-

rzystaj¹c z makra pgm_read_word_near

zak³adamy jednoczeœnie, ¿e nasze wskaŸniki

nie maj¹ wiêcej ni¿ 16 bitów – zwykle bêdzie

to prawd¹. Nale¿a³oby jedynie zweryfikowaæ

poprawnoœæ tego za³o¿enia w przypadku wy-

korzystania procesora o pamiêci programu

wiêkszej ni¿ 64KB przy wykorzystaniu bar-

dzo du¿ej iloœci sta³ych.

Ostatnie dwie funkcje modu³u langsys

pokazuje listing 99. Zwracaj¹ one wskaŸnik

na znaki specjalne (jeœli w danym jêzyku

znaki specjalne nie wystêpuj¹, zwracana jest

wartoϾ NULL) oraz tekst o podanym identy-

fikatorze.

W tym miejscu plik Ÿród³owy mamy

zakoñczony. Pozostaje nam jeszcze stworze-

nie pliku nag³ówkowego widocznego na lis-

tingu 100. Umieszczamy w nim deklaracje

funkcji oraz symboliczne oznaczenia identy-

fikatorów. Identyfikatory moglibyœmy two-

rzyæ za pomoc¹ poznanych do tej pory

dyrektyw #define. Jednak dla wiêkszej iloœ-

ci napisów dzia³anie takie jest doœæ uci¹¿li-

we i ³atwo o pomy³kê. Zamiast tego wyko-

rzystamy dziœ typ wyliczeniowy (ramka).

Dziêki temu, jeœli tylko zachowamy kolej-

noœæ identyfikatorów zgodn¹ z kolejnoœci¹

zapisania wskaŸników w tablicy, identyfika-

tory zostan¹ prawid³owo wygenerowane

automatycznie. Przy identyfikatorach napi-

sów przyjmijmy zasadê, aby zaczynaæ je

zawsze du¿ymi literami IDS_ a reszta

nazwy zgodna by³a z nazw¹ zmiennej ³añcu-

chowej bez liter EN/PL_str.

U³atwi to orientacjê w ko-

dzie oraz tworzenie wyli-

czenia – wystarczy skopio-

waæ dane inicjuj¹ce jeden

jêzyk w tablicy langsys_str

Table i dokonaæ zmian

komend¹ Edit->Replace.

Wyj¹tkiem od tej regu³y s¹

identyfikatory o specjalnym

znaczeniu, pisane w ca³oœci

du¿ymi literami (aktualnie

jedynie IDS_LANGNAME).

Dostosowanie modu³u lcd

Modu³ lcd, z którego chcemy korzystaæ,

zak³ada³ wystêpowanie w programie modu³u

local i korzysta³ z umieszczonej w nim infor-

macji o znakach specjalnych. W stosunku do

tej sytuacji pojawiaj¹ siê aktualnie trzy ró¿nice:

1. Nie ma modu³u local.

2. Nie ma mo¿liwoœci bezpoœredniego dostê-

pu do tablicy z danymi lokalnymi – nale¿y

korzystaæ z funkcji langsys_GetSpec.

3. Istnieje mo¿liwoœæ, ¿e tablica danych lokal-

nych dla danego jêzyka nie istnieje – nale¿y

zabezpieczyæ siê przed prób¹ czytania spod

adresu NULL.

Otwórz poprzednio napisany plik lcd.c

i korzystaj¹c z komendy Edit->Find, znajdŸ

wszystkie wyst¹pienia s³owa local_. Zauwa-

¿ysz, ¿e ze wspomnianego modu³u korzysta-

my jedynie w dwóch miejscach. Nie bêdzie-

my wiêc mieli du¿o pracy.

W pierwszej kolejnoœci do³¹czenie #include

„local.h” zmieniamy na #include „langsys.h”.

Resztê koniecznych zmian mo¿esz zobaczyæ

na listingach 101 oraz 102. Dodane lub zmo-

dyfikowane fragmenty oznaczam kolorem

Test

Wszystkie modu³y s¹ ju¿ gotowe, pora na ich

po³¹czenie oraz napisanie

funkcji g³ównej programu.

Utwórz plik main.c. Jeœli

korzystasz z makefile z po-

przedniej czêœci, powinien on

byæ ju¿ tam dodany. Propo-

zycjê prostego testu przedsta-

wia listing 103. Funkcja

obs³ugi przycisków zosta³a

45

Programowanie

Elektronika dla Wszystkich

ABC... C

Funkcja traktowana jak zmienna

Rozmawialiœmy ju¿ o tym, jak wygodn¹ w³asnoœci¹ C

jest mo¿liwoœæ wykonywania z³o¿onych obliczeñ

w jednej linii. Do wygody tej przyczynia siê tak¿e

mo¿liwoœæ traktowania jako zmiennej ka¿dej funkcji,

która zwraca jak¹œ wartoœæ. Co zrozumia³e, bêdzie to

zmienna tylko do odczytu. Kompilator zawsze zadba

o to, aby potrzebne funkcje zosta³y wywo³ane we

w³aœciwym momencie, a zwracana przez nie wartoœæ

wziêta do obliczeñ.

O ile sprawa wydaje siê oczywista w przypadku

prostych operacji arytmetycznych, zauwa¿, ¿e daje to

znacznie szersze mo¿liwoœci. Znakomitym przyk³a-

dem s¹ listingi 101 oraz 102. W tym miejscu funkcja

langsys_GetSpec zwraca wskaŸnik, który natychmiast

jest obs³ugiwany jako tablica. Mo¿na by skorzystaæ

ze zmiennej pomocniczej, do której zosta³aby zapisa-

na wartoœæ zwracana przez funkcje, a nastêpnie dosta-

walibyœmy siê do tablicy, tak jak robiliœmy to do tej

pory, jednak nie ma potrzeby rozbijania przedstawio-

nego dzia³ania.

Uwa¿aj: Standard nie definiuje w takim przypad-

ku, w jakiej kolejnoœci funkcje zostan¹ wywo³ane.

Mo¿e okazaæ siê nawet, ¿e pewne funkcje nie zostan¹

w ogóle wywo³ane, jeœli kompilator stwierdzi, ¿e ich

wartoœæ nie ma znaczenia dla wyniku obliczeñ. Mog¹

one byæ wywo³ywane zgodnie z kolejnoœci¹, w jakiej

wykonywane s¹ dzia³ania, ale mog¹ tak¿e byæ wywo-

³ane przed rozpoczêciem jakichkolwiek obliczeñ. Jeœli

zale¿y Ci na kolejnoœci wywo³ania funkcji, nie obej-

dzie siê bez zmiennych pomocniczych – konieczne

jest wtedy „rêczne” wywo³anie funkcji oraz zapisanie

ich wyników.

Przyk³ady: listingi 101 oraz 102

Listing 101 – modyfikacja funkcji w lcd_GetSpec

ssttaattiicc

uint8_t lcd_GetSpec((uint8_t s_index))

{{

// Zabezpieczenie

iiff

((langsys_GetSpec(()) ==== NULL))

rreettuurrnn

0x20

;;

((......))

// Nic nie znaleziono

rreettuurrnn

pgm_read_byte((&&((langsys_GetSpec(())[[s_index]]..cAlt))));;

}}

Listing 100 – plik langsys.h

#ifndef LANGSYS_H_INCLUDED

#define LANGSYS_H_INCLUDED

//_____________________________________________

// Funkcje interfejsu

iinnlliinnee

uint8_t langsys_GetNumOfLangs((

vvooiidd

));;

iinnlliinnee vvooiidd

langsys_Select((uint8_t index));;

iinnlliinnee

uint8_t langsys_GetSelected((

vvooiidd

));;

prog_char** langsys_GetLangName((uint8_t index));;

LCD_LOCAL_PGM** langsys_GetSpec((

vvooiidd

));;

prog_char** langsys_GetText((uint8_t index));;

//_____________________________________________

// Indeksy dla poszczególnych napisów

eennuumm

{

IDS_LANGNAME,,

IDS_Start

};;

#endif

//LANGSYS_H_INCLUDED

Listing 102 – modyfikacja funkcji w lcd_UpdateCGRAM

vvooiidd

lcd_UpdateCGRAM((

vvooiidd

))

{{

// Zabezpieczenie

iiff

((langsys_GetSpec(()) ==== NULL))

rreettuurrnn

;;

((......))

ffoorr

((a==

0

;; a<<ELEMS((lcd_spec));; a++++))

{

// 0xff oznacza koniec danych

iiff

((lcd_spec[[a]] ====

0xff

))

bbrreeaakk

;;

// WskaŸnik na pocz¹tek danych wygl¹du znaku

uint8_t** pdata == langsys_GetSpec(())[[lcd_spec[[a]]]]..matrix;;

((......))

}

}

background image

umieszczona bezpoœrednio tutaj – plik ten jest

plikiem „chwilowym”, s³u¿¹cym nam tylko

do testowania. W przysz³oœci przyciski bêd¹

obs³ugiwanie inaczej.

Zauwa¿ sposób dostêpu do napisów, gdy

jêzyk zosta³ ju¿ wybrany. Niezale¿nie od

dokonanego wyboru przywitanie wyœwietlane

jest w taki sam sposób. W tym prostym pro-

gramie nie widaæ jeszcze si³y tkwi¹cej w roz-

wi¹zaniu – napisów jest za ma³o, mamy jed-

nak pewnoœæ, ¿e nowy modu³ dzia³a.

U³atwienie tworzenia

tablicy wskaŸników

Przy okazji listingu 97 obieca³em, ¿e zajmie-

my siê problemem ustawiania rozmiaru tabli-

cy wskaŸników na napisy. O ile przy dwóch

ró¿nych tekstach nie ma problemu z rêcznym

podaniem ich iloœci, to przy rozwoju progra-

mu mo¿e to staæ siê doœæ uci¹¿liwe. Kompila-

tor nie obliczy dla nas rozmiaru innego ni¿

najwy¿szy wymiar tablicy. Mo¿emy jednak

wykorzystaæ fakt, ¿e wyliczamy indeksy

kolejnych wpisanych wartoœci. Niezbêdny

rozmiar tablicy to ostatni indeks + 1. Wpro-

wadŸmy dodatkowy identyfikator do wylicze-

nia zgodnie z listingiem 104. Umówmy siê

teraz, ¿e wszelkie nowe identyfikatory wpro-

wadzimy przed IDS_END. Identyfikator ten

jest równy wartoœci ostatniego identyfikatora

³añcucha + 1. Mo¿emy teraz wykorzystaæ go

do ustawienia rozmiaru naszej tablicy, tak jak

zosta³o to pokazane na listingu 105.

Inne modyfikacje

Aktualnym ograniczeniem iloœci napisów jest

wartoœæ 256. Wynika to z przyjêtego typu

parametru funkcji langsys_GetTekst. Jest

ma³o prawdopodobne, aby pojawi³a siê

potrzeba wykorzystania wiêkszej iloœci tek-

stów. Jeœli jednak taka potrzeba zaistnieje,

zawsze mo¿na zmieniæ typ parametru na war-

toœæ szesnastobitow¹ – zostanie to prawid³o-

wo obs³u¿one. Wiêksze wartoœci nie maj¹

sensu – przy wykorzystaniu ca³ego dostêpne-

go zakresu mo¿emy ca³kowicie zape³niæ

pamiêæ procesora Atmega128 sam¹ tylko tab-

lic¹ wskaŸników dla jednego jêzyka.

Podsumowanie

Dziœ utworzyliœmy kolejny element kojarzony

z zaawansowan¹ obs³ug¹ LCD. Wa¿ne jest jed-

nak to, ¿e modu³ ten jest bardzo uniwersalny –

podaje nam jedynie ³añcuch

zawieraj¹cy napis w danym

jêzyku. Mo¿e on równie dobrze

wspó³dzia³aæ z wyœwietlaczem

alfanumerycznym, graficznym

czy nawet s³u¿yæ do wyboru jêzyka komu-

nikacji poprzez port RS232.

Poznajemy jednoczeœnie coraz bardziej

zaawansowane elementy jêzyka C. Tablice

wielowymiarowe czy mo¿liwoœæ wykorzysta-

nia funkcji jak zmiennej jest czymœ co nie

pojawia³o siê w BASCOM-ie. Jeœli teraz spra-

wia Ci to k³opot, pamiêtaj, ¿e C nie zmusza

Ciê do korzystania z tych opcji. Jeœli chcesz,

mo¿esz korzystaæ ze zmiennych pomocni-

czych, a w pewnym momencie sam odkryjesz,

jak wygodnie jest zapisaæ algorytm bez nich.

W kolejnej czêœci korzystaj¹c z dotychcza-

sowego dzie³a, stworzymy modu³ umo¿liwia-

j¹cy proste tworzenie przewijalnego menu

z zagnie¿d¿aniem opcji. Na systemie menu

skoñczymy zaawansowan¹ obs³ugê LCD

alfanumerycznego.

Rados³aw Koppel

radoslaw.koppel@elportal.pl

46

Programowanie

Elektronika dla Wszystkich

Listing 104 – obliczenie iloœci napisów

eennuumm

{

IDS_LANGNAME,,

IDS_Start,,

// KONIEC

IDS_END

};;

Listing 105 – Wykorzystanie wyliczenia do obliczania rozmiaru tablicy

ccoonnsstt

prog_char** langsys_strTable[[

2

]][[IDS_END]] PROGMEM ==

((......))

Listing 103 – plik main.c

#include <avr/io.h>

#include <stdio.h>

#include <util/delay.h>

#include „harddef.h”

#include „makra.h”

#include „lcd.h”

#include „langsys.h”

ssttaattiicc iinnlliinnee

uint8_t sw_wait((

vvooiidd

))

{{

ffoorr

((;;;;))

{

// Oczekiwanie na naciœniêcie przycisku

iiff

((!!((PIN((SW_PORT)) &&

1

<<<<SW1))))

{

_delay_ms((

30

));;

iiff

((!!((PIN((SW_PORT)) &&

1

<<<<SW1))))

rreettuurrnn

1

;;

}

iiff

((!!((PIN((SW_PORT)) &&

1

<<<<SW2))))

{

_delay_ms((

30

));;

iiff

((!!((PIN((SW_PORT)) &&

1

<<<<SW2))))

rreettuurrnn

2

;;

}

}

}

iinntt

main((

vvooiidd

))

{{

// Inicjacja wyprowadzeñ

DDR((LCD_CTRLPORT)) == ((

1

<<<<LCD_E ||

1

<<<<LCD_RW ||

1

<<<<LCD_RS ||

1

<<<<LCD_LED));;

PORT((LCD_CTRLPORT)) == ~~((

1

<<<<LCD_E ||

1

<<<<LCD_LED));;

PORT((SW_PORT)) ==

1

<<<<SW1 ||

1

<<<<SW2;;

// Inicjacja wyœwietlacza

lcd_Init(());;

lcd_SetStatus((LCD_STATUS_DISP));;

// Wyœwietlenie zapytania o jêzyk

fputs_P((PSTR((

„S1 - „

)),, lcd_GetFile(())));;

fputs_P((langsys_GetLangName((

0

)),, lcd_GetFile(())));;

lcd_GoTo((

0

,,

1

));;

fputs_P((PSTR((

„S2 - „

)),, lcd_GetFile(())));;

fputs_P((langsys_GetLangName((

1

)),, lcd_GetFile(())));;

lcd_Update(());;

// Oczekiwanie na przycisk i wybranie jêzyka

langsys_Select((sw_wait(())--

1

));;

// Wyœwietlenie przywitania

lcd_Cls(());;

fputs_P((langsys_GetText((IDS_Start)),, lcd_GetFile(())));;

lcd_Update(());;

rreettuurrnn

0

;;

}}

Sterownik zegarowy i nie tylko...

- Urz¹dzenie, oprócz wbudowanego

zegara, posiada wiele ciekawych i po¿ytecznych funkcji. Oto niektóre z nich:

4 kana³y sterowane czasowo (godzina w³¹czenia, godzina wy³¹czenia),

3 kana³y sterowane rêcznie przez u¿ytkownika, 1 kana³ sterowany termicznie

(temperatura górna maksymalna, dolna minimalna z zakresu od -55 do +150

o

C),

cyfrowy czujnik temperatury i in.

Kompukser

- W popularnych kartach dŸwiêkowych

mamy do dyspozycji jedno gniazdo

wejœciowe Line In typu miniJack.

Czasem jednak zachodzi potrzeba,

aby pod³¹czyæ do komputera wiêcej

sygna³ów audio. Opisany w artykule uk³ad rozwi¹zuje ten problem.

Ponadto umo¿liwia ³atw¹ rozbudowê o kolejne kana³y.

w n a s t ê p n y c h n u m e r a c h E d W

R E K L A M A


Wyszukiwarka

Podobne podstrony:
kursC czesc007
kursC czesc006
kursC czesc001
kursC czesc003
kursC czesc006a przeprowadzka
kursC czesc000 programowanie
kursC czesc018
kursC czesc008
kursC czesc002
kursC czesc013
kursC czesc011
kursC czesc007
kursC czesc018

więcej podobnych podstron