• ベストアンサー

RS232cを用いた送信プログラム

RS232Cで文字列を送信するCプログラムを作成しています、がうまくいきません。OSはLINUX fedora7です。 状況は、 ・RS232対応の測定機器にプログラムを使ってコードを送る  →エラー表示される(何かしら送信はされている?)。 ・確認のためクロスケーブルを用いてPC-PC間で送受信を行う  →何も受信されない(受信プログラムは動作確認済みのものを使用)。 ・ポートやボーレート、パリティ、フロー制御の一致は確認しました。 送信プログラムはThe Linux Serial Programming HOWTOに置かれている受信プログラムのサンプルを基に作成しました。 私のいじったプログラムに問題があるように思っていますが、その点を把握できません。 プログラミングでもそれ以外でも、何か原因に思い当たる節のある方がいましたら、御指導の程よろしくお願いします。 以下にプログラムを記します。 *キーボードから入力した文字列を送信し、eを打ち込むと終了するプログラムのつもりです…(^^; main(){ int fd,res,; struct termios oldtio,newtio; char send[255]; fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {printf("error"); perror(fd); exit(-1); } ********************** termios設定文、省略(serial HOWTO のサンプルそのまま) ********************** while(1){ printf("入力待ち\n"); scanf("%s",send); printf("%s\n",send); if( *send =='e'){ printf("終了"); break; } else{ res = write(fd,send,255); send[res]=0; printf("%s,%d\n", send,res); } } tcsetattr(fd,TCSANOW,&oldtio); }

質問者が選んだベストアンサー

  • ベストアンサー
回答No.4

>・RS232対応の測定機器にプログラムを使ってコードを送る > →エラー表示される(何かしら送信はされている?)。 シリアル通信で何かを受け取る機器は、普通「特定のコマンド」を受け取り、受け取ったコマンドに対し、数バイトのレスポンスを返します。 機器によっては、1度コマンドを送ったら、レスポンスを拾ってあげるか、強制リセットのコマンドを送ってあげるまで、次のコマンドに応答しないのが普通です。 例えば、ある機器では、以下のようにしなければなりません。 1.'\x06'が返って来るまで、'\x16'を1文字づつ送り続ける(強制リセットコードの送信) 2.'\x06'が返って来たら、'I'を送って、'\x06'が返って来るのをまつ(初期化コマンドの送信と応答待ち) 3.'\x06'が返って来たら、'S' '1' '0' '0' '\n'の5文字を送って、'\x06'が返って来るのをまつ(設定コマンドの送信と応答まち) (以下略) このように「機器ごとに決められた手順」に従って送受信をしないと、機器がエラーを起こし「それっきり」になります(強制リセットのコードを送り付けるか、機器の電源を入れ直してやらない限り、2度と応答しません) >PC-PC間での送信では同じPCを用いたのでそこまでの差が出るようにも思われません。 いいえ。昨今のPCの速度から考えると「相対的な速度で言えばRS232Cは信じられないくらい遅い」ので、そこがボトルネックになって「送信のオーバーフロー」と「受信のアンダーフロー」が起きます。 「送信のオーバーフロー」とは「PCが大量に一気にシリアルポートに書き込もうとしたら、通信が遅くて、言われたバイト数の一部しか受け付けられないか、1バイトも受け付けられないで、残りを捨ててしまう状態」を言います。もちろん「何バイト書けたか」を見て、残りを送り直ししないと、ちゃんと全部送信出来ません。 「受信のアンダーフロー」とは「PCが一気に大量にシリアルポートから読もうとしたら、通信が遅くて、言われたバイト数の一部しか受け取れないか、1バイトも受け取れないで、そうこうしているうちにタイムアウトエラーになってしまい、途中までしか受信しなかった状態」を言います。もちろん、こっちが受信を途中で止めたのに相手はまだ送信を続けてるでしょうから、続きをちゃんと全部受信してやらないといけません。 送信PCを「製造工場」、受信PCを「商品倉庫」、RS232Cを「輸送トラック」と考えてみて下さい。 「製造工場」も「商品倉庫」も高性能です。でも「輸送トラック」は遅いです。法定速度(決められたボーレート)で走らないとなりません。 「製造工場で日に2万個製造」していて、商品倉庫は「1日に2万個の商品を仕分けできる」としても「輸送トラックは1日に千5百個しか運べない」としたら? 製造工場では「1日にトラックで運べる数しか製造しちゃダメ」ですし、商品倉庫は「トラックが来なきゃ従業員はやる事無し。本日最後に到着するトラックの荷物を仕分けし終わったら、昼前であっても帰ってよし」です。 このように「製造工場と商品倉庫の性能が同じ」でも「輸送トラックが遅い」と「商品を小出しに送り出す」「商品を小分けで受け取る」と言う処理が必要です。 RS232C通信でもそれは同じで「同じPCを用いたとしても、通信媒体の速度が比べられないほど遅ければ、送信と受信は小分けして処理せねばならず、2つのPCの性能がどうのこうのって話は無意味」です。

238884
質問者

お礼

丁寧な説明ありがとうございます。初心者には助かります。 重ね重ね恐縮ですが疑問点2つあります。 前半部分については、今プロトコルを勉強をしてまして、恐らくHDLC手順や無手順プロトコルと言った話なのではないかと思いますが、それをどのようにプログラムに書き込んだらよいのかがわかりません。 ただ単に制御コードを送りたい情報の前後等に、文字列として送ってやればよいのでしょうか? 後半部の話は、sleep等で待ち時間を調節してやれば良い。という理解でよろしいでしょうか? あまりお手を煩わせるのもなんですので、参考サイトなどで十分ですのでよろしくお願いします。

その他の回答 (4)

  • mintia007
  • ベストアンサー率59% (16/27)
回答No.5

>>ひとつ質問なのですがバッファを一度0x00で埋める理由とはなんなのでしょうか? memset( rData, 0x00, sizeof(rData) ); ←これは、 単に初期化しているだけです。これをしないとバッファの中身が不定値になりますので、変な文字が受信データとして表示される場合があるからです。 rData[0] = 0x00; ←ここの事でしょうか。 受信したデータを表示させるのですが、printf( "%s", rData ); にて%sを使用した文字列表示をさせています。文字列の終端は0x00というお約束ですので、表示をかけたらループでまた表示となりますので再表示させない為に、rData[0]を0x00にする事で、無表示になります。read関数の戻り値で1以上なら表示という条件文を付けても良いのですが、簡潔にする為、単にrData[0]=0x00として無表示にさせているだけです。

238884
質問者

お礼

質問が明確ではなく失礼しました(^^; 無事に理解できました!ありがとうございます!

  • mintia007
  • ベストアンサー率59% (16/27)
回答No.3

USB接続のRS232C(/dev/ttyUSB0)、Vine Linuxで以前作成し上手く送受信しているサンプルです。 9600Bps、8bit、ノンパリ、STOP BIT 1です。 -------------------- #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> int main( void ) { int fd; struct termios tio; int err; char sData[1]; char rData[255]; if ((fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK))<0) { return -1; } memset(&tio, 0x00, sizeof(tio)); tio.c_cflag = B9600 | CS8 | CLOCAL | CREAD; tio.c_iflag = IGNPAR | ICRNL; tio.c_oflag = 0; tio.c_lflag = 0; tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &tio); fcntl(fd, F_SETFL, FNDELAY); sData[0] = ' '; memset( rData, 0x00, sizeof(rData) ); while(1){ // 1キャラクタ送信 err = write( fd, sData, 1); sData[0]++; if( sData[0]>='z' ){ sData[0] = ' '; } // 受信 err = read( fd, rData, sizeof(rData) ); printf( "%s", rData ); // 受信データ表示 rData[0] = 0x00; // 500mSecのWAIT usleep( 500000 ); } close( fd ); } -------------------- ご参考まで。

238884
質問者

お礼

ありがとうございます!!参考にさせてもらいます! ひとつ質問なのですがバッファを一度0x00で埋める理由とはなんなのでしょうか?重ね重ねすみません。

  • 1108435
  • ベストアンサー率43% (94/217)
回答No.2

以前の経験です。それもLinuxではなく(昔懐かし)MS-DOSの時代の話ですから、そのぶんさっぴいでください。 あの時は読み取りだったのですが、一気にread文では読み取れず、ポートから1文字ずつ拾いました。計測器側のデータを準備するスピードとPCの読み取り速度に差異が生じ、一文字拾ったきりポート待ちになってしまいました。同様のことが発生していませんか?PC側で一気に吐き出してしまうと、計測器側で処理しきれずエラーになってしまうというものです。ほかの方も書いていますが、1文字ずつ贈ってみるのも悪くないような気がします。

238884
質問者

お礼

御解答ありがとうございます! 関数を変えて1キャラクタづつ送信してみましたがうまくいきませんでした。また、PC-PC間での送信では同じPCを用いたのでそこまでの差が出るようにも思われません。当面はore100さんの指摘された点を考えていきたいと思います。ありがとうございます!!

  • ore100
  • ベストアンサー率54% (34/62)
回答No.1

どのような、プロトコルで232cの通信を行うのかが不明です。 プロトコルの説明をお願い申し上げます。 ack、nacのコードが返って来ると思いますが、その処理は、ないようです。

238884
質問者

お礼

さっそくの御返答ありがとうございます! 正直を言うとプロトコルの知識が全くなく今初めて調べて、それが必要であることを知りました。 つまり、制御キャラクタを処理するプログラム、及び通信プロトコルと呼ばれる通信の型?(これが前者を包括しているのかもしれませんが)をプログラムに組み込まなければならないということでよいでしょうか? 今からプロトコルの勉強をし直してきたいと思います。ありがとうございます!

関連するQ&A