Подтвердить что ты не робот

Чтение/запись в последовательном порту Linux C

Я пытаюсь отправлять/получать данные через порт USB с помощью FTDI, поэтому мне нужно обрабатывать последовательную связь с помощью C/С++. Я работаю над Linux (Ubuntu).

В принципе, я подключен к устройству, которое прослушивает входящие команды. Мне нужно отправить эти команды и прочитать ответ устройства. Обе команды и ответ символы ASCII.

Все работает отлично, используя GtkTerm, но когда я переключаюсь на программирование на С, у меня возникают проблемы.

Здесь мой код:

#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

что я и ожидаю. Только моя программа не работает должным образом, например мое устройство не может получить то, что я на самом деле пишу на порт.

Я пробовал разные вещи и решение, также касающиеся типов данных (я пробовал использовать std::string, например cmd = "INIT \r" или const char), но ничего действительно не работало.

Может кто-нибудь сказать мне, где я ошибаюсь?

Спасибо заранее.

EDIT: Раньше версия этого кода использовала

unsigned char cmd[] = "INIT \n"

а также cmd[] = "INIT \r\n". Я изменил его, потому что команда sintax для моего устройства сообщается как

<command><SPACE><CR>.

Я также пытался избегать флага O_NONBLOCK при чтении, но тогда я блокирую только до бесконечности. Я пробовал использовать select(), но ничего не происходит. Просто для попытки, я создал цикл ожидания до тех пор, пока данные не будут доступны, но мой код никогда не выходит из цикла. Btw, ожидания или usleep() - это то, что мне нужно избегать. Сообщенный - это только отрывок из моего кода. Полный код должен работать в среде реального времени (в частности, OROCOS), поэтому мне не нужна функция спящего режима.

4b9b3361

Ответ 1

Я решил свои проблемы, поэтому я размещаю здесь правильный код, если кому-то нужен подобный материал.

Открытый порт

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;
}

Этот работал у меня. Спасибо всем!

Ответ 2

Некоторые приемники ожидают последовательность EOL, которая обычно представляет собой два символа \r\n, поэтому попробуйте в своем коде заменить строку

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

с

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

Кстати, вышеуказанный способ, вероятно, более эффективен. Нет необходимости цитировать каждого персонажа.

Ответ 3

1) Я добавил a/n после init. то есть запись (USB, "init\n", 5);

2) Дважды проверьте конфигурацию последовательного порта. Там что-то неверно. Просто потому, что вы не используете ^ Q/^ S, или аппаратное управление потоком не означает, что другая сторона этого не ожидает.

3) Скорее всего: Добавьте "usleep" (100000), после записи(). Файл-дескриптор установлен, чтобы не блокировать или ждать, правильно? Сколько времени требуется, чтобы получить ответ перед тем, как вы сможете позвонить read? (Он должен быть получен и буферизироваться ядром через системные прерывания, прежде чем вы сможете прочитать().) Рассматривали ли вы использование select() для ожидания чтения чего-либо() Возможно, с тайм-аутом?

Отредактировано для добавления:

Вам нужны линии 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;