rs232 linux win32 cz1


K U R S
Programowanie portu szeregowego
w systemach operacyjnych
Linux i Windows, część 1
Umiejętność programowej obsługi interfejsu
RS232 od strony komputera PC jest
dziś istotnym elementem elektronicznego
rzemiosła. W kolejnych częściach niniejszego
kursu 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.
Popularność Linuksa w ostat- rzystać interfejs szeregowy kompu- ra wystąpi w dwóch wersjach: dla
nich latach wyraznie wzrosła i wie- tera PC w najbardziej typowych za- Windows i dla Linuksa. Klasa ta bę-
le wskazuje na to, że zjawisko to stosowaniach. Drugim istotnym za- dzie miała identyczny interfejs dla
będzie się z roku na rok nasilać. gadnieniem jakie przedstawię jest obu tych platform, dzięki czemu bę-
Choć dla nas  elektroników  pod- pisanie aplikacji przenośnych, któ- dzie ją można stosować w obu sys-
stawową platformą pozostaje Win- re korzystają z obsługi RS232 i po- temach bez konieczności dokonywa-
dows, to powstaje coraz więcej pro- siadają graficzny interfejs użytkow- nia jakichkolwiek zmian w programie
gramów narzędziowych, kompilato- nika (GUI  Graphical User Interfa- głównym (korzystającym z tej kla-
rów dla mikrokontrolerów i innych ce). Jest to zagadnienie, które jest sy). Jej wykorzystanie będzie możli-
programów, które używamy w swo- obecnie  na topie wśród zagad- we w każdym środowisku programi-
jej codziennej praktyce. Jednym nień związanych z tematyką tworze- stycznym zawierającym kompilator
z najznamienitszych przykładów jest nia oprogramowania. Istnieje wyraz- C++, niezależnie od tego czy do
słynny kompilator języka C dla mi- ny trend, aby pisane aplikacje po- tworzenia GUI wykorzystamy MFC,
krokontrolerów AVR (AVRGCC), czy siadały swoje wersje nie tylko dla Qt, VCL/CLX,
ARM. Być może w najbliższym cza- Windows, ale także dla Linuksa dowolny tool-
sie Linux nie zawojuje zupełnie na- i innych systemów operacyjnych. Po- kit C++, czy
szego świata, ale z pewnością sta- winny przy tym, w miarę możliwo- też jakiekolwiek
nie się w nim bardziej obecny niż ści, wyglądać i zachowywać się tak inne oprogra-
dziś. Gdy elektronik decyduje się samo pracując pod kontrolą każdego mowanie.
napisać własne oprogramowanie, z nich. Problem ten omówię w dal-
to chyba najistotniejszym zagadnie- szych częściach kursu. Pisząc o nim Porty
niem z jakim musi się uporać sta- zachowam kierunek migracji z Win- w Linuksie
je się oprogramowanie portów kom- dows na Linuksa, gdyż właśnie N i e j e s t
putera PC. W tym kursie bierzemy Windows pozostaje platformą  bazo- przesadą stwier-
pod lupę port szeregowy i patrzy- wą , w której większość z nas poru- dzenie, że w Li-
my jak obsługuje się go w Linuksie sza się znacznie sprawniej niż w Li- nuksie wszyst-
i pod Windows. nuksie. Innymi słowy, będziemy pi- ko jest plikiem.
Cel niniejszego kursu jest dwo- sać aplikacje GUI pod Windows ale W istocie, nie-
jaki. Po pierwsze, ma on na prak- tak, aby dały się łatwo przenieść mal wszystkie
tycznych przykładach pokazać jak na Linuksa, a potem je przeniesie-  byty z jakimi
można oprogramować port szere- my. Wszystko to będzie zilustrowa- mamy do czy-
gowy w Linuksie oraz  niejako ne praktycznymi przykładami. Jed- nienia powią-
przy okazji  w systemie Windows. nym z nich będzie cyfrowy wolto- zane są w ten,
Nie twierdzę przy tym, że omó- mierz odczytywany przez interfejs czy inny spo-
wię wszystkie szczegóły i szczególiki RS232C. Dodatkową korzyścią jaką sób z jakimś pli-
związane z oprogramowaniem RS232 otrzymają Czytelnicy będzie przykła- kiem. Nie ina- Rys. 1. Typowy cykl
w każdym z tych systemów. Poka- dowa klasa obsługująca RS232 zaim- czej jest z por- pracy portu szere-
żę natomiast w praktyce jak wyko- plementowana w języku C++, któ- tami. Wszyst- gowego (Linux)
Elektronika Praktyczna 3/2006
89
K U R S
trzecie 3 litery  prawa pozosta-
Tab. 1. Pola struktury termios
łych użytkowników (o  other);
Pole Typ Opis
gdzie:
c_cflag unsigned int Opcje sterowania
r  prawo do czytania (Read);
c_lflag unsigned int Opcje lokalne (dyscyplina linii)
w  prawo do pisania (Write);
c_iflag unsigned int Opcje wejścia
x  prawo do wykonywania
c_oflag unsigned int Opcje wyjścia
(eXecute).
c_cc unsigned char* Tablica znaków specjalnych
W nowo zainstalowanym syste-
c_ispeed unsigned int Prędkość wejściowa
mie zwykle jest tak, że właściciel
c_ospeed unsigned int Prędkość wyjściowa
(w tym przypadku root) ma pełne
prawa pisania i czytania rw , zaś
kie urządzenia zewnętrzne, do któ- Oznacza to, że dostęp do reprezen- zarówno grupa jak i pozostali użyt-
rych zaliczają się porty komputera, towanego przez ten plik urządzenia kownicy nie mają żadnych praw 
reprezentowane są przez specjalne odbywa się  znak po znaku , czy- nie mogą ani pisać do portu, ani
pliki urządzeń znajdujące się w ka- li  bajt po bajcie . Następne 9 zna- z niego czytać. Sytuacja taka unie-
talogu /dev. Plików tych jest bardzo ków określa prawa dostępu, co ob- możliwia oczywiście uruchomienie
dużo, ale nas interesuje tylko kilka jaśniono poniżej: jakichkolwiek aplikacji korzystają-
z nich. Przykładowo  porty rów- pierwsze 3 litery  prawa wła- cych z portu szeregowego w try-
noległe (o ile jest ich więcej niż ściciela pliku (u  user); bie zwykłego użytkownika. Aby to
jeden, co się właściwie nie zda- drugie 3 litery  prawa grupy zmienić, należy dodać im prawa
rza) reprezentowane są przez pliki (g  group); pisania i czytania. W tym celu lo-
/dev/lp0, /dev/lp1 itd.. Jeśli posiada-
my tylko jeden port równoległy, to
List. 1. Przykład otwarcia portu ttyS0
//**********************************************************
związany jest z nim plik /dev/lp0
//
(oczywiście przyporządkowanie to
// Port ttyS0 (COM1) opening example
// Returns: 0 when error occured
może być zmienione). W przypadku
// 1 when everything OK
portów szeregowych domyślne przy- //
//**********************************************************
porządkowanie jest następujące:
#include
#include
#include
COM1 (adres 0x3f8)  plik /dev/
#include
ttyS0
//File descriptor
COM2 (adres 0x2f8)  plik /dev/
int fd;
ttyS1
int Open(void)
COM3 (adres 0x3e8)  plik /dev/
{
fd=open( /dev/ttyS0 , O_RDWR | O_NOCTTY | O_NDELAY);
ttyS2
if (fd<0)
COM4 (adres 0x2e8)  plik /dev/ {
//Opening error
ttyS3
return 0;
}
else
W niektórych starszych dystry-
{
//Here port configuration
bucjach Linuksa noszą one nazwy
//...
cua0...cua3.
}
return 1;
Aby dowiedzieć się o nich wię-
}
cej, przejdzmy do katalogu /dev
(komenda cd /dev z dowolnej loka-
cji) i wpiszmy ls ttyS0  l. Komenda
List. 2. Ogólny schemat konfiguracji portu szeregowego
//Copy of termios structure
ta pokaże nam podstawowe właści-
struct termios options;
wości pliku ttyS0. Oto przykładowy
//Getting the current settings for the port
wynik jej działania:
tcgetattr(fd, &options);
crw rw rw 1 root uucp 4,
//Setting baudrate (19200 for example)
64 kwi 14 2001 ttyS0 cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
Spójrzmy na pierwsze pole z le- //Modifying c_cflag by bitwise OR and AND
options.c_cflag &= ...;
wej określające rodzaj pliku i prawa
options.c_cflag |= ...;
dostępu do niego. Litera c wskazu-
//Modifying c_lflag by bitwise OR and AND
je, że jest to plik znakowy (char).
options.c_lflag &= ...;
options.c_lflag |= ...;
Tab. 2. Opcje funkcji tcsetattr
//Modifying c_iflag by bitwise OR and AND
options.c_iflag &= ...;
Opcja Opis
options.c_iflag |= ...;
TCSANOW Dokonaj zmiany natychmiast
//Modifying c_oflag by bitwise OR and AND
Dokonaj zmiany po zakończeniu
options.c_oflag &= ...;
TCSADRAIN
options.c_oflag |= ...;
transmisji danych
Wyczyść (flush) bufory wej-
//Setting the new settings for the port immediately
TCSAFLUSH ściowy i wyjściowy, po czym
tcsetattr(fd, TCSANOW, &options);
dokonaj zmiany
Elektronika Praktyczna 3/2006
90
K U R S
Tab. 3. Predefiniowane stałe dla Tab. 4. Predefiniowane stałe dla pola c_lflag
pola c_cflag
Stała Opis
Stała Opis
ISIG Włącz wysyłanie sygnałów SIGINTR, SIGSUSP, SIGDSUSP i SIGQUIT
Maska bitowa dla bitów określają-
Flaga włączona: canonical input
ICANON
CBAUD cych prędkość (obecnie używanie
Flaga wyłączona: raw input
niezalecane)
XCASE Mapuj wielkie litery na małe (przestarzała)
B0 0 bodów (wyłącz DTR)
ECHO Włącz wysyłanie odebranych znaków (echo)
B50 50 bodów
ECHOE Po odebraniu znaku kasowania (erase) wyślij kombinację znaków BS-SP-BS.
B75 75 bodów
ECHOK Wyślij znak NL (0x0A) po odebraniu znaku kasowania wiersza (kill)
B110 110 bodów
ECHONL Odsyłaj znak NL (echo)
B134 134 body
Wyłącz czyszczenie bufora wejściowego po przerwaniu lub odebraniu znaku przery-
NOFLSH
B150 150 bodów
wającego quit
B200 200 bodów
IEXTEN Włącz rozszerzone funkcje portu (dot. dyscypliny linii)
B300 300 bodów
ECHOCTL Odsyłaj znaki kontrolne (echo) jako kombinację ^znak
B600 600 bodów
ECHOPRT Poprzedz usuwane znaki znakiem \\ (backslash)
B1200 1200 bodów
ECHOKE Echem znaku kasowania wiersza jest odpowiednia kombinacja znaków SP i BS
B1800 1800 bodów
Tab. 5. Predefiniowane stałe dla pola c_iflag
B2400 2400 bodów
Stała Opis
B4800 4800 bodów
INPCK Włącz kontrolę parzystości
B9600 9600 bodów
IGNPAR Ignoruj błędy parzystości
B19200 19200 bodów
PARMRK Zaznaczaj błędy parzystości
B38400 38400 bodów
ISTRIP Zeruj bit parzystości (po kontroli parzystości)
B57600 57600 bodów
IXON Włącz programową kontrolę przepływu (wychodzącą)
B76800 76800 bodów
IXOFF Włącz programową kontrolę przepływu (wchodzącą)
B115200 115200 bodów
IXANY Dowolny znak (nie tylko VSTART) włącza przepływ danych
EXTA Zewnętrzny zegar taktujący
IGNBRK Ignoruj znak przerwania
EXTB Zewnętrzny zegar taktujący
Maska bitowa dla bitów określają- BRKINT Wyślij sygnał SIGINT gdy odebrano znak przerwania
CSIZE
cych liczbę bitów danych
INLCR Mapuj znak NL (0x0A) na CR (0x0D)
CS5 5 bitów danych
IGNCR Ignoruj znak CR
CS6 6 bitów danych
ICRNL Mapuj znak CR na NL
CS7 7 bitów danych
IUCLC Mapuj wielkie litery na małe (jeśli ustawiona flaga IEXTEN w c_lflag)
CS8 8 bitów danych
IMAXBEL Włącz sygnał dzwiękowy (bell) przy przepełnieniu bufora wejściowego
Flaga włączona: 2 bity stopu
CSTOPB
Flaga wyłączona: 1 bit stopu
cze wiedzieć, co można z nimi ro- prawność pracy pisanego opro-
CREAD Włącz odbiornik
bić, czyli jakie operacje wykonywa- gramowania. W tej części kursu
PARENB Włącz kontrolę parzystości
ne są na tych plikach w celu reali- omówię najistotniejsze z nich.
Flaga włączona: odd parity zacji transmisji pomiędzy PC, a do- 3. Używanie portu (pisanie, czyta-
PARODD
Flaga wyłączona: even parity
łączonym do niego urządzeniem. Na nie, funkcje specjalne itp.)
Wystaw 0 na DTR przy zamknię- rys. 1 przedstawiono typowy cykl W fazie  używania portu realizu-
HUPCL
ciu przez ostatni proces
pracy portu szeregowego. Wyróż- je się komunikację PC z urządze-
Linia lokalna  nie można zmienić
niłem na nim cztery główne fazy niem zewnętrznym, czyli proto-
CLOCAL
aktualnego właściciela portu
pracy oraz związane z nimi słowa kół komunikacyjny. Program po-
CNEW_
kluczowe  nazwy funkcji, struktur zostaje w tej fazie przez niemal
Włącz sprzętową kontrolę przepły-
RTSCTS
itp. Fazy te są następujące: cały czas działania aplikacji.
wu (hardware flow control)
CRTSCTS
1. Otwarcie portu 4. Zamknięcie portu
Otwarcie portu równoznaczne Równoznaczne z zamknięciem
gujemy się jako root (poleceniem jest z otworzeniem pliku ttySx pliku ttySx.
su), po czym będąc w katalogu /dev do pisania i/lub czytania. My
wpisujemy: rozważymy jedynie sytuacje, Otwieranie portu
chmod go+rw ttyS0 w których port otwarty jest za- Port szeregowy, podobnie jak
równo do czytania jak i do pisa- każdy inny plik, może być otworzo-
Komenda ta dodaje prawa pi- nia (transmisja dwukierunkowa). ny za pomocą funkcji open. Przyj-
sania do portu i czytania z niego 2. Konfiguracja portu muje ona dwa argumenty:
wszystkim użytkownikom. Dopie- Jest to niezwykle istotna faza  pierwszy argument zawiera peł-
ro teraz można uruchamiać progra- pracy z interfejsem szeregowym. ną ścieżkę dostępu do pliku
my używające RS232, także w try- Tutaj konfigurujemy port tak, portu (w przypadku portu COM1
bie zwykłego użytkownika. aby spełniał wymagania jakie będzie to /dev/ttyS0),
nakłada na niego nasza aplika-  drugi argument określa opcje
Cykl pracy portu szeregowego cja. Trzeba zaznaczyć, że w Li- otwierania portu, które można
Wiemy już z grubsza jak w sys- nuksie istnieje ogromna liczba zmieniać wykorzystując predefi-
temie plików Linuksa reprezentowa- opcji konfiguracji, a od ich pra- niowane maski bitowe.
ne są porty szeregowe. Trzeba jesz- widłowego wybrania zależy po- Wartością zwracaną jest deskryp-
Elektronika Praktyczna 3/2006
91
K U R S
Tab. 6. Predefiniowane stałe dla pola c_oflag
List. 3. Pobranie liczby bajtów pozo-
Stała Opis
stających w buforze wejściowym
//**********************************
OPOST Przetwarzaj znaki przed wysłaniem
//
(Sposób przetwarzania określają pozostałe poniższe stałe. Są one ignorowane
// Getting number of bytes
// available in inpu queue
gdy flaga OPOST nie jest ustawiona)
//
OLCUC Mapuj małe litery na wielkie
//**********************************
#include
ONLCR Mapuj znak NL (0x0A) na parę CR-NL (0x0D-0x0A)
#include
OCRNL Mapuj znak CR na znak NL
//File descriptor
ONLRET Włączona: znak NL powoduje automatyczny powrót karetki
int fd;
NLDLY Maska bitowa dla opóznienia przy przejściu do nowego wiersza
//Bytes available in input queue
(przestarzałe)
int bytes;
NL0 Brak opóznienia
//Get the number of bytes available
ioctl(fd, FIONREAD, &bytes); NL1 Odczekaj 100 ms po przejściu do nowej linii
CRDLY Maska bitowa dla flag CR0...CR3  opcji opóznień przy powrocie karetki
(przestarzałe)
tor pliku portu, który pózniej (przy
CR0 Brak opóznienia dla znaku CR
czytaniu lub pisaniu) służy jako jego
CR1 Opóznienie po znaku CR zależy od aktualnej pozycji w kolumnie
identyfikator. Jeśli otwarcie przebie-
CR2 Odczekaj 100 ms po wysłaniu znaku CR
gło pomyślnie deskryptor ma war-
CR3 Odczekaj 150 ms po wysłaniu znaku CR
tość dodatnią. W przypadku błędu
TABDLY Maska bitowa dla flag TAB0...TAB3  opcji opóznień po znaku tabulacji
otwarcia, którym może być brak od-
(przestarzałe)
powiednich praw dostępu lub uży-
TAB0 Brak opóznienia
wanie portu przez inną aplikację,
TAB1 Opóznienie po znaku TAB zależy od aktualnej pozycji w kolumnie
funkcja open zwraca wartość mniej-
TAB2 Odczekaj 100 ms po wysłaniu znaku TAB
szą od 0. Przykład jej użycia znaj-
TAB3 Rozszerz znaki tabulacji do znaków spacji (SP)
duje się na list. 1. Znaczenie wyko-
BSDLY Maska bitowa dla flag BS0...BS3  opcji opóznień po znaku backspace (BS)
rzystanych opcji jest następujące:
(przestarzałe)
O_RDWR  otwarcie portu do
BS0 Brak opóznienia
czytania i do pisania;
BS1 Odczekaj 50 ms po wysłaniu znaku BS
O_NOCTTY  nie ustawienie tej
VTDLY Maska bitowa dla flag VT0...VT3  opcji opóznień po znaku VT
opcji sprawi, że inne urządzenia
(przestarzałe)
mogą mieć wpływ na pracę otwie-
VT0 Brak opóznienia
ranego portu;
VT1 Odczekaj 2 s po wysłaniu znaku BS
O_NDELAY  ustawienie tej fla-
FFDLY Maska bitowa dla flag VT0...VT3  opcji opóznień po znaku 0xFF
gi oznacza, że program nie reaguje
(przestarzałe)
na stan linii DCD. Nie ustawienie
FF0 Brak opóznienia
tej flagi spowoduje, że program bę-
FF1 Odczekaj 2 s po wysłaniu znaku 0xFF
dzie uśpiony dopóki linia DCD nie
osiągnie stanu logicznego 0 (space).
Tab. 7. Tablica znaków specjalnych c_cc (*  patrz opis)
W typowych zastosowaniach
otwieranie portu z innymi ustawie- Stała Opis Kombinacja przycisków
niami nie ma większego sensu. Nie
VINTR Przerwanie (interrupt) CTRL-C
będziemy więc szczegółowo analizo-
VQUIT Koniec (quit) CTRL-Z
wać znaczenia innych opcji. Oczy-
VERASE Kasuj znak (erase) Backspace (BS)
wiście, jeśli ktoś miałby potrzebę
VKILL kasuj linię CTRL-U
skorzystania z nich (i wiedziałby co
VEOF Koniec linii CTRL-D
robi), może ich użyć.
VEOL Koniec linii  powrót karetki CR
VEOL2 Nowa linia LF
Konfiguracja portu
VMIN Minimalna liczba bajtów do odczytania* -
Gdy port szeregowy jest otwarty
VSTART Wznów transmisję CTRL-Q (XON)
należy go odpowiednio skonfiguro-
VSTOP Wstrzymaj transmisję CTRL-S (XOFF)
wać. Sterownik linuksowy zapewnia
Czas oczekiwania na blok danych (wyrażony w dziesiątych
nam ogromną liczbę opcji, z któ-
VTIME -
częściach sekundy)*
rych duża część służy do tego, aby
ułatwić używanie RS232 do przesy-
łania danych w sposób zorientowa- zależy nam na tym, aby sterownik takie tylko przeszkadzałyby w prze-
ny liniowo (przydatne na przykład ignorował znak powrotu karetki CR noszeniu aplikacji okienkowych
w komunikacji z drukarkami). Dla (0x0D), gdyż dla nas może to być z systemu Windows do Linuksa.
nas takie działanie jest niepożądane istotna dana pomiarowa. Podobnie Obowiązuje tu oczywiście ta sama
 chcemy mieć pełną kontrolę nad nie chcemy, aby mapował małe li- zasada co przy otwieraniu portu 
