Search code examples
c#windowsserial-portcom-port

Cannot achieve correct write timing on serial port (C#)


I am trying to write 20 bytes of data to a serial port with 5 milliseconds interval. To achieve 5 milliseconds I'm using the multimedia timer of Windows (winmm.dll) and I also verified its accuracy with the Stopwatch class. When the timer elapses my app writes 20 bytes of data to the serial port. However, sometimes (seems randomly to me) the data is buffered for a while and then is sent too fast.

The 20-byte packet contains a single CAN message - 8-byte data, 4-byte ID, CRC, magic bytes and some other stuff. While the delay between every message should be 5 ms, 3-4 of the messages are sent (flushed) together, almost at the very same instant. My hardware sends the data over the CAN bus where the timing is very important but sometimes I see 3-4 messages at the very same time on the CAN bus, not even a millisecond in between. It happens completely randomly but to give some numbers 5 out of 1000 messages are sent to fast, the rest is OK.

To see if the issue is on the software or on the hardware side, I wrote a simple helper app. I installed com0com and created two virtual serial ports COM24 and COM25. My main C# app connects COM24, the helper app connects to COM25, the com0com driver forwards all messages in between and I look at the timings of the messages in the helper app with the Stopwatch. No hardware is involved in this scenario but still the same issue so I'm thinking it's caused by Windows.

I tried

port.Write(data, 0, length);

I also tried flushing after writing the data to the serial port

port.BaseStream.Flush();

I also tried sending the data asynchronously, with/without flushing.

port.BaseStream.BeginWrite(data, 0, length, new AsyncCallback((IAsyncResult ar) => {
    port.BaseStream.EndWrite(ar);
    port.BaseStream.Flush();
}), null);

I'm wondering if the issue lies on the kernel32.dll where the serial port is handled? Does the Windows kernel introduce any delay etc. on the serial ports? How can I make sure the data is really flushed after port.Write();?


Solution

  • It is unreasonable to expect this level of precision in timing from a Windows OS. You'll have to control message spacing in the microcontroller of your UART <-> CAN bridge (it sounds like you have a custom solution here you can reprogram?)

    In particular, "Flush" just means the data is transferred completely to the kernel serial port driver. That might be com0com's virtual serial port, serial.sys, usbser.sys, or a virtual serial driver for some non-standard USB/serial device (e.g. FTDI or Prolific use custom drivers). It doesn't mean it's been transferred across the ISA bus (for a "real" 16550A serial port) or USB bus or whatever. And even when that transfer is complete, the sending UART will take time to transmit 20 bytes out (depending on the baud rate). All that is invisible to Windows.