Portál AbcLinuxu, 25. dubna 2024 17:42

Sériová komunikace pod Linuxem - I

18. 8. 2004 | Jan Martínek
Články - Sériová komunikace pod Linuxem - I  

Zajímá vás jak komunikovat přes sériový port? Ptáte se, co to je? Myslíte si, že zmizel v hlubinách času? Ne tak docela...

Sériový port (neboli rozhraní RS-232) je jednoduchá univerzální sběrnice určená pro komunikaci mezi dvěma zařízeními, z nichž jedno je ve většině případů počítač.

Mohlo by se zdát zpátečnické zabývat se sériovým portem v dnešní době, která je poznamenána bouřlivým rozmachem USB, ale sériový port ještě dlouhou dobu zůstane používaným standardem zejména pro svou rozšířenost a jednoduchost. A právě tato jednoduchost je důvodem, proč různá amatérská elektronická udělátka bývají navržena pro připojení k sériovému portu a proč je mnoho jednočipových mikropočítačů vybaveno rozhraním RS232.

Obvyklý způsob použití sériového portu je čtení a zápis dat, přičemž nejmenší datovou jednotkou je jeden znak, který představuje nejčastěji osm bitů. Ne však vždy. Nešťastný historický vývoj obdařil sériový port schopností přenášet pět, šest, či sedm bitů v jednom znaku. Jakýkoli jiný počet než osm je ovšem pouze pro zlost. Proto aby se člověk divil, že přenos binárních dat nefunguje správně.

Říká se, že v *NIXech je všechno soubor - a platí to i o sériovém portu. Tím souborem je /dev/ttyS0, což představuje první sériový port, který zcela určitě ve svém počítači máte. Druhým sériovým portem je /dev/ttyS1, který už takovou samozřejmostí být nemusí (např. u laptopů nebo některých motherboardů). Chcete-li něco poslat do sériového portu, napište

echo Hello world! > /dev/ttyS0

Nemáte-li právo zápisu do /dev/ttyS0 (obvykle to smí pouze root), dostanete nejspíš hlášku "Permission denied". Zkoušet tyhle věci jako root je poněkud nerozumné, takže pro začátek bude lepší změnit práva pomocí

chmod a+rw /dev/ttyS0

To sice není o mnoho rozumnější, ale bezpečnost ponechme stranou. Nyní se tedy řetězec "Hello world!" bez reptání spláchne kamsi do útrob sériového portu. Kdyby na port bylo připojeno nějaké zařízení, mohlo by si tento řetězec přečíst. A dost možná, že by i odpovědělo. Odpověď by bylo možné přečíst pomocí

cat /dev/ttyS0

Jestliže nemáte připojené žádné zařízení, příkaz zůstane viset, protože nejsou k dispozici žádná data. Pokud zrovna nemáte po ruce zařízení, které komunikuje se sériovým portem, nevadí, lze jej snadno zhotovit svépomocí. Potřebujete k tomu pouze kousek alobalu od svačiny. Ten postačí k tomu, abyste v konektoru sériového portu propojili vývod pro vysílání (TxD) s vývodem pro příjem (RxD). Konektor má buď 9 nebo 25 vývodů. Ať už máte kterékoli provedení, jedná se o vývody (piny) číslo 2 a 3. Jednotlivé piny jsou označeny drobnými, takřka nečitelnými číslicemi vyraženými v umělé hmotě konektoru. Situaci v případě devítipinového konektoru ilustrují následující obrázky (fotografie se po kliknutí zvětší):

Číslování a význam pinů konektoru RS-232
Číslování a význam pinů konektoru RS-232
Fotografie konektoru RS-232
Fotografie konektoru RS-232
Konektor RS-232 s propojeným 2. a 3. pinem
Konektor RS-232 s propojeným 2. a 3. pinem