tym co wysyłamy, a także chcemy tery na wielkie i odwrotnie. Kolejną kto chce może z nich skorzystać.
mieć pewność, że to co odczytu- przyczyną, dla której nie skorzysta- Konfiguracja portu odbywa się
jemy z bufora wejściowego jest na- my z większości opcji jakie zapew- poprzez modyfikowanie zawartości
prawdę tym co zostało odebrane łą- nia nam Linux jest fakt, że nie są pól struktury termios. Jest to struk-
czem szeregowym. Na przykład, nie one dostępne pod Windows i jako tura zdefiniowana w pliku termios.h
Elektronika Praktyczna 3/2006
92
K U R S
Jeśli tego nie zrobimy, to pomi-
Tab. 8. Wartości parametru cmd funkcji ioctl
mo, że nie włączyliśmy tych opcji
Funkcja POSIX
Wartość Opis
może się zdarzyć, że nasza apli-
(options  struktura termios)
kacja zacznie wykazywać takie ce-
TCGETS Pobranie aktualnych ustawień portu tcgetattr
chy! Wystarczy, że wcześniej uży-
TCSETS Ustaw nowe ustawienia portu natychmiast tcsetattr(fd, TCSANOW, &options)
wana była aplikacja, która je włą-
Wyczyść (flush) bufory wejściowy i wyjścio- tcsetattr(fd, TCSAFLUSH,
TCSETSF
czyła. Znaczenie stałych użytych
wy po czym ustaw nowe ustawienia portu &options)
w powyższej instrukcji przedstawię
Poczekaj na opróżnienie buforów wejściowego
za chwilę.
TCSETSW i wyjściowego po czym ustaw nowe ustawie- tcsetattr(fd, TCSADRAIN, &options)
Po drugie  nie wolno zmieniać
nia portu
pól struktury termios poprzez bez-
TCSBRK Wystaw sygnał break na zadany czas tcsendbreak, tcdrain
pośrednie przypisanie pewnej war-
TCXONC Konfiguracja programowej kontroli przepływu tcflow
tości, na przykład takiej
Przepłukanie bufora wejściowego i/lub wyj-
TCFLSH tcflush
ściowego
options.c_iflag = 0;
TIOCMGET Pobranie stanu linii sterujących -
TIOCMSET Zmiana stanu linii sterujących -
Postępowanie takie może być
Pobranie liczby bajtów znajdujących się w
FIONREAD -
bardzo szkodliwe, gdyż inne apli-
buforze wejściowym (jeszcze nie odczytanych)
kacje mogą korzystać z pewnych
i odpowiada za przechowywanie in- open. Teraz zmienna options zawie- opcji, których my nie powinniśmy
formacji konfiguracyjnych związa- ra kopię aktualnej struktury termios zmieniać.
nych z urządzeniami terminalowy- systemu. Możemy dowolnie mody- Gdy już dokonamy wszystkich
mi. Zawiera ona kilkadziesiąt flag, fikować tę kopię (jej pola) poprzez niezbędnych zmian w zmiennej
które można modyfikować za po- bitowe operacje OR i AND z odpo- options należy wpisać je do wła-
mocą specjalnych masek bitowych wiednimi stałymi charakterystyczny- ściwej struktury termios w systemie.
i w ten sposób zmieniać ustawienia mi dla poszczególnych pól struk- Służy do tego funkcja tcsetattr, któ-
portu szeregowego. Możemy decydo- tury termios (omówię je za chwi- ra oprócz deskryptora pliku i adresu
wać czy i jak dane wejściowe i wyj- lę). Wykonanie operacji OR ze sta- zmodyfikowanej kopii termios przyj-
ściowe mają być przetwarzane, włą- łą oznacza włączenie odpowiadają- muje dodatkowy argument określa-
czać i wyłączać odbiornik i tak da- cej jej opcji, zaś wykonanie operacji jący sposób jej działania. Możliwe
lej. Struktura termios zawiera 7 pól AND z negacją tej stałej  wyłącze- wartości tego argumentu przedsta-
wymienionych w tab. 1. nie. Należy przy tym pamiętać, że: wiono w tab. 2.
Ogólny schemat modyfikacji ter- Po pierwsze  nie wystarczy Zauważmy, że ustawienie prędko-
mios przedstawiony jest na list. 2. nie włączyć jakiejś opcji przez OR, ści transmisji (co ciekawe  osobno
Modyfikacja polega na skopiowaniu aby nie była ona włączona! Nale- dla nadajnika i odbiornika) nie od-
zawartości tej struktury do pewnej ży ją w tym celu wyłączyć opera- bywa się poprzez odpowiednie ope-
zmiennej, stosownej modyfikacji tej cją AND! Wynika to stąd, że struk- racje logiczne OR, lecz za pomocą
zmiennej, a następnie na skopiowa- tura termios dla danego portu jest funkcji cfsetispeed i cfsetospeed. Ro-
niu jej zawartości z powrotem do wspólna dla wszystkich aplikacji. bimy tak dlatego, że starsze wer-
struktury termios. W pierwszej kolej- Przykładowo, jeśli nie chcemy aby sje systemów umieszczały informa-
ności deklarujemy zmienną options nasz program korzystał z mapowa- cję o prędkości w polu c_cflag (patrz
typu struct termios. Następnie po- nia znaku CR (0x0D) na znak NL następny punkt), zaś nowe umiesz-
bieramy aktualne ustawienia por- (0x0A) i odwrotnie, to musimy ko- czają ją w polach c_ispeed i c_ospe-
tu za pomocą funkcji tcgetattr, któ- niecznie wyłączyć te funkcje pisząc ed struktury termios. Użycie cfse-
ra oprócz adresu zmiennej options tispeed i cfsetospeed uwalnia nas od
przyjmuje deskryptor pliku portu options.c_iflag &=~(ICRNL | zastanawiania się nad tym, gdzie
zwrócony wcześniej przez funkcję INLCR); w istocie informacje te się znajdują.
List. 4. Odczyt i modyfikacja stanu linii sterujących portu Konfiguracja: pole c_cflag
//*******************************************
 opcje sterowania
