Последовательное чтение в C не возвращает данных

Я использую termios в C написать простую программу для записи в последовательный порт и чтения возвращаемых данных. Связь с устройством по последовательной линии заканчивается возвратом каретки. Программа проста и на данный момент выглядит так:

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{

  struct termios s_alicat;
  int tty_fd, count;

  // Ports connected in USB as sudo
  if ((tty_fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
  {
    printf("Line failed to open with %d\n", tty_fd);
    return -1;
  }
  else
  {
    printf("fd is %d\n", tty_fd);
  }

  s_alicat.c_cflag = B19200 | CS8 | CREAD | CLOCAL;

  //No parity 8N1:
  s_alicat.c_cflag &= ~PARENB;
  s_alicat.c_cflag &= ~CSTOPB;
  s_alicat.c_cflag &= ~CSIZE;

  s_alicat.c_iflag = IGNPAR | ICRNL; // Ignore parity errors

  //Disable hardware flow control
  s_alicat.c_cflag &= ~CRTSCTS;

  //Disable software flow control
  s_alicat.c_iflag &= ~(IXON | IXOFF | IXANY);

  //Raw output
  s_alicat.c_oflag &= ~OPOST;

  //Raw input
  s_alicat.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

  tcflush(tty_fd, TCIFLUSH);
  tcsetattr(tty_fd, TCSANOW, &s_alicat);


  unsigned char transmit[2] = "C\r";
  if ((count = write(tty_fd, &transmit, 2)) < 0)
  {
    printf("Failed to write to device");
  }

  printf("Transmited %d characters\n", count);

  usleep(500000);

  unsigned char receive[255];

  if ((count = read(tty_fd, &receive, 255) < 0))
  {
    printf("Error receiving text %d", count);
  }
  else
  {
    if (count == 0)
    {
      printf("No data read in...\n");
    }
    else
    {
      printf("%s", receive);
    }
  }
  printf("Closting port...\n");
  close(tty_fd);

  return 0;
}

Так:

  • Порт открывается правильно
  • Я могу записать данные (физически вижу, что они проходят через линию с помощью светодиодов, которые загораются при передаче и получении данных)
  • Чтение возвращается с 0 символов

Если я отправлю ту же команду (C\r) через другую программу, настроенную с 19.2, 8N1, без контроля потока, я получаю следующую строку (или что-то очень похожее) обратно

C\ S +012.05\ S +031.73\ S +000.01\ S + 000,01\s010.24\s\s\s\s\ SAIR \ г

Итак, что я здесь делаю не так? Это как-то связано с тем, что IO прекращает возврат каретки? Или моя конфигурация неверна?

РЕДАКТИРОВАТЬ: Итак, кажется, что если я смотрю устройство персонажа (/dev/ttyUSB0Я действительно могу видеть возвращающиеся данные - см. Снимок ниже. Итак, похоже, моя проблема в чтении и получении информации из буфера чтения.

введите описание изображения здесь

2 ответа

Решение

Или моя конфигурация неверна?

Да.
Проблема в том, что ваша программа использует неблокирующий режим

  open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY)

и объединяет это с неканоническим режимом

  s_alicat.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

даже если вы утверждаете, что ввод - это строки, оканчивающиесяна (a) возврат каретки.

"Чтение возвращается с 0 символами" является предсказуемым и нормальным для неблокирующих необработанных чтений (таких как ваша конфигурация), когда просто нет доступных данных. Смотрите этот ответ для полной информации.

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

fcntl(tty_fd, F_SETFL, 0);    /* set blocking mode */

Смотрите это для объяснения.

Что касается конфигурации termios, ваша программа имеет серьезную ошибку: она использует структуру termios s_alicat uninitialized.
Правильный метод - использовать tcgetattr ().
Обратитесь к разделу "Настройка режимов терминала" и " Руководство по последовательному программированию" для операционных систем POSIX.

#include <errno.h>
#include <string.h>

...

  if (tcgetattr(tty_fd, &s_alicat) < 0) {
      printf("Error from tcgetattr: %s\n", strerror(errno));
      return -1;
  }
  cfsetospeed(&s_alicat, B19200);
  cfsetispeed(&s_alicat, B19200);

  s_alicat.c_cflag |= (CLOCAL | CREAD);
  s_alicat.c_cflag &= ~CSIZE;
  s_alicat.c_cflag |= CS8;         /* 8-bit characters */
  s_alicat.c_cflag &= ~PARENB;     /* no parity bit */
  s_alicat.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */

  s_alicat.c_iflag |= ICRNL;       /* CR is a line terminator */
  s_alicat.c_iflag |= IGNPAR;      // Ignore parity errors

  // no flow control
  s_alicat.c_cflag &= ~CRTSCTS;
  s_alicat.c_iflag &= ~(IXON | IXOFF | IXANY);

  // canonical input & output
  s_alicat.c_lflag |= ICANON;
  s_alicat.c_lflag &= ~(ECHO | ECHOE | ISIG);
  s_alicat.c_oflag |= OPOST;

  if (tcsetattr(tty_fd, TCSANOW, &s_alicat) != 0) {
      printf("Error from tcsetattr: %s\n", strerror(errno));
      return -1;
  }

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

write(tty_fd, &transmit, 2)
read(tty_fd, &receive, 255)

должно быть просто соответственно

write(tty_fd, transmit, 2)
read(tty_fd, receive, 255)

Системный вызов read () не возвращает и не сохраняет строку, однако ваша программа предполагает, что это происходит.
Код (с исправленной ошибкой предшествования) должен быть:

if ((count = read(tty_fd, receive, sizeof(receive) - 1)) < 0) {
    printf("Error receiving text %s\n", strerror(errno));
} else {
    receive[count] = 0;  /* terminate string */
    printf("Received %d: \"%s\"\n", count, receive);
}

Обратите внимание, что длина запроса на чтение на единицу меньше размера буфера, чтобы зарезервировать пространство для завершающего нулевого байта.


ДОПОЛНЕНИЕ

Ваш код имеет ошибку приоритета / скобок, которая перенесена в мой код. Оскорбительное заявление:

if ((count = read(tty_fd, &receive, 255) < 0))

Присвоение переменной count должно быть кодом возврата из системного вызова read (), а не вычислением логического выражения. read() < 0,
Без правильных круглых скобок сравнение выполняется в первую очередь, поскольку оператор меньше чем имеет более высокий приоритет, чем оператор присваивания.
Эта ошибка приводит к тому, что счетчику при хорошем чтении (т. Е. Положительном ненулевом коде возврата) всегда присваивается значение 0 (т. Е. Целочисленное значение для false).


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

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

Проблема в том, что ваша программа использует неблокирующий режим

Это на самом деле не проблема, и это именно то, что я хочу. Я не хочу блокировать чтение, так как это может привести к зависанию программы, если устройство не отвечает. Этот код предназначен только для проверки работоспособности моего последовательного интерфейса. Итак, установка флагов в 0 с fcntl(tty_fd, F_SETFL, 0) это то, что я не хочу делать

Назначение переменной count должно быть кодом возврата из read() Системный вызов

Я думаю, что я следую за вами здесь, но формулировка странная. Да, круглые скобки были размещены неправильно - спасибо, что указали на это. Но под "кодом возврата" я предполагаю, что вы имеете в виду -1 или количество полученных байтов? На основании вашего обновленного ответа я предполагаю, что так.

Итак, вот окончательный код. Я считаю, что я включил в него обратную связь, которую вы предоставили. Не стесняйтесь предоставлять больше, если вы видите что-то странное. Возврат из функции, когда этот прогон выглядит

root@cirrus /h/m/D/s/F/C/c/src# ./main
fd is 3
count is 49
String is C +012.17 +030.85 +000.00 +000.00 010.24     Air
Closing port...

Код:

#include <stdlib.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define PORT "/dev/ttyUSB0"

int main(void)
{

  struct termios s_alicat;
  int tty_fd, count;
  char receive[255], transmit[2];

  // Ports connected in USB as sudo
  if ((tty_fd = open(PORT, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
  {
    printf("Line failed to open with %d\n", tty_fd);
    return -1;
  }
  else
  {
    printf("fd is %d\n", tty_fd);
  }

  if (tcgetattr(tty_fd, &s_alicat) < 0)
  {
    printf("Error from tcgetattr: %s\n", strerror(errno));
    return -1;
  }

  cfsetospeed(&s_alicat, B19200);
  cfsetispeed(&s_alicat, B19200);

  // Set up receiver and set to local mode
  s_alicat.c_cflag |= (CLOCAL | CREAD | CS8);

  s_alicat.c_iflag |= IGNPAR | ICRNL; // Ignore parity errorss

  tcflush(tty_fd, TCIFLUSH); //discard file information not transmitted
  if (tcsetattr(tty_fd, TCSANOW, &s_alicat) != 0)
  {
    printf("Error from tcsetattr: %s\n", strerror(errno));
    return -1;
  }

  // Clear the port before kicking off communications
  strcpy(transmit, "\r\r");
  write(tty_fd, transmit, 2);

  strcpy(transmit, "C\r");

  if ((count = write(tty_fd, transmit, 2)) < 0)
  {
     printf("Failed to write to device");
  }

  int j = 0;
  count = 0;

  /* Attempt to read data at most 3 times if there is no data 
   * coming back.
   */

  while (count == 0 && j < 3)
  {

    usleep(100000);
    if ((count = read(tty_fd, receive, sizeof(receive) - 1)) < 0)
    {
      printf("Error receiving text %d", count);
    }
    else
    {
      printf("count is %d\n",  count);
      receive[count] = 0;
      printf("String is %s", receive);
    }
    j++;
  }

  printf("Closing port...\n");
  int p = 0;
  if ((p = close(tty_fd)) < 0)
  {
    printf("Port failed to close %d\n", p);
    return -1;
  }

  return 0;
}

редактировать

Добавлена ​​явная настройка скорости передачи через cfsetospeed а также cfsetispeed,

Другое Править

Избавился от лишнего tcgetattr вызов.

Другие вопросы по тегам