
仕事で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 | 出力バッファ内のバイト数を取得 |
データの読み取りと書き込み
読み取りや書き込みは簡単です。
システムコール関数 read
や write
を使うだけで実現できます。
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に触れ始めたばかりなので、もしかしたら間違っているかもしれません。
参考にする際は入念にチェックをお願いします。
あまりにも日本語資料の少なさに驚嘆したので、少しずつですが自分のためにも残していこうと思います。
スポンサーリンク
Leave a Comment