Чтение из com port с++

462
01 января 2018, 11:32

Есть код

int open_port()
{
  int fd; /* Файловый дескриптор для порта */
  fd = open("/dev/ttyUSB0", O_RDWR  | O_NOCTTY | O_NDELAY);
  if (fd == -1)
  {
   /* Не удалось открыть порт */
   /*  perror() выводит сообщение (в стандартный поток ошибок), затем
       описание последней случившейся ошибки (на основании errno) */ 
    perror("open_port: Unable to open /dev/ttyS0 - ");
  }
  else
  {
   /* Порт успешно открыт */
   /* fcntl выполняет одну из различных дополнительных операций над файловым дескриптором fd */
   /* F_SETFL - Устанавливает часть флагов, относящихся к состоянию файла, 
                согласно значению, указанному в аргументе arg. (0) */ 
    fcntl(fd, F_SETFL, 0);
    printf("Ok - Port open!\n");
  }
  return (fd);
}
int main(int argc, char **argv)
{
    system("clear");
    int fd;
    fd = open_port();
    /* ----------------- Запись ------------------*/
    int n; /* Количество посланных байт(write) */
    char buf[] = "qwertyuiop"; /* Буффер ввода */
    int length; /* Длина строки ввода (Количество байт) */
    length = strlen(buf);
    n = write(fd, buf, length);
    /* write возвр. -1 при ошибке */
    /* stderr - стандартный поток ошибок */
    if (n == -1) 
    {
      fputs("Записать не удалось!\n", stderr);
    }
    else
    {
      printf("Успешно записано %d байт(а) \n", n);
    }

    /* ------------------ Чтение ---------------- */
    int k,i;
    fcntl(fd, F_SETFL, FNDELAY);
    char buf1[10];
    k = read(fd, buf1, length);
    if (k == -1)
    {
      printf("buffer = %s, Kod = %d \n", buf1, k);
      printf("Ошибка чтения!\n");
    }
    else
    {
      printf("buffer = %s, Kod = %d \n", buf1, k);
    }
    close(fd);
    printf("Ok - Port Close!\n\n");
    return 0;
}

Который должен читать и писать в компорт. Пишет он исправно, а читать из него не получается. Код это не совсем мой, и в теме разбираюсь слабо, по-этому у меня 2 вопроса, первый как читать из ком порта и 2-й где почитать как это работает. Ком порт у меня сейчас замкнут сам на себя TXD на RXD через миником "это" работает.

Answer 1

По-умолчанию терминал (и последовательный порт в частности) открываются в режиме подходящем для телетайпа (что видно из названия tty) или терминала. Отсюда две особенности, которые больше всего мешают работать данному коду:

  1. Терминал открывается в «каноническом» режиме, что означает, что read()'у буфер будет выдаваться только целыми строками или чанками не менее 4096 байт.
    Решения:
    • Добавить '\n' в buf: char buf[] = "qwertyuiop\n";
    • Настроить порт в обычном режиме.
  2. Ожидаемый вывод будет только один раз. т.к. по-умолчанию для терминала включено эхо и при каждом чтении из буфера прочитанная строка снова отправляется в буфер записи (а т.к. порт замкнут, то потом она через него прийдёт в буфер чтения). Чтобы от этого избавиться придётся настраивать порт.

Также есть ещё одна проблема с неблокирующим чтением: ко времени, когда процесс начинает читать, данные из системного буфера просто не успевают передаться в порт. т.к.а скорость порта по-умолчанию всего 9600 бод, а попытка чтения происходит почти моментально.
Решения:

  • Открыть порт с O_SYNC
  • Отказаться от неблокирующего read'а, убрав fcntl(fd, F_SETFL, FNDELAY);
  • Добавить задержку прежде, чем читать usleep ((1000000L/9600L+1)*8*n*2) (осторожно, время примерное. в реальном коде полагаться на него не стоит, хотя произвольное значение в числе взято только удвоение задержки)

Настраивать порт можно примерно как в ответе @eri с помощью tcsetattr(), но я советую разобраться во флагах (2/3 в коде мёртвый груз, который ИМХО только отвлекает от сути) и внимательно прочитать man 3 tcsetattr.

Answer 2

Как Вы поняли что он пишет исправно? Если порт открыт другой программой параллельно, то читаться он будет только в одну из них.

И нужно установить параметры скорости и битов на порту.

Вот например:

Копипаста из моего кода, которая скопипасчена (найду откуда-напишу ссылку)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
void setport(int fd){

    struct termios tty;
    memset (&tty, 0, sizeof tty);
    cfsetospeed (&tty, 115200);
    cfsetispeed (&tty, 115200);
            tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= 0;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;
        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                fprintf (stderr,"error %d from tcsetattr", errno);
        }
}
int main(int argc, char **argv){
......
    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd<0) {
        return -11;
    }
    setport(fd);
......
}
READ ALSO
Чтение файла с помощью ifstream

Чтение файла с помощью ifstream

С текстом все еще хорошоИспользую:

242
Заполнение и поиск в двумерном массиве

Заполнение и поиск в двумерном массиве

Всем привет, работаю с двумерным массивом таким образом заполняю:

232
Админ. права при выключенном UAC

Админ. права при выключенном UAC

Есть программа, которой нужны админские привилегии (например, доступ в папку "C:\Program Files")Прога запускается под обычным пользователем

228
Проблемы со статичными массивами NullPointerException

Проблемы со статичными массивами NullPointerException

При открытии главного активити считываю строки из текста потом забиваю их в static String [] в отдельном классе от активитиГлавный активити делает...

236