Jsou-li vývody 2 a 3 navzájem propojené, pak cokoli, co vyšleme, bude opět beze změny přijato. Toto lze snadno ověřit tím, že se v jednom terminálu spustí příkaz pro čtení (cat /dev/ttyS0) a následně ve druhém příkaz pro zápis (echo Hello world! > /dev/ttyS0). Jestliže to nedělá vůbec nic, byl to asi ten druhý port a místo /dev/ttyS0 patří /dev/ttyS1.

Výsledek však může být poněkud nečekaný. Počáteční nastavení parametrů sériového portu nemusí být totiž pro tyto pokusy zcela vhodné, a tak kupříkladu s mým jádrem a distribucí Linuxu začne příkaz cat /dev/ttyS0 vypisovat řetezec Hello world! stále dokola, přestože do portu již žádná data neposílám. Způsobuje to parametr echo, ale o tom se zmíním později.

Data tedy tečou z výstupního pinu na vstupní - ale jakou rychlostí? Přenosová rychlost se u sériového portu udává v tzv. baudech (baudrate), což je zhruba řečeno číslo, které říká, kolik bitů se přenese za jednu sekundu. Obvykle nejvyšší rychlost představuje 115200 a další možné rychlosti musí být dělitelem této rychlosti, např. 57600 = 115200/2 nebo 38400 = 115200/3 atd. Rychlost lze nastavit příkazem stty. Například

stty -F /dev/ttyS0 14400

Nastaví přenosovou rychlost na 14400 baudů. Příkazem stty lze také zjistit takřka veškerá nastavení portu.

stty -aF /dev/ttyS0

vypíše děsivé množství nepřehledných informací, zde je ukázka:

speed 14400 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;    start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

Hned první údaj představuje přenosovou rychlost. Další informace z výpisu jsou vysvětleny v manuálové stránce příkazu stty. Parametry umožňují nastavit sériovému portu zcela nečekané chování, které mělo svůj význam v zaprášených dobách terminálů ovládaných po sériové lince. Bude nejlepší tyto vymoženosti vypnout či zakázat. Nechci zde rozebírat význam jednotlivých položek a také nezastírám, že mi mnohdy uniká. Pouze podotýkám, že rozumné nastavení sériového portu získáte příkazem

stty -F /dev/ttyS0 clocal cread -crtscts cs8 -cstopb hup -parenb parodd -brkint -icrnl ignbrk -igncr ignpar imaxbel -inlcr inpck -istrip -iuclc -ixany ixoff -ixon bs0 cr0 ff0 nl0 -ocrnl -ofdel -ofill -olcuc -onlcr -onlret onocr -opost tab0 vt0 -crterase crtkill -ctlecho -echo -echok -echonl -echoprt -icanon -iexten -isig -noflsh -tostop -xcase time 5 min 1

Příkaz stty umožňuje díky parametru -g vypsat nastavení v hexadecimální podobě, které je sice člověkem nečitelné, zato stty umí tento formát nastavení načíst zpátky. Tím je možno zazálohovat aktuální nastavení. Uvedu příklad. Pokud příkaz

stty -gF /dev/ttyS0

vypíše

500:5:cad:8a3b:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0

Je možné si toto nastavení kdykoli obnovit příkazem

stty -F /dev/ttyS0 500:5:cad:8a3b:3:1c:7f:15:4:0:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0

Jednotlivé parametry jsou několikerého typu. Nejčastěji mají možnost zapnuto/vypnuto, a pak, jestliže je nějaký parametr vypsán či zadán s předcházejícím mínusem (pomlčkou), je daná volba vypnuta. Konkrétní použití uvedu na příkladu nastavení paritního bitu. Příkaz

stty -F /dev/ttyS0 parenb parodd

zapne kontrolu parity (parenb = parity enable) a nastaví ji na lichou (parodd = parity odd). Sudou paritu (parity even) lze nastavit vypnutím liché parity, tedy příkazem

stty -F /dev/ttyS0 parenb -parodd

Pozor - existuje sice parametr evenp pro nastavení liché parity, ale ten radím nepoužívat - je to jen alias a navíc mění počet bitů v jednom znaku. Sudost či lichost ovládejte vždy pomocí parodd. Úplné vypnutí paritního bitu (parity none) se provede následovně:

