开发者

Force transmission of all serial data in C

I am running into a timing issue with a serial port in C. Long story short, I send a command to a device at one baud rate (say 9600) and expect a response from it at another (say 38400). I have no control over this protocol. A prototype of the situation would look like the following:

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

I am running into a problem where the device does not understand the command I sent because the baud rate is changing to 38400 before it completes the write. I am pretty certain write works fine because it returns the number of bytes I intend to write. I tried adding a usleep(100000) after the call to write and while that works sometimes, I cannot guarantee the entire message will be transmitted at 9600 before I change the baud and read a response. I also tried flushing the buffer with tcflush(fd, TCOFLUSH开发者_运维知识库) but I do not want to discard any data so this is also not the correct way.

How can I force write all the serial data and be guaranteed it is written before the next call to change the baud rate to 38400? This seems to be happening at the chip level so is my only hope to include the FTDI libraries (it is an FTDI chip) and access the registers to see when the data is done being transmitted? Thanks.


Use WaitCommEvent with a mask that includes EV_TXEMPTY to wait for the message to be sent out by the driver.


Why not just set the transmitter to 9600 and the receiver to 38400? Most common serial port hardware support this.

// 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;
}

I've amended my code to use TCSADRAIN instead of TCSANOW so that the rate change does not occur until after all pending output has been sent.


You should be using tcdrain() or the TCSADRAIN optional action for tcsetattr() instead of tcflush


I used the Windows FTDI API and found their entire model to be annoyingly asynchronous. With a normal serial port I would expect you could do the math on your message length (in bits, including start, stop, parity) and baud rate and have a reasonable estimate of how long to sleep until your message is transmitted. However, with the FTDI part you're dealing with USB latencies and unknown queuing in the FTDI part itself. If your device's replies come in under 1ms it might not even be possible to turn the FTDI around reliably between your TX and RX phases.

Would it be possible to hook up the RX to a different UART? That would greatly simplify your problem.

If not, you might consider using a special cable that connects your TX back to your RX so that you can see your message go out before you cut over. With a diode you could avoid the device also seeing its own transmissions. 3/4 duplex?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