La gestion des ports séries sous Linux
Article pour l'Écho de Linux (Juillet 1996)
Eric Sellin (esellin@pratique.fr)
L'ambition de cet article est bien modeste : il veut vous expliquer comment on accède aux ports séries sous Linux, c'est-à-dire comment y lire des caractères et en envoyer, comment modifier les paramètres de la liaison, etc...
Nous entrons tout de suite dans le vif du sujet avec quelques rappels élémentaires.
Le débit indique combien de bits circulent chaque seconde sur
la liaison. Cela peut aller de 50 à 460800 (!) bits/s.
La parité, lorsqu'elle est utilisée, est un bit
supplémentaire qui sert de contrôle de transmission,
contrôle qui est loin d'être parfait puisqu'il n'est
efficace que si le nombre de bits érronés dans un
caractère est impair.
Chaque caractère peut être composé de 5 à
8 bits. Dans la grande majorité des cas, on utilise 8 bits.
Le ou les bits de stop servent à marquer la fin de chaque
caractère dans la transmission. En général,
on n'utilise qu'un seul bit de stop.
Mais ne nous emballons pas, il faut d'abord paramétrer notre
liaison.
Cette structure comporte les champs suivants
La fonction tcgetattr permet d'obtenir les paramètres
actuels d'une liaison. En voici le prototype, extrait du man :
Une fois que ces champs ont été modifiés, il faut
enregistrer ces modifications au moyen de la fonction
tcsetattr :
Ils définissent un traitement à appliquer sur les
caractères en provenance de la liaison série :
Ils définissent un traitement à appliquer sur les
caractères envoyés sur la liaison série. On y
trouve notamment OLCUC qui transforme les minuscules (non
accentuées !) en majuscules.
Ce champ est important, car c'est ici que l'on définit le
débit, la parité utilisée, les bits de
donnée et de stop, ...
Deux possibilités s'offrent à nous :
À l'ouverture par open(), utilisez l'indicateur O_NONBLOCK :
Pour modifier le fonctionnement alors que le port série est
déjà ouvert, il faut utiliser l'appel système
fcntl() de la façon suivante :
Je vous rappelle qu'on peut lire et écrire avec les appels
systèmes read() et write().
Voici quelques informations pour mieux exploiter votre liaison :
Bref rappel sur les liaisons séries
Sur une liaison série, les bits qui composent un caractère
sont envoyés les uns aprés les autres, une tension étant
associée aux 0 et aux 1. Les principaux paramètres d'une
telle liaison sont les suivants :
Ouverture de la liaison
Sous Linux, chaque port série de votre machine est
représenté par un fichier de périphérique
situé dans le répertoire /dev à côté
de dizaines d'autres.
Au niveau d'un programme en C, on ouvre ces fichiers exactement comme
on ouvrirait n'importe quel autre fichier grâce à
l'appel système open() :
int fd;
if ( (fd=open("/dev/ttyS1",O_RDWR)) == -1 ) {
perror("open");
exit(-1);
}
Une fois que le port est ouvert, on peut y lire et y écrire
des caractères au moyen des primitives read() et
write().
Paramétrage de la liaison
Dans la norme POSIX, tous les paramètres d'une
liaison sont regroupés dans une structure appelée
termios et définie dans le fichier <termios.h>
qu'il nous faut donc inclure.
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
int tcgetattr ( int fd, struct termios *termios_p );
et un exemple typique d'utilisation :
struct termios termios_p;
tcgetattr(fd,&termios_p);
Notre structure termios_p étant renseignée,
on peut en modifier les champs, dont une description sera donnée
au paragraphe suivant.
tcsetattr(fd,TCSANOW,&termios_p);
Les champs de la structure termios
Nous n'allons pas détailler ici l'ensemble des champs de cette
structure car ils sont trop nombreux. Seuls les champs utiles seront
abordés.
Mode bloquant et mode non-bloquant
Que se passe-t-il lorsqu'on appelle read(), et qu'il n'y a
malheureusement rien à lire ?
C'est à vous de choisir le mode de fonctionnement. Vous
pouvez faire ce choix à l'ouverture du port série,
mais également à n'importe quel autre moment.
int fd;
if ( (fd=open("/dev/ttyS1",O_RDWR|O_NONBLOCK)) == -1 ) {
perror("open");
exit(-1);
}
/* Passe en mode bloquant */
fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)&~O_NONBLOCK);
/* Repasse en mode non-bloquant */
fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)|O_NONBLOCK);
Exploitation
tcflush(fd,TCOFLUSH);
tcflush(fd,TCIFLUSH);
tcdrain(fd);