Introduction
In this article, I have described some basics of serial communication and then written a C# class to implement the Win32 DCB structure.
Serial communication is a device communication protocol that is a standard on almost every PC. Serial communication is also a common communication protocol for instrumentation in many devices. The concept of serial communication is simple. A serial port sends and receives bytes of data, one bit at a time. Serial communication is used for low data transfer rates and longer distances.
Data Control Block (DCB) is used to define the control settings for a serial communication device.
Background
Serial communication is a popular means of transmitting data between a computer and a peripheral device such as a programmable instrument or even another computer. Serial communication uses a transmitter to send data, one bit at a time, over a single communication line, to a receiver. This method is normally used when data transfer rates are low, or when you must transfer data over long distances.
Serial communication is widely used because most computers have one or more serial ports, so no extra hardware is needed other than a cable to connect the instrument to the computer or two computers together.
Typically, serial communication is used to transmit ASCII data. Because a serial port is a full-duplex device, it uses an asynchronous method of communication; that is the port can transmit data on one line while receiving data on another.
Communication by bits
A serial port transmits data in the form of bits, that is why it's slower as compared to the parallel port. The standard data packet value is 5, 7, or 8 depending upon the selection. The communication takes place using three lines - ground, transmit, receive. The ground line is available for handshaking, but is not required. The important serial characteristics are baud rate, data bits, stop bits, and parity. For two ports to communicate, these parameters must match:
- Baud rate: is basically the number of bits transferred per second.
- Data bits: is a measurement of the actual data bits in a transmission. Standard bits are 5, 7, or 8.
- Stop bits: are used to signal the end of communication for a single packet. Typical values are 1, 1.5, 2. Because each of the device has its clocks, it's possible to have a slight synchronization delay. The stop bits not only signal the end of transmission but also gives some room for error to the devices to adjust the clocks.
- Parity: is a simple form of error checking used in serial communication. There are four types of parity -- even, odd, marked, and spaced. You also can use no parity.
Hopefully, at this point, you have a basic idea of serial communication between electronic devices.
Device Control Block
DCB is used for defining the control settings of a serial communication device. Microsoft has provided a Win32 DCB structure for this purpose. I will not go into the details of that structure at this point.
To know about the details of the DCB structure, you can visit this article: Win32 DCB Structure at MSDN.
I have written a C# class to implement the Win32 DCB structure.
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Collections.Specialized;
namespace SerialComm
{
[StructLayout(LayoutKind.Sequential)]
internal class DCB
{
private UInt32 DCBlength;
public UInt32 BaudRate;
BitVector32 Control;
internal UInt16 wReserved;
public UInt16 XonLim;
public UInt16 XoffLim;
public byte ByteSize;
public byte Parity;
public byte StopBits;
public sbyte XonChar;
public sbyte XoffChar;
public sbyte ErrorChar;
public sbyte EofChar;
public sbyte EvtChar;
internal UInt16 wReserved1;
private readonly BitVector32.Section sect1;
private readonly BitVector32.Section DTRsect;
private readonly BitVector32.Section sect2;
private readonly BitVector32.Section RTSsect;
public DCB()
{
this.DCBlength = (uint)Marshal.SizeOf(this);
Control=new BitVector32(0);
sect1=BitVector32.CreateSection(0x0f);
DTRsect=BitVector32.CreateSection(3,sect1);
sect2=BitVector32.CreateSection(0x3f,DTRsect);
RTSsect=BitVector32.CreateSection(3,sect2);
}
internal void _SuppressCompilerWarnings()
{
wReserved +=0;
wReserved1 +=0;
}
public enum DtrControlFlags
{
Disable = 0,
Enable =1 ,
Handshake = 2
}
public enum RtsControlFlags
{
Disable = 0,
Enable = 1,
Handshake = 2,
Toggle = 3
}
[Flags]
enum ctrlBit {
fBinaryMask = 0x001,
fParityMask = 0x0002,
fOutxCtsFlowMask = 0x0004,
fOutxDsrFlowMask = 0x0008,
fDtrControlMask = 0x0030,
fDsrSensitivityMask = 0x0040,
fTXContinueOnXoffMask = 0x0080,
fOutXMask = 0x0100,
fInXMask = 0x0200,
fErrorCharMask = 0x0400,
fNullMask = 0x0800,
fRtsControlMask = 0x3000,
fAbortOnErrorMask = 0x4000
}
public bool fBinary
{
get { return (Control[(int)ctrlBit.fBinaryMask]); }
set { Control[(int)ctrlBit.fBinaryMask]=value; }
}
public bool fParity
{
get { return (Control[(int)ctrlBit.fParityMask]); }
set { Control[(int)ctrlBit.fParityMask]=value; }
}
public bool fOutxCtsFlow
{
get { return (Control[(int)ctrlBit.fOutxCtsFlowMask]); }
set { Control[(int)ctrlBit.fOutxCtsFlowMask] = value; }
}
public bool fOutxDsrFlow
{
get { return (Control[(int)ctrlBit.fOutxDsrFlowMask]); }
set { Control[(int)ctrlBit.fOutxDsrFlowMask]=value; }
}
public byte fDtrControl
{
get {return (byte)Control[DTRsect]; }
set { Control[DTRsect]=(int)value; }
}
public bool fDsrSensitivity
{
get { return Control[(int)ctrlBit.fDsrSensitivityMask];}
set { Control[(int)ctrlBit.fDsrSensitivityMask] = value; }
}
public bool fTXContinueOnXoff
{
get { return Control[(int)ctrlBit.fTXContinueOnXoffMask]; }
set { Control[(int)ctrlBit.fTXContinueOnXoffMask]=value; }
}
public bool fOutX
{
get { return Control [(int)ctrlBit.fOutXMask]; }
set { Control[(int)ctrlBit.fOutXMask]=value; }
}
public bool fInX
{
get { return Control[(int)ctrlBit.fInXMask]; }
set { Control[(int)ctrlBit.fInXMask]=value; }
}
public bool fErrorChar
{
get { return Control[(int)ctrlBit.fErrorCharMask]; }
set { Control[(int)ctrlBit.fErrorCharMask]=value; }
}
public bool fNull
{
get { return Control[(int)ctrlBit.fNullMask]; }
set { Control[(int)ctrlBit.fNullMask]=value; }
}
public byte fRtsControl
{
get { return (byte)Control[RTSsect]; }
set { Control[RTSsect]=(int)value; }
}
public bool fAbortOnError
{
get { return Control[(int)ctrlBit.fAbortOnErrorMask]; }
set { Control[(int)ctrlBit.fAbortOnErrorMask]=value; }
}
public override String ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("DCB:\r\n");
sb.AppendFormat(null, " BaudRate: {0}\r\n", BaudRate);
sb.AppendFormat(null, " Control: 0x{0:x}\r\n", Control.Data);
sb.AppendFormat(null, " fBinary: {0}\r\n", fBinary);
sb.AppendFormat(null, " fParity: {0}\r\n", fParity);
sb.AppendFormat(null, " fOutxCtsFlow: {0}\r\n", fOutxCtsFlow);
sb.AppendFormat(null, " fOutxDsrFlow: {0}\r\n", fOutxDsrFlow);
sb.AppendFormat(null, " fDtrControl: {0}\r\n", fDtrControl);
sb.AppendFormat(null, " fDsrSensitivity: {0}\r\n", fDsrSensitivity);
sb.AppendFormat(null, " fTXContinueOnXoff: {0}\r\n", fTXContinueOnXoff);
sb.AppendFormat(null, " fOutX: {0}\r\n", fOutX);
sb.AppendFormat(null, " fInX: {0}\r\n", fInX);
sb.AppendFormat(null, " fNull: {0}\r\n", fNull);
sb.AppendFormat(null, " fRtsControl: {0}\r\n", fRtsControl);
sb.AppendFormat(null, " fAbortOnError: {0}\r\n", fAbortOnError);
sb.AppendFormat(null, " XonLim: {0}\r\n", XonLim);
sb.AppendFormat(null, " XoffLim: {0}\r\n", XoffLim);
sb.AppendFormat(null, " ByteSize: {0}\r\n", ByteSize);
sb.AppendFormat(null, " Parity: {0}\r\n", Parity);
sb.AppendFormat(null, " StopBits: {0}\r\n", StopBits);
sb.AppendFormat(null, " XonChar: {0}\r\n", XonChar);
sb.AppendFormat(null, " XoffChar: {0}\r\n", XoffChar);
sb.AppendFormat(null, " ErrorChar: {0}\r\n", ErrorChar);
sb.AppendFormat(null, " EofChar: {0}\r\n", EofChar);
sb.AppendFormat(null, " EvtChar: {0}\r\n", EvtChar);
return sb.ToString();
}
}