rs232_linux-win32_cz4.pdf
(
277 KB
)
Pobierz
rs232_linux-win32_cz4.indd
KURS
Programowanie portu szeregowego
w systemach operacyjnych Linux
i Windows, część 4
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.
istotne jest jej typowe wykorzysta-
nie, pozwalające na otworzenie por-
tu do zapisu i odczytu, które przed-
stawiono na
list. 13
.
Ważną rolę pełni funkcja
Setup-
Comm()
służąca do ustalenia rozmiaru
buforów: wejściowego i wyjściowego.
Jest ona określona następująco:
BOOL SetupComm(
HANDLE hCommDev,
DWORD dwInQueue,
DWORD dwOutQueue
);
Konfiguracja portu
Po otwarciu, port musi zostać
skonfigurowany. Jego konfiguracja
odbywa się podobnie do modyfikacji
struktury
termios
w systemie Linux.
W systemie Windows modyfikowana
jest specjalna struktura DCB. Sche-
mat konfiguracji polega na pobra-
niu aktualnej wartości tej struktury
za pomocą funkcji
GetCommState()
,
jej zmodyfikowaniu oraz zapisaniu
nowej wartości za pomocą funk-
cji
SetCommState()
. Prototypy tych
funkcji są określone następująco:
BOOL GetCommState(
HANDLE hCommDev,
LPDCB lpDCB
);
BOOL SetCommState(
HANDLE hCommDev,
LPDCB lpDCB
);
Argumenty
dwInQueue
i
dwOutQu-
eue
zawierają rozmiary buforów wej-
ściowego i wyjściowego.
Przykład konfiguracji portu szere-
gowego w systemie Windows przed-
stawiono na
list. 14.
Taka konfigura-
cja pozwala na pracę z ramką typu
8N1 przy wyłączonej sprzętowej kon-
troli przepływu i szybkości transmisji
19200 bodów.
Czytanie z portu
Czytanie z bufora wejściowego
portu szeregowego odbywa się za
pomocą funkcji Win32 API określo-
nej następująco:
BOOL ReadFile(
HANDLE hCommDev,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
Po dłuższej przerwie wznawiamy
kurs poświęcony programowaniu por-
tu szeregowego w systemach opera-
cyjnych Linux i Windows. W kolejnej
części poznamy sposób obsługi łącza
RS232 w systemie Windows, oraz
stworzymy przenośną klasę
CCommIn-
terface
, służącą do obsługi tego inter-
fejsu w systemach Linux i Windows.
Uchwyt
hCommDev
wskazuje na
otwarty port szeregowy, zaś wskaź-
nik
lpDCB
wskazuje na strukturę
DCB, do której mają zostać skopio-
wane dane o ustawie-
niach portu, lub z której
dane mają być pobrane
w celu modyfikacji tych
ustawień.
Obsługa łącza RS232
w systemie Windows
Sposoby obsługi portu RS232 są
w dużej mierze podobne zarówno
w systemie Windows, jak i w sys-
temie Linux. Pewną różnicę stano-
wi brak plików urządzeń systemie
Windows. Otwarcie portu polega
na tymczasowym stworzeniu od-
powiedniego pliku, zaś późniejsze
jego używanie sprowadza się do
zapisywania i odczytywania danych
do/z utworzonego pliku. Podobnie
jest realizowana komunikacja z in-
nymi urządzeniami zewnętrznymi.
List. 13. Otwarcie portu do zapisu i odczytu
hCommDev = CreateFile(lpFileName, GENERIC_READ |
GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
List. 14. Przykładowa konfiguracja portu szeregowego (ramka 8N1, 19200
bodów)
if (hCommDev != INVALID_HANDLE_VALUE)
{
dcb.DCBlength = sizeof(dcb);
if(!GetCommState(hCommDev, &dcb))
return false;
dcb.BaudRate = 19200;
//transmission parameters
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.ByteSize = 8;
//DCB flags
dcb.fParity = FALSE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
//DTR line dislabled
dcb.fRtsControl = RTS_CONTROL_DISABLE;
//RTS line disabled
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDsrSensitivity = FALSE;
dcb.fAbortOnError = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
if(!SetCommState(hCommDev,&dcb))
return false;
if(!SetupComm(hCommDev, cbInQueue, cbOutQueue))
return false;
}
Otwieranie portu
Otwieranie portu szeregowego od-
bywa się za pomocą funkcji Win32
API
CreateFile()
, która tworzy plik
związany z tym portem. Funkcja ta
przyjmuje 7 parametrów i zapewnia
różnorakie opcje związane z otwie-
raniem plików. Dla zagadnień zwią-
zanych z obsługą portu szeregowego
Elektronika Praktyczna 5/2007
89
KURS
List. 15. Dyrektywy kompilacji warun-
kowej dla biblioteki Qt
#include <QtCore/QtGlobal>
//...
#ifdef Q_WS_WIN
//Code compiled on Windows OS
#endif
#ifdef Q_WS_X11
//Code compiled on Linux OS
#endif
HANDLE hCommDev,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
tu szeregowego (funkcja jednocze-
śnie zeruje znacznik błędu) oraz
odczytać stan portu, który jest
umieszczany w specjalnej struktu-
rze
COMSTAT
, dostępnej poprzez
wskaźnik
lpStat
. Jednym z pól tej
struktury jest pole
cbInQue
za-
wierające liczbę nie odczytanych
bajtów, jakie oczekują w buforze
wejściowym portu szeregowego. Ar-
gument
hCommDev
jest uchwytem
do używanego portu.
Uchwyt
hCommDev
wskazuje na
używany port szeregowy. Argument
lpBuffer
jest wskaźnikiem na po-
czątek obszaru pamięci, w którym
są umieszczone dane przeznaczone
do wysłania do portu. Argument
nNumberOfBytesToWrite
specyfikuje
ile bajtów ma być wysłanych do
portu, zaś argument
lpNumberO-
fBytesWritten
wskazuje na zmienną,
w której umieszczana jest liczba
faktycznie wysłanych bajtów. Licz-
by te mogą się różnić, gdy nastąpił
błąd zapisu. Ostatni argument funk-
cji
WriteFile()
–
lpOverlapped
– nie
jest w tym przypadku wykorzysty-
wany i jego wartość podczas jej
wywołania wynosi
NULL
. Funkcja
WriteFile()
zwraca wartość logiczną
true
, gdy zapis przebiegł prawidło-
wo, zaś
false
, gdy wystąpił błąd.
Uchwyt
hCommDev
wskazuje na
używany port szeregowy – jest to
uchwyt zwracany podczas otwarcia
portu przez funkcję
CreateFile()
. Ar-
gument
lpBuffer
jest wskaźnikiem na
początek obszaru pamięci, w którym
zostaną umieszczone dane odczyta-
ne z portu. Argument
nNumberOfBy-
tesToRead
specyfikuje ile bajtów ma
być odczytanych z portu, zaś argu-
ment
lpNumberOfBytesRead
wskazuje
na zmienną, w której jest umiesz-
czana liczba faktycznie odczytanych
bajtów. Liczby te mogą się różnić
wtedy, gdy w buforze wejściowym
znajduje się mniej bajtów niż poda-
no w zmiennej
nNumberOfBytesToRe-
ad
, lub gdy nastąpił błąd odczytu.
Ostatni argument funkcji
ReadFile()
–
lpOverlapped
– nie jest w tym
przypadku wykorzystywany i jego
wartość podczas jej wywołania wy-
nosi
NULL
. Funkcja
ReadFile()
zwra-
ca wartość logiczną
true
, gdy odczyt
przebiegł prawidłowo, zaś
false
, gdy
wystąpił błąd.
Zamykanie portu
Zamykanie portu szeregowego od-
bywa się za pomocą funkcji Win32
API określonej następująco:
BOOL CloseHandle(HANDLE *hCommDev);
Funkcja zamyka port, a ściślej –
związany z nim plik utworzony za
pomocą funkcji
CreateFile()
, wska-
zywany przez uchwyt
hCommDev
.
Klasa CCommInterface
Klasa
CCommInterface
posiada
uniwersalne metody implementują-
ce podstawowe operacje na porcie
szeregowym, a więc: otwarcie por-
tu, zamknięcie portu, zapis ciągu
bajtów, odczyt ciągu bajtów oraz
sprawdzenie liczby nie odczytanych
bajtów znajdujących się w buforze
wejściowym. Oto lista prototypów
publicznych metod tej klasy:
bool Open(string CommID, unsigned
long Baud);
bool Close(void);
unsigned long Write(const
void *Buffer, unsigned long
NumberOfBytesToWrite);
bool Read(void *Buffer, unsigned
long *lpNumberOfBytesRead, unsigned
long NumberOfBytesToRead);
Sprawdzenie liczby bajtów
oczekujących na odczyt
w buforze wejściowym
Sprawdzenie ile nie odczyta-
nych bajtów oczekuje w buforze
wejściowym odbywa się za pomo-
cą funkcji Win32 API określonej
następująco:
BOOL ClearCommError(
HANDLE hCommDev,
LPDWORD lpErrors,
LPCOMSTAT lpStat
);
Pisanie do portu
Wysyłanie danych do bufora
wyjściowego portu szeregowego od-
bywa się za pomocą funkcji Win32
API określonej następująco:
BOOL WriteFile(
Wywołanie tej funkcji pozwala
poznać kod ostatniego błędu (wska-
zywany przez argument
lpErrors
),
jaki pojawił się podczas pracy por-
List. 16. Plik nagłówkowy klasy CCommInterface
#ifndef CommInterfaceH
#define CommInterfaceH
#include <QtCore/QtGlobal>
#include <string>
using namespace std;
//---------------------------------------------------------------------------
class CCommInterface
{
private:
const char *lpFileName;
unsigned long BaudRate;
unsigned long cbInQueue;
unsigned long cbOutQueue;
#ifdef Q_WS_WIN
void *hCommDev;
#endif
#ifdef Q_WS_X11
int fd;
#endif
protected:
public:
CCommInterface();
bool Open(string CommID, unsigned long Baud);
void Close(void);
unsigned long Write(const void* Buffer, unsigned long NumberOfBytesToWrite);
bool Read(void * Buffer, unsigned long * lpNumberOfBytesRead, unsigned long NumberOfBytesToRead);
void CheckCommInput(unsigned long * lpErrors, unsigned long * lpInQue);
};
//---------------------------------------------------------------------------
#endif
90
Elektronika Praktyczna 5/2007
KURS
bool CheckCommInput(unsigned long
*lpErrors, unsigned long *lpInQue);
Nazwy poszczególnych argumen-
tów jednoznacznie wskazują na ich
znaczenie, nie ma więc potrzeby
szczegółowego wyjaśniania roli każ-
dego z nich. Funkcja
Write()
zwra-
ca liczbę faktycznie zapisanych
bajtów (w przypadku błędu zwra-
ca 0). Funkcja
Open()
jako jedyne
argumenty przyjmuje identyfikator
portu i szybkość transmisji – port
jest otwierany do pracy z ramką
8N1 przy wyłączonej sprzętowej
kontroli przepływu. Klasę tę moż-
na dowolnie zmodyfikować we-
dług potrzeb, w celu zapewnienia
innych ustawień portu, jeśli takie
będą potrzebne.
Implementacja metod klasy
CCommInterface
polegała na zebra-
niu wiadomości dotyczących ob-
sługi portu w obu systemach ope-
racyjnych i połączeniu ich poprzez
dyrektywy kompilacji warunkowej.
Dzięki temu, fragmenty właściwe
dla systemu Windows będą kom-
pilowane, gdy kompilacja będzie
dokonywana w tym systemie, zaś
fragmenty właściwe dla systemu
Linux, gdy kompilacja będzie doko-
nywana w systemie Linux. Dyrekty-
wy kompilacji warunkowej opierają
się na predefiniowanych stałych,
charakterystycznych dla używane-
go kompilatora czy biblioteki. Już
teraz zdradzę, że przykłady aplika-
cji okienkowych omówione w kolej-
nych częściach kursu będą stworzo-
ne za pomocą rewelacyjnej i rozwo-
jowej biblioteki Qt firmy Trolltech.
Dyrektywy kompilacji warunkowej
charakterystyczne dla tej bibliote-
ki przedstawiono na
list. 15
. Stałe
Q_WS_WIN
i
Q_WS_X11
są zde-
finiowane w pliku
QtGlobal
. Plik
ten musi być wcześniej włączo-
ny do kodu aplikacji (oczywiście,
niekoniecznie w tym samym pliku
źródłowym, w którym wykorzystuje
się zdefiniowane wnim stałe). Dla
innych bibliotek i kompilatorów ist-
nieją inne stałe określające system
operacyjny. Adaptacja klasy
CCom-
mInterface
do pracy z nimi polega
tylko i wyłącznie na zamianie tych
stałych. Reszta kodu źródłowego
pozostaje bez zmian.
W celu przekazania nazwy por-
tu („COM1”, „COM2”) do funkcji
Open()
wykorzystano uniwersalny
typ
string
z biblioteki STL (
Stan-
dard Template Library
). Dzięki
temu istnieje pewność co do prze-
kazywanych danych tekstowych
niezależnie od platformy, na której
dokonywana jest kompilacja. Bi-
blioteka ta jest przy tym tak bar-
dzo popularna, że jej implementa-
cja dostarczana jest wraz z każdym
szanującym się kompilatorem. Plik
nagłówkowy klasy
CCommInterface
przedstawiono na
list. 16
.
Podsumowanie
Wiemy już jak obsłużyć port
RS232 w systemach Linux i Win-
dows, dysponujemy także uniwer-
salną klasą C++, która to umoż-
liwia. Następne części kursu będą
poświęcone tworzeniu prostych
aplikacji GUI wykorzystujących kla-
sę
CCommInterface
.
Arkadiusz Antoniak, EP
arkadiusz.antoniak@ep.com.pl
www.antoniak.ep.com.pl
1/8L
180x93 mm
Elektronika Praktyczna 5/2007
91
Plik z chomika:
sq9nip
Inne pliki z tego folderu:
rs232_linux-win32_cz7.pdf
(190 KB)
rs232_linux-win32_cz6.pdf
(799 KB)
rs232_linux-win32_cz5.pdf
(467 KB)
rs232_linux-win32_cz4.pdf
(277 KB)
rs232_linux-win32_cz3.pdf
(392 KB)
Inne foldery tego chomika:
Audio
Eagle
EasyTrax
GPS
Orcad
Zgłoś jeśli
naruszono regulamin