Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serial communication with minimal delay

I have a computer which is connected with external devices via serial communication (i.e. RS-232/RS-422 of physical or emulated serial ports). They communicate with each other by frequent data exchange (30Hz) but with only small data packet (less than 16 bytes for each packet).

The most critical requirement of the communication is low latency or delay between transmitting and receiving.

The data exchange pattern is handshake-like. One host device initiates communication and keeps sending notification on a client device. A client device needs to reply every notification from the host device as quick as possible (this is exactly where the low latency needs to be achieved). The data packets of notifications and replies are well defined; namely the data length is known. And basically data loss is not allowed.

I have used following common Win API functions to do the I/O read/write in a synchronous manner: CreateFile, ReadFile, WriteFile

A client device uses ReadFile to read data from a host device. Once the client reads the complete data packet whose length is known, it uses WriteFile to reply the host device with according data packet. The reads and writes are always sequential without concurrency.

Somehow the communication is not fast enough. Namely the time duration between data sending and receiving takes too long. I guess that it could be a problem with serial port buffering or interrupts.

Here I summarize some possible actions to improve the delay. Please give me some suggestions and corrections :)

  1. call CreateFile with FILE_FLAG_NO_BUFFERING flag? I am not sure if this flag is relevant in this context.
  2. call FlushFileBuffers after each WriteFile? or any action which can notify/interrupt serial port to immediately transmit data?
  3. set higher priority for thread and process which handling serial communication
  4. set latency timer or transfer size for emulated devices (with their driver). But how about the physical serial port?
  5. any equivalent stuff on Windows like setserial/low_latency under Linux?
  6. disable FIFO?

thanks in advance!

like image 277
rnd_nr_gen Avatar asked Apr 01 '13 21:04

rnd_nr_gen


People also ask

What are the 2 types of serial communication?

There are two broad types of serial communication: synchronous and asynchronous. There are a very large number of different standards and protocols for serial communication, ranging from the very simple to the seriously complicated.

What is latency in serial communication?

The latency timer controls the time interval between two commands in serial communication. We can confirm this by observing the communications signal on an oscilloscope.

What is example of serial communication?

Similarly there are several examples of Serial Communication Protocols such as CAN, ETHERNET, I2C, SPI, RS232, USB, 1-Wire, and SATA etc. In this article, the different types of Serial Communication Protocols will be discussed.


2 Answers

I solved this in my case by setting the comm timeouts to {MAXDWORD,0,0,0,0}.

After years of struggling this, on this very day I finally was able to make my serial comms terminal thingy fast enough with Microsoft's CDC class USB UART driver (USBSER.SYS, which is now built in in Windows 10 making it actually usable).

Apparently the aforementioned set of values is a special value that sets minimal timeouts as well as minimal latency (at least with the Microsoft driver, or so it seems to me anyway) and also causes ReadFile to return immediately if no new characters are in the receive buffer.

Here's my code (Visual C++ 2008, project character set changed from "Unicode" to "Not set" to avoid LPCWSTR type cast problem of portname) to open the port:

static HANDLE port=0;
static COMMTIMEOUTS originalTimeouts;

static bool OpenComPort(char* p,int targetSpeed) { // e.g. OpenComPort ("COM7",115200); 
    char portname[16];
    sprintf(portname,"\\\\.\\%s",p);
    port=CreateFile(portname,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
    if(!port) {
        printf("COM port is not valid: %s\n",portname);
        return false;
    }
    if(!GetCommTimeouts(port,&originalTimeouts)) {
        printf("Cannot get comm timeouts\n");
        return false;
    }
    COMMTIMEOUTS newTimeouts={MAXDWORD,0,0,0,0};
    SetCommTimeouts(port,&newTimeouts);
    if(!ComSetParams(port,targetSpeed)) {
        SetCommTimeouts(port,&originalTimeouts);
        CloseHandle(port);
        printf("Failed to set COM parameters\n");
        return false;
    }
    printf("Successfully set COM parameters\n");
    return true;
}

static bool ComSetParams(HANDLE port,int baud) {
    DCB dcb;
    memset(&dcb,0,sizeof(dcb));
    dcb.DCBlength=sizeof(dcb);
    dcb.BaudRate=baud;
    dcb.fBinary=1;
    dcb.Parity=NOPARITY;
    dcb.StopBits=ONESTOPBIT;
    dcb.ByteSize=8;
    return SetCommState(port,&dcb)!=0;
}

And here's a USB trace of it working. Please note the OUT transactions (output bytes) followed by IN transactions (input bytes) and then more OUT transactions (output bytes) all within 3 milliseconds:

USB UART packet trace with minimal timeouts

And finally, since if you are reading this, you might be interested to see my function that sends and receives characters over the UART:

    unsigned char outbuf[16384];
    unsigned char inbuf[16384];
    unsigned char *inLast = inbuf;
    unsigned char *inP = inbuf;
    unsigned long bytesWritten;
    unsigned long bytesReceived;

    // Read character from UART and while doing that, send keypresses to UART.
    unsigned char vgetc() { 
        while (inP >= inLast) { //My input buffer is empty, try to read from UART
            while (_kbhit()) { //If keyboard input available, send it to UART
                outbuf[0] = _getch(); //Get keyboard character
                WriteFile(port,outbuf,1,&bytesWritten,NULL); //send keychar to UART
            }
            ReadFile(port,inbuf,1024,&bytesReceived,NULL); 
            inP = inbuf;
            inLast = &inbuf[bytesReceived]; 
        }
        return *inP++;
    }

Large transfers are handled elsewhere in code.

On a final note, apparently this is the first fast UART code I've managed to write since abandoning DOS in 1998. O, doest the time fly when thou art having fun.

This is where I found the relevant information: http://www.egmont.com.pl/addi-data/instrukcje/standard_driver.pdf

like image 151
PkP Avatar answered Sep 23 '22 23:09

PkP


I have experienced similar problem with serial port. In my case I resolved the problem decreasing the latency of the serial port. You can change the latency of every port (which by default is set to 16ms) using control panel. You can find the method here: http://www.chipkin.com/reducing-latency-on-com-ports/

Good Luck!!!

like image 39
CMorgan Avatar answered Sep 21 '22 23:09

CMorgan