GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara membuka, membaca, dan menulis dari port serial di C?

Untuk kode demo yang sesuai dengan standar POSIX seperti yang dijelaskan dalam Mengatur Mode Terminal dengan Benar dan Panduan Pemrograman Serial untuk Sistem Operasi POSIX, berikut ini ditawarkan.
Kode ini harus dijalankan dengan benar menggunakan Linux pada prosesor x86 dan juga ARM (atau bahkan CRIS).
Itu pada dasarnya berasal dari jawaban lain, tetapi komentar yang tidak akurat dan menyesatkan telah diperbaiki.

Program demo ini membuka dan menginisialisasi terminal serial pada baud 115200 untuk mode non-kanonik yang seportabel mungkin.
Program mentransmisikan string teks berkode keras ke terminal lain, dan menunda saat output dilakukan.
Program kemudian memasuki loop tak terbatas untuk menerima dan menampilkan data dari terminal serial.
Secara default, data yang diterima ditampilkan sebagai nilai byte heksadesimal.

Untuk membuat program memperlakukan data yang diterima sebagai kode ASCII, kompilasi program dengan simbol DISPLAY_STRING, mis.

 cc -DDISPLAY_STRING demo.c

Jika data yang diterima adalah teks ASCII (bukan data biner) dan Anda ingin membacanya sebagai baris yang diakhiri oleh karakter baris baru, lihat jawaban ini untuk contoh program.

#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);
}

Untuk contoh program efisien yang menyediakan buffering data yang diterima namun memungkinkan pengiriman input byte-by-byte, lihat jawaban ini.


Saya menulis ini lama sekali (dari tahun 1985-1992, hanya dengan beberapa penyesuaian sejak saat itu), dan cukup salin dan tempel bit yang diperlukan ke dalam setiap proyek.

Anda harus memanggil cfmakeraw pada tty diperoleh dari tcgetattr . Anda tidak dapat menghapus struct termios , konfigurasikan, lalu atur tty dengan tcsetattr . Jika Anda menggunakan metode zero-out, maka Anda akan mengalami kegagalan intermiten yang tidak dapat dijelaskan, terutama pada BSD dan OS X. "Kegagalan intermiten yang tidak dapat dijelaskan" termasuk menggantung di 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

Nilai untuk kecepatan adalah B115200 , B230400 , B9600 , B19200 , B38400 , B57600 , B1200 , B2400 , B4800 , dll. Nilai paritas adalah 0 (artinya tanpa paritas), PARENB|PARODD (aktifkan paritas dan gunakan ganjil), PARENB (aktifkan paritas dan gunakan genap), PARENB|PARODD|CMSPAR (mark parity), dan PARENB|CMSPAR (paritas spasi).

"Blocking" menyetel apakah sebuah read() di port menunggu jumlah karakter yang ditentukan tiba. Menyetel tidak ada pemblokiran artinya read() mengembalikan berapa banyak karakter yang tersedia tanpa menunggu lebih banyak, hingga batas buffer.

Tambahan:

CMSPAR diperlukan hanya untuk memilih mark dan space parity, yang jarang terjadi. Untuk sebagian besar aplikasi, itu dapat dihilangkan. File header saya /usr/include/bits/termios.h mengaktifkan definisi CMSPAR hanya jika simbol preprocessor __USE_MISC didefinisikan. Definisi itu muncul (dalam features.h ) dengan

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

Komentar pengantar dari <features.h> mengatakan:

/* 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. Bagaimana cara membuka port serial di linux tanpa mengubah pin?

  2. Pendengar dan juru bahasa port serial Linux?

  3. Membaca dan menulis ke port serial di C di Linux

  1. Cara Membuka Port 80 &443 di FirewallD

  2. Bagaimana cara membaca/menulis semua pengaturan BIOS dari Linux CLI?

  3. Baca/Tulis ke Port Serial Tanpa Root?

  1. Cara membaca dan mengoreksi pesan penolakan SELinux

  2. Cara membaca dan menulis ke drive Windows NTFS sebagai pengguna mana pun

  3. Bagaimana Menghubungkan Dan Mengirim Data Ke Port Serial Bluetooth Di Linux?