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.
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:
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.
if (!GetCommMask (hPort, &fdwEventMask)) fdwEventMask = 0;
... fetches any events (using the GetCommMask function) that have already occurred and they are cleared (initialized).
fdwEventMask |= EV_RXCHAR | EV_TXEMPTY | EV_BREAK | EV_ERR |
EV_RING | EV_RLSD | EV_CTS | EV_DSR ;
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
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.
commEvent is created using the CreateEvent function and this event is synced into the
hEvent (handle event).
An event on the Comm Port is awaited using:
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_TXEMPTY are looked for.
if (fdwEventMask & (EV_RXCHAR | EV_BREAK | EV_ERR | EV_TXEMPTY))
NOTE: In my application, the use of
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).
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
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:
for (int x = 128 ; x; --x )
if (ReadRxChar( &c ))
return( (unsigned int)c );
Sleep( 10 );
CloseComPort will close the
RxThread and close the Comm., Port. Other functions in the comm_port.cpp may be used as necessary.
- 31st May, 2010: Initial post