ゼミのお話/ロボット工学者養成所/一覧/シリアル通信によるデバイス制御/シリアル通信プログラム概説
2018-04-16 (月) 15:53:00 (1584d)
対象機器の状態 †
- armadillo300にはシリアルポートがCON6とCON7の二つ用意されている.CON7はPCと接続し,ログインやコンパイルなどの各種作業に使用する.一方,CON6をセンサ・サーボコントローラに接続し,センサ情報の取得やサーボ駆動のためのコマンド送受信に用いる.プログラム上ではCON6は下記のデバイス名で使用することができる.
- /dev/ttyAM1
- 図1 armadillo300上のポート
シリアル通信プログラム †
シリアル通信プログラム骨子 †
01:#include <sys/types.h> 02:#include <sys/stat.h> 03:#include <sys/ioctl.h> 04:#include <fcntl.h> 05:#include <termios.h> 06:#include <unistd.h> 07:#include <stdio.h> 08:#include <stdlib.h> 09:#include <string.h> 10: 11:/* シリアルインターフェースに対応するデバイスファイル */ 12:#define SERIAL_PORT "/dev/ttyAM1" 13: 14:int main(){ 15: 16: /* シリアルインターフェースのオープン */ 17: /* O_RDWR: 入出力用にオープン */ 18: /* O_NOCTTY: ノイズ等による不意のctrl-cを防ぐため,tty制御なし */ 19: /* シリアルインターフェースをint型変数"fd"の名前で扱えるように */ 20: int fd; 21: fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY); 22: if(fd < 0){ 23: printf("%s doesn't open it\n",SERIAL_PORT); 24: return -1; 25: } 26: 27: /* シリアルポートの設定を行う変数を宣言 */ 28: struct termios oldtio, newtio; 29: /* 現在の設定を oldtio に保存 */ 30: tcgetattr(fd, &oldtio); 31: /* 今回使用する設定 newtio に現在の設定 oldtio をコピー */ 32: newtio = oldtio; 33: 34: /* 入出力スピードの設定 */ 35: /* 以下の方法1または方法2で設定.どちらかでよいので方法2を有効にしている */ 36: 37: /* 方法1 */ 38: /* cfsetispeed(&newtio, B115200); /* 入力スピード設定 */ 39: /* cfsetospeed(&newtio, B115200); /* 出力スピード設定 */ 40: 41: /* 方法2 */ 42: cfsetspeed(&newtio, B115200); /* 入出力スピード設定 */ 43: 44: /* 27行目-42行目までの設定を有効にする */ 45: tcflush(fd, TCIFLUSH); 46: tcsetattr(fd, TCSANOW, &newtio); /* 設定を有効に */ 47: 48: /* ここからがユーザ固有の処理:例として文字列 hello を送信(write関数).受信はread関数 */ 49: char buf[20]; 50: strcpy(buf,"hello"); 51: write(fd, buf , sizeof(buf)); /* write関数:送信処理 */ 52: 53: /* デバイスの設定を戻す */ 54: tcsetattr(fd, TCSANOW, &oldtio); 55: close(fd); 56: return 0; 57:}
シリアル通信プログラム骨子の概要 †
- 通信のための準備・終了処理
- 16行目から46行目までがシリアル通信を行うための準備の部分である.基本的にプログラムによってあまり変更がないので,丸覚えでもかまわない.
- 53行目から56行目までがシリアル通信を終了させるための後始末の部分である.基本的にプログラムによって変更はないので,丸覚えでもかまわない.
- メインプログラム
- 48行目から51行目までがシリアル通信を使ったプログラムの部分である.ここで,自分の考えた処理を行わせる.特に51行目が重要であり,write関数によって接続機器に情報を送信している.
- read関数:受信(接続機器 から Armadillo-300に情報伝達)
- write関数:送信(Armadillo-300 から 接続機器に情報伝達)
- 48行目から51行目までがシリアル通信を使ったプログラムの部分である.ここで,自分の考えた処理を行わせる.特に51行目が重要であり,write関数によって接続機器に情報を送信している.
- 基本的には,「通信のための準備・終了処理」の部分は殆ど定型であり,丸覚えでかまわない
詳細説明 †
シリアル通信プログラム骨子の説明 †
- 12行目
- #define SERIAL_PORT "/dev/ttyAM1"
- 今回使用するシリアルインターフェース
- #define SERIAL_PORT "/dev/ttyAM1"
- 21行目
- fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY);
- シリアルインターフェースを"fd"の名前で使えるようにする.
- fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY);
- 28-32行目
- struct termios oldtio, newtio
- 非同期通信ポートを制御するための汎用ターミナルインタフェース(ここではシリアルポート)の設定を司る変数.詳しくは後ほど説明.
- tcgetattr(fd, &oldtio);
- シリアルインターフェース"fd"から,現在の設定を読み込みoldtioに保存
- newtio = oldtio
- 現在の設定をnewtioにコピー.以後,newtioの設定を変え,tcsetattr(fd,&newtio)などでシリアルインターフェース"fd"の設定変更を行う.最後にtcsetattr(fd, TCSANOW, &oldtio)にてシリアルインターフェース"fd"を初期の設定に戻す.大まかな流れは後ほど説明.
- struct termios oldtio, newtio
- 38-39行目
- cfsetispeed(&newtio, B115200);
- newtioの中の入力に関する通信速度の設定
- cfsetospeed(&newtio, B115200);
- newtioの中の出力に関する通信速度の設定
- cfsetispeed(&newtio, B115200);
- 42行目
- cfsetspeed(&newtio, B115200);
- 36-38行目と同じことをしている.こちらは入出力を一度に設定している.
- cfsetspeed(&newtio, B115200);
- 45-46行目
- tcflush(fd, TCIFLUSH);
- シリアルインターフェースの送受信データをクリア
tcsetattrの実行のために必要.
- シリアルインターフェースの送受信データをクリア
- tcsetattr(fd, TCSANOW, &newtio);
- newtioを用いてシリアルインターフェース"fd"を設定.
- tcflush(fd, TCIFLUSH);
- 51行目
- write(fd, buf , sizeof(buf));
- シリアルインターフェース"fd"にsize(buf)分のデータbufを送信
- write(fd, buf , sizeof(buf));
- 54-55行目
- tcsetattr(fd, TCSANOW, &oldtio);
- シリアルインターフェース"fd"を初期の設定(oldtio)に戻す
- close(fd);
- シリアルインターフェースと名前"fd"の繋がりを開放.
- tcsetattr(fd, TCSANOW, &oldtio);
シリアルポート設定を保存する構造体struct termios †
- struct termiosはシリアルポートを含む汎用ターミナルインタフェースの設定を扱う構造体であり,定義は以下のようになっている.
- struct termios {
- tcflag_t c_iflag; /* 入力フラグ */
- tcflag_t c_oflag; /* 出力フラグ */
- tcflag_t c_cflag; /* 制御フラグ */
- tcflag_t c_lflag; /* ローカルフラグ */
- cc_t c_cc[NCCS]; /* 特殊制御文字の設定 */
- };
- struct termios {
- 入力フラグ
- 端末(この場合armadillo)への入力に関する設定である.特に文字入力の仕方(入力の8ビット目を落とす,パリティ検査を行うなど)を制御する.
- 出力フラグ
- 端末(この場合armadillo)からの出力に関する設定である.特に出力処理の実行の仕方(改行をCR/LFに変更するなど)を制御する.
- 制御フラグ
- 端末(この場合armadillo)のハードウェアに関する設定である.RS-232のシリアルライン(モデムの状態信号の無視,文字ごとのストップビット数など)に影響する.
- ローカルフラグ
- 端末(この場合armadillo)のその他の設定である.ドライバとユーザのインターフェース(エコーのオン/オフ,削除した文字の表示方法,端末生成シグナルの有無,バックグラウンドからの出力を止めるジョブ制御シグナルなど)に影響する.
その他・シリアル通信に関する情報 †
入力データの取り扱い †
入力方式と処理方法 †
- シリアルデバイスにおいてデータの入力はread関数によって行われる.この時,read関数が受け取るデータの単位と受け取る時の処理に幾通りかの方法がある.受け取るデータの単位に関する方法として,カノニカル入力処理と非カノニカル入力処理がある.また受け取る時の処理方法として,同期処理と非同期処理がある.
カノニカル入力処理 | 非カノニカル同期処理 | |
同期処理 | カノニカル・同期 | 非カノニカル・同期 |
非同期処理 | カノニカル・非同期 | 非カノニカル・非同期 |
カノニカル・非カノニカル入力方式 †
- カノニカル入力処理(デフォルト)
- 外部からの全ての入力は,行単位で処理される.つまり,readによって得られるデータは1行全体である.よってデータは以下のいずれかで終わる.
NL(ASCIIのLF)
ファイル終端
行終端文字
注意点としては,標準の設定ではCR(DOS/Windowsのデフォルトの行終端文字)は行終端とはならない.
- 外部からの全ての入力は,行単位で処理される.つまり,readによって得られるデータは1行全体である.よってデータは以下のいずれかで終わる.
- 非カノニカル入力処理
- read関数を呼び出す際に読み込むデータの大きさを指定する方法である.特に,プログラムが決まった文字数のキャラクタを読み込む時や,接続したデバイスが大量の文字を送ってくる場合に使用する.
同期・非同期処理 †
- 同期処理
- read関数にてデータ読み込みが始まると,データを読み終えるまでプログラムの処理は中断される.つまりデータが読み込まれるまで待つ.
- 非同期処理
- read関数にてデータ読み込みが始まると,プログラム自体はread関数の次の行から処理が再開される.つまり,データが読み終えるまで待たずに,次の処理が続行される.データ読み込みが終わるとシステムがプログラムにシグナル(信号・合図)を送るしくみとなる.
Armadillo300におけるシリアルデバイスの推奨入力方法 †
- シリアルに接続されたセンサなどを想定しているので非カノニカル入力処理・非同期処理が好ましい.