Sunday, July 14, 2013

Windows Serial Port Programming

Introduction

This Article covers the basics of programming and managing serial port RS232 communication using the Windows API. The Serial port communication is now almost obsolete but it is still used in computer to Micro controller communication. The Article assumes that you have basic knowledge about C/C++. We will be covering the following things here.
and we will also get an overview of some advanced functions

Opening the serial port

Opening the serial Port is very easy if you have already worked with file I/O before. Include Windows.h and then use the following code to open Serial Port.





HANDLE hSerial;
hSerial =CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, 0, 0, &nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp &nbspOPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial == INVALID_HANDLE_VALUE) {
&nbsp&nbspif(GetLastError() == ERROR_FILE_NOT_FOUND) {
&nbsp&nbsp&nbsp&nbspcout<<"Port not found\n";
&nbsp&nbsp}
}
Lets walk through this. First we declare a type HANDLE and initialize it with a call to CreateFile. The first argument to CreateFile is the name of the file you want to open i.e., port number(COM1, COM2) you want to communicate on. In windows 7 the port number is written like "\\\\.\\COM1", "\\\\.\\COM2".... The next argument tells the Windows the purpose of opening the file whether you want to read from or write to. Arguments 3 and 4 should pretty much always be 0. So we are not going to talk about them. The next argument tells us that windows should open existing file, since the port already exists. After this comes FILE_ATTRIBUTE_NORMAL, which just agrees to not doing anything out fancy. The final argument should also be zero.

Setting the serial port Properties

As we have a HANDLE to the serial port, we need to set parameters for it i.e., communication format baud rate etc.,
dcbSerialParams.BaudRate=CBR_9600; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY;
DCB dcbSerialParams = {0};
if (!GetCommState (hSerial, &dcbSerialParams)) {
&nbsp&nbsp&nbsp&nbsp cout<<"Error getting port state\n";
}
if (!SetCommState (hSerial, &dcbSerialParams)) {
&nbsp&nbsp&nbsp&nbsp cout<<"Error getting port state\n";
}
Once again let us discuss this line by line. First of all we created a variable of type DCB, clear all its fields and set its size parameter. We then use the GetCommState function, which takes in our serial port handle and the DCB struct, to fill in the parameters currently being used by serial port.
Now we need to set the parameters we care about. The important ones are the baud rate is the transfer rate (number of symbols per seconds) which can set to various predefined values e.g., 9600, 19200, 57600.
Byte size can be just specified directly, but stop bits and parity again need special constants.
There are loads of other fields in DCB structure that can be used for some other more obscure serial parameters. You can see the MSDN Library to know about them.
These settings are applied to serial port using SetCOmmState function

Setting timeouts

One big problem of Serial port communication is that if there is no data to be received than attempting to read from port can cause your application to hang while waiting for data to show up.
This problem can be fixed by telling windows not too wait forever when no data shows up i.e., by setting out the timeout intervals
COMMTIMEOUTS timeouts={0};
timeouts.ReadIntervalTimeout=50;
timeouts.ReadTotalTimeoutConstant=50
timeouts.ReadTotalTimeoutMultiplier=10
timeouts.WriteTotalTimeoutConstant=50
timeouts.WriteTotalTimeoutMultiplie=10
if (!SetCommTimeouts (hSerial, & timeouts)) {
&nbsp&nbsp&nbsp&nbsp cout<<"Error setting Tmeouts\n";
}
The COMMTIMEOUTS structure is pretty simple, and the above fields are the only ones it has.
  • ReadIntervalTimeout specifies how long (in milliseconds) to wait between receiving characters before timing out.
  • ReadTotalTimeoutConstant specifies how long to wait (in milliseconds) before returning
  • ReadTotalTimeoutMultiplier specifies how much additional time to wait (in milliseconds) before returning for each byte that was requested in read operation.
  • WriteTotalTimeout and WriteTotalTimeoutMultiplier do the same thing, just for writes instead of reads.

One special case that comes up pretty often is setting the ReadIntervalTimeout to MAXDWORD and both ReadTotalTimeoutConstant and ReadTimeoutMultiplier to zero. This casues read operations to return immediately if there is no data in buffer to be received.
After we have setup the COMMTIMEOUTS structure, we need to apply the settings to the serial port using the SetCommTimeouts function.

Reading from serial port

So once you have opened the port successfully and assigned it the desired paramters you can start doing the actual read.
Suppose we want to read n bytes from the serial port. We just need to do this. char szBuff[n+1]={0};
DWORD bytesread=0;

if (!ReadFile(hSerial, szBuff, n, &bytesread, NULL)) {
&nbsp&nbsp&nbsp cout<<"Read error\n";
}
ReadFile takes in a HANDLE to a file (our serial port), a buffer to store the data in, number of bytes to read, a pointer to an integer that will set to number of bytes actually read, and NULL.
Note that bytesread will contain the number of bytes actually read by the ReadFile operation.

Writing to serial port

Writing data to serial port is exactly the same as ReadFile except the function is called WriteFile and n represents the number of bytes to write while bytesread is replaced by number of bytes actually written

Closing the Serial Port

Once you are done using the srial port make sure you close the handle or no other program would be able to access the port and you will need to restart the Computer. to close the serial port just do this
CloseHandle(hSerial);

Some advanced Properties of Serial Port

There are some advanced properties related to serial port which are not used in normal cases. Nearly all advanced functions are accomplished using EscapeCommFunction(HANDLE, DWORD). In any event the first parameter is just the HANDLE to the serial port while DWORD represents the folloowing constants.
  1. CLRDTR
  2. SETDTR
  3. SETRTS
  4. CLRRTS
  5. SETBREAK
  6. CLRBREAK
Another advanced function is to clear the read write function which is accomplished by calling FLushFileBUffers, which just takes one argument HANDLE to the file whose buffers are desired to be flushed.
Download a sample program Here

No comments:

Post a Comment