web-dev-qa-db-ja.com

Linux上のCのシリアルポートの読み取りと書き込み

FTDIを使用してUSBポート経由でデータを送信/受信しようとしているため、C/C++を使用してシリアル通信を処理する必要があります。私はLinux(Ubuntu)に取り組んでいます。

基本的に、着信コマンドをリッスンしているデバイスに接続しています。これらのコマンドを送信し、デバイスの応答を読み取る必要があります。コマンドと応答は両方ともASCII文字です。

GtkTermを使用するとすべてが正常に機能しますが、Cプログラミングに切り替えると問題が発生します。

これが私のコードです:

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions

/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );

/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;        // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;       // no flow control
tty.c_lflag     =   0;          // no signaling chars, no echo, no canonical processing
tty.c_oflag     =   0;                  // no remapping, no delays
tty.c_cc[VMIN]      =   0;                  // read doesn't block
tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout

tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
tty.c_iflag     &=  ~(IXON | IXOFF | IXANY);// turn off s/w flow ctrl
tty.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tty.c_oflag     &=  ~OPOST;              // make raw

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );

if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}

/* *** WRITE *** */

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};
int n_written = write( USB, cmd, sizeof(cmd) -1 );

/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '\0', sizeof buf);

/* *** READ *** */
int n = read( USB, &buf , sizeof buf );

/* Error Handling */
if (n < 0)
{
     cout << "Error reading: " << strerror(errno) << endl;
}

/* Print what I read... */
cout << "Read: " << buf << endl;

close(USB);

起こるのは、read()が0を返す(バイトがまったく読み込まれない)か、タイムアウトになるまでブロックする(VTIME)ことです。 write()は何も送信しないため、これが起こると仮定しています。その場合、デバイスはコマンドを受信せず、応答を受信できません。実際、読み取りでプログラムがブロックされている間にデバイスをオフにすると、実際に応答が得られました(デバイスはシャットダウン中に何かを送信します)。

奇妙なことは、これを追加することです

cout << "I've written: " << n_written << "bytes" << endl; 

write()呼び出しの直後に、以下を受け取ります。

I've written 6 bytes

それはまさに私が期待するものです。 私のデバイスは実際に書いているものを受け取ることができませんのように、私のプログラムだけが正常に動作しません。

データ型に関してもさまざまなことと解決策を試しました(cmd = "INIT \r"const charなどのstd :: stringを使用してみました)、実際には何も機能しませんでした。

誰かが私が間違っている場所を教えてもらえますか?

前もって感謝します。

編集:以前使用されていたこのコードのバージョン

unsigned char cmd[] = "INIT \n"

また、cmd[] = "INIT \r\n"。デバイスのコマンドsintaxが次のように報告されるため、変更しました。

<command><SPACE><CR>

また、読み取り時にO_NONBLOCKフラグを回避しようとしましたが、その後は永久にブロックするだけです。 select()を使用してみましたが、何も起こりません。試してみるために、データが使用可能になるまで待機ループを作成しましたが、コードはループを終了しません。ところで、待つかusleep()は避ける必要があるものです。報告されたものは私のコードの抜粋にすぎません。 完全なコードはリアルタイム環境で動作する必要があります(具体的にはOROCOS)ですので、スリープのような機能は本当に必要ありません。

40
Lunatic999

私は自分の問題を解決したので、誰かが同様のものを必要とする場合に備えて、ここに正しいコードを投稿します。

ポートを開く

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

パラメータの設定

struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}

書き込み

unsigned char cmd[] = "INIT \r";
int n_written = 0,
    spot = 0;

do {
    n_written = write( USB, &cmd[spot], 1 );
    spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);

バイトごとにバイトを書き込む必要はありませんでした。また、int n_written = write( USB, cmd, sizeof(cmd) -1)は正常に機能しました。

最後に、読み取り

int n = 0,
    spot = 0;
char buf = '\0';

/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);

do {
    n = read( USB, &buf, 1 );
    sprintf( &response[spot], "%c", buf );
    spot += n;
} while( buf != '\r' && n > 0);

if (n < 0) {
    std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
    std::cout << "Read nothing!" << std::endl;
}
else {
    std::cout << "Response: " << response << std::endl;
}

これは私のために働いた。皆さん、ありがとうございました!

66
Lunatic999

1)initの後に/ nを追加します。すなわちwrite(USB、 "init\n"、5);

2)シリアルポート構成を再確認します。オッズはそこに間違っているものです。 ^ Q/^ Sまたはハードウェアフロー制御を使用しないからといって、反対側がそれを期待していないわけではありません。

3)ほとんどの場合: "usleep(100000);の後にwrite()。file-descriptorはブロックまたは待機しないように設定されていますか?readを呼び出す前に応答を返すまでにどれくらい時間がかかりますか?(受信してバッファリングする必要があります) read()する前に、システムハードウェア割り込みを介して、カーネルによって。)select()を使用することを検討したかread()に何かを待つ?おそらくタイムアウトを伴う?

追加して編集:

DTR/RTSラインが必要ですか?反対側にコンピューターデータを送信するよう指示するハードウェアフロー制御?例えば.

int tmp, serialLines;

cout << "Dropping Reading DTR and RTS\n";
ioctl ( readFd, TIOCMGET, & serialLines );
serialLines &= ~TIOCM_DTR;
serialLines &= ~TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
usleep(100000);
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
sleep (2);

cout << "Setting Reading DTR and RTS\n";
serialLines |= TIOCM_DTR;
serialLines |= TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
0
guest

一部のレシーバーはEOLシーケンスを想定しています。これは通常2文字\r\nであるため、コードで行を置き換えてみてください

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};

unsigned char cmd[] = "INIT\r\n";

ところで、上記の方法はおそらくより効率的です。すべての文字を引用する必要はありません。

0
radarhead