Передача бинарных данных через ttyACM

Добрый день,

У меня есть периферийное устройство, которое обменивается данными через USB через виртуальный последовательный порт. Все хорошо работает под Windows с универсальным последовательным драйвером ACM, например: https://www.kernel.org/doc/Documentation/usb/linux-cdc-acm.inf

Под Linux он использует драйверы CDC ACM. Все в системных журналах работает нормально, но связь ведет себя странно. При подключении устройства теряется около 10 байт в начале связи. Далее просто каждая вторая команда принимается ок.

Мои вопросы: 1) Коммуникационный протокол этого устройства не использует ASCII, он двоичный (может случайным образом содержать управляющие символы и т.д...). Должен ли я использовать stty только для настройки скорости, битов данных, стоповых битов и четности, или для двоичной связи необходимо настроить что-то еще? (Чтобы игнорировать управляющие биты в ядре и передавать каждый байт - необработанные данные.)

2) Любая идея, как проверить, правильно ли работают драйверы Linux ACM, или какие другие драйверы следует попробовать для моего устройства CDC ACM?

Спасибо за любую идею!


person JirkaRCK    schedule 14.09.2016    source источник
comment
Вам просто нужно убедиться, что termios (a) правильно настроен для неканонического (он же raw) режима, и ( б) отключено программное управление потоком. Вы совсем не детализированы; какое программное обеспечение вы используете для выполнения этой передачи данных? Почему он не настраивает порт должным образом?   -  person sawdust    schedule 15.09.2016
comment
stty -F /dev/ttyACM0 raw может работать (если программа переноса также не настраивает эти атрибуты). Обратите внимание, что перед параметром raw нет дефиса. Это настройка параметра, а не переключатель. Если вы введете -raw, то вы сведете на нет цель этой команды.   -  person sawdust    schedule 15.09.2016
comment
У меня нет никакого программного обеспечения, я использую echo -ne \xXY в /dev/ttyACMx для записи байта и cat /dev/ttyACM в файл журнала для чтения из порта. Чтобы увидеть полученные данные, я открываю файл журнала через xxd. Настройки выполняются stty. Я думаю, что необработанный параметр может быть решением, спасибо!   -  person JirkaRCK    schedule 15.09.2016
comment
У меня нет программного обеспечения -- Вы используете цифровой компьютер, поэтому должно быть программное обеспечение. echo и cat — это команды оболочки, поэтому программа, которую вы используете, является оболочкой.   -  person sawdust    schedule 15.09.2016
comment
Попробуйте отключить службу ModemManager, например. sudo systemctl stop ModemManager && sudo systemctl disable ModemManager. Это может помешать вашему общению, пытаясь инициализировать устройство как модем. Если это помогает, но вам нужно, чтобы служба работала, есть способ настроить udev, чтобы игнорировать только ваше устройство в ModemManager.   -  person Alexandr Zarubkin    schedule 21.09.2017


Ответы (1)


Linux часто искажает такие вещи, как символы конца строки (0x0A и 0x0D), когда вы пытаетесь отправить их через последовательный порт, что может вызвать проблемы, если они на самом деле являются двоичными данными, а не символами конца строки.

Вот фрагмент от Pololu, который показывает, как правильно настроить последовательный порт, а затем отправить и получить несколько байт. Обратите внимание на ту часть, которая вызывает tcsetattr, в частности.

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#ifdef _WIN32
#define O_NOCTTY 0
#else
#include <termios.h>
#endif

// Gets the position of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
int maestroGetPosition(int fd, unsigned char channel)
{
  unsigned char command[] = {0x90, channel};
  if(write(fd, command, sizeof(command)) == -1)
  {
    perror("error writing");
    return -1;
  }

  unsigned char response[2];
  if(read(fd,response,2) != 2)
  {
    perror("error reading");
    return -1;
  }

  return response[0] + 256*response[1];
}

// Sets the target of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
// The units of 'target' are quarter-microseconds.
int maestroSetTarget(int fd, unsigned char channel, unsigned short target)
{
  unsigned char command[] = {0x84, channel, target & 0x7F, target >> 7 & 0x7F};
  if (write(fd, command, sizeof(command)) == -1)
  {
    perror("error writing");
    return -1;
  }
  return 0;
}

int main()
{
  const char * device = "/dev/ttyACM0";  // Linux
  int fd = open(device, O_RDWR | O_NOCTTY);
  if (fd == -1)
  {
    perror(device);
    return 1;
  }

#ifdef _WIN32
  _setmode(fd, _O_BINARY);
#else
  struct termios options;
  tcgetattr(fd, &options);
  options.c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF);
  options.c_oflag &= ~(ONLCR | OCRNL);
  options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  tcsetattr(fd, TCSANOW, &options);
#endif

  int position = maestroGetPosition(fd, 0);
  printf("Current position is %d.\n", position);

  int target = (position < 6000) ? 7000 : 5000;
  printf("Setting target to %d (%d us).\n", target, target/4);
  maestroSetTarget(fd, 0, target);

  close(fd);
  return 0;
}

Вы можете сделать то же самое с утилитой командной строки stty.

person David Grayson    schedule 14.09.2016