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 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