Serial Port Hell

Had to implement a 9-bit serial port protocol under c#. Normally, the 8 bit information transfer is supported by the .NET SerialPort class, whereas the extra bit is used for parity check.

To send a 9 bit through the serial port is pretty easy: Send the first 8 bits normally with either a Mark(=1)/Space(=0) parity setting for the extra 9th bit.

The hell I went through involves SENDING each 9 bits, and this is where my story starts…

Using the .NET SerialPort Class

To recieve byte, I set my serial port to a Space(=0) parity setting, and for each byte I would check for a parity error. No parity error = 9th bit 0, whereas a parity error = 9th bit 1.

Sounds easy, right? WRONG!

.NET Serial Class gives you 2 interfaces to identify parity errors. The first is an event called ErrorRecieved which according to MS:

ErrorReceived events may be called out of order, and there may be a slight delay between when the underlying stream reports the error and when code can when the event handler is executed.

The ErrorReceived event is raised on a secondary thread when an error is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread

According to this and the event delegate signature, when a parity error occures you can not rely it will happen in a reasonable time nor will you know on which of the bytes the error had occured.

Second way is to use the ParityReplace member which quite effectively, replace a parity errored-byte into a value of your choosing. This allows parity detection for the price of the 8 bits it follows. No good as well.

Using Windows API functions

There’s a bunchload of Serial Port related resources about P/Invoking a lower-level Windows API functions to handle serial-port communication, which hopefully give me some solution to my problem.

So I wrote a new SerialPort class which encapsulates the P/Invoke’ed Win32 API functions using some nice examples across the web. This time, detecting a parity error went without a hitch, I’d ReadFile a single byte from the port and then check for the last error for a parity problem. Another nice touch is to use the fAbortOnError member in the DCB which stops all read operation upon stumbling an error (specifically, a parity error).

This proved to be a working solution, until it was used under the production environment. It appeared that detecting parity errors (the 9th bit) worked inconsistently. Further inspection of both the program and the web, revealed that the root of the problem lies in a much lower level – the windows serial.sys device driver.

The Serial Port Device Driver

Every unhandled incoming data recieved by the serial port is queued into the driver FIFO buffer to be handled by the next API read. Whoever wrote this driver, only queued the recieved bytes and not the recieved data (both the 8 bits and the parity bit). So if your program is polling information fast enough not to let any byte enter the buffer you’re safe, and if you plan any other non-serial-polling-software-centric you’re not.

So i turned to the Windows Driver Kit to revise the current serial.sys (code included with the WDK) driver to fit to my needs. It took a while, but now when the serial port recieves a byte, it enqueues two bytes: the recieved byte and the parity.

Problem solved.

This entry was posted in Uncategorized and tagged , , , , , , , . Bookmark the permalink.

4 Responses to Serial Port Hell

  1. Chris says:

    I am having the same exact issue and have resorted to the WinDDK as well. Just curious how you finally ended up reporting the parity information back from the driver. Did you have to create a custom IOCTL or were you able to somehow get the information from ReadFile? Thanks

    • Gil says:

      Actually, the solution is inside isr.c !

      Search for SerialPutChar(…) which puts a character into the recieve buffer.

      You’ll have to change in two locations: first is where no error occurred and the second is where a parity error has occurred.
      for each of them add another call to SerialPutChar(…), this time with the parity information.

      Make sure NOT to change other calls to SerialPutChar(…) since they are crucial to reading the buffer properly.
      Also take into account the current Parity settings, since an error with the PARITY_SPACE means parity bit = 1 and an error with the PARITY_MARK means parity bit = 0. This brings ReadFile() an extra byte with parity for each read byte.

      let me know how it worked out for you :)

      • Chris says:

        Thanks for the reply.

        I was in the process of creating another readbuffer for the parity bytes, but it sounds like your way is easier. Have you had any trouble syncing your app between the data bits and the parity bits? It seems like it will work as long as you always read even byte increments.

        I will give it a try and let you know how it works. Thanks again.

Leave a Reply

Your email address will not be published. Required fields are marked *

six - 5 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>