Linux I/O port programming mini-HOWTO Autor: Riku Saikkonen Riku.Saikkonen@hut.fi v, 28 Grudnia 1997 Wersja polska: Micha³ Szwaczko michalsz@lena.zsg.lublin.pl v1.0, 1 Marca 2000 Ten dokument HOWTO opisuje programowanie sprzêtowych rejestrów wej¶cia/wyj¶cia oraz zatrzymywanie na niewielkie okresy czasu pracy programów u¿ytkownika dzia³aj±cych na Linuxie pracuj±cym w architek turze Intel x86. Dokument zosta³ napisany w standardzie ISO-8859-2. Orygina³ tego dokumentu znajduje siê pod adresem ftp://ftp.icm.edu.pl/pub/Linux/sunsite/docs/HOWTO/mini ______________________________________________________________________ Spis tre¶ci 1. Wstêp 2. U¿ywanie rejestrów wej¶cia/wyj¶cia w programach napisanych w C 2.1 Metoda zwyk³a 2.2 Metoda alternatywna: /dev/port 3. Przerwania (IRQ) oraz dostêp DMA 4. Mierzenie czasu z du¿± dok³adno¶ci± 4.1 Opó¼nienia 4.1.1 Opó¼nianie za pomoc± funkcji sleep() i usleep() 4.1.2 Opó¼nianie za pomoc± funkcji nanosleep() 4.1.3 Opó¼nianie za pomoc± operacji I/O na porcie 4.1.4 Opó¼nianie za pomoc± instrukcji asemblerowych 4.1.5 Instrukcja rdtsc w Pentium 4.2 Mierzenie czasu 5. Inne jêzyki programowania 6. Niektóre przydatne porty 6.1 Port równoleg³y 6.2 Port joysticka/Port gier 6.3 Port szeregowy 7. Porady 8. W razie k³opotów 9. Przyk³adowy program 10. Wyrazy uznania 11. Od t³umacza ______________________________________________________________________ 11.. WWssttêêpp Ten dokument HOWTO opisuje programowanie sprzêtowych rejestrów wej¶cia/wyj¶cia oraz zatrzymywanie na niewielkie okresy czasu pracy programów u¿ytkownika dzia³aj±cych na Linuxie pracuj±cym w architekturze Intel x86. Dokument ten jest nastêpc± bardzo ma³ego IO- port mini-HOWTO tego samego autora. Dokument zosta³ napisany przez Riku Saikkonen. Copyright 1995-1997 Riku Saikkonen. Po szczegó³y odno¶nie praw autorskich zobacz Linux HOWTO copyright <http://sunsite.icm.edu.pl/pub/Linux/sunsite/docs/HOWTO/COPYRIGHT>. Je¶li chcesz co¶ poprawiæ lub dodaæ to ¶mia³o mo¿esz do mnie pisaæ (Riku.Saikkonen@hut.fi)... Zmiany w stosunku do poprzedniej wersji (30 Marca 1997): · Wyja¶nienie kwestii odnosz±cych siê do inb_p/outb_p oraz portu 0x80 · Usuniêcie informacji o udelay(), jako ¿e nanosleep() zapewnia ja¶niejszy sposób jego u¿ycia. · Przekszta³cenie dokumentu na format SGML-Linuxdoc oraz niewielka reorganizacja. · Wiele mniejszych dodatków i modyfikacji. 22.. UU¿¿yywwaanniiee rreejjeessttrróóww wweejj¶¶cciiaa//wwyyjj¶¶cciiaa ww pprrooggrraammaacchh nnaappiissaannyycchh ww CC 22..11.. MMeettooddaa zzwwyykk³³aa Procedury dostêpu do portów (rejestrów) we/wy umieszczone s± w /usr/include/asm/io.h (lub w linux/include/asm-i386/io.h w ¼ród³ach j±dra) Procedury te wystêpuj± w formie makr do wstawienia a wiêc wystarczy ¿e zrobisz #include <asm/io.h>; nie potrzebujesz ju¿ ¿adnych dodatkowych bibliotek. Z powodu ograniczeñ w gcc (obecna wersja 2.7.2.3 i poni¿ej) oraz egcs (wszystkie wersje) programy wykorzystuj±ce te procedury _m_u_s_z_± byæ kompilowane z w³±czon± optymalizacj± (gcc -O1 lub wiêcej) lub mo¿esz te¿ zrobiæ #define extern bez parametru (puste) zanim w³±czysz <asm/io.h>. Do odpluskwiania (debugging) mo¿esz u¿yæ gcc -g -O (przynajmniej w nowoczesnych wersjach gcc), chocia¿ optymalizacja mo¿e spowodowaæ, i¿ debugger bêdzie zachowywa³ siê trochê dziwnie. Je¶li to sprawia Ci k³opot, procedury korzystaj±ce z rejestrów we/wy wstaw do osobnego pliku i tylko ten plik poddaj optymalizacji podczas kompilacji. Zanim dostaniesz siê do jakiegokolwiek portu we/wy, musisz nadaæ swemu programowi uprawnienia do tego. Robi siê to wywo³uj±c funkcjê ioperm() (zdeklarowan± w unistd.h i zdefiniowan± w j±drze) gdzie¶ w okolicach pocz±tku programu (przed jakimkolwiek dostêpem do portów) Sk³adnia tego polecenia to ioperm(sk±d,ile,w³acz) gdzie sk±d jest pierwszym portem do którego chcesz mieæ dostêp a ile liczb± kolejnych portów do których chcesz mieæ dostêp. Dla przyk³adu ioperm(0x300, 5, 1) da ci dostêp do portów od 0x300 do 0x304 (w sumie piêc portów). Ostatni argument to warto¶c logiczna mówi±ca o tym czy program otrzyma dostêp do portów (1 - prawda) b±d¼ nie (0 - fa³sz). Mo¿esz wywo³ywaæ ioperm() wiele razy aby w³±czyæ wiele nieci±g³ych obszarów rejestrów. Zajrzyj do ioperm(2) w podrêczniku systemowym man po szczegó³y odno¶nie sk³adni. Wywo³anie ioperm() wymaga aby twój program mia³ uprawnienia root'a: wobec czego albo musisz uruchamiaæ go jako root albo daæ mu suid root'a. Po wywo³aniu ioperm() mo¿esz ju¿ zrezygnowaæ z uprawnieñ root'a. Nie wymaga siê aby¶ w sposób jawny wy³±cza³ uprzywilejowany dostêp do portów (czyli ioperm(....,0)) na koñcu programu; robi siê to automatycznie wraz z zakoñczeniem procesu. Funkcja setuid() w wypadku u¿ytkownika bez przywilejów root'a nie odbiera dostêpu do portów przydzielonego przez ioperm() ale funkcja fork() ju¿ tak. (proces potomny nie dostaje dostêpu ale proces-rodzic go zachowuje) ioperm() mo¿e jedynie umo¿liwiæ dostêp do portów od 0x000 do 0x3FF. Je¶li chcesz u¿ywaæ innych portów (wy¿szych) musisz u¿yæ funkcji iopl() (która daje ci dostêp do wszystkich portów od razu) U¿yj argumentu 3 (czyli iopl(3)) aby daæ swemu programowi dostêp do _w_s_z_y_s_t_k_i_c_h portów (b±d¼ ostro¿ny - dostêp do niew³a¶ciwych portów mo¿e wywo³aæ najró¿niejsze usterki komputera). Musisz mieæ uprawnienia root'a aby wywo³aæ iopl(). Po szczególy zajrzyj do podrêcznika systemowego man: iopl(2) Teraz w³a¶ciwy dostêp do portów.. Aby odczytaæ bajt (8 bitów) z portu, wywo³aj funkcjê inb(port), zwraca ona odczytan± warto¶æ. Aby zapisaæ bajt do portu wywo³aj outb(warto¶æ,port) (zwróæ uwagê na kolejno¶æ argumentów) Aby odczytaæ s³owo (16 bitów) z portów x i x+1 (jeden bajt z ka¿dego ³±czone w s³owo za pomoc± asemblerowej instrukcji inw) wywo³aj inw(x) Aby zapisaæ s³owo do tych dwóch portów u¿yj outw(warto¶æ,x) Je¶li nie jeste¶ pewien której instrukcji u¿yæ (operuj±cej bajtem czy s³owem) prawdopodobnie bêdziesz chcia³ u¿yæ inb() i outb() - wiêkszo¶æ urz±dzeñ zaprojektowana jest z bajtowo- zorientowanym dostêpem do portów. Zauwa¿, ¿e wykonanie ka¿dej instrukcji operuj±cej na portach zajmuje co najmniej mikrosekundê. Makra inb_p(),outb_p(),inw_p() i outw_p() dzia³aj± identycznie z tymi powy¿ej ale dodatkowo wykonuj± opó¼nienie (oko³o 1 mikrosekundy) po dostêpie do portu. Mo¿esz spowodowaæ ¿e opó¼nienie bêdzie warto¶ci ok 4 mikrosekund je¶li zrobisz #define REALLY_SLOW_IO zanim w³±czysz <asm/io.h> Makra te zwykle (chyba ¿e zrobisz #define SLOW_IO_BY_JUMPING, które jest raczej mniej dok³adne) u¿ywaj± zapisu do portu 0x80 zby uzyskaæ opó¼nienie, wobec czego musisz najpierw daæ dostêp do portu 0x80 za pomoc± ioperm(). Zapisy do portu 0x80 nie powinny mieæ wp³ywu na ¿adn± czêsæ systemu. Je¶li chcesz poznaæ bardziej wszechstronne metody dostêpu do portów czytaj dalej. W stosunkowo nowych dystrybucjach podrêcznika systemowego man znajduj± siê strony ioperm(2), iopl(2) oraz powy¿szych makr. 22..22.. MMeettooddaa aalltteerrnnaattyywwnnaa:: //ddeevv//ppoorrtt Innym sposobem dostêpu do rejestrów I/O jest otworzenie urz±dzenia /dev/port do zapisu lub/i odczytu za pomoc± funkcji open() (/dev/port to urz±dzenie znakowe, numer g³ówny 1, poboczny 4) (Funkcje f*() z biblioteki stdio maj± wewnêtrzne buforowanie wiêc ich unikaj) Nastêpnie wywo³aj lseek() do odpowiedniego bajtu w tym pliku (pozycja 0 = port 0x00 pozycja 1 = port 0x01 itd) i czytaj (read()) lub zapisuj (write()) bajt lub s³owo. Oczywi¶cie aby to zadzia³a³o twój program musi mieæ mo¿liwo¶c czytania i zapisywania do /dev/port. Metoda ta jest prawdopodobnie wolniejsza od metody normalnej pokazanej powy¿ej ale nie potrzebuje ani optymalizacji programu ani wywo³ywania ioperm(). Nie potrzebuje te¿ uprawnieñ root'a je¶li nadasz zwyk³ym u¿ytkownikom lub jakiej¶ grupie prawa dostêpu do /dev/port - jest to jednak nieporz±dane z punktu widzenia bezpieczeñstwa systemu, jako ¿e mo¿liwe jest wtedy uszkodzenie systemu, a mo¿e nawet zdobycie przywilejów root'a przez bezpo¶redni dostêp za pomoc± /dev/port do dysków, kart sieciowych itp. 33.. PPrrzzeerrwwaanniiaa ((IIRRQQ)) oorraazz ddoossttêêpp DDMMAA Nie mo¿na bezpo¶rednio korzystaæ z IRQ lub DMA w programach u¿ytkownika. Musisz napisaæ sterownik/modu³ do j±dra; zajrzyj do The Linux Kernel Hacker's Guide po szczegó³y a tak¿e do ¼róde³ j±dra po przyk³ady. W programach u¿ytkownika nie mo¿na te¿ przerwañ wy³±czaæ. 44.. MMiieerrzzeenniiee cczzaassuu zz dduu¿¿±± ddookk³³aaddnnoo¶¶ccii±± 44..11.. OOppóó¼¼nniieenniiaa Przede wszystkim trzeba stwierdziæ ¿e z powodu wielozadaniowej natury Linuxa nie mo¿na zagwarantowaæ ¿e procesy w trybie u¿ytkownika bêd± mieæ dok³adn± kontrolê czasu. Twój proces mo¿e zostaæ rozpoczêty w ka¿dej chwili i mo¿e mu to zaj±æ od 10 milisekund do kilku sekund (na bardzo obci±¿onych systemach). Jednak¿e, dla wiêkszo¶ci aplikacji u¿ywaj±cych portów I/O nie ma to wiêkszego znaczenia. Aby zminimalizowaæ to zjawisko mo¿esz nadaæ swemu procesowi wy¿szy priorytet (zobacz nice(2) w podrêczniku man) lub u¿ywaæ zarz±dzania procesami w czasie rzeczywistym (patrz ni¿ej) Je¶li chcesz berdziej precyzyjnie odmierzaæ czas ni¿ pozwalaj± ci na to procesy w trybie u¿ytkownika, mo¿esz skorzystaæ ze wsparcia dla procesów u¿ytkownika w czasie rzeczywistym. J±dra 2.x.x maj± niewielkie wsparcie dla czasu rzeczywistego; zobacz sched_setscheduler(2) w podrêczniku man. Jest równie¿ specjalne j±dro które ma zaawansowane wsparcie dla czasu rzeczywistego. Zobacz <http://luz.cs.nmt.edu/~rtlinux> aby uzyskaæ wiêcej informacji na ten temat. 44..11..11.. OOppóó¼¼nniiaanniiee zzaa ppoommoocc±± ffuunnkkccjjii sslleeeepp(()) ii uusslleeeepp(()) Zacznijmy teraz od ³atwiejszych wywo³añ. Je¶li chcesz opó¼nieñ rzêdu sekund to najpewniej jest u¿yæ sleep(). Dla opó¼nieñ rzêdu przynajmniej dziesi±tek milisekund (10ms wydaje siê byæ najmniejszym mo¿liwym opó¼nieniem) powiniene¶ u¿yæ usleep(). Funkcje te oddaj± czas innym procesom a wiêc czas procesora nie jest marnowany. Zajrzyj do sleep(3) i usleep(3) w podrêczniku man po szczegó³y dotycz±ce tych funkcji. Je¶li potrzebujesz opó¼nieñ mniejszych ni¿ 50 milisekund (w zale¿no¶ci od prêdko¶ci procesora i samego komputera oraz obci±¿enia systemu) oddawanie czasu procesora zajmuje zbyt wiele czasu poniewa¿ linuxowy zarz±dca procesów (w architekturze x86) zwykle zabiera oko³o 10-30 milisekund zanim zwróci kontrolê do twojego procesu. Z tego powodu dla ma³ych opó¼nieñ usleep() zwykle opó¼nia o trochê wiêcej czasu ni¿ podajesz w parametrze a przynajmniej oko³o 10 ms. 44..11..22.. OOppóó¼¼nniiaanniiee zzaa ppoommoocc±± ffuunnkkccjjii nnaannoosslleeeepp(()) W serii j±der 2.0.x znajduje siê nowa funkcja systemowa nanosleep() (zobacz nanosleep(2) w podrêczniku man) która pozwala opó¼niaæ lub zatrzymywaæ proces na krótkie okresy czasu (kilka mikrosekund lub wiêcej). Dla uzyskania opó¼nieñ <=2ms, je¶li (i tylko wtedy) twój proces jest ustawiony na zarz±dzanie z wsparciem dla czasu rzeczywistego (poprzez sched_setscheduler()), nanosleep() u¿ywa pêtli opó¼niaj±cej; w przeciwnym razie zatrzymuje proces tak jak usleep(). Pêtla opó¼niaj±ca u¿ywa udelay() (wewnêtrznej funkcji j±dra u¿twanej przez wiele jego modu³ów) a d³ugo¶æ pêtli jest obliczana na podstawie warto¶ci BogoMips (prêdko¶æ tego rodzaju pêtli opó¼niaj±cej jest jedn± z rzeczy któr± BogoMips dok³adnie mierzy) Zobacz /usr/include/asm/delay.h po szczegó³y odno¶nie dzia³ania tego mechanizmu. 44..11..33.. OOppóó¼¼nniiaanniiee zzaa ppoommoocc±± ooppeerraaccjjii II//OO nnaa ppoorrcciiee Inn± metod± opó¼niania o niewielkie ilo¶ci mikrosekund s± operacje I/O na portach. Czytanie/zapisywanie jakiegokolwiek bajtu z/do portu 0x80 (patrz wy¿ej jak to siê robi) powinno daæ opó¼nienie prawie dok³adnie 1 mikrosekundy bez wzglêdu na typ procesora i jego prêdko¶æ. Mo¿esz tak robiæ wiele razy aby uzyskaæ opó¼nienie rzêdu kilku mikrosekund. Sposób ten nie powinien wywo³ywaæ ¿adnych szkodliwych efektów ubocznych na ¿adnym standardowym komputerze (nawet niektóre modu³y j±dra go u¿ywaj±). W ten sposób uzyskuj± opó¼nienie funkcje {in|out}[bw]_p() (zobacz asm/io.h). W zasadzie, instrukcje I/O na wiêkszo¶ci portów w obszarze 0-0x3ff zbieraj± prawie dok³adnie 1 mikrosekundê, wiêc je¶li na przyk³ad u¿ywasz bezpo¶rednio portu równoleg³ego po prostu wykonaj kilka razy inb() z tego portu aby uzyskaæ opó¼nienie. 44..11..44.. OOppóó¼¼nniiaanniiee zzaa ppoommoocc±± iinnssttrruukkccjjii aasseemmbblleerroowwyycchh Je¶li znasz typ procesora i prêdko¶c zegara w komputerze na którym bêdzie dzia³a³ twój program, mo¿esz na sta³e uzyskaæ krótsze opó¼nienia poprzez zastosowanie pewnych rozkazów asemblerowych (pamiêtaj jednak ¿e twój proces mo¿e siê zacz±æ w dowolnym momencie wobec czego opó¼nienia te mog± byæ wiêksze od czasu do czasu) W tabeli poni¿ej wewnêtrzna prêdko¶æ procesora okre¶la liczbê cykli np dla procesora 50MHz (np. 486DX-50 lub 486DX2-50) jeden cykl zegara zajmuje 1/50000000 sekundy. (200 nanosekund). Instrukcja cykle zegara na 386 cykle zegara na 486 nop 3 1 xchg %ax,%ax 3 3 or %ax,%ax 2 1 mov %ax,%ax 2 1 add %ax,0 2 1 (Przykro mi ale niewiele wiem o Pentium. Pewnie podobnie do 486. Nie mogê znale¼æ ¿adnej instrukcji która zajmowa³a by tylko jeden cykl zegara na 386. Je¶li mo¿esz, u¿ywaj instrukcji jednocyklowych, w prze ciwnym razie architektura potokowa (pipelining) u¿ywana w nowoczesnych procesorach mo¿e daæ jeszcze krótsze czasy) Rozkazy nop i xchg z tabeli powy¿ej nie powinny powodowaæ ¿adnych skutków ubocznych. Reszta mo¿e modyfikowaæ rejestr znaczników ale nie powinno mieæ to znaczenia bo gcc powinno to wykryæ. U¿ycie nop jest dobrym wyborem. Aby skorzystaæ z powy¿szego, trzeba w swoim programie wywo³aæ funkcjê asm("instrukcja") Sk³adnia instrukcji jest taka sama jak w tabeli powy¿ej. Je¶li chcesz u¿yæ kilku instrukcji w jednym wywo³aniu funkcji asm rozdziel je ¶rednikami. Na przyk³ad asm("nop ; nop ; nop ; nop") wywo³a cztery razy instrukcjê nop opó¼niaj±c nasz program o 4 cykle na 486 lub Pentium (lub 12 cykli na 386) Funkcja asm() jest przez gcc t³umaczona na wstawkê w asemblerze a wiêc nie ma zagro¿enia przekroczenia limitu wywo³añ funkcji. Opó¼nienia krótsze ni¿ jeden cykl zegara nie s± mo¿liwe w architekturze Intel i386. 44..11..55.. IInnssttrruukkccjjaa rrddttsscc ww PPeennttiiuumm Je¶li masz Pentium, mo¿esz odczytaæ ilo¶æ cykli zegara które up³ynê³y od ostatniego uruchomienia komputera. Robi siê to za pomoc± takiego kodu w C: ______________________________________________________________________ extern __inline__ unsigned long long int rdtsc() { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; } ______________________________________________________________________ Mo¿esz odczytywaæ t± warto¶æ w celu opó¼niania o dowoln± ilo¶æ cykli. 44..22.. MMiieerrzzeenniiee cczzaassuu Dla czasów rzêdu sekundy prawdopodobnie naj³atwiej bêdzie u¿yæ funkcji time(). Dla bardziej dok³adnych pomiarów: gettimeofday() jest dok³adne co do mikrosekundy (ale zobacz wy¿ej o zarz±dzaniu procesami) Dla Pentium fragment kodu rdtsc powy¿ej jest dok³adny co do cykla zegarowego. Je¶li chcesz aby twój proces dostawa³ jaki¶ sygna³ po jakim¶ czasie, u¿yj settimer() lub alarm(). Zajrzyj do stron podrêcznika man dotycz±cych tych funkcji. 55.. IInnnnee jjêêzzyykkii pprrooggrraammoowwaanniiaa Opis powy¿ej koncentruje siê na jêzyku C. Powinien bezpo¶rednio odno¶iæ siê te¿ do C++ i Objective C. W asemblerze musisz wywo³aæ ioperm() lub iopl() tak jak w C ale potem mo¿esz ju¿ u¿ywaæ instrukcji czytania/zapisywania portów bezpo¶rednio. W innych jêzykach, je¶li nie mo¿esz wstawiaæ do programu wstawek w asemblerze lub C b±d¼ je¶li nie mo¿esz u¿yæ funkcji systemowych opisanych powy¿ej, naj³atwiej bêdzie napisaæ osobny program w C ze wszystkimi operacjami na portach I/O i wszystkimi opó¼nieniami których potrzbujesz po czym skompilowaæ go i zlinkowaæ z reszt± twojego programu. Mo¿esz te¿ u¿yæ /dev/port jak to opisano powy¿ej. 66.. NNiieekkttóórree pprrzzyyddaattnnee ppoorrttyy Teraz trochê informacji programistycznych dotycz±cych zwyk³ych portów które mog± byæ bezpo¶rednio u¿yte do operacji I/O w logice TTL (lub CMOS). Je¶li chcesz u¿ywaæ tych lub innych zwyk³ych portów zgodnie z ich normalnym przeznaczeniem (np chcesz sterowaæ normaln± drukark± b±d¼ modemem) powiniene¶ najpewniej u¿yæ istniej±cych sterowników (zwykle do³±czonych do j±dra) zamiast programowaæ porty bezpo¶rednio jak to opisuje ten dokument. Ten rozdzia³ jest dla tych którzy chc± pod³±czyæ do standardowych portów komputera wy¶wietlacze LCD, silniki krokowe lub inne niestandardowe urz±dzenia elektroniczne. Je¶li chcesz sterowaæ jakim¶ ur¿±dzeniem produkowanym na rynek masowy, np. skanerem (które to urz±dzenie jest ju¿ na rynku jaki¶ czas) poszukaj raczej istniej±cego sterownika Linuxowego. Dobrym miejscem aby zacz±æ jego poszukiwania jest Hardware-HOWTO www.hut.fi/Misc/Electronics jest dobrym ¼ród³em informacji dotycz±cych pod³±czania urz±dzeñ do komputera (jak i samej elektroniki w ogóle) 66..11.. PPoorrtt rróówwnnoolleegg³³yy Adres bazowy portu równoleg³ego (zwany poni¿ej >BASE) to 0x3bc dla /dev/lp0, 0x378 dla /dev/lp1 i 0x278 dla /dev/lp2. Je¶li chcesz sterowaæ tylko czym¶ co dzia³a jak normalna drukarka powiniene¶ zapoznaæ siê z Printing-HOWTO. Oprócz standardowego trybu tylko-do-zapisu opisanego powy¿ej, w wiêkszo¶ci portów równoleg³ych istnieje jeszcze rozsze¿ony tryb dwukierunkowy. Po informacje na ten temat oraz na temat nowych trybów ECP/EPP (jak i samego standardu IEEE 1284 w ogóle) zajrzyj na http://www.fapo.com oraz na http://www.senet.com.au/~cpeacock/parallel.htm Pamiêtaj, ¿e skoro nie mo¿esz u¿ywaæ DMA i IRQ w programach u¿ytkownika, aby u¿yæ trybów ECP/EPP bêdziesz prawdopodobnie musia³ napisaæ w³asny sterownik do j±dra. Zdaje siê, ¿e kto¶ ju¿ pisze taki sterownik ale nie znam szczegó³ów. Port BASE+0 (port danych) kontroluje sygna³y danych portu (D0 do D7 dla bitów od 0 do 7) Stany: 0=niski (0 V), 1=wysoki (5 V). Zapis do tego portu zatrzaskuje dane na pinach. Odczyt zwraca ostatnio zapisan± dan± w trybie normalnym b±d¼ rozsze¿onym lub dane z innego urz±dzenia w rozsze¿onym trybie odczytu. Port BASE+1 (port statusu) jest tylko-do-odczytu i zwraca stan poni¿szych sygna³ów wejsciowych. · Bity 0 i 1 s± zarezerwowane. · Bit 2 Status IRQ (nie jest to sygna³ z pina - nie wiem w jaki sposób to dzia³a) · Bit 3 ERROR (1=stan wysoki) · Bit 4 SLCT (1=stan wysoki) · Bit 5 PE (1=stan wysoki) · Bit 6 ACK (1=stan wysoki) · Bit 7 -BUSY (0=stan wysoki) (Nie jestem pewien stanów tych bitów.) Port BASE+2 (Port kontrolny) jest tylko do zapisu (odczyt zwraca ostatnio zapisan± warto¶æ) i kontroluje poni¿sze sygna³y kontrolne: · Bit 0 -STROBE (0=stan wysoki) · Bit 1 AUTO_FD_XT (1=stan wysoki) · Bit 2 -INIT (0=stan wysoki) · Bit 3 SLCT_IN (1=stan wysoki) · Bit 4 gdy ustawiony na 1 - w³±cza IRQ portu równoleg³ego (co ma miejsce przy przejsciu sygna³u ACK ze stanu niskiego do wysokiego) · Bit 5 kontroluje kierunek danych w trybie rozsze¿onym (0 = zapis, 1 = odczyt) i jest ca³kowicie tylko-do-zapisu. (Odczyt nie zwraca niczego u¿ytecznego dla tego bitu). · Bity 6 i 7 s± zarezerwowane. (Znowu nie jestem pewien stanów.) Rozk³ad wyprowadzeñ. (25 pinowe ¿eñskie gniazdo typu D) (i=wejscie, o=wyjscie): 1io -STROBE, 2io D0, 3io D1, 4io D2, 5io D3, 6io D4, 7io D5, 8io D6, 9io D7, 10i ACK, 11i -BUSY, 12i PE, 13i SLCT, 14o AUTO_FD_XT, 15i ERROR, 16o -INIT, 17o SLCT_IN, 18-25 Ground Specyfikacja IBM twierdzi ¿e piny 1,14,16 i 17 (wyjscia kontrolne) maj± otwarte kolektory podpiête do +5V przez 4.7 kiloomowe oporniki (dostarcza 20 mA, pobór 0.55 mA, wyjscie w stanie wysokim +5V minus to co podpiête) Reszta pinów pobiera 24 mA, dostarcza 15 mA a wyjscie w stanie wysokim to minimum 2.4V. Stan niski dla wszystkich to maximum 0.5V. Porty równoleg³e inne ni¿ IBM prawdopodobnie odbiegaj± od tego standardu. Po wiêcej informacji na ten temat zajrzyj na http://www.hut.fi/Misc/Electronics/circuits/lptpower.html Na koniec jeszcze ostrze¿enie: Uwa¿aj z uziemieniem. Zepsu³em ju¿ parê portów przez pod³±czanie siê do nich gdy komputer by³ w³±czony. Dobrze jest w takim wypadku u¿ywaæ portów równoleg³ych nie zintegrowanych z p³yt± g³ówn± (zwykle mo¿na dodaæ drugi port równoleg³y do komputera za pomoc± taniej standardowej karty I/O (tzw ajo³ki - t³um); po prostu wy³±cz porty których nie potrzebujesz i ustaw adres portu na karcie IO na jaki¶ wolny adres. Nie musisz siê martwiæ o IRQ dla portu równoleg³ego gdy¿ normalnie siê go nie u¿ywa) 66..22.. PPoorrtt jjooyyssttiicckkaa//PPoorrtt ggiieerr Port gier jest umieszczony pod adresami 0x200-0x207. Je¶li chcesz kontrolowaæ normalny joystick to jest do tego specjalny sterownik w j±drze, zobacz <ftp://sunsite.icm.edu.pl/sunsite/pub/Linux/kernel/patches> Plik nazywa siê joystick-*. Rozk³±d wyprowadzeñ od stony portu (15-pinowe ¿eñskie gniazdo typu D- shell): · piny 1,8,9,15: +5 V (zasilanie) · piny 4,5,12: Masa · piny 2,7,10,14: Wejscia cyfrowe (Odpowiednio: BA1, BA2, BB1, i BB2) · piny 3,6,11,13: Wejscia ``analogowe'' (Odpowiednio: AX, AY, BX, i BY) Piny +5V zwykle s± pod³±czane bezpo¶rednio do linii zasilania na p³ycie g³ównej, wiêc, w zale¿no¶ci od p³yty, zasilacza i portu ,powinny dawaæ ca³kiem sporo mocy. Cyfrowe wej¶cia s± u¿ywane dla przycisków joysticków które mo¿esz sobie pod³±czyæ do portu (joystick A i B, dwa przyciski ka¿dy) Wej¶cia te powinny byæ na poziomach TTL a ich stan mo¿esz odczytaæ z portu statusu (zobacz poni¿ej) Prawdziwy joystick zwraca stan niski (0V) kiedy przycisk jest naci¶niêty a stan wysoki (+5V z pinów zasilaj±cych przez jednokiloomowy rezystor) kiedy jest zwolniony. Tak-zwane wej¶cia analogowe w istocie mierz± opór. Port joysticka ma przy³±czony do tych 4 wej¶æ poczwórny multiwibrator (quad one-shot multivibrator) (scalak 558) Na ka¿dym wej¶ciu mamy rezystor 2.2k pomiêdzy pinem a wej¶ciem multiwibratora oraz kondensator 0.01uF pomiêdzy wyj¶ciem multiwibratora a mas±. Prawdziwy joystick ma jeszcze potencjometr dla ka¿dej z osi (X i Y) umieszczony pomiêdzy +5V a w³a¶ciwym pinem wejsæiowym (AX lub AY dla joysticka A oraz BX lub BY dla joysticka B) Kiedy jest aktywny, multiwibrator ustawia swoje wyj¶cia w stan wysoki (5V) i oczekuje a¿ ka¿dy z kondensatorów osi±gnie 3.3V po czym ustawia w stan niski odpowiednie linie wyj¶ciowe. Dlatego w³a¶nie czas trwania okresu kiedy multivibrator jest w stanie wysokim jest proporcjonalny do oporu potencjometru joysticka. (czyli pozycji joysticka na odpowiedniej osi) Relacja wygl±da tak: R = (t - 24.2) / 0.011, gdzie R to opór potencjometru w omach a t to czas trwania stanu wysok iego w sekundach. Wobec tego aby odczytaæ wej¶æia analogowe musisz najpierw uaktywniæ multiwibrator (za pomoc± zapisu do odpowiedniego portu - patrz ni¿ej) po czym odczytywaæ stan czterech osi (za pomoc± nastêpuj±cych po sobie odczytów z portów) a¿ zmieni± stan z wysokiego na niski po czym mierzysz czas trwania stanu wysokiego. Odczytywanie takie zu¿ywa sporo czasu procesora, co w systemie wielozadaniowym takim jak Linux nie bêd±cym systemem czasu rzeczywistego powoduje niedok³±dno¶æ rezultatów gdy¿ nie mo¿na odczytywaæ portu stale (chyba ¿e u¿yjesz sterownika niskopoziomowego i wy³±czysz przerwania na czas odczytów - ale to zabiera jeszcze wiêcej czasu procesora). Je¶li wiesz ¿e przej¶cie do stanu niskiego zajmie sygna³owi d³u¿¶zy czas (rzêdu 10 ms) mo¿esz u¿yæ usleep() przed odczytem oddaj±c w ten sposób czas procesora innym procesom. Jedynym portem do którego potrzebujesz mieæ dostêp to port 0x201 (inne porty zachowuj± siê identycznie b±d¼ nie robi± nic). Ka¿dy zapis (nie wa¿ne czego) do tego portu uaktywnia multiwibrator. Odczyt z tego portu zwraca stan poszczególnych sygna³ów wej¶ciowych. · Bit 0: AX (stan (1=stan wysoki) wyj¶æia multiwibratora) · Bit 1: AY (stan (1=stan wysoki) wyj¶æia multiwibratora) · Bit 2: BX (stan (1=stan wysoki) wyj¶æia multiwibratora) · Bit 3: BY (stan (1=stan wysoki) wyj¶æia multiwibratora) · Bit 4: BA1 (wej¶æie cyfrowe, 1=stan wysoki) · Bit 5: BA2 (wej¶æie cyfrowe, 1=stan wysoki) · Bit 6: BB1 (wej¶æie cyfrowe, 1=stan wysoki) · Bit 7: BB2 (wej¶æie cyfrowe, 1=stan wysoki) 66..33.. PPoorrtt sszzeerreeggoowwyy Je¶li urz±dzenie z którym siê komunikujesz przypomina co¶ co dzia³± jak RS-232 mo¿êsz do tego celu u¿yæ portu szeregowego. Linuxowy sterownik portu szeregowego powinien wystarczyæ w prawie wszystkich zastosowaniach (nie powinienne¶ mieæ potrzeby programowaæ port bezpo¶rednio, a nawet je¶li chcia³by¶ to robiæ prawdopodobnie musia³by¶ napisaæ w³asny modu³ do j±dra.) Sterownik Linuxowy jest ca³kiem wszechstronny a wiêc u¿ywanie na przyk³ad niestandardowych prêdko¶ci portu nie powinno byæ problemem. Je¶li chcesz dowiedzieæ siê wiêcej o programowaniu portu szeregowego w Linuxie zobacz stronê termios(3) w podrêczniku systemowym man, ¼ród³a sterownika (linux/drivers/char/serial.c), i <http://www.easysw.com/~mike/serial/index.html>. 77.. PPoorraaddyy Je¶li zale¿y Ci na dobrej obs³udze analogowej transmisji I/O, mo¿esz pod³±czyæ do portu równoleg³ego przetworniki analogowo-cyfrowe b±d¿ cyfrowo-analogowe (ADC,DAC).(Podpowied¼: we¿ zasilanie z portu joysticka b±d¿ z wolnej wtyczki zasilania dysku twardego wyprowadzonej na zewn±trz obudowy, chyba ¿e masz urz±dzenie nie wymagaj±ce du¿ej mocy, wtedy mo¿esz wzi±¶æ zasilanie z samego portu. Mo¿esz te¿ u¿yæ zewnêtrznego zasilacza.) Mo¿esz te¿ kupiæ kartê przetworników analogowo-cyfrowych i cyfrowo analogowych (AD/DA) (wiêkszo¶æ starszych/wolniejszych takich kart jest sterowana za pomoc± portów I/O) Je¶li nie przeszkadza ci ma³± ilo¶æ kana³ów (1 lub 2) niedok³adno¶æ oraz (mo¿liwe) z³e zerowanie, dobra (i szybka) bêdzie tania karta d¿wiêkowa wspierana przez Linuxowy sterownik. W czu³ych urz±dzeniach analogowych niew³a¶ciwe uziemienie mo¿ê spowodowaæ b³êdy na wej¶ciach b±d¼ wyj¶ciach. Je¶li przytrafi Ci siê co¶ takiego, mo¿esz spróbowaæ odizolowaæ elektrycznie urz±dzenie od komputera przez u¿ycie transoptorów (optocouplers) na _w_s_z_y_s_t_k_i_c_h sygna³ach pomiêdzy Twoim urz±dzeniem a komputerem. Aby zapewniæ lepsz± izolacjê zasilanie do transoptorów spróbuj wzi±¶æ z komputera (wolne sygna³y na porcie mog± daæ wystarczaj±c± ilo¶æ mocy) Je¶li szukasz oprogramowania do projektowania p³ytek drukowanych na Linuxa to jest darmowa aplikacja która nazywa siê Pcb i powinna sprawiaæ siê dobrze, przynajmniej wtedy kiedy nie robisz czego¶ bardzo skomplikowanego. Za³±czona jest ona do wielu dystrybucji Linuxa a dostêpna na <ftp://sunsite.icm.edu.pl/pub/Linux/sunsite/apps/circuits/> (plik pcb-*). 88.. WW rraazziiee kk³³ooppoottóóww PP11.. Dostaje b³±d segmentacji pamiêci kiedy dobieram siê do portów OO11.. Albo twój program nie ma uprawnieñ root'a b±d¼ wywo³anie ioperm() nie powiod³o siê z jakiego¶ innego powodu. Sprawd¼ warto¶æ powrotn± funkcji ioperm(). Sprawd¼ równie¿ czy rzeczywi¶æie operujesz na portach do których uzyska³e¶ dostêp za pomoc± ioperm() (zobacz P3). Je¶li u¿ywasz makr opó¼niaj±cych (inb_p(), outb_p(), itd), pamiêtaj aby wywo³aæ ioperm() równie¿ wtedy je¶li chcesz uzyskaæ dostêp do portu 0x80 PP22.. Nie mogê nigdzie znale¶æ deklaracji funkcji in*(), out*() i gcc narzeka na niezdefiniowane referencje. OO22.. Nie kompilowa³e¶ z w³±czon± optymalizacj± (-O), i w ten sposób gcc nie móg³ odnale¼æ makr w katalogu asm/io.h. Albo nie w³±czy³e¶ w ogóle <asm/io.h> do swojego programu. PP33.. Wywo³±nie out*() nie robi nic b±d¼ robi co¶ dziwnego. OO33.. Sprawd¼ kolejno¶æ parametrów; powinno byæ outb(warto¶æ, port), a nie outportb(port, warto¶æ) co jest popularne w MS-DOS. PP44.. Chcê sterowaæ standardowym urz±dzeniem RS-232/portem równoleg³ym/drukark±/joystickiem OO44.. Lepiej bêdzie jak u¿yjesz istniej±cych sterowników z j±dra, X serwera lub czego¶ innego. Sterowniki te s± zazwyczaj dosyæ wszechstronne wiêc nawet lekko niestandardowe urz±dzenia z nimi wspó³pracuj±. Zobacz wy¿ej informacje o zwyk³ych portach, s± tam odno¶niki do stosownej dokumentacji. 99.. PPrrzzyykk³³aaddoowwyy pprrooggrraamm Oto kawa³ek prostego przyk³adowego programu demonstruj±cego dostêp do rejestrów I/O. ______________________________________________________________________ /* * example.c: bardzo prosty przyk³ad dostêpu do portów I/O * * Program ten nie robi nic u¿ytecznego, zapisuje do portu, czeka i * odczytuje z portu. Kompilacja: gcc -O2 -o example example.c * Uruchamiac jako ./example bêd±c root'em / #include <stdio.h> #include <unistd.h> #include <asm/io.h> #define BASEPORT 0x378 /* lp1 */ int main() { /* Uzyskaj dostêp do portów */ if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);} /* Ustaw wszystkie bity danych (D0-D7) w stan niski (0) */ outb(0, BASEPORT); /* Zaczekaj chwilkê (100 ms) */ usleep(100000); /* Odczytaj z rejestru statusowego (BASE+1) i wy¶wietl rezultat */ printf("status: %d\n", inb(BASEPORT + 1)); /* Ju¿ nie potrzebujemy portów */ if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);} exit(0); } /* koniec example.c */ ______________________________________________________________________ 1100.. WWyyrraazzyy uuzznnaanniiaa Pomog³o mi zbyt wiele osób aby je tutaj wszystkie wymieniæ, ale wszystkim bardzo dziêkujê. Nie odpowiedzia³em im wszystkim; przepraszam za to i jeszcze raz dziêki za pomoc. 1111.. OOdd tt³³uummaacczzaa Niniejsze t³umaczenie objête jest prawami autorskimi Micha³a Szwaczko. Dozwolone jest rozpowszechnianie i dystrybucja na prawach takich samych jak dokument oryginalny.(Zobacz HOWTO-Copyright) Zmiany wprowadzone przeze mnie do dokumentu polegaj± na zast±pieniu adresów niektórych serwerów ftp przez ich mirrory w Polsce. Zdajê sobie sprawê ¿e t³umaczenie nie jest wolne od wad i b³êdów. Je¶li znajdziesz jakie¶, proszê napisz do mnie: michalsz@lena.zsg.lublin.pl Oficjaln± stron± grupy t³umaczy HOWTO jest http://www.jtz.org.pl