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