チュートリアル RTLinuxによる調歩同期シリアル通信ボード 制御プログラミング チュートリアル www.interface.co.jp 商標/登録商標 本ドキュメントに掲載されている会社名,製品名は、それぞれ各社の商標または登録商標です。 保障の内容と制限 弊社はドキュメント内の情報の正確さに万全を期しています。万一、誤記または誤植等があった 場合、弊社は予告なく改訂する場合があります。ドキュメントまたはドキュメント内の情報に起 因するいかなる損害に対しても弊社は責任を負いません。 製品に含まれるバグ、あるいは製品の供給(納期遅延),性能、もしくは使用に起因する付帯的損害 もしくは間接的損害に対して、弊社に全面的に責がある場合でも、弊社はその製品に対する改良 (正常に動作する)、代品交換までとし、金銭面での賠償の責任は一切負わないものとしますので、 予めご了承ください。 ドキュメント内の図や表は説明のためであり、ユーザ個別の応用事例により変化する場合があり ます。 著作権,知的所有権 弊社は本製品に含まれるおよび本製品に対する権利や知的所有権を保持しています。 本製品はコンピュータ ソフトウェア(プログラム),図,文章,写真等を含んでいます。 複製の禁止 弊社の許可なく、本製品(ドキュメント含む)の全て、または一部に関わらず、複製,改変等を行う ことはできません。 責任の制限 弊社は、弊社または再販売者の予見の有無に関わらず、発生したいかなる特別損害,偶発的損害, 間接的な損害,重大な損害について、責任を負いません。 補償の内容 本ドキュメントで使用している弊社製品の補償については、各製品のマニュアルを参照してくだ さい。 本書の内容の一部または全部を、無断で転載することを禁止します。 本書の内容は、将来予告なく変更することがありますので、予めご了承ください。 © 2002, 2007 Interface Corporation. All rights reserved. www.interface.co.jp TUT-0043 改訂履歴 年 月 Ver. 1.5 2007年1月 1.4 2005年 10月 1.3 2004年 9月 1.2 1.1 2003年 9月 2002年 9月 1.0 2002年 6月 改 訂 内 容 ●対応型式追加。 ●マルチメータ型式変更。 HP34401A→Agilent34401A ●技術資料一覧更新。 ●対応型式追加。 ●誤記修正。 ●技術資料一覧更新。 ●対応型式追加。 ●技術資料一覧更新。 ●誤記修正。 ●対応型式追加。 ●誤記修正。 新規作成 本チュートリアルをご使用の際は、必ず各製品型式の最新のドキュメント(ユーザーズマニュア ル,Help)をあわせて参照してください。また、最新のドライバソフトウェアをご使用ください。 ユーザーズマニュアル,ドライバソフトウェアは弊社Web site(www.interface.co.jp)からダウンロー ドできます。(Helpはドライバソフトウェアに含まれています) -1- Interface Corporation TUT-0043 目 次 第1章 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 第2章 調歩同期通信の概要 7 シリアル通信................................................................................................................................. 7 RS-232CとRS-485.......................................................................................................................... 8 同期方式 ......................................................................................................................................... 9 調歩同期通信................................................................................................................................. 9 データ長 ....................................................................................................................................... 10 パリティビット........................................................................................................................... 10 通信速度(bps)とスループット .................................................................................................. 10 その他の用語解説....................................................................................................................... 11 調歩同期通信を行うにあたってのアドバイス ...................................................................... 11 調歩同期通信 on RTLinux 13 2.1 パフォーマンス........................................................................................................................... 13 2.1.1 応答性 ................................................................................................................................ 13 2.1.2 周期性 ................................................................................................................................ 15 2.2 RTLinuxによる調歩同期通信I/Oモジュール制御 .................................................................. 16 2.3 シリアルポート制御概略........................................................................................................... 17 第3章 3.1 3.2 3.3 第4章 シリアルポート制御の第一歩 18 RTLinux調歩同期通信ドライバの組み込み............................................................................ 19 シリアルポートの情報を列挙する .......................................................................................... 21 シリアルポートの情報を列挙するプログラムの解説 .......................................................... 24 より高度な処理を行おう 26 4.1 4.2 周期的なデータの送受信........................................................................................................... 26 周期的なデータの送受信の解説............................................................................................... 34 4.2.1 調歩同期通信ドライバモジュールの役割.................................................................... 36 4.2.2 共通定義ファイルの役割................................................................................................ 37 4.2.3 RTLinuxモジュールの動き ............................................................................................. 38 4.2.4 Linuxプロセスの動き ...................................................................................................... 42 4.3 マルチメータからのデータの取り込み .................................................................................. 43 4.4 マルチメータからのデータの取り込み解説 .......................................................................... 51 4.4.1 sample2/module2 との違い .............................................................................................. 53 4.4.2 RTLinuxモジュールの動き ............................................................................................. 53 第5章 5.1 デバッグ手法 57 ドライバデバッグ支援機能を使ってみる .............................................................................. 57 5.1.1 関数呼び出しトレース .................................................................................................... 58 5.1.2 エラー情報 ........................................................................................................................ 59 5.1.3 I/Oモジュールリソース情報 .......................................................................................... 60 5.1.4 制御信号情報 .................................................................................................................... 61 Interface Corporation -2- TUT-0043 第6章 6.1 6.2 リファレンス 62 関数一覧 ....................................................................................................................................... 62 戻り値一覧 ................................................................................................................................... 62 技術資料紹介 63 -3- Interface Corporation TUT-0043 はじめに 平素は格別のご高配を賜り、厚くお礼申し上げます。本冊子はRTLinux/Free上で弊社 PCI,CompactPCI 調歩同期 I/Oモジュールを制御したい方を対象に、調歩同期 I/Oモジュールの概 要,ソフトウェアのインストール方法,プログラミング方法を記載しています。 RTLinux上での弊社調歩同期 I/Oモジュールを使ったプログラミングにお役に立てれば幸いです。 本書では、以下記載がない限り、「RTLinux」は「RTLinux/Free」のことを指します。 なお本冊子はRTLinuxバージョン3.1を対象に作成されています。RTLinuxのバージョンが異なる場 合、動作保証はいたしかねますので予めご了承ください。 ●ご意見・ご要望 弊社へのご意見,ご要望がございましたら、下記までお問い合わせください。 www.interface.co.jp E-mail:[email protected] ●本冊子で扱うソフトウェア 本冊子で扱うソフトウェア製品は下記の通りです。 ソフトウェア製品名 調歩同期シリアル通信ボード Linux/RT対応ドライバソフトウェア 型 式 GPG-4141 ●ソフトウェアの入手方法について ソフトウェアは弊社Web siteよりダウンロード(無料)できます。 CD-ROM等、媒体による提供は有償となります。価格に送料,消費税は含まれません。 ※ ダウンロードするためには、ユーザID登録が必要になります。 Interface Corporation -4- TUT-0043 ●調歩同期 I/OモジュールのLinux/RTLinux対応ドライバソフトウェア(GPG-4141)のドキュメント 構成について 各ドキュメントに掲載している内容は下記のとおりです。 ドキュメント 内 容 Readme 製品のインストール方法や、アンインストール方法,ファイル構成の他、製品 に関する最新情報を掲載しています。 このドキュメントを最初に確認してください。 Help ドライバソフトウェアの仕様,関数の個別説明、および使用方法等の説明を掲載し ています。プログラム作成時に確認してください。 チュートリアル 本ドキュメントです。初めてRTLinux上で調歩同期 I/Oモジュールを制御する 時や、調歩同期 I/Oモジュールを使用したシステム構築時の参考として確認 してください。 本ドキュメントの他、RTLinuxの導入編のチュートリアルを合わせて参照して ください。 RTLinuxのインストールや、基本的なプログラミング等が記載されています。 また、 本ドキュメントは、 予めRTLinuxのインストール、 および使用されるソフトウェア(GPG-4141) のインストールを済ませた方を対象に記述しています。 ●本書での表記について コマンドの実行例において、行頭に「%」がつく場合は一般ユーザでの実行、「#」がつく場合 はrootでの実行を意味します。「%」や「#」は実際に入力する文字ではありませんのでご注意 ください。 -5- Interface Corporation TUT-0043 対象環境 本チュートリアルは以下の制約事項があります。 PCI-4141PE PCI-4141P PCI-4141 対象型式 PCI-4142PE PCI-4142P PCI-4142 (PCI) PCI-4146 PCI-4145 PCI-4144 PCI-4149C PCI-4148C PCI-4147 PCI-4161 PCI-4155 PCI-4150 PCI-466102P PCI-466102 PCI-4646 PCI-466104P PCI-466104A PCI-466104 PCI-466120 PCI-466108 PCI-466104PA PCI-466140A PCI-466140 PCI-466120P PCI-466180 PCI-466140PA PCI-466140P CPZ-4142 CPZ-4141P CPZ-4141 対象型式 CPZ-4145 CPZ-4144 CPZ-4142P (CPZ) CPZ-4148 CPZ-4147 CPZ-4146 CPZ-466102P CPZ-466102 CPZ-4149 CPZ-466104P CPZ-466104A CPZ-466104 CPZ-466120 CPZ-466108 CPZ-466104PA CPZ-466140A CPZ-466140 CPZ-466120P CPZ-466180 CPZ-466140PA CPZ-466140P CTP-4142 CTP-4141P CTP-4141 対象型式 CTP-4145 CTP-4144 CTP-4142P (CTP) CTP-4148 CTP-4147 CTP-4146 CTP-466120 CTP-466102 CTP-4149 CSI-466120 CSI-466202 CSI-466302 対象型式 CSI-466402 (CSI) PEX-466102 PEX-466104 PEX-466120 対象型式 PEX-466140 (PEX) LPC-466102 LPC-466104 LPC-466120 対象型式 LPC-466140 (LPC) 対象ユーザ 制御用電子機器および、コンピュータ等に関して基本的な知識を有している方。 ※ 本冊子は上記の弊社製品型式のみに対応しています。 製品の詳細は弊社Web siteを参照してください。 Interface Corporation -6- TUT-0043 第1章 調歩同期通信の概要 本章では、コンピュータや計測器等の装置との間で、データをやり取りする方法の1つである調歩 同期通信について概要を説明します。 この調歩同期通信とは、「シリアル通信」と呼ばれる通信方式で、データをやり取りするために 用いる同期方式の一種です。 コンピュータで、アナログモデムやISDNのTA(ターミナルアダプタ)を使って通信を行う場合、 「シリアルポート」と呼ばれるインタフェースの口に、「シリアルケーブル」と呼ばれるケーブ ルを挿して、機器と繋ぐと思います。 こうして通信を行う時、コンピュータと通信機器の間では、データのやり取りが行われています。 このやり取りは、実は「調歩同期通信」と呼ばれる方式でデータのやり取りが行われています。 弊社では、この調歩同期通信が行えるインタフェース製品を提供しております。 このインタフェース製品を使用することで、お客様は、複数の機器とデータのやり取りが行える ようになります。 1.1 シリアル通信 シリアル通信とは、1本の線を使ってデータをやり取りする通信方式です。対語として、パラレル 通信があり、これは複数の線を使ってデータをやり取りする通信方式です。 コンピュータに接続する機器は、シリアル通信かパラレル通信と呼ばれる通信方式で、データを やり取りします。 シリアル通信は、1 本の線でデータを 送る方式。 パラレル通信は、複数の線でデータを 送る方式。 シリアル通信とパラレル通信の違い -7- Interface Corporation TUT-0043 一般に、シリアル通信方式を採用した通信インタフェースは、他の通信インタフェースに比べ、 ケーブル敷設等のコストが安価であることが多いです。逆に、パラレル通信方式を採用した通信 インタフェースは、高価であることが多いですが、一度に複数のデータを送ることが可能なため、 大量のデータを高速に送るのに向いていると言われています。 しかし最近では、高速なシリアル通信方式が開発され、このようなことは 一概には言えなくなっ てきました。 シリアル通信方式の代表的なものとしては、RS-232C,RS-485,USB,Ethernet等が挙げられます。こ の内、調歩同期通信をサポートする通信方式は、RS-232CとRS-485です。 1.2 RS-232CとRS-485 RS-232Cとは通信インタフェースの規格の一つです。電気的仕様,信号線の種類,機能,特性等が規定 されています。 この規格は、米国電子工業会(EIA = Electronic Industries Association)が定めました。 なお、RS-232Cという呼び方は正式なものではなく、ANSI/TIA/EIA-232-Fと言います。日本規格で は、JIS X5101(旧名:JIS C6361)が、これにあたります。 しかし、今でもRS-232Cと呼ばれることが多いです。 コンピュータで、シリアルポートと一般に呼ばれているものは、このRS-232Cを通信インタ フェースに採用したシリアル通信を指しています。 RS-485も同じくEIAが規定した規格です。RS-422(ANSI/EIA/TIA-422-B)という規格があり、その上 位に相当する規格です。 RS-232Cに比べ、以下の特長を有しています。 ・長距離の通信が可能 RS-232Cは最大15mまで、RS-485では最大1.2kmまで通信可能です。 ・1対n接続が可能 RS-232Cは1対1の通信しかできませんが、RS-485は1対31までの多点間(マルチドロップ)の通信 が行えます。下図に示すように、1本のシリアル・バスに複数のドライバ/レシーバを接続して、 任意のドライバ,レシーバ間でデータの送信を行います。 1 本のバスを複数のドライバ/レシーバで 共用する シリアル・バス 終端抵抗R 終端抵抗R 任意 のド ラ イ バか ら任 意 の レシーバにデ ータ を送 る こ とができる レシーバ ドライバ ドライバレシーバ間でのデータ転送 この接続では複数のドライバが動作するとバス上でデータが衝突しますので、送信を行うときの みドライバをデータラインに接続し、送信を行わないときはデータラインから切り離す必要があ ります。 Interface Corporation -8- TUT-0043 1.3 同期方式 ある機器から別の機器にデータを送る時、2つの機器はタイミングを合わせて、データの受け渡し を行う必要があります。 タイミングを合わせないと、受け手はどの時点が受け取るべきデータなのか判断することができ ません。このタイミングを合わせることを「同期を取る」と言います。 同期の方法は千差万別です。RS-232CやRS-485の通信インタフェースで使用される同期方法は、 大きく分けて、調歩同期,キャラクタ同期,フラグ同期が挙げられます。 調歩同期は、1キャラクタのデータを送るごとに、データの先頭にスタートビット、末尾にストッ プビットと呼ばれる制御ビットを挿入して同期を取る方式です。 受け手側では、この特定のビットを検出することで、1キャラクタの受信データの始まりと終わり を認識します。 キャラクタ同期は、ブロックと呼ばれる複数キャラクタの集まりの先頭に、特定のコードを付加 して同期を取る方式です。 フラグ同期は、フラグと呼ばれる特定のビット列で、フレームと呼ばれるデータ列を挟んで同期 を取る方式です。 本書では、この内、調歩同期を使用する通信制御について解説します。 1.4 調歩同期通信 調歩同期は、通信の効率という面から見ると、キャラクタごとにスタートビット,ストップビット を挿入しなくてはならないため、通信効率は、他の同期方式に比べて良くありません。 ただしデータに同期したクロックを送信する必要がなく、装置が簡略化できるため、現在のコン ピュータ通信で広く普及している通信方式です。 スタート データビット ビット H L start 0 1 パリティ ストップ ビット ビット 2 3 4 5 6 7 parity stop 調歩同期通信のデータ構成 データがやりとりされていないとき(アイドル時)、データを送る信号線は、High状態 即ち”1”にな っているため、最初のデータが”1”の場合には、アイドル時との区別がつきません。 そこで、データの送信開始を受信装置に知らせるために、まずLow状態 即ち”0”を1ビット送信し ます。この最初に送信する”0”がスタートビットです。 スタートビットに続いて送信するビット列をデータビットと呼びます。 送信側は、まずスタートビットを送り、次に1データを最下位ビット(LSB)から送信します。 最後のデータビットを送り終わるとデータラインを再び“1”(High状態)に戻し、次のデータが連続 している場合は、一定時間待ってから次のスタートビットを送信します。 この最後に送信する”1”がストップビットです。ストップビットの長さは、1 / 1.5 / 2ビットの中か ら選択できます(通信コントローラによっては選択できない長さもあります)。 -9- Interface Corporation TUT-0043 1.5 データ長 データ長は、スタートビットとストップビットの間のパリティビットを除いた、データビット数 のことです。 コンピュータ間の通信では、7ビットまたは8ビットが よく使用されます。 一般的なデータ通信は、送信するキャラクタをキャラクタコードで表し、1キャラクタずつ送りま す。キャラクタとキャラクタコードの対応は、キャラクタ符号規格で定められています。昔は5 ビット符号もありましたが、現在は7ビットまたは8ビット符号(ASCII/JIS符号)が使用されます。 バイナリデータを送信する場合は、データ長は8ビットにする必要があります。 1.6 パリティビット パリティビットは、通信においてあるデータを送信するとき、データが正確に送られたかどうか 検査するためのものです。 例えばASCII 7ビットコードの“a”は16進数で61h、2進数で1100001bとなります。 ここでパリティチェックを偶数パリティに設定した場合、送信側は、転送する“1”の総数が偶数個 になるようにします。この例では“1”の数が3個ですので、8ビット目を“1”にして、11100001bとな る8ビットのデータ列を作り、“1”の数を4個(偶数)にして転送します。受信側では、“1”の数を数え、 偶数であるか確認します。もし、“1”の数が奇数の場合、データの受信に失敗したと判断できます。 このようにデータビットの後にパリティ情報として付加されるビットを、パリティビットと呼び ます。 パリティには偶数と奇数があり、奇数パリティの場合は、転送する“1”の数が奇数になるようにパ リティビットを付加します。 1.7 通信速度(bps)とスループット シリアル通信では、データを送る速さの指標として、“bps”がよく使われます。 “bps”は1秒間に送れるビット数を表す単位です。SI単位系の原則からいえば“bit/s”と書くべきです が、“bps”を使うのが一般的です。 通信速度が9600bpsの時、1ビットの時間幅が1/9600 sということを表し、 この速度でデータビットを連続して送ることができれば、9600ビット/秒の実効通信速度 (スループット)が得られます。しかし、調歩同期通信の場合、1キャラクタのデータビットには最 低2ビット(1ビットのスタートビットと、1ビットのストップビット)が付加されます。 そのため8ビット送るのに少なくとも10ビット分の時間がかかることから、キャラクタ間が隙間な く連続して送ったとしても、実効通信速度は7680bpsしか得られません。 Interface Corporation - 10 - TUT-0043 1.8 その他の用語解説 その他、調歩同期通信を行う際に、出てくる用語について解説します。 用 語 解 説 シリアルポート シリアル通信を行う際、送受信を行う口を、一般に「シリアルポート」と呼び ます。 個々のシリアルポートは、ドライバソフトウェアから見た場合、独立しており、 個別に取り扱うことが可能です。 カタログ等の仕様では、チャンネル数が2チャンネル,4チャンネルと記載して いますが、これを2ポート,4ポートと呼ぶ書籍等もあります。 本書では、通信を行う1つの口を特に、シリアルポートと称しています。 全二重/半二重 全二重通信とは両方向通信が可能で、かつ送受信が同時に行える通信方式で す。半二重通信における送受信切り替えの時間的ロスがなく、データの送信効 率を高めることができます。 半二重通信とは両方向通信が可能で、かつ送受信を同時に行えないものをいい ます。送受信が同時に行えないため、送受信の切り替えを行う必要があります。 RS-232Cインタフェースを搭載した半二重モデムと接続する場合、制御信号(一 般にはRS信号)により送受信の切り替えを行います。 RS-485マルチドロップ接続の場合、ドライバ・レシーバの接続変更により、送 受信を切り替えます。 1.9 調歩同期通信を行うにあたってのアドバイス 調歩同期通信I/Oモジュールを使って調歩同期通信を行う際、知っておくとためになる事項を以下 に列挙します。 項 目 RS-232CとRS-485の どちらを使うべき か? 家電品店で売ってい るシリアルケーブル は使用できるか? 内 容 RS-232CとRS-485のどちらを通信方式として採用すべきかについては、接続相 手,使用用途,環境等を鑑みる必要があります。 例えば、計測器や接続相手が決まっている時は、相手の通信インタフェース に合わせる必要があります。 通信インタフェースを自由に選択できる場合は、使用用途で選択した方が良 いと思われます。 ・ コンピュータのシリアルポートの口を増やしたいのならば、RS-232Cをお奨 めします。 ・ 接続相手との距離が15mを越えるようならば、RS-485を選択してください。 ・ 1対1の接続でなく、1対2∼1対31の接続を行いたいならば、RS-485を選択し てください。 ・ 通信速度が1Mbpsを越えるような場合、RS-485を選択してください。 家電品店で一般にシリアルケーブルとして売られているものは、RS-232C用の ものがほとんどです。 コンピュータとモデムを接続する場合は、ほとんどの場合、ストレートタイ プのケーブルを使用します。 機器同士を直接接続する場合は、リバース(クロス)タイプのケーブルを使用し ます。 ただし、購入前に結線図をよく確認して、使用目的と合致するケーブルを選 択してください。 - 11 - Interface Corporation TUT-0043 項 目 送受信バッファメモ リとは、何のために あるのか? 内 容 シリアル通信でよく問題となるのが、データの受け取り側が、今あるデータ を取り出す前に、次のデータが送られる状況が発生することです。 これは、ベルトコンベアに載せられて送られてくる物品を、捌くのが追いつ かなくなる状況に似ています。これを俗に「取りこぼしが発生した」と言い ます。 取りこぼしが発生する状況としては、幾つか原因が考えられます。代表的な 例としては、CPUの処理速度が遅いために取りこぼしたり、OSがリアルタイ ムに受け取り処理に入らないため、取りこぼすといったものです。 受け取ったデータを内部で溜め込んでおき、一括して取り出すことができれ ば、取りこぼしを回避できると思われます。 送受信バッファメモリは、この送受信するデータを溜め込む領域です。この サイズが多ければ多い程、受け取り処理に入るまでの反応が遅いOSでも、よ り確実にデータを受け取ることが可能となります。 標準COMポート互換 PC/AT互換機のPCに内蔵されているシリアルポートは、標準COMポートと呼 とは、どういう意味 ばれます。 か? シリアル通信I/Oモジュールの中で、PC/AT互換機のシリアルポートの通信コ ントローラ16550と互換の通信コントローラを搭載したI/Oモジュールを、標準 COMポート互換通信I/Oモジュールと呼んでいます。 またWindowsのシリアルポート(COMポート)とAPI互換であるという意味で 使用されることもあります。 通信を行うために、 全二重の場合、送信ライン,受信ライン,グランドの3本のラインを接続すれば、 最低何本の線が必要 最低限の通信は行えます。 か? ハードウェアフロー制御等通信制御を行う場合、制御信号を別途接続する必 要があります。 接続する制御信号は、接続相手の通信機器の仕様により変わります。 詳細については、ハードウェアマニュアルを参照してください。 通信速度は、1bpsずつ 通信に使用するクロックは、I/Oモジュールに搭載されている基準クロックを 変更することは可能 分周して生成されます。 か? 分周するため、ビットレートは全ての整数値に設定することはできません。 次の計算式の結果が整数値になる値が、設定できる通信速度となります。 [基準クロック] ――――――――― [通信速度(bps)] × 16 Interface Corporation - 12 - TUT-0043 第2章 調歩同期通信 on RTLinux RTLinuxは、Linux上でリアルタイム処理を可能にするOSです。そのRTLinux上で調歩同期通信I/O モジュールを動かすことにより、リアルタイム性が要求されるシステムを構築することが可能に なります。 2.1 パフォーマンス 実際にどの程度のリアルタイム性が保証されているかを測定した結果を見てみます。通常のLinux 上での結果と比較しています。 2.1.1 応答性 データを受信してから、コールバック関数内ですぐにデータ送信関数を呼び出した場合、データ 受信完了から、実際にデータが送信されるまでの時間を測定しました。 RTLinux,Linuxともに、1000回応答時間を測定しています。 コールバック関数呼び出し データ送信開始 受信完了 データ受信 コールバック関数 データ送信 この時間を測定 応答性を測定した時間 - 13 - Interface Corporation TUT-0043 応答時間の分布 データ区間 (μs) 44 45 46 47 48 49 50 51∼999 1000∼1999 2000∼2999 3000∼3999 4000∼4999 5000∼5999 6000∼6999 7000∼7999 8000∼8999 9000∼ 件 数 RTLinux 0 10 254 529 186 18 3 0 0 0 0 0 0 0 0 0 0 Linux 0 0 0 0 0 0 0 92 96 102 102 97 103 99 100 99 110 RTLinuxでは、平均応答時間,最大応答時間,最小応答時間の全てにおいてLinuxよりも優れた結果が 出ています。 特に注目されるのが、最大応答時間です。負荷が入った場合、Linuxでは、10msにまで応答時間の ぶれが発生しているのに対し、RTLinuxでは、50μsまでしかぶれが発生していません。 最悪値の保証が、RTLinuxとLinuxでは相当の差があるわけです。 応答時間のトータル結果 測定環境 トータル結果 平均(μs) 最大(μs) 最小(μs) 標準偏差(μs) RTLinux Linux 47.0 50.0 45.0 0.8 5089.0 10070.0 80.0 2883.5 拡 大 600 600 RTLinux の応答時間はぶれが少ない。 500 500 件数 400 400 300 300 600 500 400 200 200 100 100 件数 0 0 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 300 RTLinux Linux データ区間(μs) 200 Linux の応答時間はぶれが大きい。 100 9720 1004 3240 3564 3888 4212 4536 4860 5184 5508 5832 6156 6480 6804 7128 7452 7776 8100 8424 8748 9072 9396 0 324 648 972 1296 1620 1944 2268 2592 2916 0 データ区間(μs) RTLinuxとLinuxの応答時間の比較 Interface Corporation - 14 - TUT-0043 2.1.2 周期性 次に、指定した周期でデータ送信を繰り返し行い、その周期のぶれや限界値を測定しました。 こちらも各周期で1000回ずつ測定を行っています。 ネットワーク負荷なし 測定環境 指定周期 10000 1000 Linux 平均 (μs) 最大 (μs) 最小 (μs) 標準偏差 平均 (μs) 最大 (μs) 最小 (μs) 標準偏差 (μs) 10001.1 1000.1 10005.0 1004.0 9997.0 996.0 1.2 1.2 10001.3 10001.3 10061.0 10071.0 9943.0 9935.0 5.2 4.5 ネットワーク負荷あり 測定環境 指定周期 10000 1000 RTLinux RTLinux (μs) Linux 平均 (μs) 最大 (μs) 最小 (μs) 標準偏差 10001.1 1000.1 10007.0 1005.0 9995.0 996.0 最大 (μs) 最小 (μs) 標準偏差 (μs) 平均 (μs) 1.4 1.2 10001.2 10001.2 10939.0 11081.0 9051.0 8938.0 56.6 50.6 (μs) Linuxでは1ms周期を実行しても10ms以下の周期実行は行えておらず、最大値と最小値のぶれも大 きくなっていますが、RTLinuxでは最大値と最小値のぶれも小さくなっています。 またネットワーク負荷をかけたとき、Linuxではデータばらつきが10倍に増えましたが、RTLinux ではほとんど増えていません。 違いを分かりやすくするために、10ms(ネットワーク負荷あり)での周期実行の違いを図に示しま した(縦軸は、件数、横軸は測定時間を示しています)。 LinuxよりもRTLinuxのほうが、ぶれが小さいことが分かります。 件数 データ区間(μs) RTLinuxとLinuxの周期性測定結果 - 15 - Interface Corporation TUT-0043 2.2 RTLinuxによる調歩同期通信I/Oモジュール制御 RTLinux上で弊社 調歩同期通信I/Oモジュールを制御するには、お客様の作成するRTLinux モジュールからRTLinux対応 調歩同期通信ドライバソフトウェアの関数を呼び出すことで行いま す。通常、RTLinuxモジュールではRTLinuxスレッドを生成し、ここから関数を呼び出して処理を 行います。 また、RTLinuxモジュールとLinuxアプリケーションがデータのやりとりを行う場合は、RT-FIFO を使用します。 制御構成を簡単に示すと、下図のようになります。 リアルタイム FIFO Linux アプリケーション RTLinuxモジュール RTLinuxスレッド RTLinux対応 ドライバモジュール (rcp4141.o) データ送信 通信機器 データ受信 シリアル通信 I/Oモジュール RTLinux上での調歩同期通信I/Oモジュール制御構成 ●RTLinuxモジュール リアルタイム処理を行うモジュール本体です。お客様はRTLinux対応 調歩同期通信ドライバソフト ウェアを使って、シリアルポートを制御するために、このモジュールを作成する必要があります。 ●RTLinuxスレッド 実際にリアルタイム処理を行う際、RTLinuxモジュール下で動作するRTLinuxスレッドを作成します。 このRTLinuxスレッドは通常のLinuxのプロセスよりも優先して実行されます。 ●RT-FIFO リアルタイムFIFOとも呼ばれます。RTLinuxスレッドとLinuxアプリケーション、またはRTLinuxス レッド同士のデータの受け渡しを行うために使用されます。 ★RT-FIFO RT-FIFO はデバイスとして扱われており、/dev/rtf0 等という名前で存在しています。 Interface Corporation - 16 - TUT-0043 2.3 シリアルポート制御概略 調歩同期通信I/Oモジュールでは、制御はシリアルポート単位に行います。 シリアルポートの制御は、下記の制御シーケンス(順番)で行います。 (1) シリアルポート 初期化 (2) 各種処理 (3) 終了処理 シリアルポートの制御シーケンス ①シリアルポートの初期化 シリアルポートへの操作を行うため、まず、シリアルポートを利用可能な状態にします。この処理 がシリアルポートの初期化です。I/Oモジュールの初期化を行うと、プログラムはシリアルポートへ のアクセスが可能となります。本処理が行われないとシリアルポートへのアクセスは行えません。 ②各種処理 シリアルポートから、データの送信および受信を行います。 ③終了処理 シリアルポートの使用終了を行うための手続きです。プログラム終了時に行います。 - 17 - Interface Corporation TUT-0043 第3章 シリアルポート制御の第一歩 ここでは、実際に弊社 調歩同期通信I/Oモジュールを用いて、シリアルポートの制御プログラム を作成します。 この章では例として、以下の製品を使用しています。 PCI-4141 1枚:調歩同期通信I/Oモジュール 下図に、設定例を示します。 PCI-4141 RSW1:0 I/Oモジュール識別用ロータリスイッチ(RSW1)を0に設定し、コンピュータに調歩同期シリアル通 信製品を実装します。 (この章では、ケーブル等を接続して通信を行いません) I/Oモジュールをシステムに組み込んでから、動かすまでの流れは下記のようになります。 各項目に従って、RTLinux上でのシリアルポート制御プログラミングを行います。 RTLinux リアルタイムカーネルの 組み込み RTLinux 調歩同期通信ドライバ 組み込み プログラミング コンパイル 実行 RTLinux上でのシリアルポート制御までの流れ Interface Corporation - 18 - TUT-0043 3.1 RTLinux調歩同期通信ドライバの組み込み RTLinuxの調歩同期通信ドライバを組み込む前に、RTLinux導入編のチュートリアルを参照し、 RTLinuxのインストールとRTLinuxモジュールを動かすために必要なリアルタイムカーネルのイ ンストールを行ってください。 参照箇所: RTLinuxのインストール リアルタイムカーネルの組み込み 第1章 RTLinuxの導入 第2章 RTLinuxのインストール リアルタイムカーネルの組み込み例: ← リアルタイムカーネルを組み込むため、 スーパーユーザになります。 Password: ----← root のパスワードを入力します。 # cd /usr/src/rtlinux/rtlinux-3.1 # sh scripts/insrtl← リアルタイムカーネルの組み込みスクリプトを実行します。 # lsmod ← リアルタイムカーネルの組み込みを確認します。 Module Size Used by rtl_sched 43104 0 (unused) rtl_fifo 9968 0 (unused) rtl_posixio 7184 0 [rtl_fifo] rtl_time 10000 0 [rtl_sched rtl_posixio] rtl 27184 0 [rtl_sched rtl_fifo rtl_posixio rtl_time] ↑rtl、rtl_time 等のリアルタイムカーネルが組み込まれています。 リアルタイムカーネルは正常に組み込まれています。 % su リアルタイムカーネルを組み込んだ後に、調歩同期通信ドライバモジュールを組み込むことによ り、シリアルポートのリアルタイム制御が行えるようになります。 シリアルポートを実際に制御するには、ドライバモジュールを事前に組み込む必要があります。 組み込み方法は、Readme,Helpを参照してください。 例として、PCI-4141を組み込んだ場合を記載します。 # lsmod Module Size Used by rcp4141 22000 0 (unused) dpg0102 790448 0 [rcp4141] rcpcom 9088 1 [rcp4141] rtl_sched 42720 0 [rcp4141] rtl_fifo 9824 0 (unused) rtl_posixio 7136 0 [rcp4141 rcpcom rtl_fifo] rtl_time 9872 0 [rcp4141 rcpcom rtl_sched rtl_posixio] rtl 26432 0 [rcp4141 dpg0102 rtl_sched rtl_fifo rtl_posixio rtl_time] mbuff 6048 0 (unused) dpg0102,rcpcom.o,rcp4141が組み込まれていることが分かります。 - 19 - Interface Corporation TUT-0043 /procファイルシステムを参照することにより、各シリアルポートのポート番号を確認します。 RTLinux調歩同期通信ドライバの各関数はポート番号で、制御を行うポートを指定します。 行頭の数値がポート番号になります。 #cat /proc/tty/driver/rcpcom 0: PCI-4141(bid=0h)CH1 [9600bps] tx:0 rx:0 1: PCI-4141(bid=0h)CH2 [9600bps] tx:0 rx:0 以上で、シリアルI/Oモジュールのリアルタイム制御を行うプログラミングまでの準備が整いまし た。 取り外すときは以下のように実行します。 # modprobe –r rcp4141 以上で準備は終わりです。次からいよいよプログラミングを行っていきます。 ★modprobe コマンドを使わずに組み込むには? modprobe コマンドを使うと、ドライバモジュールの組み込みが簡単になりますが、他の組み込み方法も あります。 以下に insmod コマンドを使った場合の組み込み例を示します。 insmod コマンドを使って、ドライバモジュールを組み込む場合> 例) # insmod dpg0102 # insmod rcpcom # insmod rcp4141 rmmod コマンドを使って、ドライバモジュールを取り外す場合> 例) # rmmod rcp4141 # rmmod rcpcom # rmmod dpg0102 Interface Corporation - 20 - TUT-0043 3.2 シリアルポートの情報を列挙する ここでは、ComRTGetPortInformation関数を使って、システム内のシリアルポートの情報を列挙す るプログラムを作成します。 エディタを起動し、Lsit3-1に示すプログラムを入力して、ファイル名を「sample1.c」として保存 してください。 ★プログラム中のコメントについて サンプルプログラムには、理解し易いよう、日本語コメントを用いています。 しかし、プログラム中に日本語を用いてコンパイルすると、正常にコンパイルできない場合があります。 その時は、日本語コメントを削除してコンパイルしてください。 List 3-1 sample1.c 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 #include #include <rtl.h> "pcicomrt.h" int g_port_no = -1; /* 調歩同期通信I/Oモジュールのシリアルポート番号 */ /* 初期化関数(モジュールが組み込まれた時、呼ばれる関数) */ int init_module(void) { int port_no; COMRT_PORTINFO port_info; int ret; rtl_printf("init_module called¥n"); EXPORT_NO_SYMBOLS; /* オープン可能なシリアルポートと、その情報を調べます */ for(port_no = 0; port_no < COMRT_MAX_PORTS; port_no++) { ret = ComRTGetPortInformation(port_no, &port_info); if(ret == 0){ rtl_printf( "----- Serial Port Information -----¥n" "PortNo:%d¥n" "VendorID:%04lxh¥n" "DeviceID:%04ld¥n" "SubsystemID:%04lxh¥n" "RevisionID:%ld¥n" "BoardID:%ld¥n" "ChannelNumber:%ld¥n" "BaseAddress:%04lxh¥n" "IRQ No:%ld¥n", port_no, port_info.VendorID, port_info.DeviceID, port_info.SubsystemID, port_info.RevisionID, port_info.BoardID, port_info.ChannelNumber, port_info.BaseAddress, port_info.IrqNumber); } } /* オープン可能なポート番号を記録する */ if(g_port_no < 0) g_port_no = port_no; /* 最初に見つかったシリアルポートのオープン */ ret = ComRTOpen(g_port_no); if(ret){ rtl_printf("ComRTOpen error [ret=%x]¥n", ret); return -1; } else { rtl_printf("ComRTOpen success!! [PortNo=%d]¥n", g_port_no); } return 0; - 21 - Interface Corporation TUT-0043 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 } /* 終了関数(モジュールが取り外される時、呼ばれる関数) */ void cleanup_module(void) { int ret; rtl_printf("cleanup_module called¥n"); } /* シリアルポートのクローズ */ ret = ComRTClose(g_port_no); if(ret){ rtl_printf("ComRTClose error [PortNo=%d ret=%x]¥n", g_port_no, ret); } 次に、List3-2に示すプログラムを入力し、ファイル名を「makefile」として保存してください。 これは、上記プログラムをコンパイルするメイクファイルです。 List 3-2 makefile 1 2 3 4 5 include /usr/include/rtlinux/rtl.mk all: sample1.o sample1.o:sample1.c $(CC) $(INCLUDE) $(CFLAGS) -o sample1.o -c sample1.c コンパイルすると、RTLinuxモジュールsample1.oが作成されます。insmodコマンドで、RTLinuxモ ジュールを組み込みます。 # make # ls makefile sample1.c sample1.o # insmod sample1.o 組み込む前に、リスト中のrtl_printf関数の出力内容が画面で確認できるよう、X Windowで使用さ れている方は、コンソールをもう一つ立ち上げ、次のコマンドを入力してください。 # tail –f /var/log/messages コンソール画面で作業されている方は、次のように打ち込んでください。 # tail –f /var/log/messages & これにより、rtl_printf関数が出力する内容を確認できます。 ★/var/log/messages syslogやcron等で実行されたサービスのログ等の情報は、「/var/log/」に保存されます。 通常、ログは「/var/log/messages」に保存されることが多いです。 RTLinuxでは、rtl_printf関数を使って、ここに出力し、デバッグプリントができるようになっていま す。(そのままコンソール画面に出力されるようには、なっていません。) 上の用例では、tailコマンドを使って、このログ出力を常に表示状態にしています。 コンソール画面の例で用いた&(アンパサンド)コマンドはジョブコマンドの一種で、tailコマンドを バックグランドで実行させています。 コンソール画面では、X Windowのように幾つもコンソールを起動することができないので、このよ うにします。 Interface Corporation - 22 - TUT-0043 sample1.oを組み込むと、システム内の各シリアルポートの情報が列挙表示されます。 (以下は例です。お客様の環境で実行した際、表示される内容が異なる場合があります) # insmod sample1.o init_module called ----- Serial Port Information ----PortNo:0 VendorID:1147h DeviceID:4141 SubsystemID:0001h RevisionID:0 BoardID:0 ChannelNumber:1 BaseAddress:b800h IRQ No:7 ----- Serial Port Information ----PortNo:1 VendorID:1147h DeviceID:4141 SubsystemID:0001h RevisionID:0 BoardID:0 ChannelNumber:2 BaseAddress:b400h IRQ No:7 ComRTOpen success!! [PortNo=0] 次に、組み込んだRTLinuxモジュールを取り外します。 コンソールに以下を入力し、Enterキーを押します。 # rmmod sample1 すると、コンソール画面は、以下のメッセージが表示されます。 # rmmod sample1 cleanup_module called RTLinuxモジュールが取り外されたことが分かります。 何度かinsmodコマンドによるモジュール組み込みとrmmodコマンドによるモジュール取り外しを 繰り返してみてください。 その都度、メッセージが表示されることが分かります。 - 23 - Interface Corporation TUT-0043 3.3 シリアルポートの情報を列挙するプログラムの解説 それでは、先程のプログラムの解説を行います。 まず、このプログラムの動作概要を示します。 RTLinux モジュール sample1.o 組み込み(insmod) 取り外し(rmmod) init_module 関数 cleanup_module 関数 RTLinux 作成したRTLinuxモジュールをinsmodコマンドでRTLinuxに組み込む時、init_module関数が呼ばれ ます。init_module関数ではComRTGetPortInformation関数を呼び出し、システム内に存在する全て のシリアルポートの情報を列挙し、最初に見つけたシリアルポートに対してオープン処理を行っ ています。 また、rmmodコマンドで削除する時、cleanup_module関数が呼ばれます。cleanup_module関数では シリアルポートのクローズ処理をしています。 (18~40行目:ComRTGetPortInformation関数による情報の列挙) ここでは、ComRTGetPortInformation関数を用いて、コンピュータ内に存在する全ての弊社調歩同 期シリアル通信製品の情報を列挙しています。 この関数は、第1引数でポート番号を指定して呼び出すと、第2引数のCOMRT_PORTINFO構造体 にシリアルポートのI/Oモジュール情報を返します。 得られる情報は、以下の通りです。 メンバ変数名 内 容 VendorID ベンダIDが返ります。1147hが返ります。 DeviceID デバイスIDが返ります。 SubsystemIDと組み合わせることで、I/Oモジュールの型式を特定すること ができます。(組み合わせについては、Helpを参照してください) SubsystemID サブシステムIDが返ります。 RevisionID リビジョンIDが返ります。 BoardID I/Oモジュール上のRSW1の値が返ります。 同一型式のI/Oモジュールを、システム内で特定する時、このRSW1を変 えておくことで、どのI/Oモジュールにシリアルポートが割り当てられて いるのか、判断することができます。 ChannelNumber チャンネル番号が返ります。 I/Oモジュール上の各シリアルポートに対して、チャンネル順番に割り振 られます。 BaseAddress このシリアルポートに対応する通信コントローラが使用するI/Oポートの 先頭アドレスが返ります。 IrqNumber このシリアルポートに対応する通信コントローラが使用する割り込み番 号が返ります。 Interface Corporation - 24 - TUT-0043 GPG-4141では、シリアルポートのポート番号の最大値は、COMRT_MAX_PORTS(=256)です。つ まり、256のシリアルポートを同時に扱うことが可能です。 このforループ処理では、全てのポート番号に対して、デバイスが割り当てられているかチェック を行い、デバイスが割り当てられているポート番号に対して、シリアルポートの情報を表示して います。 38行目では、シリアルポートが最初に見つかった時、見つかったポート番号をg_port_no変数に記 録しています。これは、以降の処理で使うために行っています。 (43行目:ComRTOpen関数) ComRTOpen関数はシリアルポートのオープン処理を行っています。 この関数でオープンを行うと、送受信等の関数が使用できるようになります。 ★ComRTGetPortInformation 関数の特殊性 ComRTGetPortInformation 関数は、少々特殊な関数です。 GPG-4141 に用意されているほとんどの関数は、ComRTOpen 関数で、シリアルポートをオープンしない 限り呼び出すことはできません。オープンしないまま、呼び出すと全てエラーとなります。 しかし、ComRTGetPortInformation 関数だけは特殊で、オープン処理を行っていなくても呼び出すことが できます。 これは、シリアルポートをオープンする時、システム内の特定のポートを明示的にオープンできるよう にするために用意されているからです。 この関数を併用することで、例えば、RSW1 が 2 に設定された PCI-4141 のチャンネル番号 2 のシリアル ポートを、指定してオープンさせる等といったことが可能になります。 こうすることで、構築したシステムを他所の環境に移築した時や、移築した時システム内の I/O モジュー ルの順番を替えてしまった時でも、目的のシリアルポートをオープンさせることが可能になります。 次章のサンプルでは、実際に特定の I/O モジュールからシリアルポートをオープンさせていますので、 参考にしてください。 (62行目:ComRTClose関数) ComRTClose関数は、シリアルポートをクローズさせる関数です。 オープンしたシリアルポートは、使用後は必ずクローズするようにしてください。 ★ComRTClose 関数を呼ばないと、どうなるか? ComRTClose 関数を呼び出さないと、シリアルポートはオープンしたままの状態になります。 オープン状態なので、ComRTOpen 関数を呼び出した場合、エラーが返ります。 このような場合、対処方法としては、GPG-4141 ドライバモジュール(rcp4141/rcp4161)を一旦取り外す必 要があります。 オープンしたら必ずクローズしてください。 - 25 - Interface Corporation TUT-0043 第4章 より高度な処理を行おう 先程のプログラムは、RTLinuxモジュールの組み込みと取り外しの時にしか動作しないものでした。 実際のプログラミングでは、誰かから(大抵は上位アプリケーション)指令を受け、その指令に従っ て何らかのアクションを起こすのが普通です。 この章では、Linuxプログラムから、RTLinuxモジュールを制御するプログラムを作成します。 4.1 周期的なデータの送受信 ここでは、周期的にデータを送信し、これを別のシリアルポートで受け取り、Linuxプロセスでの データを送るという処理を行います。 ここでは、以下の製品を使用します。 PCI-4141 1枚:調歩同期通信I/Oモジュール RS-232Cリバースケーブル 1本:9pin D-subコネクタ←→9pin D-subコネクタ接続ケーブル 下図に、接続構成を示します。 RS-232C リバースケーブル CH1 PCI-4141 RSW1:0 CH2 I/Oモジュール識別用ロータリスイッチ(RSW1)を0に設定し、コンピュータに調歩同期シリアル通 信製品を実装します。 PCI-4141の2つのチャンネルを、RS-232C リバースケーブルで接続します。 お手持ちの調歩同期通信I/Oモジュールで、どのように接続すれば良いかについては、ハードウェ アマニュアルの『外部接続』を参照してください。 以下の4つのファイルを作成します。 ファイル名 備 考 sample2.h LinuxプロセスとRTLinuxモジュールの双方で共有する定義ファイル module2.c RTLinuxモジュールのソースコード sample2.c Linuxプロセスのソースコード makefile 上記ソースコードをコンパイルするためのmakefile Interface Corporation - 26 - TUT-0043 List4-1は、LinuxプロセスとRTLinuxモジュール間で、共通で使う定義ファイルです。ファイル名 を「sample2.h」として保存してください。 List4-1 sample2.h 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 /* sample2.h 共通定義ヘッダファイル Copyright 2002 Interface Corporation. All rights reserved. */ #if !defined(___SAMPLE2_H) #define ___SAMPLE2_H #include "pcicomrt.h" /* Constants --------------------------------------------------------------- */ #define FIFO_COMMAND 1 /* Linuxプロセスから指令を受け取るRT-FIFO */ #define FIFO_THRU_CMD 2 /* ハンドラからRTLinuxスレッドへ指令を受け渡しするRT-FIFO */ #define FIFO_RESULT 3 /* RTLinux内で受信したデータをLinuxプロセスに送るRT-FIFO */ #define DEVICE_ID #define SUB_SYSTEM_ID #define RSW_NO #define SEND_CH #define RECV_CH 4141 0x0001 0 1 2 /* /* /* /* /* I/Oモジュールを特定する一意のID値(PCI-4141を表す) */ I/Oモジュールを特定する一意のID値(PCI-4141を表す) */ 同一型式のI/Oモジュールを区別するRSW1設定値 */ 送信用に指定するチャンネル番号 */ 受信用に指定するチャンネル番号 */ #define BUFF_SIZE 1000 /* Linuxプロセスへ結果を送るRT-FIFOのサイズ */ /* Command ID -------------------------------------------------------------- */ /* コマンドID群(RTLinuxスレッドへの指示用) */ enum CMD_IDS { ID_START, /* 周期的な送信のスタート指示 */ ID_STOP /* 周期的な送信のストップ指示 */ }; /* コマンド指示用の構造体 */ struct CMD_STRUCT { enum CMD_IDS id; /* コマンドID */ /* 各コマンドに対する設定パラメータ */ long smp_period_ms; /* ID_START時使用:周期の設定(ms単位) */ }; #endif ★ ワンポイント お使いの調歩同期通信製品が PCI-4141 でない場合、sample2.h の 16,17 行目を使用している I/O モジュー ルに合わせて修正してください。 型式と ID 値の対応は、Help の COMRT_PORTINFO 構造体の説明を参照してください。 以下に、CTP-4142 を使用する場合の例を示します。 #define DEVICE_ID 4142 ← 4141 を 4142 に変更。 #define SUB_SYSTEM_ID 0x0101 ← 0x0001 を 0x0101 に変更。 - 27 - Interface Corporation TUT-0043 List4-2は、RTLinuxモジュールのソースファイルです。ファイル名を「module2.c」として保存し てください。 List4-2 module2.c 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 58 59 60 61 62 63 64 65 66 67 68 /* */ module2.c RTLinuxモジュールのソースコード Copyright 2002 Interface Corporation. All rights reserved. #include #include #include #include <rtl.h> <rtl_sched.h> <rtl_fifo.h> "sample2.h" pthread_t my_task_info; int g_send_port_no = -1; int g_recv_port_no = -1; /* 送信用のシリアルポート番号 */ /* 受信用のシリアルポート番号 */ /* 指定したデバイスID等の情報に合致するポート番号を検索する */ int search_rcp4141port(unsigned long DeviceID, unsigned long SubsystemID, unsigned long BoardID, unsigned long ChannelNumber) { int port_no; COMRT_PORTINFO port_info; } /* 最大ポート番号まで、条件に合致するポート番号を検索する */ for(port_no = 0; port_no < COMRT_MAX_PORTS; port_no++) { if(ComRTGetPortInformation(port_no, &port_info) == 0){ if( port_info.DeviceID == DeviceID && port_info.SubsystemID == SubsystemID && port_info.BoardID == BoardID && port_info.ChannelNumber == ChannelNumber) { return port_no; /* 見つかった:ポート番号を返す */ } } } return -1; /* 見つからなかった:-1を返す */ /* 受信時にコールバックされる関数 */ void my_recv_callback(unsigned long event_factor, unsigned long user_data) { int recv_len; char recv_buff[50]; /* 受信用のバッファ */ rtl_printf("my_recv_callback called event_factor=%lx user_data=%ld¥n", event_factor, user_data); do{ /* 目的のイベント(受信トリガサイズに達した)か? */ if((event_factor & COMRT_EVENT_RXTRIGGER) == 0){ rtl_printf("my_recv_callback: disappointed event factor¥n"); break; } /* データの受信 */ recv_len = ComRTRead(g_recv_port_no, recv_buff, sizeof(recv_buff) - 1); if(recv_len < 0){ rtl_printf("my_recv_callback: ComRTRead error [recv_len=%x]¥n", recv_len); break; } else { recv_buff[recv_len] = '¥0'; rtl_printf("my_recv_callback: recv data='%s'¥n", recv_buff); } } /* 受信したデータを、RT-FIFO経由でLinuxプロセスに送る */ rtf_put(FIFO_RESULT, recv_buff, recv_len); }while(0); /* 周期的な送信を行うRTLinuxスレッド */ void* my_task(void* arg) Interface Corporation - 28 - TUT-0043 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 { int ret; struct CMD_STRUCT cmd; struct timespec t; char send_buff[50]; /* 送信用のバッファ */ rtl_printf("my_task called arg=%d¥n", arg); /* RTLinuxスレッドが組み込み中、動作する永久ループ */ while(1){ /* 自スレッドを、次の周期までスリープさせる */ pthread_wait_np(); /* RT-FIFOからの指示があれば取り込み */ ret = rtf_get(FIFO_THRU_CMD, &cmd, sizeof(cmd)); if(ret == sizeof(cmd)){ rtl_printf("my_task: get command id=%d¥n", cmd.id); } switch(cmd.id){ case ID_START: /* 周期的な送信のスタート */ rtl_printf("my_task: send cycle is start!!¥n"); pthread_make_periodic_np(pthread_self(), gethrtime(), cmd.smp_period_ms * 1000 * 1000); break; case ID_STOP: /* 周期的な送信のストップ */ rtl_printf("my_task: send cycle is stop!!¥n"); pthread_suspend_np(pthread_self()); break; default: rtl_printf("unknown id!!¥n"); break; } /* 文字列の送信 */ t = timespec_from_ns(clock_gethrtime(CLOCK_REALTIME)); sprintf(send_buff, "now clock=%08ld", t.tv_sec); ret = ComRTWrite(g_send_port_no, send_buff, strlen(send_buff)); if(ret){ rtl_printf("my_task: ComRTWrite error [ret=%x]¥n", ret); } rtl_printf("my_task: send data='%s'¥n", send_buff); } } return 0; /* Linuxプロセスからの指令を受け取るハンドラ */ int my_handler(unsigned int fifo) { int ret; struct CMD_STRUCT cmd; rtl_printf("my_handler called fifo=%d¥n", fifo); } /* Linuxプロセスの指示に従って、RTLinuxスレッド等の制御を行う */ while((ret = rtf_get(FIFO_COMMAND, &cmd, sizeof(cmd))) == sizeof(cmd)){ rtf_put(FIFO_THRU_CMD, &cmd, sizeof(cmd)); rtl_printf("my_handler: get command id=%d¥n", cmd.id); pthread_wakeup_np(my_task_info); } if(ret != 0){ rtl_printf("my_handler: error!!(%d)¥n", ret); return -EINVAL; } return 0; /* 初期化関数(モジュールが組み込まれた時、呼ばれる関数) */ int init_module(void) { int ret; COMRT_CONFIG conf; rtl_printf("init_module called¥n"); EXPORT_NO_SYMBOLS; /* Linuxプロセスからの指示を受け取るハンドラとRT-FIFOを生成する */ - 29 - Interface Corporation TUT-0043 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 rtf_destroy(FIFO_COMMAND); rtf_create(FIFO_COMMAND, sizeof(struct CMD_STRUCT)); rtf_create_handler(FIFO_COMMAND, my_handler); /* ハンドラとRTLinuxスレッドの間のRT-FIFOを生成する */ rtf_destroy(FIFO_THRU_CMD); rtf_create(FIFO_THRU_CMD, sizeof(struct CMD_STRUCT)); /* コールバックとLinuxプロセスの間のRT-FIFOを生成する */ rtf_destroy(FIFO_RESULT); rtf_create(FIFO_RESULT, sizeof(char) * BUFF_SIZE); /* 送信用シリアルポートのオープン */ g_send_port_no = search_rcp4141port(DEVICE_ID, SUB_SYSTEM_ID, RSW_NO, SEND_CH); if(g_send_port_no < 0){ rtl_printf("Fail find send channel device.¥n"); return -1; } ret = ComRTOpen(g_send_port_no); if(ret){ rtl_printf("ComRTOpen error [ret=%x]¥n", ret); return –2; } else { rtl_printf("ComRTOpen success!! [port no=%d]¥n", g_send_port_no); } /* 受信用シリアルポートのオープン */ g_recv_port_no = search_rcp4141port(DEVICE_ID, SUB_SYSTEM_ID, RSW_NO, RECV_CH); if(g_recv_port_no < 0){ rtl_printf("Fail find recv channel device.¥n"); return –3; } ret = ComRTOpen(g_recv_port_no); if(ret){ rtl_printf("ComRTOpen error [ret=%x]¥n", ret); return –4; } else { rtl_printf("ComRTOpen success!! [port no=%d]¥n", g_recv_port_no); } /* シリアルポートの通信パラメータのデフォルト値の取得 */ ret = ComRTGetConfig(g_send_port_no, &conf); if(ret){ rtl_printf("ComRTGetConfig error [ret=%x]¥n", ret); return –5; } /* 送信用通信パラメータの変更 */ conf.DuplexMode = COMRT_FULL_DUPLEX; conf.BaudRate = 9600; conf.Parity = COMRT_PARITY_NONE; conf.StopBits = COMRT_ONE_STOPBIT; conf.WordLength = 8; /* 送信用シリアルポートへの通信パラメータの設定 ret = ComRTSetConfig(g_send_port_no, &conf); if(ret){ rtl_printf("ComRTSetConfig error [ret=%x]¥n", ret); return –6; } /* /* /* /* /* */ 全二重に設定 */ 通信速度を9600bpsに設定 */ パリティビットなし */ ストップビットは1ビット */ データ長は8ビット */ /* 受信用通信パラメータの変更(差分のみ追加) */ conf.RxEventTrigger = 18; /* 18バイト受信時に受信トリガイベント発生 */ conf.CallbackProc = my_recv_callback; /* 受信用シリアルポートのコールバックを登録 */ conf.UserData = g_recv_port_no; /* コールバックの引数に、ポート番号を指定 */ /* 受信用シリアルポートへの通信パラメータの設定 */ ret = ComRTSetConfig(g_recv_port_no, &conf); if(ret){ rtl_printf("ComRTSetConfig error [ret=%x]¥n", ret); return –7; } /* 受信用シリアルポートに対し、コールバックが行われる条件を設定 */ ret = ComRTSetEventMask(g_recv_port_no, COMRT_EVENT_RXTRIGGER); if(ret){ rtl_printf("ComRTSetEventMask error [ret=%x]¥n", ret); return –8; } Interface Corporation - 30 - TUT-0043 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 } /* RTLinuxスレッドを作成、起動する */ return pthread_create(&my_task_info, NULL, (void*)my_task, 0); /* 終了関数(モジュールが取り外される時、呼ばれる関数) */ void cleanup_module(void) { int ret; rtl_printf("cleanup_module called¥n"); /* 送信用シリアルポートをクローズする */ ret = ComRTClose(g_send_port_no); if(ret){ rtl_printf("ComRTClose error [ret=%d]¥n", ret); } /* 受信用シリアルポートをクローズする */ ret = ComRTClose(g_recv_port_no); if(ret){ rtl_printf("ComRTClose error [ret=%d]¥n", ret); } /* RT-FIFOを閉じる */ rtf_destroy(FIFO_COMMAND); rtf_destroy(FIFO_THRU_CMD); rtf_destroy(FIFO_RESULT); } /* RTLinuxスレッドを終了させる */ pthread_cancel(my_task_info); pthread_join(my_task_info, NULL); List4-3は、Linuxプロセスのソースファイルです。ファイル名を「sample2.c」として保存してくだ さい。 List4-3 sample2.c 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 /* */ sample2.c Linuxプロセスのソースコード Copyright 2002 Interface Corporation. All rights reserved. #include #include #include #include #include #include #include #include <stdio.h> <fcntl.h> <unistd.h> <math.h> <sys/time.h> <sys/ioctl.h> <rtl_fifo.h> "sample2.h" /* Linuxプロセスのメインルーチン */ int main(void) { int fd_result, fd_cmd; fd_set rfds; struct timeval tv; struct CMD_STRUCT cmd; char rt_fifo_name[80]; time_t start_time; char recv_buff[80]; int recv_len; /* RTLinuxモジュールに指令を送るRT-FIFOのオープン */ sprintf(rt_fifo_name, "/dev/rtf%d", FIFO_COMMAND); if((fd_cmd = open(rt_fifo_name, O_WRONLY)) < 0){ fprintf(stderr, "Fail to open %s¥n", rt_fifo_name); return -1; } /* RTLinuxモジュールからの結果を受け取るRT-FIFOのオープン */ sprintf(rt_fifo_name, "/dev/rtf%d", FIFO_RESULT); if((fd_result = open(rt_fifo_name, O_RDONLY)) < 0){ - 31 - Interface Corporation TUT-0043 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 } fprintf(stderr, "Fail to open %s¥n", rt_fifo_name); return –2; /* 周期的な送信の開始指示 */ cmd.id = ID_START; cmd.smp_period_ms = 500; /* 500ms周期の送信設定 */ if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){ fprintf(stderr, "Failled to send the start command.¥n"); return –3; } /* 5秒間、受信監視を行う */ start_time = time(NULL); while(fabs(difftime(time(NULL), start_time)) < 5.0){ FD_ZERO(&rfds); FD_SET(fd_result, &rfds); tv.tv_sec = 30; /* select関数のタイムアウト値の設定:30秒 */ tv.tv_usec = 0; } /* 受信データの受け取り */ if(select(FD_SETSIZE, &rfds, NULL, NULL, &tv) > 0){ if(FD_ISSET(fd_result, &rfds)){ /* fd_resultに対してRT-FIFOの書き込みがあった */ recv_len = read(fd_result, recv_buff, sizeof(recv_buff) - 1); if(recv_len >= 0){ recv_buff[recv_len] = '¥0'; printf("recv data='%s'¥n", recv_buff); } } } /* 周期的な送信の停止指示 */ cmd.id = ID_STOP; cmd.smp_period_ms = 0; if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){ fprintf(stderr, "Failled to send the stop command.¥n"); return –4; } printf("fd_cmd:%d¥n", close(fd_cmd)); printf("fd_result:%d¥n", close(fd_result)); } printf("The Linux proccess is successfully completed.¥n"); return 0; List4-4は、上記ファイルをコンパイルするメイクファイルです。ファイル名を「makefile」として 保存してください。 List4-4 makefile 1 2 3 4 5 6 7 8 include /usr/include/rtlinux/rtl.mk all: module2.o sample2 sample2: sample2.c $(CC) $(INCLUDE) $(USER_CFLAGS) -O2 -Wall -o sample2 sample2.c module2.o:module2.c $(CC) $(INCLUDE) $(CFLAGS) -o module2.o -c module2.c コンパイルし、insmodコマンドでRTLinuxモジュールを組み込みます。 # make ← コンパイルします # ls ← コンパイルしたファイルを一覧表示します makefile sample2 module2.c module2.o sample2.c sample2.h # insmod module2.o ← RTLinux モジュールを組み込んでいます 次に、Linuxプロセスを実行します。 # ./sample2 Interface Corporation ← Linux プロセスを実行します - 32 - TUT-0043 実行画面を以下に示します。 (画面左上のウィンドウがLinuxプロセスを実行しているコンソール。画面右下のウィンドウはRTLinux モジュールのログメッセージを表示させているコンソールです) サンプルの実行画面例 sample2を実行させると、「recv data=’now clock=xxxxxxxx’」と表示が繰り返し出力され、暫くし てプログラムが終了します。 - 33 - Interface Corporation TUT-0043 4.2 周期的なデータの送受信の解説 それでは、先程のプログラムの解説を行います。 プログラムの大まかな動きを、下図に示します。 Linux プロセス チャンネル 1 チャンネル 2 Linux プロセス スタート指令 データ送信 “now clock=xxxxxxxx” XXms 周期 データ送付 データ受信 データ送信 “now clock=xxxxxxxx” データ送付 データ受信 データ送信 ストップ指令 “now clock=xxxxxxxx” データ送付 データ受信 my_task 関数が処理します my_recv_callback 関数が 処理します sample2の動作概要 Linuxプロセスからスタート指示があると、チャンネル1のシリアルポートから特定のデータがチ ャンネル2に対して、周期的に送信されます。 チャンネル2のシリアルポートは、受け取ったデータを、RT-FIFOを介してLinuxプロセスに送りま す。 チャンネル1の送信処理は、RTLinuxスレッド(my_task)が行っています。 チャンネル2の受信処理は、調歩同期通信のドライバモジュールから呼ばれるコールバック関数 (my_recv_callback)が行っています。 Linuxプロセスの仕事は、通信処理を行うRTLinuxモジュールに対する周期処理のスタート指令,ス トップ指令,チャンネル2が受信したデータを、RT-FIFO経由で受け取ることです。 Interface Corporation - 34 - TUT-0043 次に、LinuxプロセスとRTLinuxモジュールの関係を、下図に示します。 main ユーザ空間 Linux プロセス(sample2) RTLinux モジュール(module2.o) FIFO_COMMAND カーネル空間 指令 my_handler 破棄 生成 FIFO_THRU_CMD init_module cleanup_module 指令の転送 生成 my_task 破棄 FIFO_RESULT コールバック生成 受信イベント 受信データ my_recv_callback LinuxプロセスとRTLinuxモジュールの相関関係 LinuxプロセスからRTLinuxモジュールに対する指示は、RT-FIFOを経由して、一旦ハンドラ (my_handler)に送られ、そこから別のRT-FIFOを介してRTLinuxスレッド(my_task)に送られます。 my_taskでは、周期的なデータの送信処理が行われますが、送られたデータは、調歩同期通信ドラ イバモジュールが呼び出すコールバック関数(my_recv_callback)にて受け取られます。 my_recv_callbackで受け取ったデータは、RT-FIFOを経由して、Linuxプロセスに対して送られます。 図中のinit_moduleとcleanup_moduleから伸びている矢印は、これらの関数からハンドラ (my_handler)とRTLinuxスレッド(my_task)が生成,破棄されていることを表しています。 init_moduleでは、RTLinuxスレッドおよびRT-FIFOの生成の他、調歩同期通信ドライバモジュール のオープン処理,通信パラメータの初期化,コールバック関数(my_recv_callback)の登録が行われま す。 cleanup_moduleでは、RTLinuxスレッドおよびRT-FIFOの破棄の他、調歩同期通信ドライバモジュー ルのクローズ処理が行われます。 - 35 - Interface Corporation TUT-0043 4.2.1 調歩同期通信ドライバモジュールの役割 調歩同期通信ドライバモジュールは内部で様々な仕事を行います。 ここでは、ドライバモジュールの構造について、簡単に紹介します。 プログラムコード コールバック関数 関数呼び出し コールバック呼び出し ドライバモジュール 内部バッファ 送信管理処理 受信管理処理 シリアル通信 コントローラ 調歩同期通信ボード ドライバモジュールの構造 調歩同期通信ドライバモジュールは、調歩同期通信I/Oモジュール上のシリアル通信コントローラ の制御を行います。 ドライバは、データの送受信管理を行いますが、処理を円滑に行うため、内部にバッファを持っ ています。(デフォルトでは約4KBが内部バッファに確保されます) プログラムからデータの送信が指示された時、内部バッファに一旦格納された後、ドライバがシ リアル通信コントローラを制御して、データを送信します。 データの受信は、ドライバがシリアル通信コントローラのデータ受信を検知し、データを取り出 した後、内部バッファに一旦蓄積しています。プログラムは、この蓄積したデータを取り出すよ うになっています。 このような構造になっているため、プログラマは通信コントローラを直接制御した際に発生する、 面倒なデータの管理処理を行わなくて済むようになっています。 通信コントローラ内部で発生した様々な事象の変化を、即プログラムが受け取って処理できるよ う、ドライバモジュールはコールバック機構を提供しています。 プログラマは、コールバック関数と、コールバックさせたいイベント内容を予め登録しておくこ とで、ドライバモジュール内部で発生した事象の変化を、検知することができます。 実際、module2.cでは、指定件数のデータが受信されたらコールバック関数が呼び出されるようプ ログラミングされています。 コールバック関数では、ドライバからデータを取り出して然るべき処理を行っています。 Interface Corporation - 36 - TUT-0043 ★内部バッファの構造 内部バッファは、1 つのシリアルポートに対して、送信用バッファ,受信用バッファが、それぞれ 1 つず つ用意されます。 内部バッファは、キュー構造になっており、送信指示された時や未送信のデータが内部バッファにあっ た時に、データは内部バッファの後尾に格納されます。 内部バッファはドライバモジュールが組み込まれる時、固定長で確保されます。 従って、あまりに大きいデータを送信しようとしたり、受信したデータを取り出さず溜め込み続けると、 データが溢れ出してしまいます。 実際には送信バッファが一杯になると、送信用の関数を呼び出した際、エラーが返ります。 受信バッファの場合、受信されたデータから破棄されます。 受信バッファが溢れないよう、受信データが溜まったら然るべきタイミングで抜き取るよう、注意して プログラミングしてください。 4.2.2 共通定義ファイルの役割 共通定義ファイル(sample2.h)は、LinuxプロセスとRTLinuxモジュールが、共通で使用する定数等 を抜き出したものです。 共通定義ファイルでは、以下の設定を行っています。 行番号 内 容 #define宣言> 12∼22行目 RT-FIFOの番号の定義,各関数で使用する定数値の指定,バッファサイズの指定。 enum宣言> 26~29行目 LinuxプロセスからRTLinuxモジュールに対して指令を出す際のコマンドのID値を指 定。 struct宣言> 32∼37行目 LinuxプロセスからRTLinuxモジュールに対して指令を出す際のコマンドの構造を指 定。 ここで重要なのがLinuxプロセスからRTLinuxモジュールに対して指令を出すために使用されるコ マンドの構造体(CMD_STRUCT)です。 Linuxプロセスでは、この構造体に値をセットしてRTLinuxモジュールのハンドラに渡します。ハ ンドラでは、この構造体ごと受け取って、指示された内容に従って処理を行います。 Linux プロセス struct CMD_STRUCT { enum CMD_IDS id; long smp_period_ms; RTLinux モジュール LinuxプロセスからRTLinuxモジュールのハンドラへのコマンドの流れ - 37 - Interface Corporation TUT-0043 コマンドの構造体は、2つのメンバ変数から構成されています。RTLinuxモジュールに、どんな作 業をして欲しいか指示を出すID値(idメンバ変数)と、ID値を補足する情報(smp_period_msメンバ変 数)です。 作業を指示するID値は、26∼29行の列挙体定数 ID_STARTとID_STOPで定義されています。以下 に、LinuxプロセスからRTLinuxモジュールに指示する際の取り決めを、簡単にまとめます。 id変数 内 容 ID_START smp_period_ms で指 定し た時間間隔 で、RTLinux モジュール内の RTLinuxスレッド(my_task)をスタートする。 ID_STOP RTLinuxスレッド(my_task)をストップさせる。 この構造体は、後の説明にも出てきますので注意して読み進めてください。 4.2.3 RTLinuxモジュールの動き ここでは、RTLinuxモジュール(module2.o)の動きに注目して見てみます。 RTLinuxモジュールでは、周期的な送受信処理を実現するために、init_module関数内で、幾つか リソースを生成しています。それを下表に示します。 項 目 内 容 RT-FIFO(FIFO_COMMAND) Linuxプロセスからの指示を受けるためのRT-FIFOです。指示は、ハ ンドラmy_handlerに渡されます。 RT-FIFO(FIFO_THRU_CMD) ハンドラにて、Linuxプロセスから受け取った情報を、周期送信処理 を実現するRTLinuxスレッド(my_task)に渡すためのRT-FIFOです。 RT-FIFO(FIFO_RESULT) コールバック関数(my_recv_callback)にて受け取ったデータを、Linux プロセスに返すためのRT-FIFOです。 Linuxプロセスからの指示を受け取るための処理の入り口です。 ハンドラ(my_handler) RT-FIFO経由で送られた情報は、一旦このハンドラが受け取り、然る 処理に回されます。 周期送信処理を実現するRTLinuxスレッドです。 RTLinuxスレッド(my_task) Linuxプロセスから送られる指示は、最終的にここに送られ、周期送 信処理を実現します。 Interface Corporation - 38 - TUT-0043 次に、生成された各リソースの相互関係と処理の流れを下図に示します。 Linux プロセス FFO_COMMAND コマンド指示 ハンドラ(my_handler) FIFO_THRU_CMD FIFO_RESULT RTLinux スレッド(my_task) ID_START 受信データ ID_STOP 周期送信 ComRTWrite 関数 ←周期呼び出し チャンネル 1 からチャンネル 2 に データの送信 コールバック関数(my_recv_callback) 調歩同期通信 ドライバモジュール 受信イベント発生 データ受信 ComRTRead 関数 RTLinuxモジュール内の処理の流れ RTLinuxモジュール内で中心となるのは、周期送信処理を実現するRTLinuxスレッド(my_task)と、 受信処理を行うコールバック関数(my_recv_callback)です。 RTLinuxスレッドは、Linuxプロセスからの指令により、スタート(ID_START),ストップ(ID_STOP) を行います。スタート後の実行中、スレッドは周期ごとにデータの送信を行います。 コールバック関数は、調歩同期通信ドライバモジュール内で、送信されたデータが受信された時 や、溜まったデータが一定数に達する時に呼ばれます。ここでは、溜まったデータを抜き取り、 LinuxプロセスへRT-FIFOを経由して送ります。 (145∼155行目:RT-FIFOおよびハンドラの生成) init_module関数の最初の段階では、RTLinuxモジュールおよびLinuxプロセスで使用するRT-FIFO および、Linuxプロセスからの指示を受け取るハンドラを生成しています。 (158∼169行目:送信用シリアルポートのオープン) 158行目のserarch_rcp4141port関数は、指定するデバイスID,サブシステムID,RSW1番号,チャンネル 番号に合致するデバイスを検索し、該当するデバイスに関連付けられているポート番号を返しま す。 163行目のComRTOpen関数は、返されたポート番号を基にオープンを行っています。 この処理は、構築したシステムを別の場所に移築した際、ポート番号の割り振りが変化しても、 常に目的のシリアルポートをオープンさせるための工夫です。 (172∼183行目:受信用シリアルポートのオープン) 先のコードは、送信用シリアルポートのオープンを行いましたが、こちらでは受信用のシリアル ポートをオープンしています。 - 39 - Interface Corporation TUT-0043 (186∼214行目:シリアルポートの通信パラメータの設定) ここでは、送信用シリアルポートと受信用シリアルポートに対して、通信用パラメータの設定を 行っています。 まず、186行目のComRTGetConfig関数で通信パラメータのデフォルト値を取り出し、通信パラ メータを幾つか修正した後、199行目のComRTSetConfig関数で送信用シリアルポートの通信パラ メータを、210行目のComRTSetConfig関数で受信用シリアルポートの通信パラメータの設定を行 っています。 受信用シリアルポートでは、送信用シリアルポートの設定に比べ、後の処理でコールバック関数 を使用するために、コールバック関数の登録と、コールバック関数が呼び出される受信トリガイ ベントの設定を行っています。 (216行目:コールバック関数の条件設定) 216行目のComRTSetEventMask関数は、先のComRTSetConfig関数で登録した受信用シリアルポー トのコールバック関数に対して、どのような条件でコールバック関数を呼び出させるか設定して います。 ここでは、受信データサイズが受信トリガサイズに達した時に呼ばれるよう、 COMRT_EVENT_RXTRIGGER(=1)を設定しています。 (223行目:pthread_create関数) pthread_create関数では、周期送信処理を実行するRTLinuxスレッド(my_task)を生成しています。 (122∼130行目:ハンドラの処理) ハンドラ(my_handler)の役目は、LinuxプロセスからRT-FIFOを経由して送られる指示を、RTLinux スレッド(my_task)に渡すことにあります。 この処理は、ほとんど定型的なものです。 Linux プロセス側 rtf_get RT-FIFO my_handler ハンドラ rtf_put RTLinux スレッド側 RT-FIFO LinuxプロセスからRTLinuxスレッドへのデータの流れ (78∼109行目:RTLinuxスレッドの処理) RTLinuxスレッド(my_task)の処理の中心は、この78∼109行のwhileループです。 ここで、Linuxプロセスから与えられた指示によって、周期実行の開始と停止を行い(87∼98行の 処理)、周期実行ごとに102∼103行目のsprintfで生成したデータを、104行目のComRTWrite関数で 送信しています。 Interface Corporation - 40 - TUT-0043 周期実行の開始と停止は、先に述べたCMD_IDS列挙体の定数値により決定されます。 ID_START smp_period_msメンバ変数の値をms単位の実行周期として、 pthread_make_periodic_np関数を呼び出し、自身の実行周期の間隔を指定している。 ID_STOP pthread_suspend_np関数を呼び出し、自身のスレッドをスリープ状態にしている。 周期実行の間隔は、smp_period_msの値により決定されます。 例えばここで100の値が指定されると、100msごとに送信処理が行われます。 (38∼65行目:コールバック関数の処理) RTLinuxスレッドのComRTWrite関数でチャンネル1より送信されたデータは、チャンネル2で受信 され、ドライバモジュールがデータを内部バッファに溜め込みます。 そして、データの量が指定バイト数に達した時(206行目のRxEventTriggerメンバ変数の値)、調歩同 期通信ドライバモジュールはコールバック関数(my_recv_callback)を呼び出します。 47行目のif文は、コールバック関数が呼ばれる際に渡された第1引数(event_factor)の値をチェック しています。この引数は、コールバック関数が呼ばれた理由がセットされているので、ここでは、 処理する目的に合致しているか確認しています。 53行目のComRTRead関数は、内部バッファに溜まった受信データを取り出す処理を行っています。 この関数を呼び出すと、取り出した受信データのバイト数が返ります。 呼び出しに失敗したら負の値が返ります。 呼び出しに成功すると、58行目に移ります。ここで取り出した受信データの最後にヌル文字(‘¥0’) をセットしています。これは、取り出した受信データを、文字列として扱えるようにするために 行っています。 ComRTRead関数自体は、受信データをバイナリデータとして扱うため、文字列の終端記号(ヌル文 字)をバッファの最後尾に書き込むことは行いません。そのため、このような処理を行っています。 63行目のrtf_put関数は、取り出した受信データを、RT-FIFO経由でLinuxプロセスに送っています。 - 41 - Interface Corporation TUT-0043 4.2.4 Linuxプロセスの動き ここでは、Linuxプロセス(sample2)の動きに注目して見てみます。 まず、LinuxプロセスとRTLinuxモジュールとの関係を、下図に示します。 コマンド指令 RTLinux RT-FIFO(FIFO_COMMAND) モジュール main 受信データ取得 RT-FIFO(FIFO_RESULT) LinuxプロセスとRTLinuxモジュールとのRT-FIFOの関係 Linuxプロセスは、RT-FIFOを2つほどオープンし、それぞれコマンド指令と、RTLinuxモジュール からの受信データの取得に使用しています。 (29∼39行目:RT-FIFOのオープン処理) ここでは、open関数を使用し、RTLinuxモジュールとデータのやり取りを行うRT-FIFOをオープン しています。 (42∼47行目:周期的な送信の開始) ここでは、RTLinuxモジュールに対して、周期的な送信を開始させるため、CMD_STRUCT構造体 に、周期送信開始を意味するID_START列挙定数と周期間隔を指定し、write関数を使ってRT-FIFO 経由で指示を送っています。 RTLinuxモジュール側では、この指示をハンドラで受けて処理を行います。 (50∼67行目:受信データの取得) ここでは、RTLinuxモジュール内で実行される周期的な送信データを受け取った受信データを、 select関数とread関数を使って取得しています。 受信データの取得はtime関数を用い、約5秒間行っています。 (70∼75行目:周期的な送信の停止) ここでは、RTLinuxモジュールに対して周期的な送信を停止させるため、CMD_STRUCT構造体に 周期送信停止を意味するID_STOP列挙定数を指定し、write関数を使ってRT-FIFO経由で指示を送 っています。 RTLinuxモジュールはこの指示を受け取った後、実行を停止します。 Interface Corporation - 42 - TUT-0043 4.3 マルチメータからのデータの取り込み 先程は、周期的なデータの送信と受信を行いました。ここでは、計測器と接続して制御を行って 見ます。 ! 注意 ここで使う調歩同期通信I/Oモジュールは、接続相手であるAgilent34401AがRS-232C通信イ ンタフェースを採用しているため、RS-232Cを通信インタフェースに持つI/Oモジュールでない と動かせません。予めご了承ください。 ここでは、以下の製品を使用します。 PCI-4141 RS-232Cリバースケーブル Agilent34401A 1枚:調歩同期通信I/Oモジュール 1本:9ピンD-subコネクタ←→9ピンD-subコネクタ接続ケーブル 1台:マルチメータ アジレント・テクノロジー社製 下図に、接続構成を示します。 CH1 PCI-4141 RSW1:0 Agilent34401A RS-232C リバースケーブル I/Oモジュール識別用ロータリスイッチ(RSW1)を0に設定し、コンピュータに調歩同期シリアル通 信製品を実装します。 PCI-4141のチャンネル1とAgilent34401Aの背面のRS-232Cコネクタを、RS-232C リバースケーブル で接続します。 以下の4つのファイルを作成します。 ファイル名 備 考 sample3.h LinuxプロセスとRTLinuxモジュールの双方で共有する定義ファイル module3.c RTLinuxモジュールのソースコード sample3.c Linuxプロセスのソースコード makefile 上記ソースコードをコンパイルするためのmakefile - 43 - Interface Corporation TUT-0043 List4-5は、LinuxプロセスとRTLinuxモジュール間で、共通で使う定義ファイルです。ファイル名 を「sample3.h」として保存してください。 List4-5 sample3.h 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 /* sample3.h 共通定義ヘッダファイル Copyright 2002 Interface Corporation. All rights reserved. */ #if !defined(___SAMPLE2_H) #define ___SAMPLE2_H #include "pcicomrt.h" /* Constants --------------------------------------------------------------- */ #defineFIFO_COMMAND 1 /* Linuxプロセスから指令を受け取るRT-FIFO */ #defineFIFO_THRU_CMD 2 /* ハンドラからRTLinuxスレッドへ指令を受け渡しするRT-FIFO */ #defineFIFO_RESULT 3 /* RTLinux内で受信したデータをLinuxプロセスに送るRT-FIFO */ #defineDEVICE_ID #defineSUB_SYSTEM_ID #defineRSW_NO #defineSEND_RECV_CH 4141 /* I/Oモジュールを特定する一意のID値(PCI-4141を表す) */ 0x0001 /* I/Oモジュールを特定する一意のID値(PCI-4141を表す) */ 0 /* 同一型式のI/Oモジュールを区別するRSW1設定値 */ 1 /* 送受信用に指定するチャンネル番号 */ #defineBUFF_SIZE 1000 /* Linuxプロセスへ結果を送るRT-FIFOのサイズ */ /* Command ID -------------------------------------------------------------- */ /* コマンドID群(RTLinuxスレッドへの指示用) */ enum CMD_IDS { ID_START, /* 周期的な送信のスタート指示 */ ID_STOP /* 周期的な送信のストップ指示 */ }; /* コマンド指示用の構造体 */ struct CMD_STRUCT { enum CMD_IDS id; }; /* コマンドID */ /* 各コマンドに対する設定パラメータ */ long smp_period_ms; /* ID_START時使用:周期の設定(ms単位) */ #endif Interface Corporation - 44 - TUT-0043 List4-6は、RTLinuxモジュールのソースファイルです。ファイル名を「module3.c」として保存し てください。 List4-6 module3.c 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 58 59 60 61 62 63 64 65 66 67 68 69 /* */ module3.c RTLinuxモジュールのソースコード Copyright 2002 Interface Corporation. All rights reserved. #include #include #include #include <rtl.h> <rtl_sched.h> <rtl_fifo.h> "sample3.h" pthread_t my_task_info; int g_port_no = -1; /* 送受信用のシリアルポート番号 */ /* 指定したデバイスID等の情報に合致するポート番号を検索する */ int search_rcp4141port(unsigned long DeviceID, unsigned long SubsystemID, unsigned long BoardID, unsigned long ChannelNumber) { int port_no; COMRT_PORTINFO port_info; } /* 最大ポート番号まで、条件に合致するポート番号を検索する */ for(port_no = 0; port_no < COMRT_MAX_PORTS; port_no++) { if(ComRTGetPortInformation(port_no, &port_info) == 0){ if( port_info.DeviceID == DeviceID && port_info.SubsystemID == SubsystemID && port_info.BoardID == BoardID && port_info.ChannelNumber == ChannelNumber) { return port_no; /* 見つかった:ポート番号を返す */ } } } return -1; /* 見つからなかった:-1を返す */ /* Agilent34401Aに対して、送信処理を行う関数 */ int send_hp34401a(int port_no, char* msg) { int ret; rtl_printf("send_hp34401a port_no=%d msg=%s¥n", port_no, msg); } ret = ComRTWrite(port_no, msg, strlen(msg)); /* データの送信 */ if(ret){ rtl_printf("send_hp34401a: ComRTWrite error [ret=%x]¥n", ret); } return ret; /* Agilent34401Aから受信処理を行う関数 */ int recv_hp34401a(int port_no, char* buff, int buff_size) { int recv_len; rtl_printf("recv_hp34401a port_no=%d buff=%p buff_size=%d¥n", port_no, buff, buff_size); } recv_len = ComRTRead(port_no, buff, buff_size); /* データの受信 */ if(recv_len < 0){ rtl_printf("recv_hp34401a: ComRTRead error [recv_len=%x]¥n", recv_len); } return recv_len; /* 受信時にコールバックされる関数 */ void my_recv_callback(unsigned long event_factor, unsigned long user_data) { int recv_len; - 45 - Interface Corporation TUT-0043 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 char recv_buff[50]; rtl_printf("my_recv_callback called event_factor=%lx user_data=%ld¥n", event_factor, user_data); do{ /* 目的のイベント(受信トリガサイズに達した)か? */ if((event_factor & COMRT_EVENT_RXTRIGGER) == 0){ rtl_printf("my_recv_callback: disappointed event factor¥n"); break; } /* データの受信 */ recv_len = recv_hp34401a(g_port_no, recv_buff, sizeof(recv_buff) - 1); if(recv_len < 0){ rtl_printf("my_recv_callback: recv_hp34401a error [recv_len=%d]¥n", recv_len); break; } else { recv_buff[recv_len] = '¥0'; rtl_printf("my_recv_callback: recv data=%s", recv_buff); } /* 受信したデータを、RT-FIFO経由でLinuxプロセスに送る */ rtf_put(FIFO_RESULT, recv_buff, recv_len); } /* RTLinuxスレッドを起床させる */ pthread_wakeup_np(my_task_info); }while(0); /* 周期的な送信を行うRTLinuxスレッド */ void* my_task(void* arg) { int ret; struct CMD_STRUCT cmd; struct timespec t; rtl_printf("my_task called arg=%d¥n", arg); /* Agilent34401Aを初期化させる */ send_hp34401a(g_port_no, "*RST¥r¥n"); /* RTLinuxスレッドが組み込み中、動作する永久ループ */ while(1){ /* 自スレッドを、次の周期までスリープさせる */ pthread_wait_np(); /* RT-FIFOからの指示があれば取り込み */ ret = rtf_get(FIFO_THRU_CMD, &cmd, sizeof(cmd)); if(ret == sizeof(cmd)){ rtl_printf("my_task: get command id=%d¥n", cmd.id); switch(cmd.id){ case ID_START: /* 周期的な送信のスタート */ rtl_printf("my_task: send cycle is start!!¥n"); /* Agilent34401Aをリモート状態にする */ send_hp34401a(g_port_no, "System:REMOTE¥r¥n"); pthread_make_periodic_np(pthread_self(), gethrtime(), cmd.smp_period_ms * 1000 * 1000); break; case ID_STOP: /* 周期的な送信のストップ */ rtl_printf("my_task: send cycle is stop!!¥n"); /* Agilent34401Aをリモート状態から解除する */ send_hp34401a(g_port_no, "System:LOCAL¥r¥n"); pthread_suspend_np(pthread_self()); break; default: rtl_printf("unknown id!!¥n"); break; } } else { t = timespec_from_ns(clock_gethrtime(CLOCK_REALTIME)); rtl_printf("now clock=%ld¥n", t.tv_sec); /* Agilent34401Aに対して、電圧の計測を要求 */ Interface Corporation - 46 - TUT-0043 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 send_hp34401a(g_port_no, ":Measure:Voltage:DC?¥r¥n"); } } } return 0; /* 自スレッドをサスペンドさせ、受信完了時の起床待ちに入る */ pthread_suspend_np(pthread_self()); /* Linuxプロセスからの指令を受け取るハンドラ */ int my_handler(unsigned int fifo) { int ret; struct CMD_STRUCT cmd; rtl_printf("my_handler called fifo=%d¥n", fifo); } /* Linuxプロセスの指示に従って、RTLinuxスレッド等の制御を行う */ while((ret = rtf_get(FIFO_COMMAND, &cmd, sizeof(cmd))) == sizeof(cmd)){ rtf_put(FIFO_THRU_CMD, &cmd, sizeof(cmd)); rtl_printf("my_handler: get command id=%d¥n", cmd.id); pthread_wakeup_np(my_task_info); } if(ret != 0){ rtl_printf("my_handler: error!!(%d)¥n", ret); return -EINVAL; } return 0; /* 初期化関数(モジュールが組み込まれた時、呼ばれる関数) */ int init_module(void) { int ret; COMRT_CONFIG conf; rtl_printf("init_module called¥n"); EXPORT_NO_SYMBOLS; /* Linuxプロセスからの指示を受け取るハンドラとRT-FIFOを生成する */ rtf_destroy(FIFO_COMMAND); rtf_create(FIFO_COMMAND, sizeof(struct CMD_STRUCT)); rtf_create_handler(FIFO_COMMAND, my_handler); /* ハンドラとRTLinuxスレッドの間のRT-FIFOを生成する */ rtf_destroy(FIFO_THRU_CMD); rtf_create(FIFO_THRU_CMD, sizeof(struct CMD_STRUCT)); /* コールバックとLinuxプロセスの間のRT-FIFOを生成する */ rtf_destroy(FIFO_RESULT); rtf_create(FIFO_RESULT, sizeof(char) * BUFF_SIZE); /* シリアルポートのオープン */ g_port_no = search_rcp4141port(DEVICE_ID, SUB_SYSTEM_ID, RSW_NO, SEND_RECV_CH); if(g_port_no < 0){ rtl_printf("Fail find send channel device.¥n"); return -1; } ret = ComRTOpen(g_port_no); if(ret){ rtl_printf("ComRTOpen error [ret=%x]¥n", ret); return –2; } else { rtl_printf("ComRTOpen success!! [port no=%d]¥n", g_port_no); } /* シリアルポートの通信パラメータのデフォルト値の取得 */ ret = ComRTGetConfig(g_port_no, &conf); if(ret){ rtl_printf("ComRTGetConfig error [ret=%x]¥n", ret); return –3; } /* 送信用通信パラメータの変更 */ conf.DuplexMode = COMRT_FULL_DUPLEX; - 47 - /* 全二重に設定 */ Interface Corporation TUT-0043 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 conf.BaudRate = 9600; /* 通信速度を9600bpsに設定 */ conf.Parity = COMRT_PARITY_NONE;/* パリティビットなし */ conf.StopBits = COMRT_TWO_STOPBITS; /* ストップビットは2ビット */ conf.WordLength = 8; /* データ長は8ビット */ /* 17バイト受信時に受信トリガイベント発生 */ conf.RxEventTrigger = 17; conf.CallbackProc = my_recv_callback; /* 受信用シリアルポートのコールバックを登録 */ /* コールバックの引数に、ポート番号を指定 */ conf.UserData = g_port_no; /* シリアルポートへの通信パラメータの設定 */ ret = ComRTSetConfig(g_port_no, &conf); if(ret){ rtl_printf("ComRTSetConfig error [ret=%x]¥n", ret); return –4; } /* シリアルポートに対し、コールバックが行われる条件を設定 */ ret = ComRTSetEventMask(g_port_no, COMRT_EVENT_RXTRIGGER); if(ret){ rtl_printf("ComRTSetEventMask error [ret=%x]¥n", ret); return –5; } /* ER,RS信号をONにする */ ComRTSetModemStatus(g_port_no, 6); } /* RTLinuxスレッドを作成、起動する */ return pthread_create(&my_task_info, NULL, (void*)my_task, 0); /* 終了関数(モジュールが取り外される時、呼ばれる関数) */ void cleanup_module(void) { int ret; rtl_printf("cleanup_module called¥n"); /* 送信用シリアルポートをクローズする */ ret = ComRTClose(g_port_no); if(ret){ rtl_printf("ComRTClose error [ret=%d]¥n", ret); } /* RT-FIFOを閉じる */ rtf_destroy(FIFO_COMMAND); rtf_destroy(FIFO_THRU_CMD); rtf_destroy(FIFO_RESULT); } /* RTLinuxスレッドを終了させる */ pthread_cancel(my_task_info); pthread_join(my_task_info, NULL); List4-7は、Linuxプロセスのソースファイルです。ファイル名を「sample3.c」として保存してくだ さい。 List4-7 sample3.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /* */ sample3.c Linuxプロセスのソースコード Copyright 2002 Interface Corporation. All rights reserved. #include #include #include #include #include #include #include #include <stdio.h> <fcntl.h> <unistd.h> <math.h> <sys/time.h> <sys/ioctl.h> <rtl_fifo.h> "sample3.h" /* Linuxプロセスのメインルーチン */ int main(void) { int fd_result, fd_cmd; Interface Corporation - 48 - TUT-0043 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 fd_set rfds; struct timeval tv; struct CMD_STRUCT cmd; char rt_fifo_name[80]; time_t start_time; char recv_buff[80]; int recv_len; /* RTLinuxモジュールに指令を送るRT-FIFOのオープン */ sprintf(rt_fifo_name, "/dev/rtf%d", FIFO_COMMAND); if((fd_cmd = open(rt_fifo_name, O_WRONLY)) < 0){ fprintf(stderr, "Fail to open %s¥n", rt_fifo_name); return -1; } /* RTLinuxモジュールからの結果を受け取るRT-FIFOのオープン */ sprintf(rt_fifo_name, "/dev/rtf%d", FIFO_RESULT); if((fd_result = open(rt_fifo_name, O_RDONLY)) < 0){ fprintf(stderr, "Fail to open %s¥n", rt_fifo_name); return –2; } /* 周期的な送信の開始指示 */ cmd.id = ID_START; cmd.smp_period_ms = 500; /* 500ms周期の送信設定 */ if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){ fprintf(stderr, "Failled to send the start command.¥n"); return –3; } /* 5秒間、受信監視を行う */ start_time = time(NULL); while(fabs(difftime(time(NULL), start_time)) < 5.0){ FD_ZERO(&rfds); FD_SET(fd_result, &rfds); tv.tv_sec = 30; /* select関数のタイムアウト値の設定:30秒 */ tv.tv_usec = 0; } /* 受信データの受け取り */ if(select(FD_SETSIZE, &rfds, NULL, NULL, &tv) > 0){ if(FD_ISSET(fd_result, &rfds)){ /* fd_resultに対してRT-FIFOの書き込みがあった */ recv_len = read(fd_result, recv_buff, sizeof(recv_buff) - 1); if(recv_len >= 0){ recv_buff[recv_len] = '¥0'; printf("recv data=%s¥n", recv_buff); } } } /* 周期的な送信の停止指示 */ cmd.id = ID_STOP; cmd.smp_period_ms = 0; if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){ fprintf(stderr, "Failled to send the stop command.¥n"); return –4; } printf("fd_cmd:%d¥n", close(fd_cmd)); printf("fd_result:%d¥n", close(fd_result)); } printf("The Linux proccess is successfully completed.¥n"); return 0; - 49 - Interface Corporation TUT-0043 List4-8は、上記ファイルをコンパイルするメイクファイルです。ファイル名を「makefile」として 保存してください。 List4-8 makefile 1 2 3 4 5 6 7 8 include /usr/include/rtlinux/rtl.mk all: module3.o sample3 sample3: sample3.c $(CC) $(INCLUDE) $(USER_CFLAGS) -O3 -Wall -o sample3 sample3.c module3.o:module3.c $(CC) $(INCLUDE) $(CFLAGS) -o module3.o -c module3.c プ ロ グ ラ ム を 動 か す 前 に 、 Agilent34401A の マ ニ ュ ア ル を 参 照 し 、 フ ロ ン ト パ ネ ル か ら Agilent34401Aを以下の設定に合わせてください。 項目(E:Input/Output MENU内) INTERFACE RS-232C BAUD RATE 9600 BAUD PARITY NONE: 8BITS LANGUAGE SCPI 設定値 コンパイルし、insmodコマンドでRTLinuxモジュールを組み込みます。 # make # ls makefile sample3 module3.c module3.o sample3.c sample3.h # insmod module3.o ← RTLinux モジュールを組み込んでいます 次に、Linuxプロセスを実行します。 # ./sample3 ← Linux プロセスを実行しています 実行画面を以下に示します。 (画面左上のウィンドウがLinuxプロセスを実行しているコンソール。画面右下のウィンドウは RTLinuxモジュールのログメッセージを表示させているコンソールです) サンプルの実行画面例 sample3を実行させると、Agilent34401Aから取得した電圧値のデータが繰り返し出力され、暫くし てプログラムが終了します。 Interface Corporation - 50 - TUT-0043 4.4 マルチメータからのデータの取り込み解説 それでは、先程のプログラムの解説を行います。 プログラムの大まかな動きを、下図に示します。 Linux プロセス Agilent34401A チャンネル 1 データ送信 “*RST¥r¥n” スタート指令 データ受信 データ送信 “System:REMOTE¥r¥n” データ受信 データ送信 “:Measure:Voltage:DC?¥r¥n” XXms 周期 データ受信 計測した電圧値のデータ 計測 データ送信 データ受信 データ送信 “:Measure:Voltage:DC?¥r¥n” データ受信 計測した電圧値のデータ 計測 データ送信 データ受信 ストップ指令 データ送信 “System:LOCAL¥r¥n” データ受信 sample3の動作概要 プログラム起動直後、チャンネル1のシリアルポートからAgilent34401Aに対して、Agilent34401A を初期化する命令(” *RST¥r¥n”)が送信されます。 これは、RTLinuxスレッドの起動直後に行われます。 Linuxプロセスからスタート指示があると、まず、チャンネル1のシリアルポートからAgilent34401A をリモート状態にする命令(” System:REMOTE¥r¥n”)が送信されます。 その後、Agilent34401Aに計測を指示する命令(“:Measure:Voltage:DC?¥r¥n”)が送信されます。 この命令は、周期的に送信されます。 Agilent34401Aでは、計測を指示する命令を受けると、現在の電圧を計測します。 チャンネル1は、Agilent34401Aが計測した電圧値を受け取るためにデータの受信を行います。 プログラムでは、データの受信を行った時、RT-FIFOを介してLinuxプロセスにデータを送ってい ます。 - 51 - Interface Corporation TUT-0043 Linuxプロセスからストップ指示があると、チャンネル1からリモート状態を解除する命令 (“System:LOCAL¥r¥n”)が送信されます。 チャンネル1の送信処理は、RTLinuxスレッド(my_task)が行っています。 受信処理は、調歩同期通信のドライバモジュールから呼ばれるコールバック関数 (my_recv_callback)が行っています。 Linuxプロセスの仕事は、通信処理を行うRTLinuxモジュールに対する周期処理のスタート指令,ス トップ指令,Agilent34401Aから受信した電圧値のデータをRT-FIFO経由で受け取ることです。 次に、LinuxプロセスとRTLinuxモジュールの関係を、下図に示します。 main ユーザ空間 Linux プロセス(sample3) RTLinux モジュール(module3.o) FIFO_COMMAND カーネル空間 指令 my_handler 破棄 生成 FIFO_THRU_CMD init_module cleanup_module 指令の転送 生成 my_task 破棄 FIFO_RESULT コールバック生成 受信イベント 受信データ my_recv_callback LinuxプロセスとRTLinuxモジュールの相関関係 実は、各々の生成関係は、『34ページ 4.2 周期的なデータの送受信の解説』と全く同じです。 Linuxプロセスからの指示は、RT-FIFOを経由して、my_handler→my_taskの順に流れていきます。 受信結果をmy_recv_callbackからRT-FIFOを経由してLinuxプロセスに返すのも同じです。 Interface Corporation - 52 - TUT-0043 4.4.1 sample2/module2との違い 先のサンプルと比較すると分かりますが、双方のコードは、非常に似ています。 共通定義ファイル(sample2.hとsample3.h),Linuxプロセスのコード(sample2.cとsample3.c)は、コメン ト文等一部が異なるだけで、全く同じと言っても差し支えないでしょう。 また、RTLinuxモジュールのコード(module2.cとmodule3.c)も、内容を吟味すると、非常に構造が 似ていることがわかります。 sample2/module2は2チャンネル間の通信、sample3/module3は計測器の制御で、処理が異なると思 われますが、基本的な部分で類似点が多いため、このように似ています。 4.4.2 RTLinuxモジュールの動き ここでは、RTLinuxモジュール(module3.o)の動きに注目して見てみます。 RTLinuxモジュールでは、周期的な送受信処理を実現するために、init_module関数内で、幾つかリ ソースを生成しています。それを下表に示します。 項 目 内 容 RT-FIFO(FIFO_COMMAND) Linuxプロセスからの指示を受けるためのRT-FIFOです。指示は、ハ ンドラmy_handlerに渡されます。 RT-FIFO(FIFO_THRU_CMD) ハンドラにて、Linuxプロセスから受け取った情報を、周期送信処理 を実現するRTLinuxスレッド(my_task)に渡すためのRT-FIFOです。 RT-FIFO(FIFO_RESULT) コールバック関数(my_recv_callback)にて受け取ったデータを、Linux プロセスに返すためのRT-FIFOです。 ハンドラ(my_handler) Linuxプロセスからの指示を受け取るための処理の入り口です。 RT-FIFO経由で送られた情報は、一旦このハンドラが受け取り、然 る処理に回されます。 RTLinuxスレッド(my_task) 周期送信処理を実現するRTLinuxスレッドです。 Linuxプロセスから送られる指示は、最終的にここに送られ、周期送 信処理を実現します。 - 53 - Interface Corporation TUT-0043 次に、生成された各リソースの相互関係と処理の流れを下図に示します。 Linux プロセス FFO_COMMAND コマンド指示 ハンドラ(my_handler) FIFO_THRU_CMD FIFO_RESULT RTLinux スレッド(my_task) ID_START 受信データ ID_STOP 周期送信 ComRTWrite 関数 ←周期呼び出し HP34401A に命令を送信 コールバック関数(my_recv_callback) 調歩同期通信 ドライバモジュール 受信イベント発生 データ受信 ComRTRead 関数 RTLinuxモジュール内の処理の流れ 基本的な流れは、先のsample2/module2と変わりません。 送受信している処理の中身が、計測器とのやり取りに置き換わっただけです。 (177∼248行目:初期化の処理) RT-FIFOとハンドラの生成部分は変わりがありません。 違いは、オープンしているシリアルポートが2つから1になり(200∼211行目)、通信用パラメータが 少し変化しています(221∼228行目)。 この変化は、接続相手であるAgilent34401Aの通信設定に合わせたものです。 後は、コールバック関数の登録とイベントの値,スレッドの生成まで同じです。 唯一異なる点は、244行目のComRTSetModemStatus関数で、ER信号をONにしていることです。 Agilent34401Aとの通信では、ER信号をONにしないと通信が行えないので、このようにしていま す。 (156∼174行目:ハンドラの処理) ハンドラの処理内容は、先のサンプルと全く同じです。 (100∼153行目:RTLinuxスレッドの処理) RTLinuxスレッドの処理は、基本的には先のサンプルと同じですが、送信内容と、受信用のコール バック関数の連携方法が少々異なります。 送信内容の意味については、Agilent34401Aのマニュアル中のコマンド説明と見比べながら読み進 めることをお奨めします。 Interface Corporation - 54 - TUT-0043 まず、RTLinuxスレッド起動直後、109行目で"*RST¥r¥n"という文字列を、Agilent34401Aに送信し、 Agilent34401Aを初期化させています。 次に、Linuxプロセスからスタート指示が入ると、122∼130行目の処理が行われます。 ここでは、Agilent34401Aに対して"System:REMOTE¥r¥n"を送信し、リモート状態に設定するのと (126行目)、RTLinuxスレッド自身の周期実行時間の設定(128行目)を行っています。 スタートが指示されると、周期的にスレッドが呼び出されます。Linuxプロセスから指示が無い場 合、142∼149行目の処理が実行されます。 ここでは、Agilent34401Aに対して":Measure:Voltage:DC?¥r¥n"を送信し、Agilent34401Aが電圧の計 測を行うよう指示しています。 次のpthread_suspend_np関数は、自スレッドを一旦サスペンド状態にしています。 このサスペンド処理の意味については、受信用コールバック関数の最後の方、95行目の pthread_wakeup_np関数と比較して読まれると良いです。 つまり、ここで一旦サスペンド状態にした後、内部ではAgilent34401Aからのデータを受信し、受 信用のコールバック関数が呼ばれます。 ここで、受信データの取り出し処理が行われた後、pthread_wakeup_np関数で、サスペンド状態に されたRTLinuxスレッドを再び起床させているのです。 RTLinux スレッド 受信用コールバック pthread_suspend_np 関数 Agilent34401A ":Measure:Voltage:DC?¥r¥n" サスペンド 計測 計測した電圧値のデータ pthread_make_periodic_np 関数の設定時間 pthread_wakeup_np 関数 起床 pthread_suspend_np 関数 ":Measure:Voltage:DC?¥r¥n" サスペンド 計測 計測した電圧値のデータ pthread_wakeup_np 関数 起床 RTLinuxスレッドとコールバックとAgilent34401Aの処理関係 - 55 - Interface Corporation TUT-0043 (67∼97行目:コールバック関数の処理) 受信用のコールバック関数の処理は、Agilent34401Aから受け取った電圧値のデータを取り出し、 RT-FIFO経由でLinuxプロセスに送ることにあります。 その後、先に説明したように、スリープ状態のRTLinuxスレッドを95行目のpthread_wakeup_np関 数で起床させています。 (37∼63行目:送信と受信のサブルーチン) 37∼48行目のsend_hp34401a関数と、52∼63行目のrecv_hp34401a関数は、Agilent34401Aに対する送 信と受信のサブルーチンです。 内部ではそれぞれ、ComRTWrite関数とComRTRead関数を用いています。 送受信のエラー判定をまとめるために、このようにしています。 Interface Corporation - 56 - TUT-0043 第5章 デバッグ手法 これまでの章で、RTLinux上での調歩同期通信制御プログラミングはどのようなものかがお分かり 頂けたと思います。この後、プログラミングを行っていく上で、避けては通れないデバッグをサ ポートする機能を説明します。 作成したプログラムは、調歩同期通信ドライバを使用した部分の処理と、調歩同期通信ドライバ を使用しない部分の処理に分かれると思います。調歩同期通信ドライバを使用しない部分につい ては、デバッグのやり方(rtl_printfの説明,gdbの使用方法等)をRTLinuxチュートリアル導入編に記載 していますので、そちらを参照してください。 ここでは、RTLinux 調歩同期通信ドライバを使用した部分の処理について、デバッグ手法を紹介 していきます。 5.1 ドライバデバッグ支援機能を使ってみる GPG-4141のドライバには、デバッグ情報を出力するデバッグ支援機能がついています。ドライバ 組み込み時、rcp4141_debuglevelパラメータにデバッグレベルを指定することでドライバデバッグ 情報が出力されます。 デバッグレベルは以下の5段階があります。 rcp4141_debuglevelパラメータはrcp4141使用時に指定します。rcp4161使用時はrcp4161_debuglevel パラメータを使用してください。 デバッグレベル 機 能 0 デバッグ情報を出力しません 1 関数呼び出しトレース 2 エラー情報 4 I/Oモジュールリソース情報 16 制御信号情報 ドライバ組み込み時にオプションを指定しない場合は、デバッグレベルは0となります。 デバッグ支援機能を使うと、システムの負荷が高くなってしまうため、デバッグ時以外は、この 機能を使用しないようにしてください。 ★デバッグ情報を複数出力する デバッグレベルの値の算術和を取った値を指定することで、複数のデバッグレベルを使用できます。 例)関数呼び出しトレースとエラー情報を出力する場合(‘=’の前後にスペースは要りません) # modprobe rcp4141 rcp4141_debuglevel=3 ← rcp4141 使用時 # modprobe rcp4161 rcp4161_debuglevel=3 ← rcp4161 使用時 - 57 - Interface Corporation TUT-0043 5.1.1 関数呼び出しトレース この関数呼び出しトレースを使用すると、処理の実行状況や関数呼び出し時の引数の値を容易に 知ることができます。では実際にデバッグ支援機能を使用して、さきほど作成したサンプルプロ グラムを動かしてみます。 関数呼び出しトレースを指定して、ドライバを組み込みます。 (‘=’の前後にスペースは必要ありません) # modprobe rcp4141 rcp4141_debuglevel=1 次にシステム内のI/Oモジュール情報の列挙,初期化,終了処理を行うモジュール(「List 3-1 sample1.c」で作成したsample1.o)を組み込んでみます。 # insmod sample1.o 現段階では、ComRTGetPortInformation関数とComRTOpen関数が呼び出されているはずです。それ では、ログを確認します。 #less /var/log/messages … May 21 11:11:47 localhost May 21 11:11:47 localhost (0, [0xc4bcdee4]) May 21 11:11:47 localhost ----May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost May 21 11:11:47 localhost (1, [0xc4bcdee4]) May 21 11:11:47 localhost ----… May 21 11:11:47 localhost (5, [0xc4bcdee4]) May 21 11:11:47 localhost (6, [0xc4bcdee4]) May 21 11:11:47 localhost (7, [0xc4bcdee4]) … (END) kernel: init_module called kernel: rcp4141:ComRTGetPortInformation kernel: ----- Serial Port Information kernel: kernel: kernel: kernel: kernel: kernel: kernel: kernel: kernel: kernel: PortNo:0 VendorID:1147h DeviceID:4141 SubsystemID:0001h RevisionID:0 BoardID:0 ChannelNumber:1 BaseAddress:b800h IRQ No:7 rcp4141:ComRTGetPortInformation kernel: ----- Serial Port Information kernel: rcp4141:ComRTGetPortInformation kernel: rcp4141:ComRTGetPortInformation kernel: rcp4141:ComRTGetPortInformation ログにもComRTGetPortInformation関数,ComRTOpen関数の順番に呼び出されていることが分かり ます。ログ中の[ ]で表されている部分は、引数がポインタの場合の変数のアドレスを示していま す。 次に組み込んだsample1.oを取り外してみます。 #rmmod sample1 Interface Corporation - 58 - TUT-0043 ログを見てみます。ComRTClose関数が呼び出されていることが分かります。 #less /var/log/messages … May 21 11:31:33 localhost kernel: cleanup_module called May 21 11:31:33 localhost kernel: rcp4141:ComRTClose(0) (END) 以上のように、関数が呼ばれた順番にログが残されています。 5.1.2 エラー情報 エラー情報を出力するように設定しておくと、関数のエラーコードよりも詳細なエラー情報を出 力します。 では、エラー情報出力を指定してドライバを組み込みます。 # modprobe rcp4141 rcp4141_debuglevel=2 「List5-1 sample1.c」を以下のように修正してください。 List 5-1 sample1.c修正 12 51 52 53 追加> COMRT_CONFIG cfg; ComRTGetConfig(g_port_no, &cfg); cfg.WordLength = 9; ComRTSetConfig(g_port_no, &cfg); コンパイルしてモジュールを組み込んでください。 # make # insmod sample1.o sample1.o: init_module: 許可されていない操作です Hint: insmod errors can be caused by incorrect module parameters, including invalid IO or IRQ parame ters エラーが出てドライバモジュールを組み込むことができません。 それでは、ログを見ます。 # less /var/log/messages … Jun 18 19:38:57 localhost kernel: rcp4141:ttyST0: ComRTConfig:WordLength parameter error 最終行のメッセージの内容から、引数パラメータの値が不正であることがわかります。 - 59 - Interface Corporation TUT-0043 5.1.3 I/Oモジュールリソース情報 I/Oモジュールのリソースが正常かどうかを見ることにより、ソフトウェアが原因かハードウェア が原因かを知ることができます。では、I/Oモジュールリソース情報を出力させます。 # modprobe rcp4141 rcp4141_debuglevel=4 I/Oモジュールリソース情報は、ドライバを組み込んだ時点で出力されます。 ではログを見ます。 #less /var/log/messages Jun 18 18:22:33 localhost kernel: rcp4141:ttyST0:DeviceID=0x102d, SubsystemID=0x1, RevisionID=0x1 Jun 18 18:22:33 localhost kernel: rcp4141:I/O address=0xb800, Irq=7 Jun 18 18:22:33 localhost kernel: rcp4141:ttyST1:DeviceID=0x102d, SubsystemID=0x1, RevisionID=0x1 Jun 18 18:22:33 localhost kernel: rcp4141:I/O address=0xb400, Irq=7 Jun 18 18:22:33 localhost kernel: PCI-4141 support clocks Jun 18 18:22:33 localhost kernel: 1.8432MHz 7.3728MHz 14.7456MHz 4.9152MHz 4.096MHz 8.0MHz 拡張スロットに実装されているrcp4141が対応しているPCI I/Oモジュールのリソースが表示され ます。 項 目 DeviceID SubsystemID RevisionID I/O address Irq support clocks 内 容 デバイスID サブシステムID リビジョンID I/Oポートアドレス 割り込み番号 サポートする基準クロック この値が異常の場合(I/Oポートアドレスや割り込み番号が0と表示された場合)は、ハードウェアに 問題が発生していることも考えられます。 Interface Corporation - 60 - TUT-0043 5.1.4 制御信号情報 I/Oモジュールの制御信号の状態を確認することができます。 では、制御信号の状態を確認してみましょう。 # modprobe rcp4141 rcp4141_debuglevel=16 sample1.oは、制御信号を制御していないので、Agilent34401Aとの通信制御を行うモジュール「List 4-6 module3.c」で作成したmodule3.oを組み込みます。 このモジュールは、ComRTSetModemStatus関数でER,RS信号を制御しています。 #insmod sample3.o May 21 13:14:30 localhost May 21 13:14:30 localhost May 21 13:14:30 localhost May 21 13:14:30 localhost kernel: kernel: kernel: kernel: init_module called ComRTOpen success!! [port no=0] RS:^ ER:^ ログには「RS:^」と「ER:^」が出力されており、RS信号とER信号が変化したことが記録されてい ます。 記号の意味を以下に示します。 記 号 意 味 ^ ON v OFF - 61 - Interface Corporation TUT-0043 第6章 リファレンス この章では、GPG-4141の関数,構造体,および戻り値の一覧を掲載しています。より詳しい情報は、 Helpを参照してください。 6.1 関数一覧 No. 1 2 区 分 初期化終了 3 4 5 6 7 8 9 10 11 12 13 14 15 16 関数名 ComRTOpen ComRTClose ComRTSetup ComRTGetPortInformation ハード ウェア 通信設定 ComRTSetConfig ComRTGetConfig ComRTWrite ComRTRead ComRTSetModemStatus ComRTGetModemStatus ComRTErrorStatus ComRTSendBreak ComRTFlush ComRTEnableTransmitter ComRTSetEventMask ComRTGetEventMask 送受信 制御信号 その他 イベント 機 能 ポートのオープンを行います。 ポートのクローズを行い、ポートアクセスのた めに使用されていた各種リソースの解放を行 い、以後シリアルポートへのアクセスを禁止し ます。 ポートのオープン/クローズと通信設定を行い ます。 指定したポートのハードウェア情報を取得し ます。 ポートの通信設定を行います。 ポートの通信設定を取得します。 データを送信します。 受信データを取得します。 制御信号の出力状態を変更します。 制御信号の状態を取得します。 通信エラー情報,通信統計情報を取得します。 ブレーク信号を送信します。 送受信バッファをクリアします。 データラインの切り替えを行います。 イベントマスクを設定します。 イベントマスクを取得します。 6.2 戻り値一覧 エラー識別子 なし -ENODEV 値 0 -19 -EINVAL -22 -ENOSPC -28 -ENOSYS -38 Interface Corporation 意 味 対処方法 正常終了 ポートが見つかりません。 指定したポートは存在しないか、 オープン されていません。 設定に間違いがあります。 設定の内容に間違いが無いか、 再度ご確認ください。 送信バッファに空きがあり 送信バッファに空きが生じるまで、 呼び出 ません。 しを待機ください。 現在のモードで、使用でき このAPIを使用できないモードで、APIを ないAPIを呼び出そうとし 使用しようとしています。 モードを切り替 ました。 えてください。 - 62 - TUT-0043 技術資料紹介 弊社では下記の技術資料を提供しております。 詳しくは、弊社Web site(www.interface.co.jp)、または弊社窓口までお問い合わせください。 カタログ PRM-0061 PRM-0062 PRM-0063 CPZカタログ(日本語版) PCIカタログ(日本語版) CSIカタログ(日本語版) チュートリアル TUT-0058 TUT-0056 TUT-0055 TUT-0054 TUT-0053 TUT-0050 TUT-0048 TUT-0044 TUT-0043 TUT-0041 TUT-0040 TUT-0039 TUT-0038 TUT-0037 TUT-0036 TUT-0034 TUT-0033 TUT-0032 TUT-0031 TUT-0030 TUT-0029 TUT-0028 TUT-0027 TUT-0026 TUT-0025 TUT-0024 TUT-0023 TUT-0022 TUT-0021 TUT-0020 TUT-0019 TUT-0018 TUT-0017 TUT-0016 TUT-0015 TUT-0014 TUT-0008 TUT-0007 TUT-0006 TUT-0005 TUT-0004 TUT-0003 TUT-0002 TUT-0001 チュートリアル CPZ拡張ユニット 入門編 チュートリアル XP Embedded OS構築編 チュートリアル 画像入力I/Oモジュール CANチュートリアル モーションコントロールチュートリアル RTLinuxによるモーションコントローラI/Oモジュール制御プログラミング チュートリアル(GPG-7400用) RTLinuxによるメモリンクI/Oモジュール制御プログラミング チュートリアル RTLinuxによるメモリ共有インタフェースI/Oモジュール制御プログラミング チュートリアル RTLinuxによる調歩同期シリアル通信I/Oモジュール制御プログラミング チュートリアル RTLinuxによるGP-IBI/Oモジュール制御プログラミング チュートリアル RTLinuxによるDAI/Oモジュール制御プログラミング チュートリアル RTLinuxによるADI/Oモジュール制御プログラミング チュートリアル RTLinuxによるDIOI/Oモジュール制御プログラミング チュートリアル RTLinuxによるHDLCI/Oモジュール制御プログラミング チュートリアル RTLinuxによるPCI/CompactPCI/CardBus制御入門書(導入編) Visual C++によるPPI入門書 Visual Basicによるメモリ共有インタフェース入門書 Visual C++によるメモリ共有インタフェース入門書 Visual Basicによるメモリンク入門書 Visual C++によるメモリンク入門書 Visual BasicによるHDLC入門書 Visual C++によるHDLC入門書 Visual BasicによるGP-IB入門書 Visual C++によるGP-IB入門書 Visual BasicによるDIO入門書 Visual C++によるDIO入門書 Visual BasicによるDA入門書 Visual C++によるDA入門書 Visual BasicによるAD入門書 Visual C++によるAD入門書 Visual Basicによるモーションコントローラ入門書 Visual C++によるモーションコントローラ入門書 メモリンクを使用した負荷分散システム事例チュートリアル Visual BasicによるPPI入門書 モーションコントロールチュートリアル Microsoft Visual Studio .NET移行ガイド 拡張ユニット チュートリアル(問題解決編) 拡張ユニットチュートリアル(入門編) C(98)/ISA製品からPCI/CompactPCI製品への移行チュートリアル(DOS編) DOSによるLAP-B入門書 DOSによるAD入門書 LinuxによるPCI/CompactPCI/CardBus制御 入門書 PCI-ISAバスブリッジチュートリアル PCI-Cバスブリッジチュートリアル 技術情報資料 初めてのCANインタフェース Linux, リアルタイムLinux移植(SH-4)経験談及び当社の今後の取り組みについて LinuxからPCI/CompactPCII/Oモジュールを制御する方法 ActiveXコントロールによるシステム組み込み技術 CompactPCIへの置き換え+システム構築/移行ガイド MS-DOSからPCI/CompactPCII/Oモジュールを制御する方法 - 63 - Interface Corporation TUT-0043 参考文献 著 者 森 友一郎,薬師 輝久,馬場 秀忠 題 名 RTLinuxリアルタイム処理プログラミング ハンドブック(株式会社秀和システム:2000年) ! 警告 本ドキュメントの一部または全てを弊社の許可なく、複写,複製,転載,電子化することを禁じま す。 Interface Corporation - 64 - TUT-0043 2007年 1月 Ver. 1.5 発行 発行所 〒732-0828 広島県広島市南区京橋町10-21 TEL 082-262-7777 FAX 082-262-5066 定価 ¥2,000 本書の内容の一部または全部を、無断で転載することを禁止します。 本書の内容は、将来予告なく変更することがありますので、予めご了承ください。 © 2002, 2007 Interface Corporation. All rights reserved. サポート体制 本製品についてのお問い合わせは、お客様相談センタで承ります。弊社Web siteのオンライ ンQA(「サポート」→「お客様相談センタ」をクリック)、E-mailまたはフリーダイヤルをご利用く ださい。 お問い合わせ先 <お客様相談センタ> 0120-447213 FAX 0120-458257 TEL (祝日および弊社休業日を除く月~金 AM9:00~PM5:00迄) E-mail [email protected] TUT-0043 Ver. 1.5 Vol. 1/1 www.interface.co.jp RTLinuxによる調歩同期シリアル通信ボード制御プログラミング チュートリアル TUT-0043 Ver. 1.5 www.interface.co.jp
© Copyright 2026 Paperzz