Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Objective-C

Serial Port R/W With Read Thread

4.10/5 (10 votes)
31 May 2010CPOL4 min read 62.5K   4.3K  
A Simple Comm Port Implementation with Read Thread to be used with Embedded Systems

Introduction

It is often necessary to make an Embedded System which is running an RTOS, to communicate with a PC. Most often with the Serial port. The RTOS on the Embedded System time slices its tasks and might not respond to the requests made to it from the PC (GUI Program/DOS Based UI program). Hence it would be useful, if the PC program is able to make a request, wait for responses and grab the responses based on events that happen on the PC's serial port.

Background

I have used ARM7 and ARM9 Serial Port (UART & USART) interfaces. I have often used the serial FIFO "not empty" condition, i.e., when a character or a buffer of characters are available, the Micro Controller may be interrupted, and the data can be read based on the condition in hardware rather than poll the port registers every so often.

I felt, why not try to figure out if this can be done on a PC's serial port? Windows is a Multitasking Multi threaded OS, so it must provide a means to do this. Well, it does and here is the app that will help you realize the same.

Using the Code

All you really have to do is call the:

C++
OpenComPort( int port, int rate, int parity, int retries )  

function, this opens the Comm., port specified in the int port using the CreateFile function for Generic R/W for overlapped (asynchronous) I/O.

This application follows the Microsoft KB article q115831 (http://support.microsoft.com/kb/115831) which allows you to open Comm port numbers > 9. The specified baud rate and the parity and other Serial Port parameters will be entered into the Opened Comm., Port's DCB Structure.

Then...

C++
if (!GetCommMask (hPort, &fdwEventMask)) //get current event mask
          fdwEventMask = 0;   

... fetches any events (using the GetCommMask function) that have already occurred and they are cleared (initialized).

Now,

C++
fdwEventMask |= EV_RXCHAR | EV_TXEMPTY | EV_BREAK | EV_ERR |
       EV_RING | EV_RLSD | EV_CTS | EV_DSR  ;

SetCommMask(hPort, fdwEventMask);

fdwEventMask is initialized with the required events, and Set (using the SetCommMask) into the Events register. When the required/specified events happen, then the Read Thread will run(which is explained further below).

Click events, to see the bit masks of the various Serial Port Events (Look at Table 1 for events specifically, the entire KB article was referred to while writing this com_port.cpp). I have used events similar to the following in Embedded Systems:

  • EV_RXCHAR(0x0001) - A character was received and placed in the input buffer
  • EV_TXEMPTY(0x0004) - The last character in the output buffer was sent

The next step is to initialize the critical section rxLock & the memory for the rxBuffer.

At this point, the Read Thread is created. In the RxThread, the initialization includes the setting up of a OVERLAPPED structure commSync for receiving data.

An event, commEvent is created using the CreateEvent function and this event is synced into the commSync's hEvent (handle event).

An event on the Comm Port is awaited using:

C++
waitState = WaitCommEvent(hPort, &fdwEventMask, &commSync);  

To Quote: WaitCommEvent Waits for an event to occur for a specified communications device. The set of events that are monitored by this function is contained in the event mask associated with the device handle.

Once the event(s) occur, the specific events, viz., EV_RXCHAR | EV_BREAK | EV_ERR | EV_TXEMPTY are looked for.

C++
if (fdwEventMask & (EV_RXCHAR | EV_BREAK | EV_ERR | EV_TXEMPTY))

NOTE: In my application, the use of EV_BREAK and EV_ERR were for errors in transmission (example: wrong parity or loss of Comm due to hardware failure). The other two events were used extensively.

The received data is transferred into the rxBuffer (with some error checks and data size checks).

Example

Call:

C++
WriteComPort( char *sendString, unsigned char txCount ); 

function, to write an array of characters to the Embedded System. The array to be written should be formed in accordance to any protocol that may be necessary for the communication. In my application, the packet contains an SOF and an EOF.

The SOF is an address of the embedded system (if addressing is enabled) and/or the byte count.

The EOF is a modulo 256 checksum of the rest of the (other than checksum) bytes.

Typically, a message written out will be responded to by the Embedded System, the Embedded System responds with data, when the data arrives into the Serial Port registers on the PC, one or all of the specified events are generated. The response message will be available in the rxBuffer.

The received data in rxBuffer can be read using the ReadRxChar function. A for loop can be used to read a chunk of data as follows:

C++
for (int x = 128 ; x; --x )
 {
    if (ReadRxChar( &c ))
              return( (unsigned int)c );
    Sleep( 10 );
 }

The CloseComPort will close the RxThread and close the Comm., Port. Other functions in the comm_port.cpp may be used as necessary.

History

  • 31st May, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)