Click here to Skip to main content
Licence CPOL
First Posted 25 Aug 2008
Views 23,467
Bookmarked 34 times

Serial Port Communication and Implementation of the Win32 DCB Structure in C#

By | 25 Aug 2008 | Article
This article contains a C# implementation of the Win32 DCB structure.

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:

  1. Baud rate: is basically the number of bits transferred per second.
  2. Data bits: is a measurement of the actual data bits in a transmission. Standard bits are 5, 7, or 8.
  3. 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.
  4. 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
{

//
// The Win32 DCB structure is implemented below in a C# class.
//

[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()
    {
          //
          // Initialize the length of the structure. Marshal.SizeOf returns
          // the size of the unmanaged object (basically the object that
          // gets marshalled).
          //
          this.DCBlength = (uint)Marshal.SizeOf(this);
          // initialize BitVector32
          Control=new BitVector32(0);
          // of the following 4 sections only 2 are needed
          sect1=BitVector32.CreateSection(0x0f);
          // this is where the DTR setting is stored
          DTRsect=BitVector32.CreateSection(3,sect1);
          sect2=BitVector32.CreateSection(0x3f,DTRsect);
          // this is where the RTS setting is stored
          RTSsect=BitVector32.CreateSection(3,sect2);
    }

    //
    // We need to have to define reserved fields in the DCB class definition
    // to preserve the size of the 
    // underlying structure to match the Win32 structure when it is 
    // marshaled. Use these fields to suppress compiler warnings.
    //

    internal void _SuppressCompilerWarnings()
    {
          wReserved +=0;
          wReserved1 +=0;
    }

    //
    // Enumeration for fDtrControl bit field. 
    //

    public enum DtrControlFlags
    {
          Disable = 0,
          Enable =1 ,
          Handshake = 2
    }

    //
    // Enumeration for fRtsControl bit field. 
    //

    public enum RtsControlFlags 
    {
          Disable = 0,
          Enable = 1,
          Handshake = 2,
          Toggle = 3
    }

    // Helper constants for manipulating the bit fields.
    // these are defined as an enum in order to preserve memory

    [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
    }

    // get and set of bool works with the underlying BitVector32
    // by using a mask for each bit field we can let the compiler
    // and JIT do the work
    //

    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; }
    }

    // we have to use a segment because the width of the underlying information
    // is wider than just one bit
    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; }
    }

    // we have to use a segment because the width of the underlying information
    // is wider than just one bit
    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; }
    }

    //
    // Method to dump the DCB to take a look and help debug issues.
    //

    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();
    }
}

License

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

About the Author

Aftab Zaheer Satti

Software Developer

Pakistan Pakistan

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 1 PinmemberSteffen Niekler3:29 3 Feb '11  
GeneralFYI - Issues Pinmemberdavidko12:27 11 Mar '09  
GeneralVery Well Done.. PinmemberAmbreen Aslam20:27 26 Aug '08  
GeneralToo much left unexplained PinmemberGordon Brandly10:41 26 Aug '08  
GeneralRe: Too much left unexplained PinmemberAftab Zaheer Satti19:22 26 Aug '08  
GeneralRe: Too much left unexplained PinmemberInvincibleAngel1:25 27 Aug '08  
GeneralGreat work PinmemberSaad Aslam Khan21:13 25 Aug '08  
GeneralKeep it up! PinmemberShakeel Iqbal18:22 25 Aug '08  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 25 Aug 2008
Article Copyright 2008 by Aftab Zaheer Satti
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid