シリアル通信プログラム概説

ここではArmadillo300を対象にシリアル通信プログラムの概要を述べる.ただし,Armadillo300に固有の部分は,デバイスファイル名(/dev/ttyAM*)のみであり,他は一般的な情報である.

対象機器

armadillo300にはシリアルポートがCON6とCON7の二つ用意されている.CON7はPCと接続し,ログインやコンパイルなどの各種作業に使用する.一方,CON6をセンサ・サーボコントローラに接続し,センサ情報の取得やサーボ駆動のためのコマンド送受信に用いる.プログラム上ではCON6は下記のデバイス名で使用することができる.

  • /dev/ttyAM1
serial port on armadillo300

図1. Armadillo300上のシリアルポート

シリアル通信プログラム

シリアル通信プログラム骨子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* シリアルインターフェースに対応するデバイスファイル */
#define SERIAL_PORT     "/dev/ttyAM1"

int main(){

  /* シリアルインターフェースのオープン */
  /* O_RDWR: 入出力用にオープン */
  /* O_NOCTTY: ノイズ等による不意のctrl-cを防ぐため,tty制御なし */
  /* シリアルインターフェースをint型変数"fd"の名前で扱えるように */
  int fd;
  fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY);
  if(fd < 0){
    printf("%s doesn't open it\n",SERIAL_PORT);
    return -1;
  }

  /* シリアルポートの設定を行う変数を宣言 */
  struct termios oldtio, newtio;
  /* 現在の設定を oldtio に保存 */
  tcgetattr(fd, &oldtio);
  /* 今回使用する設定 newtio に現在の設定 oldtio をコピー */
  newtio = oldtio;

  /* 入出力スピードの設定 */
  /* 以下の方法1または方法2で設定.どちらかでよいので方法2を有効にしている */

  /* 方法1 */
  /* cfsetispeed(&newtio, B115200); /* 入力スピード設定 */
  /* cfsetospeed(&newtio, B115200); /* 出力スピード設定 */

  /* 方法2 */
  cfsetspeed(&newtio, B115200); /* 入出力スピード設定 */

  /* 27行目-42行目までの設定を有効にする */
  tcflush(fd, TCIFLUSH);
  tcsetattr(fd, TCSANOW, &newtio); /* 設定を有効に */

  /* ここからがユーザ固有の処理:例として文字列 hello を送信(write関数).受信はread関*/
  char buf[20];
  strcpy(buf,"hello");
  write(fd, buf , sizeof(buf)); /* write関数:送信処理 */

  /* デバイスの設定を戻す */
  tcsetattr(fd, TCSANOW, &oldtio);
  close(fd);
  return 0;
}

シリアル通信プログラム骨子の概要

通信のための準備・終了処理

16行目から46行目までがシリアル通信を行うための準備の部分である.基本的にプログラムによってあまり変更がないので,丸覚えでもかまわない.

53行目から56行目までがシリアル通信を終了させるための後始末の部分である.基本的にプログラムによって変更はないので,丸覚えでもかまわない.

メインプログラム

48行目から51行目までがシリアル通信を使ったプログラムの部分である.ここで,自分の考えた処理を行わせる.特に51行目が重要であり,write関数によって接続機器に情報を送信している.

  • read関数:受信(接続機器 から Armadillo-300に情報伝達)
  • write関数:送信(Armadillo-300 から 接続機器に情報伝達)

基本的には,「通信のための準備・終了処理」の部分は殆ど定型であり,丸覚えでかまわない

シリアル通信プログラム骨子の詳細説明

プログラム各行詳細

12行目

#define SERIAL_PORT “/dev/ttyAM1”

今回使用するシリアルインターフェース

21行目

fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY);

シリアルインターフェースを”fd”の名前で使えるようにする.

28-32行目

struct termios oldtio, newtio

非同期通信ポートを制御するための汎用ターミナルインタフェース(ここではシリアルポート)の設定を司る変数.詳しくは後ほど説明.

tcgetattr(fd, &oldtio);

シリアルインターフェース”fd”から,現在の設定を読み込みoldtioに保存

newtio = oldtio

現在の設定をnewtioにコピー.以後,newtioの設定を変え,tcsetattr(fd,&newtio)などでシリアルインターフェース”fd”の設定変更を行う.最後にtcsetattr(fd, TCSANOW, &oldtio)にてシリアルインターフェース”fd”を初期の設定に戻す.大まかな流れは後ほど説明.

