GNU/Linux >> Tutoriels Linux >  >> Linux

Comment ouvrir, lire et écrire à partir du port série en C ?

Pour le code de démonstration conforme à la norme POSIX, comme décrit dans Définition correcte des modes de terminal et Guide de programmation série pour les systèmes d'exploitation POSIX, ce qui suit est proposé.
Ce code devrait s'exécuter correctement en utilisant Linux sur x86 ainsi que les processeurs ARM (ou même CRIS).
Il est essentiellement dérivé de l'autre réponse, mais les commentaires inexacts et trompeurs ont été corrigés.

Ce programme de démonstration ouvre et initialise un terminal série à 115200 bauds pour un mode non canonique aussi portable que possible.
Le programme transmet une chaîne de texte codée en dur à l'autre terminal et retarde l'exécution de la sortie.
Le programme entre alors dans une boucle infinie pour recevoir et afficher les données du terminal série.
Par défaut, les données reçues sont affichées sous forme de valeurs d'octets hexadécimaux.

Pour que le programme traite les données reçues comme des codes ASCII, compilez le programme avec le symbole DISPLAY_STRING, par exemple

 cc -DDISPLAY_STRING demo.c

Si les données reçues sont du texte ASCII (plutôt que des données binaires) et que vous souhaitez les lire sous forme de lignes terminées par le caractère de nouvelle ligne, consultez cette réponse pour un exemple de programme.

#define TERMINAL    "/dev/ttyUSB0"

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }

    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */

    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}


int main()
{
    char *portname = TERMINAL;
    int fd;
    int wlen;
    char *xstr = "Hello!\n";
    int xlen = strlen(xstr);

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);
    //set_mincount(fd, 0);                /* set to pure timed read */

    /* simple output */
    wlen = write(fd, xstr, xlen);
    if (wlen != xlen) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple noncanonical input */
    do {
        unsigned char buf[80];
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
#ifdef DISPLAY_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#else /* display hex */
            unsigned char   *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Timeout from read\n");
        }               
        /* repeat read to get full message */
    } while (1);
}

Pour un exemple de programme efficace qui fournit une mise en mémoire tampon des données reçues tout en permettant une manipulation octet par octet de l'entrée, consultez cette réponse.


J'ai écrit ceci il y a longtemps (des années 1985 à 1992, avec seulement quelques ajustements depuis), et il suffit de copier et coller les éléments nécessaires dans chaque projet.

Vous devez appeler le cfmakeraw sur un tty obtenu à partir de tcgetattr . Vous ne pouvez pas mettre à zéro un struct termios , configurez-le, puis définissez le tty avec tcsetattr . Si vous utilisez la méthode de mise à zéro, vous rencontrerez des pannes intermittentes inexpliquées, en particulier sur les BSD et OS X. Les "pannes intermittentes inexpliquées" incluent le blocage dans read(3) .

#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <unistd.h>

int
set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                error_message ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void
set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error_message ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                error_message ("error %d setting term attributes", errno);
}


...
char *portname = "/dev/ttyUSB1"
 ...
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
        error_message ("error %d opening %s: %s", errno, portname, strerror (errno));
        return;
}

set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0);                // set no blocking

write (fd, "hello!\n", 7);           // send 7 character greeting

usleep ((7 + 25) * 100);             // sleep enough to transmit the 7 plus
                                     // receive 25:  approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf);  // read up to 100 characters if ready to read

Les valeurs de vitesse sont B115200 , B230400 , B9600 , B19200 , B38400 , B57600 , B1200 , B2400 , B4800 , etc. Les valeurs de parité sont 0 (c'est-à-dire sans parité), PARENB|PARODD (activer la parité et utiliser l'impair), PARENB (activer la parité et utiliser pair), PARENB|PARODD|CMSPAR (marquer la parité), et PARENB|CMSPAR (parité spatiale).

"Blocage" définit si un read() sur le port attend que le nombre de caractères spécifié arrive. Définir pas de blocage signifie qu'un read() renvoie quel que soit le nombre de caractères disponibles sans en attendre davantage, jusqu'à la limite de la mémoire tampon.

Addendum :

CMSPAR n'est nécessaire que pour choisir la parité de marque et d'espace, ce qui est rare. Pour la plupart des applications, il peut être omis. Mon fichier d'en-tête /usr/include/bits/termios.h permet la définition de CMSPAR uniquement si le symbole du préprocesseur __USE_MISC est défini. Cette définition se produit (dans features.h ) avec

#if defined _BSD_SOURCE || defined _SVID_SOURCE
 #define __USE_MISC     1
#endif

Les commentaires introductifs de <features.h> dit :

/* These are defined by the user (or the compiler)
   to specify the desired environment:

...
   _BSD_SOURCE          ISO C, POSIX, and 4.3BSD things.
   _SVID_SOURCE         ISO C, POSIX, and SVID things.
...
 */

Linux
  1. Comment lire et écrire sur les lecteurs Windows NTFS en tant qu'utilisateur

  2. Comment se connecter et envoyer des données à un port série Bluetooth sous Linux ?

  3. Comment trouver quel port série est utilisé ?

  4. Linux - Comment trouver des processus utilisant le port série ?

  5. Comment ouvrir le port série sous Linux sans changer de broche ?

Comment utiliser la commande Netcat pour lire et écrire des données sur le réseau

Comment rechercher et fermer des ports ouverts sous Linux

Comment changer le port WordPress dans Apache et Nginx

Comment écrire et exécuter un programme C sous Linux

Journalctl :comment lire et modifier les journaux Systemd

Pourquoi le port 1111 est-il ouvert et est-il sûr de l'être ?