rs232_linux-win32_cz3.pdf

(392 KB) Pobierz
106-107_rs232_cz3.indd
K U R S
Programowanie portu szeregowego
w systemach operacyjnych
Linux i Windows, część 3
Umiejętność programowej obsługi interfejsu
RS232 od strony komputera PC jest dziś
istotnym elementem elektronicznego rzemiosła.
W niniejszym kursie piszemy jak w praktyce
oprogramować port szeregowy w środowiskach
Linux i Windows. Wiele miejsca poświęcamy
pisaniu przenośnych aplikacji GUI, które
korzystają z interfejsu szeregowego i zachowują
się tak samo w systemach Windows jak
i Linux. Wszystkie omawiane zagadnienia
poparte są szczegółowo opisanymi praktycznymi
przykładami.
Aplikacja konsolowa używająca
portu szeregowego
Aplikacja konsolowa została napi-
sana w języku C i skompilowana za
pomocą kompilatora GCC, który spo-
tkamy w każdej dystrybucji Linuksa.
Składa się ona z trzech plików:
main.c – główny plik aplikacji od-
powiedzialny za jej funkcjonalność
( list. 6 );
LinuxRS232.h – plik nagłówkowy
zawierający deklaracje funkcji ob-
sługi interfejsu RS232 ( list. 7) ;
LinuxRS232.c – plik źródłowy za-
wierający definicje (ciała) funk-
cji obsługi interfejsu RS232
( list. 8...11 ).
Na list. 6 zamieściłem zawartość
pliku main.c . Działanie programu po-
lega na pobraniu jednej cyfry (je-
den znak+Enter) z klawiatury i wysła-
niu przez port szeregowy /dev/ttyS0
(COM1) bajta o wartości równej poda-
nej cyfrze (0...9). Jeśli wpiszemy znak
inny niż 0...9 to przez port zostanie
wysłany bajt o wartości równej kodo-
wi ASCII tego znaku pomniejszonemu
o 0x30. Następnie program zasypia na
czas ok. 1 sekundy ( break time ), po
czym sprawdza czy w buforze wej-
ściowym portu znajdują się jakiekol-
wiek dane (odpowiedź zestawu). Jeśli
tak jest są one odczytywane i w kon-
soli pojawia się zarówno ich interpre-
tacja ASCII jak i wartości kolejnych
bajtów zapisane dziesiętnie. Pokazu-
je to rys. 4 zawierający zrzut z ekra-
nu zrobiony podczas pracy aplikacji,
gdy użytkownik jako daną do wysła-
nia wpisał liczbę 3. Zmienna InQue
zawiera odczytaną za pomocą funkcji
CheckCommInput liczbę bajtów ocze-
kujących w buforze wejściowym, zaś
zmienna BytesRead zawiera liczbę baj-
tów faktycznie wyjętych z tego bufora
za pomocą funkcji Read .
List. 8 przedstawia implementację
funkcji Open i Close . Funkcja Open
otwiera port, po czym ustawia żąda-
ną przez użytkownika prędkość trans-
misji oraz wyłącza co tylko można
z funkcjonalności obróbki danych wej-
ściowych i wyjściowych. Innymi sło-
wy, wybieramy wejście raw input
i wyjście raw output wyłączając wszel-
kie funkcje mapowania jednych zna-
ków na drugie, sprzętową i programo-
wą kontrolę przepływu, itp..
List. 9 zawiera ciało funkcji Write
służącej do wysłania do portu Num-
berOfBytesToWrite bajtów, począwszy
od miejsca w pamięci określonego
wskaźnikiem Buffer . Funkcja ta zwra-
ca liczbę faktycznie wysłanych baj-
tów. W przypadku błędu zwraca 0.
List. 10 zawiera ciało funk-
cji Read , za pomocą której możemy
wybrać z bufora wejściowego portu
NumberOfBytesToRead bajtów. Specy-
fikujemy przy tym pewne ogranicze-
nie określone stałą cbInQueue zdefi-
niowaną w pliku LinuxRS232.h . Ogra-
niczenie to zapobiega próbie odczy-
tu wielu bajtów z portu. Wartość cbI-
nQueue powinna być dobrana tak,
aby podczas normalnej pracy aplika-
cji w żadnym momencie nie zacho-
dziła potrzeba odczytu z portu liczby
bajtów większej od cbInQueue . Funk-
cja Read zwraca 0 w przypadku błę-
du i 1, gdy odczyt zakończył się suk-
cesem. Za pośrednictwem wskaźnika
lpNumberOfBytesRead zwracana jest
liczba faktycznie odczytanych bajtów.
List. 7. Plik nagłówkowy LinuxRS232.h
//---------------------------------------------------------------------------
// File: LinuxRS232.h
// Description: Linux RS232 header file
// Compiler: gcc
// Author: Arkadiusz Antoniak, 2005
//---------------------------------------------------------------------------
#ifndef LinuxRS232H
#define LinuxRS232H
#define cbInQueue 1024
#define cbOutQueue 16
int Open(char *Port, unsigned long Baud);
void Close(void);
unsigned long Write(const void* Buffer, unsigned long NumberOfBytesToWrite);
int Read(void * Buffer, unsigned long * lpNumberOfBytesRead, unsigned long NumberO-
fBytesToRead);
void CheckCommInput(unsigned long * lpInQue);
//---------------------------------------------------------------------------
#endif
Uwaga!
Ze wzgledu na objętość tekstu listingów 6 i 8, publikujemy je wyłącznie na CD-EP5/2006B oraz na stronie internetowej
w dziale Download.
106
Elektronika Praktyczna 5/2006
66534694.001.png
K U R S
List. 9. Funkcja Write
unsigned long Write(const void* Buffer, unsigned long NumberOfBytesToWrite)
{
int iOutWrite;
if (fd<1) return 0;
iOutWrite = write(fd, Buffer, NumberOfBytesToWrite);
if (iOutWrite<0) return 0;
return iOutWrite;
}
dzeniem czy część sprzętowa na to
pytanie odpowiedziała, program od-
czekuje pewien czas – w naszym
przykładzie jest to 1 sekunda. Czas
taki nazywany jest break time . Na-
leży w tym miejscu zwrócić uwagę
na różnicę w znaczeniu pojęć bre-
ak time i timeout używanych czę-
sto w opisach aplikacji komunika-
cyjnych.
Otóż break time jest minimalnym
czasem jaki aplikacja musi odczekać
na wystąpienie pewnego zdarzenia.
Jeśli zdarzeniem tym jest odpowiedź
układu pomiarowego dołączonego do
komputera przez RS232, to break
time jest wyznaczony przez czas do-
konania pomiaru i czas potrzebny na
przesłanie przez port szeregowy wy-
niku tego pomiaru. W naszym przy-
padku break time jest wyznaczony je-
dynie przez czas odpowiedzi – oczy-
wiście 1 s jest czasem znacznie dłuż-
szym niż minimalny czas jaki należa-
łoby tutaj odczekać.
Czas timeout jest czasem przeter-
minowania (przedawnienia) pewnej
operacji. Jeśli po upływie tego czasu
chcemy ponownie wywołać tą opera-
cję, to musimy powtórzyć całą proce-
durę związaną z jej wywołaniem.
List. 10. Funkcja Read
int Read(void * Buffer, unsigned long * lpNumberOfBytesRead, unsigned long Numbe-
rOfBytesToRead)
{
unsigned long nNumberOfBytesToRead;
int iInRead;
if (NumberOfBytesToRead>cbInQueue)
nNumberOfBytesToRead=cbInQueue;
else
nNumberOfBytesToRead=NumberOfBytesToRead;
if(fd<1) return 0;
iInRead=read(fd, Buffer, nNumberOfBytesToRead);
if(iInRead<0)
return 0;
else
*lpNumberOfBytesRead=iInRead;
return 1;
}
Rys. 4. Okno aplikacji konsolowej
Podsumowanie
W tej części kursu pragnąłem za-
prezentować wykorzystanie omówio-
nych poprzednio funkcji na prostym
przykładzie, tak aby bez wgłębiania
się w szczegóły pokazać że „to dzia-
ła”. Proponuję Czytelnikom poekspe-
rymentować z opisanym zestawem
i oprogramowaniem – ludzie wszak
uczą się najlepiej i najszybciej na
przykładach. W następnej części zmie-
nimy nieco tematykę i zobaczymy jak
oprogramowanie portu szeregowego
wygląda w systemie Windows. Skupi-
my się przy tym na tych cechach,
które są takie same lub podobne do
ich odpowiedników występujących
w Linuksie. Wszystko to ma na celu
stworzenie klasy obsługi RS232 w ję-
zyku C++ identycznej w obu syste-
mach co do interfejsu, więc przeno-
śnej. Klasę tą – w wersji dla Win-
dows – stworzymy w następnej części
cyklu. Oprócz tego, opiszemy też pro-
stą aplikację GUI dla Windows, której
funkcjonalność będzie taka sama jak
funkcjonalność opisanego dziś proste-
go programu konsolowego. W dalszych
częściach kursu aplikację tą przenie-
siemy na Linuksa.
Arkadiusz Antoniak, EP
arkadiusz.antoniak@ep.com.pl
Wskaźnik Buffer określa miejsce w pa-
mięci, gdzie zostaną zapisane odczy-
tane bajty.
List. 11 przedstawia funk-
cję CheckCommInput, która pozwa-
la zapytać sterownik portu szerego-
wego ile bajtów aktualnie znajdu-
je się w buforze wejściowym. Ich
liczba zwracana jest przez wskaźnik
lpInQue .
Oczywiście, funkcje przedstawio-
ne na list. 8...11 (plik LinuxRS232.c )
to tylko moja propozycja wykorzysta-
nia funkcji systemowych, a Czytelnicy
zechcą zapewne napisać własne funk-
cje obsługi portu szeregowego (byłoby
to nawet wskazane ze względów edu-
kacyjnych).
Całość kompilujemy za pomocą
GCC używając standardowo programu
make . Przykładowy plik makefile za-
mieściłem na list. 12. Jest to bardzo
prosty plik, mówiący kompilatorowi
jedynie co kompilować, zaś linkerowi
– z jakich plików *.o stworzyć plik
wykonywalny o nazwie main . Aby
otrzymać ten plik wystarczy wpi-
sać „make” będąc w katalogu zawie-
rającym pliki źródłowe aplikacji i plik
makefile . Program uruchamiamy wpi-
sując w konsoli „./main”.
Jeśli do portu COM1 komputera
dołączony jest nasz zestaw, to bę-
dziemy mogli obserwować działanie
całości. Polega ono na zadaniu py-
tania (wpisana przez użytkownika
wartość) i obserwacji odpowiedzi.
Pomiędzy zadaniem pytania a spraw-
List. 11. Funkcja CheckCommInput
void CheckCommInput(unsigned long *
lpInQue)
{
ioctl(fd, FIONREAD, lpInQue);
}
List. 12. Plik makefile
main: main.c LinuxRS232.c
gcc -g -c -Wall main.c -o main.o
gcc -g -c -Wall LinuxRS232.c -o
LinuxRS232.o
gcc LinuxRS232.o main.o -o main
Elektronika Praktyczna 5/2006
107
66534694.002.png 66534694.003.png
Zgłoś jeśli naruszono regulamin