38-39行目

cfsetispeed(&newtio, B115200);

newtioの中の入力に関する通信速度の設定

cfsetospeed(&newtio, B115200);

newtioの中の出力に関する通信速度の設定

42行目

cfsetspeed(&newtio, B115200);

36-38行目と同じことをしている.こちらは入出力を一度に設定している.

45-46行目

tcflush(fd, TCIFLUSH);

シリアルインターフェースの送受信データをクリア

tcsetattrの実行のために必要.

tcsetattr(fd, TCSANOW, &newtio);

newtioを用いてシリアルインターフェース”fd”を設定.

51行目

write(fd, buf , sizeof(buf));

シリアルインターフェース”fd”にsize(buf)分のデータbufを送信

54-55行目

tcsetattr(fd, TCSANOW, &oldtio);

シリアルインターフェース”fd”を初期の設定(oldtio)に戻す

close(fd);

シリアルインターフェースと名前”fd”の繋がりを開放.

シリアルポート設定を保存する構造体 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]; /* 特殊制御文字の設定 */
};

入力フラグ

端末(この場合armadillo)への入力に関する設定である.特に文字入力の仕方(入力の8ビット目を落とす,パリティ検査を行うなど)を制御する.

出力フラグ

端末(この場合armadillo)からの出力に関する設定である.特に出力処理の実行の仕方(改行をCR/LFに変更するなど)を制御する.

制御フラグ

端末(この場合armadillo)のハードウェアに関する設定である.RS-232のシリアルライン(モデムの状態信号の無視,文字ごとのストップビット数など)に影響する.

ローカルフラグ

端末(この場合armadillo)のその他の設定である.ドライバとユーザのインターフェース(エコーのオン/オフ,削除した文字の表示方法,端末生成シグナルの有無,バックグラウンドからの出力を止めるジョブ制御シグナルなど)に影響する.
構造体 struct termiosの基本的な使い方
  1. tcgetattrにてプログラム開始時の初期設定を読み込む
  2. new = old などとして初期設定と同じ新しい設定変数を作成し,以後新しい設定変数を使う
  3. cfsetspeedなど必要に応じて必要な箇所のみの変更を行う
  4. 通信処理
  5. プログラム終了時に初期設定に戻す

その他のシリアル通信に関する情報

入力方式と処理方法

シリアルデバイスにおいてデータの入力はread関数によって行われる.この時,read関数が受け取るデータの単位と受け取る時の処理に幾通りかの方法がある.受け取るデータの単位に関する方法として,カノニカル入力処理と非カノニカル入力処理がある.また受け取る時の処理方法として,同期処理と非同期処理がある.

  カノニカル入力処理 非カノニカル入力処理
同期処理 カノニカル・同期 非カノニカル・同期
非同期処理 カノニカル・非同期 非カノニカル・非同期

カノニカル・非カノニカル入力処理

カノニカル入力処理(デフォルト)

外部からの全ての入力は,行単位で処理される.つまり,readによって得られるデータは1行全体である.よってデータは以下のいずれかで終わる.

  • NL(ASCIIのLF)
  • ファイル終端
  • 行終端文字

注意点としては,標準の設定ではCR(DOS/Windowsのデフォルトの行終端文字)は行終端とはならない.

非カノニカル入力処理

read関数を呼び出す際に読み込むデータの大きさを指定する方法である.特に,プログラムが決まった文字数のキャラクタを読み込む時や,接続したデバイスが大量の文字を送ってくる場合に使用する.

同期・非同期入力処理

同期処理

read関数にてデータ読み込みが始まると,データを読み終えるまでプログラムの処理は中断される.つまりデータが読み込まれるまで待つ.

非同期処理

read関数にてデータ読み込みが始まると,プログラム自体はread関数の次の行から処理が再開される.つまり,データが読み終えるまで待たずに,次の処理が続行される.データ読み込みが終わるとシステムがプログラムにシグナル(信号・合図)を送るしくみとなる.

Armadillo300におけるシリアルデバイスの推奨入力方法

シリアルに接続されたセンサなどを想定しているので非カノニカル入力処理・非同期処理が好ましい.