ゼミのお話/アルマジロゼミ/Linux組み込みボードでのプログラミング

2018-04-16 (月) 15:53:00 (840d)

概要

  • Linux組み込みボード上でシリアル通信を使ったプログラムを行う.これまでLinux組み込みボード上のシリアルポート(CON7)を介してPCと繋げ,ログイン・各種設定を行ってきた.このようにLinux組み込みボード上のシリアルポート(CON7)はシステムが使用する.そこでLinux組み込みボード上のシリアルポート(CON6)を使用して,Linux組み込みボード上のプログラムの出力をPCに表示するプログラムの作成を行う.その為,システムへはssh(TCP/IPネットワーク)によってログイン等を行い,PCと繋げていたシリアルケーブルはLinux組み込みボード上のシリアルポート(CON6)に繋げなおして使用する.
  • 前回まで
    con7control.png
    • 図 シリアルポート経由でのログイン
  • 今回から
    con6obsrv.png
    • 図 ネットワーク経由でのログイン

準備

開発環境のインストール

  • apt-getを用いてLinux組み込みボード上でプログラムをコンパイルする環境を整える.
    • apt-get install binutils-dev gcc g++ libpopt-dev make patch

SSH経由でログインできることを確認

  • TeraTerm?, minicomなどで,SSH経由でログインできることを確認する.
    • SSH経由でのログインの場合,ホスト名/アドレスが必要になる.分からない場合,シリアル経由でArmadillo 300にログインし,以下のコマンドでIPアドレスを確認しておく.
      • 有線LAN使用の場合:ifconfig eth0
      • 無線LAN使用の場合:ifconfig ath0

配線の変更

  • con7のケーブルをcon6へ挿す
    picsay-1280997322.jpg
    • 図 con7に挿さっている状態
      picsay-1280997383.jpg
    • 図 con6に挿さっている状態

プログラミング

Hello World!を出力

  • 下記のプログラムを入力
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で設定.どちらかでよい */
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:  /* 通信処理:シリアルデバイスに出力 */
49:  char buf[255];
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:}
  • gcc -o sample1 sample.c
    • プログラム sample.c をコンパイル
    • コンパイル後の実行ファイルの名前をsample1として保存
  • ./sample1
    • プログラムを実行
    • Hello World!と表示されることを確認

プログラムの説明

  • 通信のための準備・終了処理
    • 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 temiosは構造体であり,以下のようになっている.
    • 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)のその他の設定である.ドライバとユーザのインターフェース(エコーのオン/オフ,削除した文字の表示方法,端末生成シグナルの有無,バックグラウンドからの出力を止めるジョブ制御シグナルなど)に影響する.
    • tcflag_t c_lflag;

struct termiosの使い方

  • tcgetattrにてプログラム開始時の初期設定を読み込む
  • new = old などとして初期設定と同じ新しい設定変数を作成し,以後新しい設定変数を使う
  • cfsetspeedなど必要に応じて必要な箇所のみの変更を行う
  • 通信処理
  • プログラム終了時に初期設定に戻す

シリアルデバイスにおける入力処理の概念

入力方式

  • シリアル通信では,シリアルデバイスを介して外部からデータが入力される.プログラム上では,read関数を用いて外部から入力されたデータを読み取る.この読み取りに関して,シリアルデバイスでは大きく分けて2種類の入力処理がある.
    • カノニカル入力処理(デフォルト)
      • 外部からの全ての入力は,行単位で処理される.つまり,readによって得られるデータは1行全体である.よってデータは以下のいずれかで終わる.

        NL(ASCIIのLF)

        ファイル終端

        行終端文字

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

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

処理方式

  • カノニカル入力処理/非カノニカル入力処理においては外部から入力されるデータはread関数にて読み込む.このとき,read関数の処理の仕方が大別すると2種類ある.
    • 同期処理
      • read関数にてデータ読み込みが始まると,データを読み終えるまでプログラムの処理は中断される.つまりデータが読み込まれるまで待つ.
    • 非同期処理
      • read関数にてデータ読み込みが始まると,プログラム自体はread関数の次の行から処理が再開される.つまり,データが読み終えるまで待たずに,次の処理が続行される.データ読み込みが終わるとシステムがプログラムにシグナル(信号・合図)を送るしくみとなる.

Armadilloでの処理

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