Принудительная передача всех последовательных данных на C

#c #serial-port

#c #последовательный порт

Вопрос:

Я столкнулся с проблемой синхронизации с последовательным портом в C. Короче говоря, я отправляю команду на устройство с одной скоростью передачи (скажем, 9600 бод) и ожидаю от него ответа с другой (скажем, 38400).). Я не контролирую этот протокол. Прототип ситуации будет выглядеть следующим образом:

  open() serial port
 set baud rate to 9600 using termios struct
 write() command
 change baud rate to 38400 using termios struct
 read() response
  

Я столкнулся с проблемой, когда устройство не понимает отправленную мной команду, потому что скорость передачи данных меняется на 38400 до завершения записи. Я почти уверен, что запись работает нормально, потому что она возвращает количество байтов, которые я намереваюсь записать. Я попытался добавить usleep (100000) после вызова write, и хотя иногда это работает, я не могу гарантировать, что все сообщение будет передано со скоростью 9600, прежде чем я изменю скорость передачи и прочитаю ответ. Я также пытался очистить буфер с помощью tcflush (fd, TCOFLUSH), но я не хочу отбрасывать какие-либо данные, так что это тоже неправильный способ.

Как я могу принудительно записать все последовательные данные и гарантировать, что они будут записаны до следующего вызова для изменения скорости передачи данных на 38400? Похоже, это происходит на уровне чипа, так что моя единственная надежда включить библиотеки FTDI (это чип FTDI) и получить доступ к регистрам, чтобы увидеть, когда данные будут переданы? Спасибо.

Комментарии:

1. Зачем вам нужно менять скорость передачи данных взад и вперед? Согласно руководству APUE, вы можете установить входные и выходные данные в бодах отдельно с cfsetispeed() помощью и cfsetospeed() .

2. К сожалению, это только первая часть рукопожатия. После этого небольшого лакомого кусочка я отправляю и получаю команды от устройства с еще более высокой скоростью передачи данных (и он остается на этом уровне до конца «сеанса», когда мой сервер работает). cfsetspeed() и cfsetospeed(), возможно, сработали бы, если бы меня интересовали только эти две команды. Я мог бы найти какой-то способ заставить это работать, используя ваше предложение, спасибо.

Ответ №1:

Используйте WaitCommEvent с маской, которая включает EV_TXEMPTY, чтобы дождаться отправки сообщения драйвером.

Комментарии:

1. Извините, я должен был быть более ясным в своем первоначальном сообщении. Я использую POSIX C, уверен, что это не поддерживается.

2. Я почти уверен, что, основываясь на комментариях о termios, платформа — это UNIX, а не Windows.

3. А в UNIX нет ничего эквивалентного?

Ответ №2:

Почему бы просто не установить для передатчика значение 9600, а для приемника — 38400? Большинство аппаратных средств с последовательным портом поддерживают это.

 // fd = file descriptor of serial port.  For example:
//  int fd = open ("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC);
int
somefunction (int fd)  
{
        struct termios tty;
        memset (amp;tty, 0, sizeof tty);
        if (tcgetattr (fd, amp;tty) != 0)
        {
                error ("error %d from tcgetattr: %s", errno, strerror (errno));
                return -1;
        }

        cfsetospeed (amp;tty, B9600);    // set tty transmitter to 9600
        cfsetispeed (amp;tty, B38400);   // set receiver to 38400

        if (tcsetattr (fd, TCSADRAIN, amp;tty) != 0)  // waits until pending output done before changing
        {
                error ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}
  

Я внес изменения в свой код, чтобы использовать TCSADRAIN вместо TCSANOW , чтобы изменение скорости не происходило до тех пор, пока не будут отправлены все ожидающие вывода.

Ответ №3:

Вы должны использовать tcdrain() или TCSADRAIN необязательное действие для tcsetattr() вместо tcflush

Комментарии:

1. Я только что проверил таблицу данных FTDI, и, к сожалению, в ней нет отдельных генераторов бод.

2. Ах, tcdrain(). Я знал, что что-то подобное существует, я некоторое время пытался просмотреть заголовок termios для чего-то подобного, но безрезультатно. Я не уверен, что это сработает, поскольку, похоже, это на аппаратном уровне, но спасибо, я попробую это и отчитаюсь.

Ответ №4:

Я использовал Windows FTDI API и обнаружил, что вся их модель раздражающе асинхронна. Я бы ожидал, что с обычным последовательным портом вы могли бы посчитать длину вашего сообщения (в битах, включая начало, остановку, четность) и скорость передачи в бодах и иметь разумную оценку того, как долго ждать, пока ваше сообщение не будет передано. Однако с частью FTDI вы имеете дело с задержками USB и неизвестными очередями в самой части FTDI. Если ответы вашего устройства поступают менее чем за 1 мс, возможно, даже невозможно надежно переключить FTDI между фазами TX и RX.

Можно ли подключить RX к другому UART? Это значительно упростило бы вашу проблему.

Если нет, вы можете рассмотреть возможность использования специального кабеля, который соединяет ваш TX обратно с вашим RX, чтобы вы могли видеть, как ваше сообщение отправляется, прежде чем прервать его. С помощью диода вы можете избежать того, чтобы устройство также видело свои собственные передачи. Дуплекс 3/4?