VxWorksでシリアル通信をする

No Image

仕事でVxWorksで開発することが稀にあります。
ただ、あまりネット上には資料が転がっていないので苦労することが多いです。
Linux系はたくさん見つかるんですが、使う関数が微妙に違ったりしてコピペはあまりできないんです...
シリアル通信は割とよく使用しますし、VxWorks特有のものが多いので備忘録として残しておこうと思います。

スポンサーリンク

シリアルポートの設定

シリアルポートを開く

設定するにはまずシリアルポートを開く必要があります。
シリアルポートを開くにはopen関数を使います。

int open
    (
    const char * name,  /* name of the file to open */
    int          flags, /* O_RDONLY, O_WRONLY, O_RDWR, or O_CREAT */
    int          mode   /* mode of file to create (UNIX chmod style) */
    )

Linux系と同じなので抵抗はないでしょう。
この関数でファイルディスクリプタを取得して設定等に使います。

fd = open("/tyCo/0", O_RDWR, 0);

第1引数の/tyCo/0はシリアルポートのデバイス名です。
デバイスリストを表示するdevsコマンドなどで確認しておきます。
VxWorksではシリアルポートはtyで始まるようです。

第2引数は読み書きの指定で、下記から指定します。
シリアル通信では送受信が多いと思うので、今回はO_RDWRにしています。

フラグ 説明
O_RDONLY 読み取り専用でオープン
O_WRONLY 書き込み専用でオープン
O_RDWR 読み書き両用でオープン
O_CREAT ファイルが存在しない場合は新規作成する(第3引数の指定要)

第3引数はO_CREATのときに指定するファイルのモードで、シリアルポートでは読み書きしかしないので"0"です。

シリアルポートのモード設定とバッファのクリア

VxWorksではシリアルポートの設定や制御は ioctl をよく使います。

int ioctl
    (
    int fd,       /* file descriptor */
    int function, /* function code */
    int arg       /* arbitrary argument */
    )

下記のプログラムは1行目がモード設定(FIOSETOPTIONS)で2行目がバッファのクリア(FIOFLUSH)です。

ioctl(fd, FIOSETOPTIONS, OPT_RAW);
ioctl(fd, FIOFLUSH, 0);

あまり私も分かっていないですが、第2引数のファンクションコードがFIOSETOPTIONSの場合には第3引数には色々入れられますが、基本はRAWモードでいいかなと思います。
一応どんなオプションがあるかは貼っておきます。

オプション 説明
OPT_LINE ラインモード(NewLineが入力されるまで保持される)
OPT_ECHO 入力文字を同じチャネルの出力にエコーする
OPT_CRMOD \nを\r\nに変換
OPT_TANDEM X-ON/X-OFF(フロー制御)に対応
OPT_7_BIT 7ビットモード、すべての入力バイトから最上位ビットを削除
OPT_MON_TRAP モニタートラップ文字を有効
OPT_ABORT シェルの中止文字を有効
OPT_TERMINAL 上記のオプションビットをすべて設定する
OPT_RAW 上記のオプションビットはいずれも設定しない

ボーレート、データ長、ストップビット、パリティビットの設定

ボーレートの設定には固有のファンクションコードが用意されています。

ioctl(fd, FIOBAUDRATE, 115200);

第2引数にFIOBAUDRATEを指定して、第3引数にボーレートを数値で指定します。
上記の例では115200bpsに設定しています。

データ長、ストップビット、パリティビットは第2引数でSIO_HW_OPTS_SETを指定して、第3引数に論理和でまとめて設定します。

ioctl(fd, SIO_HW_OPTS_SET, CS8 | PARENB | CLOCAL | CREAD);

上記の例では「データ長:8bit」「ストップビット:1ビット」「パリティビット:偶数」の設定になっています。

スポンサーリンク

設定できるパラメータは下記のとおりです。
これらはLinuxでもだいたい同じだと思います。
CLOCALとCREADは必須で、記述しないと受信できないようです。

パラメータ 説明
CLOCAL ローカルライン(モデム制御なし)
CREAD 受信を有効にする
CSIZE データ長:CS5~CS8
HUPCL 最後のクローズでハングアップ
STOP8 ストップビットを2に設定する(設定しなければ1ビット)
PARENB パリティビットを有効(設定しなければパリティなし)
PARODD パリティビットを奇数にする(設定しなければ偶数[有効の場合のみ])

その他のファンクションコード

シリアル通信に関するファンクションコードには他にもありますので下記に挙げます。

