Linux I/O port programmering mini-HOWTO
Av: Riku Saikkonen <
[email protected]> �vers�ttning till
svenska: Sven Wilhelmsson, <
[email protected]>
v ,28 December 1997 , �versatt: 10 Augusti 1998
I detta HOWTO dokument beskrivs hur man programmerar I/O portar samt
hur man f�rdr�jer eller m�ter korta tidsintervall i user-mode Linux
program f�r Intel x86 arkitekturen.
______________________________________________________________________
Inneh�llsf�rteckning
1. Inledning
2. Att anv�nda I/O portar i C-program
2.1 Det vanliga s�ttet
2.2 En alternativ metod:
3. Avbrott (IRQs) och DMA
4. H�guppl�sande timing
4.1 F�rdr�jningar
4.1.1 (TT
4.1.2 nanosleep()
4.1.3 F�rdr�jningar med port I/O
4.1.4 Att f�rdr�ja med assemblerinstruktioner
4.1.5 rdtsc() f�r Pentium
4.2 Att m�ta tid
5. Andra programmeringsspr�k
6. N�gra anv�ndbara portar
6.1 Parallellporten
6.2 Spelporten (joystick)
6.3 Serieporten
7. Tips
8. Fels�kning
9. Kod exempel
10. Erk�nnande
______________________________________________________________________
1. Inledning
I detta HOWTO dokument beskrivs hur man n�r I/O-portar och hur man
f�rdr�jer korta tidsintervall i user-mode Linux program som k�rs p�
Intel x86 arkitekturen. Detta dokumentet �r en uppf�ljare till den
mycket lilla IO-Port mini-HOWTO fr�n samma f�rfattare.
This document is Copyright 1995-1997 Riku Saikkonen. See the Linux
HOWTO copyright
<
http://sunsite.unc.edu/pub/Linux/docs/HOWTO/COPYRIGHT> for details.
If you have corrections or something to add, feel free to e-mail me
(
[email protected])...
�ndringar fr�n tidigare publicerad version (Mar 30 1997):
� F�rklaringar ang�ende inb_p/outb_p/ och port 0x80.
� Tagit bort information om udelay(), eftersom nanosleep() medger
ett renare s�tt att g�ra samma sak.
� Konverterat till Linuxdoc-SGML, och �ndrat om n�got.
� M�nga mindre till�gg och �ndringar.
2. Att anv�nda I/O portar i C-program
2.1. Det vanliga s�ttet
Rutiner f�r att n� I/O-portar finns i/usr/include/asm/io.h (eller
linux/include/asm-i386/io.h i 'the kernel source distribution').
Rutinerna d�r �r inline makron, s� det r�cker att g�ra #include
<asm/io.h>, inga ytterligare bibliotek beh�vs.
Beroende p� brister i gcc (�tminstone i 2.7.2.3 och l�gre) och i egcs
(alla versioner), m�ste du kompilera k�llkod som anv�nder dessa
rutiner med optimeringsflaggan p� (gcc -O1 eller h�gre), eller
alternativt #define extern till ingenting, (dvs. #define extern p� en
i �vrigt blank rad) innan du g�r #include <asm/io.h>.
Om du vill avlusa, 'debug', kan du anv�nda gcc -g -O (�tminstone med
moderna versioner av gcc), �ven om optimeringen ibland g�r att
debuggern beter sig lite underligt. Om detta besv�rar dig, kan du
l�gga de rutiner som anropar I/O-portarna i separata filer och
kompilera endast dem med optimeringsflaggan p�.
Innan du anropar en port, m�ste du ge ditt program tillst�nd till
detta. Detta g�r man genom att anropa ioperm() funktionen (som finns
deklarerad i unistd.h, och definierad i 'kernel') n�gonstans i b�rjan
av ditt program, innan n�gon I/O-port anropas. Syntaxen �r
ioperm(from, num, turn_on), d�r from �r den f�rsta portadressen som
ska ges tillst�nd och num �r antalet konsekutiva adresser. Till
exempel, ioperm(0x300, 5, 1) ger tillst�nd till portarna 0x300 till
0x304 (totalt 5 portar). Det sista argumentet �r en bool som
specificerar om du vill ge accesstillst�nd (true(1)) eller ta bort
tillst�ndet (false(0)). Du kan anv�nda ioperm() upprepade g�nger f�r
att ge tillst�nd till ickekonsekutiva portadresser. Se ioperm(2)
manualen.
ioperm() anropet kr�ver att ditt program har rootprivilegier. Det
kr�vs att du antingen k�r som root, eller g�r setuid root. Du kan
sl�ppa rootprivilegierna s� snart du har anropat ioperm(). Det �r inte
n�dv�ndigt att explicit sl�ppa dina accessr�ttigheter med ioperm(...,
0) mot slutet av ditt program. Detta sker automatiskt n�r processen
avslutas.
Om du g�r setuid() till en non-root user f�rst�rs inte de
accessr�ttigheter som �r redan givna av ioperm(), men fork() f�rst�r
dem (child processen f�r inga r�ttigheter, men parent beh�ller dem).
ioperm() kan endast ge access r�ttigheter till portarna 0x000 - 0x3ff.
F�r att komma �t h�gre portadresser, kan man anv�nda iopl(), som ger
access till alla portar p� en g�ng. Anv�nd niv� 3 (dvs iopl(3)) f�r
att ge ditt program tillg�ng till alla portar. (Men var f�rsiktig -
att skriva p� fel port kan orsaka allehanda otrevliga saker med din
dator). Du beh�ver rootprivilegier f�r att anropa iopl(). Se
iopl(2) manualen.
Sedan, f�r att komma �t portarna... F�r att l�sa in en byte, (8 bitar)
fr�n en port, call inb(port), den returnerar den byte den l�ser. F�r
att st�lla ut, call outb(value,port) (notera parameterordningen). F�r
att l�sa in 16 bitar fr�n port x och x+1 ,en byte fr�n vardera, call
inw(x) och f�r att st�lla ut, call outw(value,x). �r du os�ker p� om
du skall anv�nda byte eller word instruktioner, �r det troligen inb()
och outb() - flertalet apparater konstrueras f�r bytevis portaccess.
Notera att alla portaccesser tar �tminstone cirka en mikrosekund att
utf�ra.
F�r �vrigt fungerar makroanropen inb_p(), outb_p(), inw_p(), och
outw_p() p� samma s�tt som ovann�mnda, f�rutom att de l�gger till ca
en mikrosekund efter varje portaccess. Du kan g�ra f�rdr�jningen �nnu
l�ngre, ca 4 mikrosekunder, med #define REALLY_SLOW_IO innan du g�r
#include <asm/io.h>. Dessa makron g�r normalt (s�vida du inte g�r
#define SLOW_IO_BY_JUMPING, vilket blir mindre noggrant) access till
port 0x80 f�r att skapa delay, s� du beh�ver f�rst ge accessr�tt till
port 0x80 med ioperm(). (Skrivning p� port 0x80 p�verkar ingenting).
F�r mer flexibla delay-metoder, l�s vidare.
Det finns sidor till ioperm(2), iopl(2) och ovann�mnda makron i
n�gorlunda f�rska utg�vor av Linux manual.
2.2. En alternativ metod: /dev/port
Ett annat s�tt att komma �t I/O-portar �r open() /dev/port (en
'character device', major number 1, minor 4) f�r l�sning och/eller
skrivning (stdio f*() funktionerna har intern buffring, s� anv�nd inte
dem). G�r sedan lseek() till den aktuella byten i filen (fil position
0 = port 0x00, fil position 1 = 0x01, och s� vidare), och read() eller
write() en byte eller ett ord till eller fr�n den.
Naturligtvis beh�ver ditt program accessr�ttigheter till /dev/port f�r
att metoden skall fungera. Denna metod �r sannolikt l�ngsammare �n den
normala metoden enligt ovan, men beh�ver varken optimeringsflaggan vid
kompilering eller ioperm(). Det beh�vs inte heller 'root access', bara
du ger 'non-root user' eller 'group' access till /dev/port - l�t vara
att detta �r dumt ur systems�kerhetssynpunkt, eftersom det �r m�jligt
att skada systemet, kanske till och med vinna 'root access', genom att
anv�nda /dev/port f�r att komma �t h�rddisk, n�tverkskort, etc.
direkt.
3. Avbrott (IRQs) och DMA
Man kan inte anv�nda IRQ eller DMA direkt i en usermode process. Man
m�ste skriva en kernel driver; se The Linux Kernel Hacker's Guide
<
http://www.redhat.com:8080/HyperNews/get/khg.html> D�r finns detaljer
och kernel k�llkod som exempel. Man kan heller inte st�nga av ett
avbrott fr�n ett user-mode program.
4. H�guppl�sande timing
4.1. F�rdr�jningar
F�rst och fr�mst m�ste s�gas att det inte g�r att garantera user mode
processer exakt kontroll avseende timing eftersom Linux �r ett
multiprocess system. Din process kan bli utskyfflad under vad som
helst mellan 10 millisekunder upp till n�gra sekunder (om belastningen
�r h�g). Detta spelar emellertid ingen roll f�r flertalet program som
anv�nder I/O-portar. F�r att reducera effekterna, kan du med hj�lp av
kommandot nice ge din process h�g prioritet. Se nice(2) manualen eller
anv�nd real-time scheduling enligt nedan.
Om du beh�ver b�ttre tidsprecision �n vad normala user-mode processer
kan ge, s� finns vissa f�rberedelser f�r 'user-mode real time'
support. Linux 2.x k�rnor har 'soft real time support', se manualen
f�r sched_setscheduler(2). Det finns en speciell k�rna som st�der h�rd
realtid, se <
http://luz.cs.nmt.edu/~rtlinux/> f�r ytterligare
information om detta.
4.1.1. sleep() och usleep()
L�t oss b�rja med de l�tta funktionsanropen. F�r att f�rdr�ja flera
sekunder, �r det troligtvis b�st att anv�nda sleep(). F�rdr�jningar p�
10-tals millisekunder (ca 10 ms verkar vara minimum) g�rs med
usleep(). Dessa funktioner frig�r CPU f�r andra processer, s� att
ingen CPU-tid g�r f�rlorad. Se manualerna sleep(3) och usleep(3).
Om f�rdr�jningar �r p� mindre �n 50 ms ( beror p� din processor och
dess belastning), tar det on�digt mycket tid att sl�ppa CPUn, d�rf�r
att det f�r Linux scheduler (f�r x86 arkitekturen) vanligtvis tar
minst 10-30 millisekunder innan den �terger din process kontrollen.
Beroende p� detta f�rdr�jer usleep(3) n�got mer �n vad du specificerar
i dina parametrar, och alltid minst ca 10 ms.
4.1.2. nanosleep()
I 2.0.x serien av Linuxk�rnor finns ett nytt systemanrop: nanosleep()
(se nanosleep(2) manualen), som m�jligg�r s� korta f�rdr�jningar som
ett par mikrosekunder eller mer.
Vid f�rdr�jningar p� mindre �n 2 ms, om (och endast om) din process �r
satt till soft real time scheduling (med sched_setscheduler()),
anv�nder nanosleep() en v�nteloop, i annat fall frig�rs CPU p� samma
s�tt som med usleep().
V�nteloopen anv�nder udelay() (en intern kernelfunktion som anv�nds av
m�nga 'kernel drivers'), och loopens l�ngd ber�knas med hj�lp av
BogoMips v�rdet (det �r bara denna sorts hastighet BogoMips v�rdet
m�ter noggrant). Se hur det fungerar i /usr/include/asm/delay.h
4.1.3. F�rdr�jningar med port I/O
Ett annat s�tt att f�rdr�ja ett f�tal mikrosekunder �r att anv�nda
port I/O. L�sning eller skrivning p� port 0x80 (se ovan hur man g�r)
tar n�stan precis 1 mikrosekund oberoende av processortyp och
hastighet. Du kan g�ra det upprepade g�nger om du vill v�nta ett antal
mikrosekunder. Skrivning p� denna port torde inte ha n�gra skadliga
sidoeffekter p� n�gon standardmaskin och vissa 'kerneldrivers'
anv�nder denna metod. det �r p� detta s�ttet {in|out}[bw]_p()
normalt g�r sin f�rdr�jning. (se asmio.h)/.
Flertalet port I/O instruktioner i adressomr�det 0-0x3ff tar n�stan
exakt 1 mikrosekund, s� om du t.ex. anv�nder parallellporten direkt,
g�r bara n�gra extra inb() fr�n porten f�r att skapa f�rdr�jning.
4.1.4. Att f�rdr�ja med assemblerinstruktioner
Om man k�nner till processortyp och klockhastighet, kan man h�rdkoda
korta f�rdr�jningar med vissa assemblerinstruktioner (men kom ih�g,
processen kan skyfflas ut n�r som helst, s� f�rdr�jningarna kan ibland
bli l�ngre). Tabellen nedan ger n�gra exempel. F�r en 50MHz processor
tar en klockcykel 20 ns.
Instruktion i386 klock cykler i486 klock cykler
nop 3 1
xchg %ax,%ax 3 3
or %ax,%ax 2 1
mov %ax,%ax 2 1
add %ax,0 2 1
tyv�rr k�nner jag inte till Pentium; f�rmodligen n�ra i486. Jag
hittar ingen instruktion som tar EN klockcykel i i386. Anv�nd en-
cykel instruktioner om du kan, annars kanske pipelinen i moderna
processortyper f�rkortar tiden.
Instruktionerna nop och xchg i tabellen b�r inte ha n�gra
sidoeffekter. �vriga modifierar statusregistret, men det b�r inte
betyda n�got eftersom gcc detekterar detta. nop �r ett bra val.
Om du vill anv�nda dem, skriv call asm("instruktion") i ditt program.
Syntaxen ge i tabellen ovan. Vill du g�ra multipla instruktioner i en
asm()-sats, s� separera med semikolon. Till exempel exekveras i satsen
asm(" nop; nop; nop; nop") fyra nop instruktioner, som f�rdr�jer fyra
klockcykler med i486 eller pentium (eller 12 cykler med i386).
asm() �vers�tts av gcc till inline assembler kod, s� det blir inget
overhead med funktionsanrop.
Kortare f�rdr�jningar �n en klockcykel �r inte m�jligt med x86
arkitekturen.
4.1.5. rdtsc() f�r Pentium
Med Pentium kan du erh�lla antalet klockcykler som g�tt sedan senaste
uppstart med hj�lp av f�ljande C kod:
______________________________________________________________________
extern __inline__ unsigned long long int rdtsc()
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
______________________________________________________________________
Du kan polla v�rdet f�r hur m�nga cykler som helst.
4.2. Att m�ta tid
F�r att m�ta tider med en sekunds uppl�sning, �r det nog enklast att
anv�nda time(). Kr�vs b�ttre noggrannhet, ger gettimeofday() cirka en
mikrosekunds uppl�sning (men se ovan ang�ende 'scheduling',
utskyffling). F�r Pentium �r rdtsc kodfragmentet ovan noggrant till en
klockcykel.
Om din process skall ha en signal efter en viss tid, s� anv�nd
setitimer() eller alarm(). Se manualsidorna.
5. Andra programmeringsspr�k
Beskrivningen ovan koncentrerar sig p� programmeringsspr�ket C. Det
b�r vara till�mpbart �ven p� C++ och Objective C. I assembler f�r man
anropa ioperm() eller iopl() som i C, och d�refter kan man anv�nda
I/O-port read/write instruktionerna direkt.
I andra spr�k, s�vida inte du kan infoga inline assembler eller C kod
i ditt program eller anv�nda ovann�mnda systemanrop, �r det nog
enklast att skriva en C k�llkodsfil med funktionerna f�r I/O-
portaccess och separatkompilera och l�nka den till �vriga delar av
ditt program. Eller anv�nda /dev/port enligt ovan.
6. N�gra anv�ndbara portar
H�r f�ljer programmeringsinformation om n�gra portar som direkt kan
anv�ndas f�r TTL (eller CMOS) digitala kretsar.
Om du vill anv�nda dessa eller andra g�ngse portar f�r det �ndam�l de
�r �gnade (t.ex. f�r att styra en printer eller modem), skall du
troligen anv�nda en befintlig drivrutin (vanligtvis inkluderad i
k�rnan) i st�llet f�r att programmera dessa portar direkt som beskrivs
i detta HOWTO. Denna sektion �r avsedd f�r dem som vill ansluta LCD-
displayer, stegmotorer, eller annan speciell elektronik till en PC's
standardport.
Ska du styra en massproducerad produkt som t.ex. scanner (som har
funnits p� marknaden ett tag), s�k efter en befintlig drivrutin.
Hardware-HOWTO <
http://sunsite.unc.edu/pub/Linux/docs/HOWTO/Hardware-
HOWTO> �r ett bra st�lle att b�rja.
<
http://www.hut.fi/Misc/Electronics/> �r en annan bra k�lla till
information om hur man ansluter utrustning till datorer, och om
elektronik i allm�nhet.
6.1. Parallellporten
Parallellportens basadress (kallad ''BASE'' nedan) �r 0x3bc f�r
/dev/lp0, 0x378 f�r /dev/lp1, och 0x278 f�r /dev/lp2. Ska du styra
n�got som beter sig som en normal printer, se Printing-HOWTO
<
http://sunsite.unc.edu/pub/Linux/docs/HOWTO/Printing-HOWTO>.
F�rutom den vanliga output-only moden som beskrivs nedan, finns det en
'extended' bidirektionell mod i flertalet parallellportar.
Information om detta och om de nyare ECP/EPP moderna (och om IEEE
1284 standarden allm�nt), se <
http://www.fapo.com/> och
<
http://www.senet.com.au/~cpeacock/parallel.htm>. Kom ih�g att
eftersom du inte kan anv�nda IRQs eller DMA i ett user-mode program,
s� kommer du nog att beh�va skriva en 'kernel-driver' f�r att anv�nda
ECP/EPP. Jag tror att n�gon h�ller p� att skriva en s�dan driver men
jag k�nner inte till n�gra detaljer.
Porten BASE+0 (Data port) styr data signalerna (D0 till D7 f�r
bitarna 0 to 7, respektive; tillst�nden: 0 = l�g (0 V), 1 = h�g (5
V)). Skrivning p� denna port st�ller ut data till donet. En l�sning
returnerar senast skrivna data i standard- eller extended-moden, eller
data p� stiften fr�n en ansluten apparat i 'extended read mode'.
Portarna BASE+1 ('Status port') �r 'read-only', och returnerar
tillst�ndet p� f�ljande insignaler:
� Bits 0 och 1 �r reserverade.
� Bit 2 IRQ status (inget stift, vet inte hur detta fungerar)
� Bit 3 ERROR (1=h�g)
� Bit 4 SLCT (1=h�g)
� Bit 5 PE (1=h�g)
� Bit 6 ACK (1=h�g)
� Bit 7 -BUSY (0=h�g)
(Vet inte vilka sp�nningar som motsvarar h�g respektive l�g.)
Porten BASE+2 ('Control port') �r 'write-only' (l�sning returnerar
senast inskrivna data), och styr f�ljande status signaler:
� Bit 0 -STROBE (0=h�g)
� Bit 1 AUTO_FD_XT (1=h�g)
� Bit 2 -INIT (0=h�g)
� Bit 3 SLCT_IN (1=h�g)
� Bit 4 aktiverar ('enables') parallell portens IRQ (vilket sker p�
uppflanken hos ACK) n�r den s�tts till 1.
� Bit 5 styr 'extended mode direction' (0 = skriv, 1 = l�s), den �r
'write-only' (l�sning p� denna bit returnerar ingenting
meningsfullt).
� Bits 6 och 7 �r reserverade.
(�terigen, �r inte s�ker p� vad som �r h�g och l�g.)
Pinout (ett 25-pin D-don , hona ) (i=input, o=output):
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
IBM specifikationen s�ger att stiften 1, 14, 16, och 17 ('control-
outputs') har 'open-collektor' utg�ngar dragna till 5 V genom ett 4.7
K motst�nd (s�nker 20 mA, ger 0.55 mA, h�gniv� utg�ng 5.0 V minus
eventuellt sp�nningsfall). �vriga stift s�nker 24 mA och ger 15 mA,
och deras h�gniv� sp�nning �r minst 2.4 V. L�gniv� sp�nningen �r i
b�da fallen minst 0.5 V. Icke-IBM parallell portar avviker troligen
fr�n denna standard. F�r ytterligare information om detta se
<
http://www.hut.fi/Misc/Electronics/circuits/lptpower.html>.
Slutligen en varning: Var noga med jordningen. Jag har f�rst�rt flera
parallellportar genom att ansluta dem med datorn ig�ng. Det kan vara
ett bra alternativ att anv�nda parallellportar som inte sitter p�
moderkortet f�r s�dana h�r saker. (Du kan antagligen f� en andra
parallellport med ett billigt standard 'multi-I/O' kort; St�ng bara av
de portar du inte beh�ver, och s�tt parallellkortets I/O-adress till
en ledig adress. Du beh�ver inte bekymra dig om parallellportens IRQ,
eftersom den vanligtvis inte anv�nds.)
6.2. Spelporten (joystick)
Spelporten finns p� adresserna 0x200-0x207. F�r att styra normala
joystickar finns en kernel-level joystick driver, se
<
ftp://sunsite.unc.edu/pub/Linux/kernel/patches/>, filename
joystick-*.
Pinout (ett 25-pin D-don , hona ) (i=input, o=output):
� 1,8,9,15: +5 V (kraftmatning)
� 4,5,12: Ground
� 2,7,10,14: Digitala ing�ngar BA1, BA2, BB1, och BB2, respektive
� 3,6,11,13: ''Analoga'' ing�ngar AX, AY, BX, och BY, respektive
+5 V stiften verkar ofta vara direkt anslutna till moderkortets
kraftmatning, s� de b�r klara ganska mycket str�m, beroende p�
moderkort, kraftaggregat och spelport.
De digitala ing�ngarna anv�nds till de tv� joystickarna (joystick A
och joystick B, med tv� knappar vardera) som du kan ansluta till
porten. De torde vara normala TTL ing�ngar, och du kan l�sa deras
status direkt p� statusporten (se nedan). En joystick ger l�g (0 V)
status n�r knappen �r nedtryckt och eljest h�g (5 V fr�n matningen via
ett 1K motst�nd).
De s� kallade analoga ing�ngarna m�ter egentligen resistans.
Spelportarna har en fyrfaldig one-shot multivibrator (ett 558 chip)
anslutet till de fyra ing�ngarna. P� varje ing�ngsshylsa i
kontaktdonet finns ett 2.2K motst�nd mellan ing�ngshylsan och
multivibratorns 'open-collector' utg�ng, och en 0,01 uF 'timing'
kondensator mellan multivibratorns utg�ng och jord. En joystick har
en potentiometer f�r varje axel (X och Y), dragen mellan +5 V och
respektive ing�ngshylsa ( AX och AY f�r joystick A, eller BX och BY
f�r joystick B).
N�r multivibratorn aktiveras s�tts dess utg�ng h�g (5 V) och den
inv�ntar att timing kondensatorn n�r 3.3 V innan den s�nker respektive
utg�ng. P� s� s�tt blir multivibratorns pulsl�ngd proportionell mot
potentiometerns resistans (dvs. joystickens position f�r respektive
axel) enligt f�ljande:
R = (t - 24.2) / 0.011,
d�r R �r potentiometerns resistans och t pulsens l�ngd i mikrosekun�
der.
S�ledes, f�r att l�sa dessa analoga ing�ngar, skall man f�rst aktivera
multivibratorn (med en skrivning p� porten; se nedan), sedan polla
(g�ra upprepade l�sningar) tillst�ndet f�r de fyra axlarna tills de
g�r fr�n h�g till l�g, och p� s� s�tt m�ta pulstiden. Denna pollning
tar mycket CPU tid och i ett ickerealtids multiprocess system som
Linux blir inte resultatet s� tillf�rlitligt eftersom man inte kan
polla kontinuerligt, s�vida du inte g�r en 'kernel-driver' och st�nger
avbrottsing�ngar n�r du pollar (vilket tar �nnu mer CPU tid). Om du
vet att signalen kommer dr�ja tiotals millisekunder s� kan du anropa
usleep() innan du b�rjar polla f�r att ge CPU tid till andra
processer.
Den enda I/O-port som du beh�ver n� �r port 0x201 (de andra portarna
beter sig precis likadant eller �r inaktiva). En skrivning till porten
(spelar ingen roll vad) aktiverar multivibratorn. L�sning fr�n porten
returnerar signalernas status:
� Bit 0: AX (status (1=high) of the multivibrator output)
� Bit 1: AY (status (1=high) of the multivibrator output)
� Bit 2: BX (status (1=high) of the multivibrator output)
� Bit 3: BY (status (1=high) of the multivibrator output)
� Bit 4: BA1 (digital input, 1=high)
� Bit 5: BA2 (digital input, 1=high)
� Bit 6: BB1 (digital input, 1=high)
� Bit 7: BB2 (digital input, 1=high)
6.3. Serieporten
Om den apparat som du vill kommunicera med st�der n�got som liknar
RS-232 b�r du kunna anv�nda en serieport f�r att tala med den. Linux
drivrutin f�r serieportar b�r r�cka f�r n�stan alla t�nkbara
till�mpningar ( du ska inte beh�va programmera serieportarna direkt,
och skulle s� vara m�ste du skriva en 'kernel driver'); den �r mycket
flexibel, s� att anv�nda ickestandardiserade bithastigheter torde inte
vara n�got problem.
Se termios(3) manualen, eller seriedriverns k�llkod,
(linux/drivers/char/serial.c), och
<
http://www.easysw.com/~mike/serial/index.html> f�r mer info om hur
man programmerar serieportar p� Unix system.
7. Tips
Om du beh�ver bra analog I/O kan du ansluta ett ADC- och/eller DAC-
chip till parallellporten (tips: kraft kan du ta fr�n anslutningsdonet
till spelporten eller fr�n ett don till en yttre diskenhet eller
anv�nda ett separat kraftaggregat. Har du str�msn�la kretsar kan du ta
kraftmatning fr�n parallellporten. Du kan ocks� k�pa ett AD/DA-kort
(de flesta �ldre/l�ngsammare typerna ansluts till I/O-portar. Eller,
om det r�cker med 1 eller 2 kanaler och m�ttlig noggrannhet, k�p ett
billigt ljudkort som har st�d fr�n Linux sound driver. Ett s�dant �r
ocks� t�mligen snabbt.
Noggranna analoga apparater st�rs l�tt om jordningen �r bristf�llig.
Om du f�r problem av detta slag, kan du pr�va att isolera din
utrustning fr�n datorn med hj�lp av optokopplare. (p� alla signaler
mellan datorn och din utrustning. F�rs�k att f� matning till
optokopplarna fr�n datorn (lediga signaler fr�n porten kan ge
tillr�ckligt med kraft) f�r b�sta isolation fr�n st�rning.
Letar du efter Linux mjukvara f�r m�nsterkortframtagning, s� finns det
en fri s�dan som kallas Pcb. Den torde g�ra ett bra jobb, �tminstone
om inte du g�r n�got alltf�r komplicerat. Den finn med i m�nga Linux
distributioner, och den finns tillg�nglig i
<
ftp://sunsite.unc.edu/pub/Linux/apps/circuits/> (filename pcb-*).
8. Fels�kning
Q1.
Jag f�r segmentation faults n�r jag adresserar portar.
A1.
Antingen har ditt program inte rootprivilegier, eller har
ioperm() falerat av n�gon annan orsak. Testa det returnerade
v�rdet fr�n ioperm(). Testa ocks� att du verkligen adresserar de
portar som du gett tillst�nd till med ioperm() (se Q3).
Om du anv�nder 'delaying macros' (inb_p(), outb_p(), osv.), kom
ih�g att du d� m�ste anropa ioperm() f�r att ge accesstillst�nd
till adress 0x80.
Q2.
Jag kan inte hitta var in*(), out*() funktionerna definieras,
och gcc klagar �ver odefinierade referenser.
A2.
Du kompilerade inte med optimeringsflaggan p� (-O1 eller h�gre),
och d�rf�r kunde inte gcc l�sa upp de makron som finns i
asm/io.h. Eller gl�mde du kanske #include <asm/io.h>.
Q3.
out*() g�r ingenting, eller g�r n�got konstigt.
A3.
Kolla ordningen p� parametrarna; set skall vara outb(value,
port), inte outb(port, value) som f�rekommer i MS-DOS.
Q4.
Jag vill k�ra en standard RS-232 device/parallel
printer/joystick...
A4.
D� �r det nog b�st att anv�nda en befintlig driver (i Linux
kernel eller en X-server eller n�gon annanstans) f�r detta.
Drivrutinerna �r vanligtvis mycket flexibla, s� att �ven en
ickestandard apparat fungerar vanligtvis med dem. Se info om
standard portar ovan efter h�nvisningar till dokumentation.
9. Kod exempel
H�r f�ljer ett enkelt exempel p� kod f�r I/O-port access:
______________________________________________________________________
/*
* example.c: very simple example of port I/O
*
* This code does nothing useful, just a port write, a pause,
* and a port read. Compile with 'gcc -O2 -o example example.c',
* and run as root with './example'.
*/
#include <stdio.h>
#include <unistd.h>
#include <asm/io.h>
#define BASEPORT 0x378 /* lp1 */
int main()
{
/* Get access to the ports */
if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);}
/* Set the data signals (D0-7) of the port to all low (0) */
outb(0, BASEPORT);
/* Sleep for a while (100 ms) */
usleep(100000);
/* Read from the status port (BASE+1) and display the result */
printf("status: %d\n", inb(BASEPORT + 1));
/* We don't need the ports anymore */
if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);}
exit(0);
}
/* end of example.c */
______________________________________________________________________
10. Erk�nnande
Alltf�r m�nga har bidragit till artikeln f�r att jag skall kunna r�kna
upp alla, men tack allesammans. Jag har inte besvarat alla bidrag som
jag f�tt; ledsen f�r det, men �terigen tack f�r all hj�lp.