stty -F /dev/ttyS0 -parenb

V tomto případě může být nastavení parodd jakékoli, protože se ignoruje. Pro úplnost uvádím, že parita se používá jako způsob detekce chyb v přenosu.

Dalším často zmiňovaným komunikačním parametrem je počet tzv. stopbitů. Bývá buď jeden, nebo dva. Přednastaven je jeden, ale volba cstopb umí zapnout další stop bit, čímž se jejich počet zvýší na dva. Tedy

stty -F /dev/ttyS0 cstopb

nastaví počet stopbitů na dva, zatímco

stty -F /dev/ttyS0 -cstopb

nastaví počet stopbitů na jeden.

Kromě parametrů, které mají možnost zapnuto/vypnuto, rozeznává příkaz stty ještě parametry s argumentem. Například počet bitů v jednom znaku se nastavuje volbou csn, kde n je počet bitů. Opět uvedu příklad:

stty -F /dev/ttyS0 cs8

cs8 nastaví počet bitů na osm. Jestliže najdete na smetišti nějaké pošetilé zařízení, které vyžaduje sedm bitů, lze sériový port přinutit i k tomuto poklesku:

stty -F /dev/ttyS0 cs7

ale raději to rychle vraťte zpátky na osm.

Dovolil bych si malé srovnání příkazu stty a jeho oškubaného dosovského protějšku MODE při nastavování parametrů sériového portu. Řekněme, že chceme nastavit baudrate = 9600, osm bitů na znak, sudá parita, jeden stop bit. Příkazem MODE v DOSu toho docílíme takto:

MODE COM1 9600,8,E,1

Totéž pomocí stty:

stty -F /dev/ttyS0 cs8 parenb -parodd cstopb 9600

přičemž k dispozici je celá škála dalších parametrů pro řízení toku dat, zacházení s chybami, timeouty, řídicí sekvence atd.

Nebývá jednoduché najít chybu v komunikaci, když sériový port (či spíše jeho driver) ze své vlastní iniciativy mění některé bajty, některé ignoruje a některé zopakuje vícekrát. Celkem častá je situace, kdy chceme, aby sériový port vyslal přesně to, co zapíšeme do /dev/ttyS0 a naopak z /dev/ttyS0 přečteme přesně to, co přišlo po drátech do portu. Mám tedy na mysli nastavení vhodné pro binární přenos. V tomto případě můžeme provést následující test: pošleme do /dev/ttyS0 posloupnost všech 256 možných bajtů a měli bychom dostat zpátky totéž. Pro tento účel přikládám ke stažení soubor bytes_00-FF.bin, který obsahuje bajty 0 až 255. Příkazem

xxd bytes_00-FF.bin

se můžete přesvědčit, co v souboru je:

