Принудительная передача всех последовательных данных в C - PullRequest
2 голосов
/ 13 октября 2011

Я столкнулся с проблемой синхронизации с последовательным портом в 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) после вызова для записи, и хотя это иногда работает, я не могу гарантировать, что все сообщение будет передано на 9600, прежде чем я изменю скорость передачи и прочту ответ.Я также попытался очистить буфер с помощью tcflush (fd, TCOFLUSH), но я не хочу отбрасывать какие-либо данные, так что это также не правильный путь.

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

Ответы [ 4 ]

1 голос
/ 13 октября 2011

Почему бы просто не установить передатчик на 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 (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                error ("error %d from tcgetattr: %s", errno, strerror (errno));
                return -1;
        }

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

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

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

1 голос
/ 13 октября 2011

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

0 голосов
/ 13 октября 2011

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

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

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

0 голосов
/ 13 октября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...