//
// Getting and setting the control signals
Zawartość pola c_cflag kontro-
//
luje liczbę bitów danych w ramce
//*******************************************
#include
RS232, steruje kontrolą parzystości,
#include
określa liczbę bitów stopu oraz po-
//File descriprot
int fd;
Tab. 9. Stałe określające stan linii
//Variable for holding the control signals
sterujących portu
int status;
Stała Opis
//Getting the control signals
ioctl(fd, TIOCMGET, &status);
TIOCM_DTR Sterowanie stanem DTR
TIOCM_RTS Sterowanie stanem RTS
// Setting the control signals
TIOCM_CTS Sterowanie stanem CTS
// - logic 0 to DTR
ioctl(fd, TIOCMGET, &status);
TIOCM_CAR Sterowanie stanem DCD
status &= ~TIOCM_DTR;
TIOCM_CD Synonim dla TIOCM_CAR
ioctl(fd, TIOCMSET, &status);
TIOCM_DSR Sterowanie stanem DSR
Elektronika Praktyczna 3/2006
93
K U R S
zwala włączyć sprzętową kontrolę nical input) lub wejścia bez prze- options.c_iflag &= ~(IXON | IXOFF
przepływu (hardware flow control). twarzania danych (raw input). Wej- | IXANY);
Stałe pozwalające włączać i wyłą- ście typu canonical input uzyskamy
czać poszczególne opcje przedsta- następująco: Konfiguracja: pole c_oflag
wiono w tab. 3. Wartości określa-  opcje wyjścia
jące baudrate interesują nas tylko options.c_lflag |= (ICANON | Stałe dla opcji wyjścia przed-
w kontekście użycia funkcji cfseti- ECHO | ECHOE); stawia tab. 6. Ich znaczenie jest
speed i cfsetospeed  jak napisałem zaś wejście bez przetwarzania w pewnym sensie analogiczne do
wyżej, nie należy ustawiać pręd- (raw input) następująco: opcji pola c_iflag  określają sposób
kości transmisji korzystając z po- options.c_lflag &= ~(ICANON | przetwarzania bajtów przed umiesz-
la c_cflag. Zupełnie nieprzydatne ECHO | ECHOE | ISIG); czeniem ich w buforze wyjściowym
są z naszego punktu widzenia sta- i wysłaniem do urządzenia, z któ-
łe EXTA i EXTB. Natomiast istotną Opcje pozwalające uzyskać echo rym komunikuje się komputer. Jak
rolę pełnią pola CSIZE i CS5...CS8. są ciekawe i w wielu przypadkach nietrudno się domyślić  w typo-
Pozwalają one na wybór liczby bi- mogą być użyteczne. Jednak nie wych zastosowaniach nie skorzysta-
tów danych. W ogromnej większości przydadzą się zbytnio w typowych my z tych ułatwień. W przypadku
przypadków ustawimy 8 bitów da- zastosowaniach łącza szeregowe- pola c_oflag mamy ułatwione zada-
nych przy użyciu następujących in- go, dlatego wyłączymy je wybiera- nie. Otóż wszelkie manipulacje na
strukcji: jąc wejście typu raw input. Należy wysyłanych danych następują tyl-
pamiętać, że włączenie echa przy ko wtedy, gdy ustawiona jest flaga
options.c_cflag &= ~CSIZE; współpracy z urządzeniami, które OPOST. W takiej sytuacji pozosta-
options.c_cflag |= CS8; odpowiadają na zadawane im pyta- łe flagi pola c_oflag określają spo-
nia (np. w architekturze Master Sla- sób ich przetwarzania. My wyłączy-
Stała CSTOPB ustawia liczbę bi- ve) może doprowadzić do nieskoń- my OPOST i tym samym uzyskamy
tów stopu  włączona daje 2 bity czonej pętli, która zablokuje port. wyjście bez przetwarzania  raw
stopu, wyłączona  1 bit. CREAD output. Posłuży do tego instrukcja:
włącza odbiornik  aby móc od- Konfiguracja: pole c_iflag
bierać dane nie wystarczy otwo-  opcje wejścia options.c_oflag &= ~OPOST;
rzyć port z opcją O_RDWR (funkcja W tab. 5 zamieściłem stałe dla
open), musi być dodatkowo usta- pola c_iflag. Pole to określa zacho- Konfiguracja: tablica znaków
wiona flaga CREAD. Flaga PARENB wanie się portu przy wykryciu błę- specjalnych c_cc
włącza kontrolę parzystości. Jeśli ją dów parzystości oraz odpowiada Tab. 7 zawiera tablicę znaków
włączymy, to za pomocą PARODD za włączenie programowej kontro- specjalnych, do której wskaznik sta-
możemy określić rodzaj parzystości li przepływu (software flow control). nowi pole c_cc struktury termios.
 odd parity lub even parity. Pod- Flagi INPCK i PARMRK określają Zawiera ona kody znaków specjal-
