Sophie

Sophie

distrib > Mandriva > 8.1 > i586 > by-pkgid > 1d876fa8c1caf5809b8232d098efff65 > files > 56

howto-text-pl-8.1-1mdk.noarch.rpm

  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