ファンクションコード 説明
FIOCANCEL 読み取りまたは書き込みをキャンセル
FIOGETNAME ファイルディスクリプタのファイル名を取得
FIOGETOPTIONS 現在のデバイスオプションを取得
FIONREAD 入力バッファ内の未読バイト数を取得
FIONWRITE 出力バッファ内のバイト数を取得

データの読み取りと書き込み

読み取りや書き込みは簡単です。
システムコール関数 readwrite を使うだけで実現できます。

int read
    (
    int    fd,      /* file descriptor from which to read */
    char * buffer,  /* pointer to buffer to receive bytes */
    size_t maxbytes /* max no. of bytes to read into buffer */
    )
int write
    (
    int    fd,     /* file descriptor on which to write */
    char * buffer, /* buffer containing bytes to be written */
    size_t nbytes  /* number of bytes to write */
    )

open関数で取得したファイルディスクリプタを利用するだけなのでそんなに苦労するところはないはずです。
設定が難解なだけで送受信はLinux系のコピペでもいけるかもしれません。

実際のコード

設定と読み取り・書き込みをまとめたコードは下記のようになります。
もしかしたら環境にもよるかもしれませんので注意してください。

#include <vxworks.h>
#include <taskLib.h>
#include <usrLib.h>
#include <ioLib.h>
#include <stdio.h>

void read_serial_data(int fd) {

  char rd_buf[100] = {0};
  char rd;
  int i = 0;
  int read_cnt = 0;

  /* Flush */
  ioctl(fd, FIORFLUSH, 0);

  FOREVER {
    /* Get the number of unread data */
    ioctl(fd, FIONREAD, &read_cnt);

    /* Read Buffer */
    if (read_cnt > 0) {
      printf("/tyCo/0: read %d bytes", read_cnt);
      i = 0;
      while (read_cnt > 0) {
        read(fd, &rd, 1);
        read_cnt--;
        rd_buf[i++] = rd;
        taskDelay(1);
      }
      rd_buf[i] = '\0';
      printf(" -> %s\n", rd_buf);

      /* Flush */
      ioctl(fd, FIORFLUSH, 0);
    }
    /* Wait 1sec */
    taskDelay(sysClkRateGet());
  }

  return;

}


void write_serial_data(int fd) {
  int wrt_cnt;
  char buf[] = "Hello, VxWorks!\n";

  FOREVER {
    wrt_cnt = write(fd, buf, strlen(buf));
    printf("/tyCo/0: write %d bytes\n", wrt_cnt);

    /* Wait 2sec */
    taskDelay(sysClkRateGet() * 2);
  }
}


int task_serial_port() {

  int com_fd, status;
  const int task_priority = 200;
  const int stack_size    = 20000;

  /* Open the device for reading */
  com_fd = open("/tyCo/0", O_RDWR, 0);
  if (com_fd == ERROR) {
    printf("Error: Opening Serial Port\n");
    return ERROR;
  }

  /* RAW mode */
  status = ioctl(com_fd, FIOOPTIONS, OPT_RAW);
  if (status == ERROR) {
    printf("ioctl (raw) error; exiting...\n");
    return ERROR;
  }

  /* BaudRate:115200[bps] */
  status = ioctl(com_fd, FIOBAUDRATE, 115200);
  if (status == ERROR) {
    printf("ioctl (b_rate) error; exiting...\n");
    return ERROR;
  }

  /* DataLength:8bit StopBit:1 Parity:None */
  status = ioctl(com_fd, SIO_HW_OPTS_SET, CS8 | CLOCAL | CREAD);
  if (status == ERROR) {
    printf("ioctl (options) error; exiting...\n");
    return ERROR;
  }

  /* Read data task */
  taskSpawn("tSerialRead", task_priority, 0, stack_size, (FUNCPTR)read_serial_data, com_fd, 0, 0, 0, 0, 0, 0, 0, 0, 0);

  /* Send data task (2 second period) */
  taskSpawn("tSerialWrite", task_priority, 0, stack_size, (FUNCPTR)write_serial_data, com_fd, 0, 0, 0, 0, 0, 0, 0, 0, 0);

  return EXIT_SUCCESS;

}

シリアルポートを設定している関数で送受信のタスクを生成しています。
上のプログラムでは送信は2秒毎です。
設定用の関数は違うところからタスクとして生成される想定です。


まだVxWorksに触れ始めたばかりなので、もしかしたら間違っているかもしれません。
参考にする際は入念にチェックをお願いします。
あまりにも日本語資料の少なさに驚嘆したので、少しずつですが自分のためにも残していこうと思います。

VxWorksでシリアル通信をする

スポンサーリンク

Leave a Comment