sumowując, kombinacja stałych PA- jak zaznaczane są znaki obciążone nych, w szczególności znaków słu-
RENB, PARODD, CSIZE i CS5... błędem parzystości  mogą być one żących realizacji programowej kon-
CS8 określa rodzaj ramki. Popular- na przykład zerowane (zamiana na troli przepływu, czyli VSTART
ną ramkę 8n1 otrzymujemy za po- znak \0, czyli po prostu na licz- (XON) i VSTOP (XOFF). Pola VMIN
mocą następujących instrukcji: bę 0). Jeśli włączona jest opcja PA- i VTIME pozwalają na wykorzysta-
RENB w polu c_cflag, to należy też nie wbudowanej w sterownik funk-
options.c_cflag &= ~PARENB; zdefiniować, co sterownik ma robić cji kontroli przeterminowania ope-
options.c_cflag &= ~CSTOPB; z błędnymi bajtami. racji odczytu danych. Ich zawar-
options.c_cflag &= ~CSIZE; Pole c_iflag umożliwia także tość jest ignorowana gdy wybrano
options.c_cflag |= CS8; włączenie mapowania pewnych baj- wejście typu canonical input lub
tów na inne bajty przed umiesz- gdy skonfigurowano port z włączo-
Powinniśmy ustawić flagę CLO- czeniem ich w buforze wejściowym ną opcją NDELAY podczas otwarcia
CAL. Dzięki temu proces związa- oraz ignorowanie pewnych war- lub przez wywołanie odpowiedniej
ny z naszą aplikacją będzie miał tości. Służą do tego stałe INLCR, funkcji fcntl (opis w dalszej czę-
wyłączność na korzystanie z por- IGNCR, ICRNL, IUCLC. My chcemy, ści). Elementy VMIN i VTIME po-
tu. Flaga CRTSCTS (nowa CNEW_ aby w buforze wejściowym znajdo- zwalają poinformować sterownik, że
RTSCTS) włącza sprzętową kontrolę wały się dokładnie te znaki, któ- oczekujemy bloków danych o ściśle
przepływu. W typowych zastosowa- re wysłało urządzenie dołączone do określonej długości. Znaczenie tych
niach nie jest nam ona potrzebna, PC  w związku z tym wyłączymy pól jest następujące: VTIME okre-
dlatego flagę tę wyzerujemy. mapowanie CR na NL (i odwrotnie) śla czas oczekiwania na odebranie
oraz ignorowanie znaku CR, a także pierwszego znaku z bloku danych
Konfiguracja: pole c_lflag mapowanie wielkich liter na małe. o długości VMIN. Jeśli znak ten na-
 opcje lokalne Oczywiście użyjemy do tego nastę- dejdzie w czasie VTIME (od wywo-
W tab. 4 przedstawiłem stałe dla pującej instrukcji: łania funkcji read), to operacja od-
pola c_lflag. Pole to pozwala okre- czytywania będzie czekać, aż liczba
ślić, czy dane wejściowe będą prze- options.c_iflag &= ~(ICRNL | IN- odebranych znaków osiągnie VMIN
twarzane przed umieszczeniem ich LCR | IGNCR | IUCLC); i dopiero wtedy się zakończy zwra-
w buforze wejściowym. Generalnie cając liczbę odczytanych bajtów
pozwala ono na ustawienie wej- Wyłączymy także programową (patrz opis funkcji read). Jeśli zaś
ścia zorientowanego liniowo (cano- kontrolę przepływu pisząc: w czasie VTIME nie nadejdzie ani
Elektronika Praktyczna 3/2006
94
K U R S
jeden znak, to funkcja read zwróci zanim ją wywołamy sprawdzimy, liczbę argumentów. Tak jednak nie
0. Dzięki temu informujemy sterow- ile nieodczytanych bajtów oczeku- jest  funkcja przyjmuje co najwy-
nik, że oczekujemy bloków o dłu- je w kolejce wejściowej. żej 3 argumenty, zaś użycie kropek
gości VMIN i wszelkie wywołania sprawia, że kompilatory C/C++ nie
funkcji read zwrócą albo oczekiwa- Wysyłanie danych  funkcja kontrolują typu trzeciego argumen-
ną liczbę bajtów danych, albo 0. write tu. Jego konkretne znaczenie zależy
Jeśli wartość VMIN ustawi- W przeciwieństwie do czytania, od wartości argumentu cmd.
my na 0, to VTIME określać bę- pisanie do portu pozbawione jest Tab. 8 przedstawia niektóre war-
dzie maksymalny czas oczekiwania  sztuczek (jest z resztą ogólną pra- tości argumentu cmd przydatne
na każdy nadchodzący bajt. Ozna- widłowością, że łatwiej implemen- przy pracy z interfejsem szerego-
cza to, że jeśli wywołamy funkcję tuje się nadajniki niż odbiorniki). wym. Pokazane są tam także funk-
read w chwili, gdy w buforze wej- Zajmuje się tym funkcja write okre- cje standardu POSIX, których dzia-
ściowym nie ma danych, to jeśli ślona następująco: łanie odpowiada działaniu funkcji
nie nadejdą one w czasie VTIME ioctl wywołanej z określonym argu-
 funkcja zwróci 0. Jeśli nadejdą, int write(int fd, void *buffer, int mentem. Jak widać są wśród nich
to zwróci 1 natychmiast po otrzy- NumberOfBytesToWrite); znane nam już funkcje tcgetattr
maniu pierwszego bajta. Jeśli zaś i tcsetattr (w systemach unixowych
w chwili wywołania dane w buforze gdzie: używają one ioctl).
są (choćby 1 bajt), to zostaną one  fd  deskryptor pliku portu;
natychmiast odczytane.  buffer  wskaznik na początek Odczyt i modyfikacja stanu
obszaru pamięci, gdzie znajdują linii sterujących portu
Czytanie z portu  funkcja read się dane przeznaczone do wysła- Przydać się mogą wywołania
Odczyt danych z bufora wejścio- nia; ioctl z argumentem cmd równym
wego odbywa się za pomocą funkcji  NumberOfBytesToWrite  liczba TIOCMGET i TIOCMSET  pozwa-
read. Jej prototyp jest następujący: bajtów do wysłania. lają one odczytywać i zmieniać stan
Funkcja zwraca liczbę faktycznie linii sterujących portu szeregowego.
int read(int fd, void *buffer, int wysłanych bajtów. Tab. 9 przedstawia stałe określają-
NumberOfBytesToRead); ce różne stany tych linii, zaś na
Pobranie liczby list. 4 zamieszczony jest przykład
Funkcja ta przyjmuje następują- nieodczytanych bajtów ich użycia. Jak widać zmiana stanu
ce argumenty: znajdujących się w buforze linii sterujących polega na pobra-
 fd  deskryptor pliku portu; wejściowym  wywołanie niu kopii ich stanu do tymczasowej
 buffer  wskaznik na początek funkcji ioctl zmiennej, modyfikacji tej zmiennej
obszaru pamięci, gdzie mają Jest to niezwykle cenna właści- i w końcu na skopiowaniu jej war-
być umieszczone dane odczyta- wość sterownika portu  umożliwia tości z powrotem do systemu.
ne z bufora wejściowego portu; poznanie, ile bajtów zostało odebra-
 NumberOfBytesToRead  żądana nych przez UART komputera bez Zamykanie portu
liczba bajtów do odczytu. konieczności ich odczytywania. Po- Port zamykamy za pomocą funk-
Funkcja read zwraca liczbę fak- zwala to na przykład na dokonanie cji close określonej następująco:
tycznie odczytanych bajtów. odczytu dopiero wtedy, gdy nade-
Ciekawe jest jej działanie, gdy szła taka ilość informacji, jaka nas close(int fd)
w buforze wejściowym nie ma żad- interesuje. W Linuksie możemy to
nych danych i ustawiono wejście zrobić za pomocą funkcji ioctl, na Podsumowanie
bez przetwarzania (raw input). Do- przykład tak, jak to przedstawiłem Mamy już wszystkie niezbędne
myślne zachowanie jest takie, że na list. 3. informacje pozwalające wykorzystać
funkcja ta blokuje aktualny wątek port szeregowy w systemie Linux.
i czeka na nadejście danych lub Funkcja ioctl Pozostało zebrać je do przysłowio-
na opisane wyżej przeterminowanie W poprzednim punkcie użyta zo- wej kupy i zastosować.
oczekiwania. Można wyłączyć takie stała funkcja systemowa ioctl. Funkcja W drugiej części artykułu wyko-
zachowanie i sprawić, że funkcja ta pozwala na wykonanie przeróżnych rzystamy zdobyte informacje w prak-
read będzie w takiej sytuacji na- operacji na plikach, a więc w szcze- tyce. Pokażę przykład prostej apli-
tychmiast zwracać 0. Dokonuje się gólności na porcie szeregowym. Posia- kacji konsolowej komunikującej się
tego w sposób następujący: da ona następujący prototyp: z dołączonym do PC urządzeniem
za pośrednictwem RS232C, zaś
fcntl(fd, F_SETFL, FNDELAY); int ioctl(int fd, int cmd, ...) w następnych częściach kursu wez-
miemy na warsztat aplikacje GUI.
Przywrócenie domyślnego, blo- gdzie: W części drugiej znajdzie się także
kującego działania uzyskamy nastę-  fd  deskryptor pliku (np. pli- opis prostego zestawu laboratoryjne-
pująco: ku portu); go zbudowanego na mikrokontrole-
 cmd  stała określająca opera- rze ATmega8, który w dalszych czę-
fcntl(fd, F_SETFL, 0); cję, jaką chcemy wykonać na ściach cyklu posłuży do testowania
pliku wskazywanym przez fd; prezentowanych przykładów.
Pisząc prostą aplikację jedno- Trzy kropki (...) występujące Arkadiusz Antoniak, EP
wątkową skorzystamy z niebloku- w prototypie funkcji ioctl mogą su- arkadiusz.antoniak@ep.com.pl
jącego działania funkcji read, zaś gerować, że przyjmuje ona zmienną
Elektronika Praktyczna 3/2006
95


Wyszukiwarka