0000000: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ................
0000010: 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................
0000020: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
0000030: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
0000040: 4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
0000050: 5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[\]^_
0000060: 6061 6263 6465 6667 6869 6a6b 6c6d 6e6f `abcdefghijklmno
0000070: 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
0000080: 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f ................
0000090: 9091 9293 9495 9697 9899 9a9b 9c9d 9e9f ................
00000a0: a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf ................
00000b0: b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf ................
00000c0: c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf ................
00000d0: d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf ................
00000e0: e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef ................
00000f0: f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff ................

V jednom terminálu pusťte

cat /dev/ttyS0 | xxd

a v jiném

cat bytes_00-FF.bin > /dev/ttyS0

Výsledkem by mělo být

cat /dev/ttyS0 | xxd
0000000: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ................
0000010: 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f ................
0000020: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f !"#$%&'()*+,-./
0000030: 3031 3233 3435 3637 3839 3a3b 3c3d 3e3f 0123456789:;<=>?
0000040: 4041 4243 4445 4647 4849 4a4b 4c4d 4e4f @ABCDEFGHIJKLMNO
0000050: 5051 5253 5455 5657 5859 5a5b 5c5d 5e5f PQRSTUVWXYZ[\]^_
0000060: 6061 6263 6465 6667 6869 6a6b 6c6d 6e6f `abcdefghijklmno
0000070: 7071 7273 7475 7677 7879 7a7b 7c7d 7e7f pqrstuvwxyz{|}~.
0000080: 8081 8283 8485 8687 8889 8a8b 8c8d 8e8f ................
0000090: 9091 9293 9495 9697 9899 9a9b 9c9d 9e9f ................
00000a0: a0a1 a2a3 a4a5 a6a7 a8a9 aaab acad aeaf ................
00000b0: b0b1 b2b3 b4b5 b6b7 b8b9 babb bcbd bebf ................
00000c0: c0c1 c2c3 c4c5 c6c7 c8c9 cacb cccd cecf ................
00000d0: d0d1 d2d3 d4d5 d6d7 d8d9 dadb dcdd dedf ................
00000e0: e0e1 e2e3 e4e5 e6e7 e8e9 eaeb eced eeef ................
00000f0: f0f1 f2f3 f4f5 f6f7 f8f9 fafb fcfd feff ................

Připomínám, že piny 2 a 3 musí být navzájem propojené. Dostanete-li jiný výsledek, neznamená to, že je něco špatně. Vše závisí na způsobu použití. Kupříkladu volba

stty -F /dev/ttyS0 igncr

způsobí, že se všechny bajty přenesou beze změny, kromě bajtu s hodnotou 0x0d = 13 a dostaneme tedy

0000000: 0001 0203 0405 0607 0809 0a0b 0c0e 0f10 ................
0000010: 1112 1314 1516 1718 191a 1b1c 1d1e 1f20 ...............
0000020: 2122 2324 2526 2728 292a 2b2c 2d2e 2f30 !"#$%&'()*+,-./0
0000030: 3132 3334 3536 3738 393a 3b3c 3d3e 3f40 123456789:;<=>?@
0000040: 4142 4344 4546 4748 494a 4b4c 4d4e 4f50 ABCDEFGHIJKLMNOP
0000050: 5152 5354 5556 5758 595a 5b5c 5d5e 5f60 QRSTUVWXYZ[\]^_`
0000060: 6162 6364 6566 6768 696a 6b6c 6d6e 6f70 abcdefghijklmnop
0000070: 7172 7374 7576 7778 797a 7b7c 7d7e 7f80 qrstuvwxyz{|}~..
0000080: 8182 8384 8586 8788 898a 8b8c 8d8e 8f90 ................
0000090: 9192 9394 9596 9798 999a 9b9c 9d9e 9fa0 ................
00000a0: a1a2 a3a4 a5a6 a7a8 a9aa abac adae afb0 ................
00000b0: b1b2 b3b4 b5b6 b7b8 b9ba bbbc bdbe bfc0 ................
00000c0: c1c2 c3c4 c5c6 c7c8 c9ca cbcc cdce cfd0 ................
00000d0: d1d2 d3d4 d5d6 d7d8 d9da dbdc ddde dfe0 ................
00000e0: e1e2 e3e4 e5e6 e7e8 e9ea ebec edee eff0 ................

Všimněte si, že bajt 0x0d chybí. Měl by být hned v prvním řádku. Uvádím to pouze pro demonstraci zdánlivě podivného chování, které někdy může být i žádoucí.

Související články

Sériová komunikace pod Linuxem - II
Jednočipy pod Linuxem - I
Jednočipy pod Linuxem - II

Odkazy a zdroje

Schéma pinů v SVG formátu
Serial-HOWTO.html
Serial Programming Guide for POSIX Operating Systems

Další články z této rubriky

HW novinky: podzimní přehled #2
HW novinky: podzimní přehled #1
HW novinky: návrat skleněných ploten v HDD
HW novinky: PCI Express 4.0 prý ještě letos
HW novinky: i Skylake-X s 12 jádry používá levnou teplovodivou pastu

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.