GNU/Linux >> Linux の 問題 >  >> Linux

Cでシリアルポートを開き、読み書きする方法は?

Setting Terminal Modes Properly and Serial Programming Guide for POSIX Operating Systemsで説明されているように、POSIX標準に準拠するデモコードについては、以下が提供されています。
このコードは、Linux on x86 および ARM (または CRIS) プロセッサを使用して正しく実行されるはずです。
基本的に他の回答から派生したものですが、不正確で誤解を招くコメントは修正されています。

このデモ プログラムは、シリアル ターミナルを 115200 ボーで開いて初期化し、可能な限りポータブルな非標準モードにします。
プログラムは、ハードコードされたテキスト文字列を他の端末に送信し、出力が実行されるまで遅延します。
その後、プログラムは無限ループに入り、シリアル端末からデータを受信して​​表示します。
デフォルトでは、受信データは 16 進数のバイト値として表示されます。

プログラムが受信したデータを ASCII コードとして扱うようにするには、シンボル DISPLAY_STRING を使用してプログラムをコンパイルします。

 cc -DDISPLAY_STRING demo.c

受信したデータが (バイナリ データではなく) ASCII テキストであり、それを改行文字で終了する行として読み取りたい場合は、サンプル プログラムのこの回答を参照してください。

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

受信データのバッファリングを提供し、入力のバイト単位の処理を可能にする効率的なプログラムの例については、この回答を参照してください。


私はこれをかなり前に書きました (1985 年から 1992 年にかけて、それ以降はわずかに微調整しただけです)。必要な部分をコピーして各プロジェクトに貼り付けるだけです。

cfmakeraw を呼び出す必要があります ttytcgetattr から取得 . struct termios をゼロにすることはできません 、それを構成し、次に tty を設定します tcsetattr で .ゼロアウト方式を使用すると、特に BSD と OS X で原因不明の断続的な障害が発生します。「原因不明の断続的な障害」には 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

速度の値は B115200 です 、 B230400B9600B19200B38400B57600B1200B2400B4800 など。パリティの値は 0 です (パリティがないことを意味します)、PARENB|PARODD (パリティを有効にして奇数を使用)、PARENB (パリティを有効にして偶数を使用)、PARENB|PARODD|CMSPAR (パリティをマーク)、および PARENB|CMSPAR (スペース パリティ).

「ブロッキング」は read() かどうかを設定します ポート上で、指定された文字数が到着するのを待ちます。 ブロックしない設定 read() を意味します 戻りますが、バッファ制限まで、さらに待機することなく多くの文字が利用可能です。

補遺:

CMSPAR マークとスペースのパリティを選択する場合にのみ必要ですが、これは一般的ではありません。ほとんどのアプリケーションでは、省略できます。私のヘッダーファイル /usr/include/bits/termios.h CMSPAR の定義を有効にします プリプロセッサ シンボル __USE_MISC の場合のみ が定義されています。その定義が発生します (features.h) )と

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

<features.h> の導入コメント 言います:

/* 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. FirewallDでポート80と443を開く方法

  2. 任意のユーザーとしてWindowsNTFSドライブを読み書きする方法

  3. 使用されているシリアルポートを見つける方法は?

  1. Linux –シリアルポートを使用してプロセスを見つける方法は?

  2. Linux シリアルポートのリスナーとインタープリター?

  3. Linux の C でのシリアル ポートへの読み取りと書き込み

  1. Linux CLI からすべての BIOS 設定を読み書きする方法は?

  2. ルートなしでシリアルポートへの読み取り/書き込み?

  3. bash スクリプトを作成する方法、または vi を開いてドキュメントを編集する方法を自動化するには?