[FreeBSD-users-jp 94050] Re: umodem で PICC18F2550 にアクセスすると不安定

2012-11-13 スレッド表示 Koh-ichi Ito
いとうです。何度もすみません。

From: Koh-ichi Ito k...@kkdlabs.jp
Subject: [FreeBSD-users-jp 94048] Re: umodem で PICC18F2550 にアクセスすると不安定
Date: Mon, 12 Nov 2012 19:38:44 +0900

 デバイスドライバを書く側から見たら、いつデータが来るか
 わからないときは「100回以上回しても平気」なほうの構造
 にするか、センサ側がデータ送出指令を待つようにすべきだ
 と思いますが、運がよければ、最初のプログラム構造でも、
 sleep() を close() の後に移動すると(完全ではないにして
 も)改善されるかもしれないですね。
 
 平林 浩一
 
 実は本番の(というほど大げさな話じゃないんですけど :-)プログラムは Perl
 じゃなくって C で書いているんですが、ともかく close()の後ろに sleep()を
 入れると改善されることはわかりましたので、がんばってみます。

C のコードでは、close()の後ろに sleep()を入れてみても現象は変わらず、
read()の前の select()がタイムアウトし、そのまま再実行すると、select()が
タイムアウトすらせず返ってこなくなります。^C を打鍵するとプロンプトは返っ
てきます。

長くて恐縮ですが試したコードを載せます。PAUSE の値は1でダメだったので5
にしてみましたが、やはりダメでした。

#include err.h
#include fcntl.h
#include stdio.h
#include stdlib.h
#include string.h
#include termios.h
#include unistd.h
#include sys/ioctl.h
#include sys/select.h
#include sys/stat.h
#include sys/types.h
#include sys/uio.h

#define PAUSE   5

main()
{
int i,
fd;
size_t  rlen,
tlen;
ssize_t n;
char*tmesg,
rmesg[16];
fd_set  fds;
struct termios  tios;
struct timeval  timeout;

for (i = 0; ; i++) {
printf(i = %d\n, i);
if ( (fd = open(/dev/ttyU0, O_RDWR | O_NONBLOCK) )  0) {
err(1, open());
}
if (ioctl(fd, TIOCEXCL)  0) {
err(1, ioctl(TIOCEXCL));
}
if (tcflush(fd, TCOFLUSH)  0) {
err(1, tcflush(TCOFLUSH));
}
if (tcflush(fd, TCIFLUSH)  0) {
err(1, tcflush(TCIFLUSH));
}
if (tcgetattr(fd, tios)  0) {
err(1, tcgetattr());
}
tios.c_cflag |= (CREAD | CLOCAL);
tios.c_iflag |= IXANY;
tios.c_iflag = ~(ISTRIP | ICRNL | INLCR | IXON | IXOFF
| IMAXBEL);
tios.c_lflag |= ICANON;
tios.c_lflag = ~(ECHO | ECHONL);
tios.c_oflag = ~OPOST;
if (tcsetattr(fd, TCSADRAIN, tios)  0) {
err(1, tcsetattr());
}
FD_ZERO(fds);
FD_SET(fd, fds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
tmesg = BARH\r\n;
tlen = strlen(tmesg);
if ( (n = select(fd + 1, NULL, fds, NULL, timeout) )  0) {
err(1, select() for write());
} else if (n == 0) {
fprintf(stderr, select() for write() timeout.\n);
close(fd);
sleep(PAUSE);
exit(1);
}
if (write(fd, tmesg, tlen)  0) {
err(1, write());
}
FD_ZERO(fds);
FD_SET(fd, fds);
if ( (n = select(fd + 1, fds, NULL, NULL, timeout) )  0) {
err(1, select() for read());
} else if (n == 0) {
fprintf(stderr, select() for read() timeout.\n);
close(fd);
sleep(PAUSE);
exit(1);
}
bzero(rmesg, 16);
if ( (n = read(fd, rmesg, 15) )  0) {
err(1, read());
close(fd);
sleep(PAUSE);
exit(1);
}
printf(rmesg: %s\n, rmesg);
close(fd);
sleep(PAUSE);
}
}

-
kkdlabs.jp, featuring Koh-ichi Ito as just another DNS freak in town.


[FreeBSD-users-jp 94051] Re: umodem で PICC18F2550 にアクセスすると不安定

2012-11-13 スレッド表示 Kouichi Hirabayashi
2回分まとめますが、まず、前回の疑問

 「いつデータがくるかわからない」という状況ではない

は、よくあるパターンだと思って、私が相手のデバイスのマニ
ュアルを読まずに、perl のコーディングだけ見て書いてしまっ
たのが原因です。

open(), close() はハードウェアを初期化してしまいますから、
open() で行うたくさんの仕事がハードウェアで全て完了するま
で、接続相手のデバイスと正常なコンタクトができません。ま
た、相手側のデバイスが open() の後、一定時間しないとコマ
ンドを受け付けられる状態にならないケースもあります。

次に、今回の C のプログラムのほうは、

  tios.c_lflag |= ICANON;

でしたら、

  tios.c_cc[VEOL] = '\n';
  tios.c_cc[VMIN] = 0;
  tios.c_cc[VTIME] = 100;

とか、デリミタなどの待機終了条件を指定しておかないと、運任
せになります。

あと、相手のデバイスの仕様書を見ると、単純なコマンド・レス
ポンス方式ですから、本来 select() のような入出力多重化機構
は要らないわけで、素直に \n をデリミタにした canonical 入力
(ICANON) でタイミングを合わせるのが一番ではないでしょうか?

つまり、

  open();   // デリミタ LF の CANONICAL 入力
  sleep();  // 相手側で必要なら
  write();  // コマンド出力
  read();   // データ入力
  close();

の繰り返しになります。気圧計でしたら、プログラム中でのループ
でなく、cron からの起動になると思いますが。

平林 浩一