Le Linux Serial Programming HOWTO
par Peter H. Baumann,
[email protected]
Adaptation francaise Etienne BERNARD
[email protected]
v0.3, 14 Juin 1997
Ce document decrit comment programmer sous Linux la communication avec
des peripheriques sur port serie.
11..
IInnttrroodduuccttiioonn
Voici le Linux Serial Programming HOWTO, qui explique comment
programmer sous Linux la communication avec des peripheriques ou des
ordinateurs via le port serie. Differentes techniques sont abordees :
Entrees/Sorties canoniques (envoi ou reception ligne par ligne),
asynchrones, ou l'attente de donnees depuis de multiples sources.
Ce document ne decrit pas comment configurer les ports series, puisque
c'est decrit par Greg Hankins dans le Serial-HOWTO.
Je tiens a insister sur le fait que je ne suis pas un expert dans ce
domaine, mais j'ai eu a realiser un projet utilisant la communication
par le port serie. Les exemples de code source presentes dans ce
document sont derives du programme miniterm, disponible dans le _L_i_n_u_x
_p_r_o_g_r_a_m_m_e_r_'_s _g_u_i_d_e
(
ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-
guide/lpg-0.4.tar.gz et les miroirs, par exemple
ftp://ftp.lip6.fr/pub/linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz)
dans le repertoire contenant les exemples. Je me ferai un plaisir
d'incorporer d'eventuels commentaires (voyez la section sur les
commentaires).
Tous les exemples ont ete testes avec un i386, utilisant un noyau
Linux de version 2.0.29.
11..11..
CCooppyyrriigghhtt
Le Linux Serial-Programming-HOWTO est copyright (c) 1997 Peter
Baumann. Les HOWTO de Linux peuvent etre reproduits et distribues
integralement ou seulement par partie, sur quelconque support physique
ou electronique, aussi longtemps que ce message de copyright sera
conserve dans toutes les copies. Une redistribution commerciale est
autorisee, et encouragee; cependant, l'auteur _a_p_p_r_e_c_i_e_r_a_i_t d'etre
prevenu en cas de distribution de ce type.
Toutes les traductions ou travaux derives incorporant un document
HOWTO Linux doit etre place sous ce copyright. C'est-a-dire que vous
ne pouvez pas produire de travaux derives a partir d'un HOWTO et
imposer des restrictions additionnelles sur sa distribution. Des
exceptions a cette regle peuvent etre accordees sous certaines
conditions ; contactez le coordinateur des HOWTO Linux a l'adresse
donnee ci-dessous.
En resume, nous desirons promouvoir la distribution de cette
information par tous les moyens possibles. Neanmoins, nous desirons
conserver le copyright sur les documents HOWTO, et nous _a_i_m_e_r_i_o_n_s etre
informes de tout projet de redistribution des HOWTO.
Pour toute question, veuillez contacter Greg Hankins, le coordinateur
des HOWTO Linux, a
[email protected] par mail.
11..22..
NNoouuvveelllleess vveerrssiioonnss ddee ccee ddooccuummeenntt..
Les nouvelles version du Serial-Programming-HOWTO seront disponibles a
ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO
et les sites miroir, comme par exemple
ftp://ftp.lip6.fr/pub/linux/docs/HOWTO/Serial-Programming-HOWTO. Il
existe sous d'autres formats, comme PostScript ou DVI dans le sous
repertoire other-formats. Le Serial-Programming-HOWTO est egalement
disponible sur
http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-
HOWTO.html, et sera poste dans comp.os.linux.answers tous les mois
(NdT : la version francaise de ce document est egalement postee dans
fr.comp.os.linux.annonce tous les mois).
11..33..
CCoommmmeennttaaiirreess
Envoyez moi, s'il vous plait tout correction, question, commentaire,
suggestion ou complement. Je desire ameliorer cet HOWTO ! Dites moi
exactement ce que vous ne comprenez pas, ou ce qui pourrait etre plus
clair. Vous pouvez me contacter a
[email protected] par courrier
electronique. Veuillez inclure le numero de version de ce document
pour tout courrier. Ce document est la version 0.3.
22..
DDeemmaarrrraaggee
22..11..
DDeebbuuggggaaggee
Le meilleur moyen de debugguer votre code est d'installer une autre
machine sous Linux et de connecter les deux ordinateurs par un cable
null-modem. Utilisez miniterm (disponible dans le Linux programmers
guide --
ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-
guide/lpg-0.4.tar.gz -- dans le repertoire des exemples) pour
transmettre des caracteres a votre machine Linux. Miniterm est tres
simple a compiler et transmettra toute entree clavier directement sur
le port serie. Vous n'avez qu'a adapter la commande #define
MODEMDEVICE "/dev/ttyS0" a vos besoins. Mettez ttyS0 pour COM1, ttyS1
for COM2, etc... Il est essentiel, pour les tests, que _t_o_u_s les
caracteres soient transmis bruts (sans traitements) au travers de la
ligne serie. Pour tester votre connexion, demarrez miniterm sur les
deux ordinateurs et taper au clavier. Les caracteres ecrit sur un
ordinateur devraient apparaitre sur l'autre ordinateur, et vice versa.
! ! ! L'entree clavier sera egalement recopiee sur l'ecran de
l'ordinateur local.
Pour fabriquer un cable null-modem, pour devez croiser les lignes TxD
(_t_r_a_n_s_m_i_t) et RxD (_r_e_c_e_i_v_e). Pour une description du cable, referez
vous a la section 7 du Serial-HOWTO.
Il est egalement possible de faire cet essai avec uniquement un seul
ordinateur, si vous disposez de deux ports serie. Vous pouvez lancez
deux miniterms sur deux consoles virtuelles. Si vous liberez un port
serie en deconnectant la souris, n'oubliez pas de rediriger /dev/mouse
si ce fichier existe. Si vous utilisez une carte serie a ports
multiples, soyez sur de la configurer correctement. La mienne n'etait
pas correctement configuree, et tout fonctionnait correctement lorsque
je testais sur mon ordinateur. Lorsque je l'ai connecte a un autre, le
port a commence a perdre des caracteres. L'execution de deux
programmes sur un seul ordinateur n'est pas totalement asynchrone.
22..22..
CCoonnffiigguurraattiioonn ddeess ppoorrttss
Les peripheriques /dev/ttyS* sont destines a connecter les terminaux a
votre machine Linux, et sont configures pour cet usage apres le
demarrage. Vous devez vous en souvenir lorsque vous programmez la
communication avec un peripherique autre. Par exemple, les ports sont
configures pour afficher les caracteres envoyes vers lui-meme, ce qui
normalement doit etre change pour la transmission de donnees.
Tous les parametres peuvent etre facilement configure depuis un
programme. La configuration est stockee dans une structure de type
struct termios, qui est definie dans <asm/termbits.h> :
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* Modes d'entree */
tcflag_t c_oflag; /* Modes de sortie */
tcflag_t c_cflag; /* Modes de controle */
tcflag_t c_lflag; /* Modes locaux */
cc_t c_line; /* Discipline de ligne */
cc_t c_cc[NCCS]; /* Caracteres de controle */
};
Ce fichier inclus egalement la definition des constantes. Tous les
modes d'entree dans c_iflag prennent en charge le traitement de
l'entree, ce qui signifie que les caracteres envoyes depuis le
peripherique peuvent etre traites avant d'etre lu par read. De la meme
facon, c_oflags se chargent du traitement en sortie. c_cflag contient
les parametres du port, comme la vitesse, le nombre de bits par
caractere, les bits d'arret, etc... Les modes locaux, stockes dans
c_lflag determinent si les caracteres sont imprimes, si des signaux
sont envoyes a votre programme, etc... Enfin, le tableau c_cc definit
les caracteres de controle pour la fin de fichier, le caractere stop,
etc... Les valeurs par defaut pour les caracteres de controle sont
definies dans <asm/termios.h>. Les modes possibles sont decrits dans
la page de manuel de termios(3)
La structure termios contient l'element c_line. Cet element n'est ni
mentionne dans la page de manuel de termios de Linux, ni dans celle de
Solaris 2.5. Quelqu'un peut-il m'eclairer sur cet attribut ? Est-ce
qu'il doit reellement etre inclus dans la structure termios ?
22..33..
FFaaccoonnss ddee lliirree ssuurr lleess ppeerriipphheerriiqquueess sseerriiee
Voici trois facons de lire sur les peripheriques serie. Le moyen
approprie doit etre choisi pour chaque application. Lorsque cela est
possible, ne lisez pas les chaines caractere par caractere. Lorsque
j'utilisais ce moyen, je perdais des caracteres, alors qu'un read sur
la chaine complete ne donnait aucune erreur.
22..33..11..
EEnnttrreeee ccaannoonniiqquuee
C'est le mode de fonctionnement normal pour les terminaux, mais peut
egalement etre utilise pour communiquer avec d'autres peripheriques.
Toutes les entrees sont traitees lignes par lignes, ce qui signifie
qu'un read ne renverra qu'une ligne complete. Une ligne est terminee
par defaut avec un caractere NL (ACII LF), une fin de fichier, ou un
caractere de fin de ligne. Un CR (le caractere de fin de ligne par
defaut de DOS et Windows) ne terminera pas une ligne, avec les
parametres par defaut.
L'entree canonique peut egalement prendre en charge le caractere
erase, d'effacement de mot, et de reaffichage, la traduction de CR
vers NL, etc...
22..33..22..
EEnnttrreeee nnoonn ccaannoonniiqquuee
L'entree non canonique va prendre en charge un nombre fixe de
caractere par lecture, et autorise l'utilisation d'un compteur de
temps pour les caracteres. Ce mode doit etre utilise si votre
application lira toujours un nombre fixe de caracteres, ou si le
peripherique connecte envoit les caracteres par paquet.
22..33..33..
EEnnttrreeee aassyynncchhrroonnee
Les deux modes ci-dessus peut etre utilise en mode synchrone ou
asynchrone. Le mode synchrone est le mode par defaut, pour lequel un
appel a read sera bloquant, jusqu'a ce que la lecture soit satisfaite.
En mode asynchrone, un appel a read retournera immediatement et
lancera un signal au programme appelant en fin de transfert. Ce signal
peut etre recu par un gestionnaire de signal.
22..33..44..
AAtttteennttee dd''eennttrreeee ddeeppuuiiss ddee mmuullttiipplleess ssoouurrcceess
Cela ne constitue pas un mode d'entree different, mais peut s'averer
etre utile, si vous prenez en charge des peripheriques multiples. Dans
mon application, je traitais l'entree depuis une socket TCP/IP et
depuis une connexion serie sur un autre ordinateur quasiment en meme
temps. L'exemple de programme donne plus loin attendra des caracteres
en entree depuis deux sources. Si des donnees sur l'une des sources
deviennent disponibles, elles seront traitees, et le programme
attendra de nouvelles donnees.
L'approche presentee plus loin semble plutot complexe, mais il est
important que vous vous rappeliez que Linux est un systeme multi-
tache. L'appel systeme select ne charge pas le processeur lorsqu'il
attend des donnees, alors que le fait de faire une boucle jusqu'a ce
que des caracteres deviennent disponibles ralentirait les autres
processus.
33..
EExxeemmpplleess ddee pprrooggrraammmmeess
Tous les exemples ont ete extraits de miniterm.c. Le tampon d'entree
est limite a 255 caracteres, tout comme l'est la longueur maximale
d'une ligne en mode canonique (<linux/limits.h> ou <posix1_lim.h>).
Referez-vous aux commentaires dans le code source pour l'explication
des differents modes d'entree. J'espere que le code est
comprehensible. L'exemple sur l'entree canonique est la plus
commentee, les autres exemples sont commentes uniquement lorsqu'ils
different, afin de signaler les differences.
Les descriptions ne sont pas completes, mais je vous encourage a
modifier les exemples pour obtenir la solution la plus interessante
pour votre application.
N'oubliez pas de donner les droits corrects aux ports serie (par
exemple, chmod a+rw /dev/ttyS1) !
33..11..
TTrraaiitteemmeenntt ccaannoonniiqquuee
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
/* les valeurs pour la vitesse, baudrate, sont definies dans <asm/termbits.h>, qui est inclus
dans <termios.h> */
#define BAUDRATE B38400
/* changez cette definition pour utiliser le port correct */
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* code source conforme a POSIX */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
/*
On ouvre le peripherique du modem en lecture/ecriture, et pas comme
terminal de controle, puisque nous ne voulons pas etre termine si l'on
recoit un caractere CTRL-C.
*/
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* sauvegarde de la configuration courante */
bzero(newtio, sizeof(newtio)); /* on initialise la structure a zero */
/*
BAUDRATE: Affecte la vitesse. vous pouvez egalement utiliser cfsetispeed
et cfsetospeed.
CRTSCTS : controle de flux materiel (uniquement utilise si le cable a
les lignes necessaires. Voir la section 7 du Serial-HOWTO).
CS8 : 8n1 (8 bits,sans parite, 1 bit d'arret)
CLOCAL : connexion locale, pas de controle par le modem
CREAD : permet la reception des caracteres
*/
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
/*
IGNPAR : ignore les octets ayant une erreur de parite.
ICRNL : transforme CR en NL (sinon un CR de l'autre cote de la ligne
ne terminera pas l'entree).
sinon, utiliser l'entree sans traitement (device en mode raw).
*/
newtio.c_iflag = IGNPAR | ICRNL;
/*
Sortie sans traitement (raw).
*/
newtio.c_oflag = 0;
/*
ICANON : active l'entree en mode canonique
desactive toute fonctionnalite d'echo, et n'envoit pas de signal au
programme appelant.
*/
newtio.c_lflag = ICANON;
/*
initialise les caracteres de controle.
les valeurs par defaut peuvent etre trouvees dans
/usr/include/termios.h, et sont donnees dans les commentaires.
Elles sont inutiles ici.
*/
newtio.c_cc[VINTR] = 0; /* Ctrl-c */
newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
newtio.c_cc[VERASE] = 0; /* del */
newtio.c_cc[VKILL] = 0; /* @ */
newtio.c_cc[VEOF] = 4; /* Ctrl-d */
newtio.c_cc[VTIME] = 0; /* compteur inter-caractere non utilise */
newtio.c_cc[VMIN] = 1; /* read bloquant jusqu'a l'arrivee d'1 caractere */
newtio.c_cc[VSWTC] = 0; /* '\0' */
newtio.c_cc[VSTART] = 0; /* Ctrl-q */
newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
newtio.c_cc[VEOL] = 0; /* '\0' */
newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */
newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */
newtio.c_cc[VWERASE] = 0; /* Ctrl-w */
newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */
newtio.c_cc[VEOL2] = 0; /* '\0' */
/*
a present, on vide la ligne du modem, et on active la configuration
pour le port
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/*
la configuration du terminal est faite, a present on traite
les entrees
Dans cet exemple, la reception d'un 'z' en debut de ligne mettra
fin au programme.
*/
while (STOP==FALSE) { /* boucle jusqu'a condition de terminaison */
/* read bloque l'execution du programme jusqu'a ce qu'un caractere de
fin de ligne soit lu, meme si plus de 255 caracteres sont saisis.
Si le nombre de caracteres lus est inferieur au nombre de caracteres
disponibles, des read suivant retourneront les caracteres restants.
res sera positionne au nombre de caracteres effectivement lus */
res = read(fd,buf,255);
buf[res]=0; /* on termine la ligne, pour pouvoir l'afficher */
printf(":%s:%d\n", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
/* restaure les anciens parametres du port */
tcsetattr(fd,TCSANOW,&oldtio);
}
33..22..
EEnnttrreeee nnoonn ccaannoonniiqquuee
Dans le mode non canonique, les caracteres lus ne sont pas assembles
ligne par ligne, et ils ne subissent pas de traitement (erase, kill,
delete, etc...). Deux parametres controlent ce mode : c_cc[VTIME]
positionne le _t_i_m_e_r de caracteres, et c_cc[VMIN] indique le nombre
minimum de caracteres a recevoir avant qu'une lecture soit satisfaite.
Si MIN > 0 et TIME = 0, MIN indique le nombre de caracteres a recevoir
avant que la lecture soit satisfaite. TIME est egal a zero, et le
_t_i_m_e_r n'est pas utilise.
Si MIN = 0 et TIME > 0, TIME est utilise comme une valeur de _t_i_m_e_o_u_t.
Une lecture est satisfaite lorsqu'un caractere est recu, ou que la
duree TIME est depassee (t = TIME * 0.1s). Si TIME est depasse, aucun
caractere n'est retourne.
Si MIN > 0 et TIME > 0, TIME est employe comme _t_i_m_e_r entre chaque
caractere. La lecture sera satisfaite si MIN caracteres sont recus, ou
que le _t_i_m_e_r entre deux caracteres depasse TIME. Le _t_i_m_e_r est
reinitialise a chaque fois qu'un caractere est recu, et n'est active
qu'apres la reception du premier caractere.
Si MIN = 0 et TIME = 0, le retour est immediat. Le nombre de
caracteres disponibles, ou bien le nombre de caracteres demande est
retourne. Selon Antonino (voir le paragraphe sur les participations),
vous pouvez utiliser un fcntl(fd, F_SETFL, FNDELAY); avant la lecture
pour obtenir le meme resultat.
Vous pouvez tester tous les modes decrit ci-dessus en modifiant
newtio.c_cc[VTIME] et newtio.c_cc[VMIN].
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* code source conforme a POSIX */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* sauvegarde de la configuration courante */
bzero(newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/* positionne le mode de lecture (non canonique, sans echo, ...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* timer inter-caracteres non utilise */
newtio.c_cc[VMIN] = 5; /* read bloquant jusqu'a ce que 5 */
/* caracteres soient lus */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
while (STOP==FALSE) { /* boucle de lecture */
res = read(fd,buf,255); /* retourne apres lecture 5 caracteres */
buf[res]=0; /* pour pouvoir les imprimer... */
printf(":%s:%d\n", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
tcsetattr(fd,TCSANOW,&oldtio);
}
33..33..
LLeeccttuurree aassyynncchhrroonnee
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>
#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* code source conforme a POSIX */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
void signal_handler_IO (int status); /* le gestionnaire de signal */
int wait_flag=TRUE; /* TRUE tant que recu aucun signal */
main()
{
int fd,c, res;
struct termios oldtio,newtio;
struct sigaction saio; /* definition de l'action du signal */
char buf[255];
/* ouvre le port en mon non-bloquant (read retourne immediatement) */
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
/* installe le gestionnaire de signal avant de passer le port en
mode asynchrone */
saio.sa_handler = signal_handler_IO;
saio.sa_mask = 0;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
/* permet au processus de recevoir un SIGIO */
fcntl(fd, F_SETOWN, getpid());
/* rend le descripteur de fichier asynchrone (la page de manuel
indique que seuls O_APPEND et O_NONBLOCK fonctionnent avec
F_SETFL...) */
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio); /* sauvegarde de la configuration courante */
/* positionne les nouvelles valeurs pour lecture canonique */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VMIN]=1;
newtio.c_cc[VTIME]=0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/* on boucle en attente de lecture. generalement, on realise
des traitements a l'interieur de la boucle */
while (STOP==FALSE) {
printf(".\n");usleep(100000);
/* wait_flag = FALSE apres reception de SIGIO. Des donnees sont
disponibles et peuvent etre lues */
if (wait_flag==FALSE) {
res = read(fd,buf,255);
buf[res]=0;
printf(":%s:%d\n", buf, res);
if (res==1) STOP=TRUE; /* on arrete la boucle si on lit une
ligne seule */
wait_flag = TRUE; /* on attend de nouvelles donnees */
}
}
/* restaure les anciens parametres du port */
tcsetattr(fd,TCSANOW,&oldtio);
}
/***************************************************************************
* gestionnaire de signal. Positionne wait_flag a FALSE, pour indiquer a *
* la boucle ci-dessus que des caracteres ont ete recus. *
***************************************************************************/
void signal_handler_IO (int status)
{
printf("reception du signal SIGIO.\n);
wait_flag = FALSE;
}
33..44..
MMuullttiipplleexxaaggee eenn lleeccttuurree
Cette section est reduite au minimum, et n'est la que pour vous
guider. Le code source d'exemple presente est donc reduit au strict
minimum. Il ne fonctionnera pas seulement avec des ports serie, mais
avec n'importe quel ensemble de descripteurs de fichiers.
L'appel systeme select et les macros qui lui sont attachees utilisent
un fd_set. C'est un tableau de bits, qui dispose d'un bit pour chaque
descripteur de fichier valide. select accepte un fd_set ayant les bits
positionnes pour les descripteurs de fichiers qui conviennent, et
retourne un fd_set, dans lequel les bits des descripteurs de fichier
ou une lecture, une ecriture ou une exception sont positionnes. Toutes
les manipulations de fd_set sont faites avec les macros fournies.
Reportez vous egalement a la page de manuel de select(2).
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
main()
{
int fd1, fd2; /* entrees 1 et 2 */
fd_set readfs; /* ensemble de descripteurs */
int maxfd; /* nombre max des descripteurs utilises */
int loop=1; /* boucle tant que TRUE */
/* open_input_source ouvre un peripherique, configure le port
correctement, et retourne un descripteur de fichier. */
fd1 = open_input_source("/dev/ttyS1"); /* COM2 */
if (fd1<0) exit(0);
fd2 = open_input_source("/dev/ttyS2"); /* COM3 */
if (fd2<0) exit(0);
maxfd = MAX (fd1, fd2)+1; /* numero maximum du bit a tester */
/* boucle d'entree */
while (loop) {
FD_SET(fd1, &readfs); /* test pour la source 1 */
FD_SET(fd2, &readfs); /* test pour la source 2 */
/* on bloque jusqu'a ce que des caracteres soient
disponibles en lecture */
select(maxfd, &readfs, NULL, NULL, NULL);
if (FD_ISSET(fd1)) /* caracteres sur 1 */
handle_input_from_source1();
if (FD_ISSET(fd2)) /* caracteres sur 2 */
handle_input_from_source2();
}
}
L'exemple ci-dessus bloque indefiniment, jusqu'a ce que des caracteres
venant d'une des sources soient disponibles. Si vous avez besoin d'un
_t_i_m_e_o_u_t, remplacez juste l'appel a select par :
int res;
struct timeval Timeout;
/* fixe la valeur du timeout */
Timeout.tv_usec = 0; /* millisecondes */
Timeout.tv_sec = 1; /* secondes */
res = select(maxfd, &readfs, NULL, NULL, &Timeout);
if (res==0)
/* nombre de descripteurs de fichiers avec caracteres
disponibles = 0, il y a eu timeout */
Cet exemple verra l'expiration du delai de _t_i_m_e_o_u_t apres une seconde.
S'il y a _t_i_m_e_o_u_t, select retournera 0, mais faites attention, Timeout
est decremente du temps reellement attendu par select. Si la valeur de
_t_i_m_e_o_u_t est 0, select retournera immediatement.
44..
AAuuttrreess ssoouurrcceess dd''iinnffoorrmmaattiioonn
+o Le Linux Serial-HOWTO decrit comment mettre en place les ports
serie et contient des informations sur le materiel.
+o Le Serial Programming Guide for POSIX Compliant Operating Systems
<
http://www.easysw.com/~mike/serial>, par Michael Sweet.
+o La page de manuel de termios(3) decrit toutes les constantes
utilisees pour la structure termios.
55..
CCoonnttrriibbuuttiioonnss
Comme je l'ai dit dans l'introduction, je ne suis pas un expert dans
le domaine, mais j'ai rencontre des problemes, et j'ai trouve les
solutions avec l'aide d'autres personnes. Je tiens a remercier pour
leur aide M. Strudthoff du European Transonic WindTunnel, Cologne,
Michael Carter (
[email protected]) et Peter Waltenberg
(
[email protected]).
Antonino Ianella (
[email protected] a ecrit le Serial-Port-Programming
Mini HOWTO, au meme moment ou je preparais ce document. Greg Hankins
m'a demande d'inclure le Mini-HOWTO d'Antonino dans ce document.
La structure de ce document et le formattage SGML ont ete derives du
Serial-HOWTO de Greg Hankins.