2011/11/08

Linuxのシリアル通信プログラム

前回はLinux上でのネットワークプログラミングをやって(コピペして)みましたが、
今回はシリアル通信のプログラミングをやってみたいと思います。

参考元:The Linux Serial Programming HOWTO(日本語版)
時々見れない場合もあるので、そんなときは英語版の方を参照してください。
検索すれば他の日本語訳のサイトも見つかるかもしれません。


上記ページにあったサンプルコードをいじって、受信と送信でプロセスを分けて処理する形に変更してみました。
実験したところ、無事Arduinoからのデータの受信と、送信ができるようになりました。



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/wait.h>

/* <asm/termbits.h> で定義されているボーレートの設定.これは
 <termios.h>からインクルードされる. */
#define BAUDRATE B9600
//Arduinoのソフトウェアシリアルでも使えるように9600bpsにする

/* 適切なシリアルポートを指すように,この定義を変更
 * 我が家の環境ではArduinoは/dev/ttyACM0になってました*/
#define MODEMDEVICE "/dev/ttyACM0"

#define BUFFSIZE 256
#define COULDNOTFORK -1

#define FALSE 0
#define TRUE 1

volatile int STOP = FALSE;
static int fd = -1;

/* Functions */
void serial_init(int fd);
void child_process();
void parent_process(pid_t result_pid);

/* Main */
int main(void);

// シリアルポートの初期化
void serial_init(int fd) {
    struct termios tio;
    memset(&tio, 0, sizeof(tio));
    tio.c_cflag = CS8 | CLOCAL | CREAD;
    /*
     BAUDRATE: ボーレートの設定.cfsetispeed と cfsetospeed も使用できる.
     CS8     : 8n1 (8 ビット,ノンパリティ,ストップビット 1)
     CLOCAL  : ローカル接続,モデム制御なし
     CREAD   : 受信文字(receiving characters)を有効にする.
     */

    tio.c_cc[VTIME] = 0; /* キャラクタ間タイマは未使用 */

    /*
     ICANON  : カノニカル入力(行単位入力)を有効にする
     */
    tio.c_lflag = ICANON;

    /*
     IGNPAR  : パリティエラーのデータは無視する
     ICRNL   : CR を NL に対応させる(これを行わないと,他のコンピュータで
     CR を入力しても,入力が終りにならない)
     それ以外の設定では,デバイスは raw モードである(他の入力処理は行わない)
     */
    tio.c_iflag = IGNPAR | ICRNL;

    // ボーレートの設定
    cfsetispeed(&tio, BAUDRATE);
    cfsetospeed(&tio, BAUDRATE);
    // デバイスに設定を行う
    tcsetattr(fd, TCSANOW, &tio);
}

void child_process() {

    char buf[BUFFSIZE];
    char input[BUFFSIZE];
    int count;
    int i;
    int writecount = 0;

    fprintf(stdout, "CHILD:RCV Start\n");

    //STOPになるまで無限ループ
    while (FALSE == STOP) {
        memset(&buf, 0, sizeof(buf));
        count = read(fd, &buf, BUFFSIZE);
        if (count < 0) {
            fprintf(stdout, "CHILD:Could not read from serial port\n");
            STOP = TRUE;
        } else {
            fprintf(stdout, "CHILD:RCVD CHAR %s %d", buf, count);

        }
    }

    fprintf(stdout, "CHILD:BYE!\n");
}

void parent_process(pid_t result_pid) {

    char input[BUFFSIZE];
    int writecount = 0;
    fprintf(stdout, "Parent:Waiting for Input\n");
    int i = 0;
    int inputcount = 0;
    while (1) {
        memset(&input, 13, sizeof(input));

        fgets(input, sizeof(input), stdin);
        fflush(stdin);

        //改行コード埋め込み
        for (i = 0; i < BUFFSIZE; i++) {
            if (input[i] == 0) {
                inputcount = i;
                input[i] = 13;
                input[i + 1] = 10;
                break;
            }
        }

        writecount = write(fd, &input, inputcount);
        if (writecount < 0) {
            fprintf(stdout, "Could not write to serial port %d\n", writecount);
            break;
        } else {
            fprintf(stdout, "Send %d bytes\n", writecount);

        }

    }
}

int main(void) {
    pid_t result_pid;

    struct termios oldtio, newtio;
    char buf[255];
    /*
     読み書きのためにモデムデバイスをオープンする.ノイズによって CTRL-C
     がたまたま発生しても接続が切れないように,tty 制御はしない.
     */

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror(MODEMDEVICE);
        return (-1);
    }

    tcgetattr(fd, &oldtio); /* 現在のシリアルポートの設定を待避させる*/
    memset(&newtio, 0, sizeof(newtio));/* 新しいポートの設定の構造体をクリアする */

    //シリアルポート準備
    serial_init(fd);

    //forkして受信用と送信用に分ける
    result_pid = fork();

    if (result_pid == -1) {
        fprintf(stderr, "fork failed.\n");
        return COULDNOTFORK;
    }

    if (result_pid == 0) {
        child_process();
    } else {
        fprintf(stderr, "fork completed");

        parent_process(result_pid);
    }
    STOP = TRUE;

    return 0;
}


ではまた。

0 件のコメント:

コメントを投稿