C#串口操作

*
 * Author: Marcus Lorentzon, 2001
 *         d98malor@dtek.chalmers.se
 * 
 * Freeware: Please do not remove this header
 * 
 * File: SerialStream.cs
 * 
 * Description: Implements a Stream for asynchronous
 *              transfers and COMM. Stream version.
 *
 * Version: 2.4
 * 
 */


#region Using


using System;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;


#endregion Using


namespace LoMaN.IO {


    public class SerialStream : Stream {
        
        #region Attributes


        private IOCompletionCallback m_IOCompletionCallback;
        private IntPtr m_hFile = IntPtr.Zero;
        private string m_sPort;
        private bool m_bRead;
        private bool m_bWrite;


        #endregion Attributes


        #region Properties


        public string Port {
            get {
                return m_sPort;
            }
            set {
                if (m_sPort != value) {
                    Close();
                    Open(value);
                }
            }
        }


        public override bool CanRead {
            get {
                return m_bRead;
            }
        }


        public override bool CanWrite {
            get {
                return m_bWrite;
            }
        }


        public override bool CanSeek {
            get {
                return false;
            }
        }


        public bool Closed  {
            get {
                return m_hFile.ToInt32()  0;
            }
        }


        public bool Dsr {
            get {
                uint status;
                if (!GetCommModemStatus(m_hFile, out status)) {
                    throw new Win32Exception();
                }
                return (status & MS_DSR_ON) > 0;
            }
        }


        public bool Ring {
            get {
                uint status;
                if (!GetCommModemStatus(m_hFile, out status)) {
                    throw new Win32Exception();
                }
                return (status & MS_RING_ON) > 0;
            }
        }


        public bool Rlsd {
            get {
                uint status;
                if (!GetCommModemStatus(m_hFile, out status)) {
                    throw new Win32Exception();
                }
                return (status & MS_RLSD_ON) > 0;
            }
        }


        #endregion Properties


        #region Constructors


        public SerialStream() : this(FileAccess.ReadWrite) {
        }


        public SerialStream(FileAccess access) {
            m_bRead  = ((int)access & (int)FileAccess.Read) != 0;
            m_bWrite = ((int)access & (int)FileAccess.Write) != 0;
            unsafe {
                m_IOCompletionCallback = new IOCompletionCallback(AsyncFSCallback);
            }
        }


        public SerialStream(string port) : this(FileAccess.ReadWrite) {
            Open(port);
        }


        public SerialStream(string port, FileAccess access) : this(access) {
            Open(port);
        }


        #endregion Constructors


        #region Methods


        public void Open(string port) {
            if (m_hFile != IntPtr.Zero) {
                throw new IOException("Stream already opened.");
            }
            m_sPort = port;
            m_hFile = CreateFile(port, (uint)((m_bRead?GENERIC_READ:0)|(m_bWrite?GENERIC_WRITE:0)), 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
            if (m_hFile.ToInt32() == INVALID_HANDLE_VALUE) {
                m_hFile = IntPtr.Zero;
                throw new FileNotFoundException("Unable to open " + port);
            }


            ThreadPool.BindHandle(m_hFile);


            SetTimeouts(0, 0, 0, 0, 0);
        }


        public override void Close() {
            CloseHandle(m_hFile);
            m_hFile = IntPtr.Zero;
            m_sPort = null;
        }


        public IAsyncResult BeginRead(byte[] buffer) {
            return BeginRead(buffer, 0, buffer.Length, null, null);
        }


        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            GCHandle gchBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            SerialAsyncResult sar = new SerialAsyncResult(this, state, callback, true, gchBuffer);
            Overlapped ov = new Overlapped(0, 0, sar.AsyncWaitHandle.Handle.ToInt32(), sar);
            unsafe {
                NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
                byte* data = (byte*)((int)gchBuffer.AddrOfPinnedObject() + offset);


                uint read = 0;
                if (ReadFile(m_hFile, data, (uint)count, out read, nov)) {
                    sar.m_bCompletedSynchronously = true;
                    return sar;
                }
                else if (GetLastError() == ERROR_IO_PENDING) {
                    return sar;
                }
                else
                    throw new Exception("Unable to initialize read. Errorcode: " + GetLastError().ToString());
            }
        }


        public IAsyncResult BeginWrite(byte[] buffer) {
            return BeginWrite(buffer, 0, buffer.Length, null, null);
        }


        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            GCHandle gchBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            SerialAsyncResult sar = new SerialAsyncResult(this, state, callback, false, gchBuffer);
            Overlapped ov = new Overlapped(0, 0, sar.AsyncWaitHandle.Handle.ToInt32(), sar);
            unsafe {
                NativeOverlapped* nov = ov.Pack(m_IOCompletionCallback);
                byte* data = (byte*)((int)gchBuffer.AddrOfPinnedObject() + offset);


                uint written = 0;
                if (WriteFile(m_hFile, data, (uint)count, out written, nov)) {
                    sar.m_bCompletedSynchronously = true;
                    return sar;
                }
                else if (GetLastError() == ERROR_IO_PENDING) {
                    return sar;
                }
                else
                    throw new Exception("Unable to initialize write. Errorcode: " + GetLastError().ToString());
            }
        }


        private int EndOperation(IAsyncResult asyncResult, bool isRead) {
            SerialAsyncResult sar = (SerialAsyncResult)asyncResult;
            if (sar.m_bIsRead != isRead)
                throw new IOException("Invalid parameter: IAsyncResult is not from a " + (isRead ? "read" : "write"));
            if (sar.EndOperationCalled) {
                throw new IOException("End" + (isRead ? "Read" : "Write") + " called twice for the same operation.");
            }
            else {
                sar.m_bEndOperationCalled = true;
            }


            while (!sar.m_bCompleted) {
                sar.AsyncWaitHandle.WaitOne();
            }


            sar.Dispose();


            if (sar.m_nErrorCode != ERROR_SUCCESS && sar.m_nErrorCode != ERROR_OPERATION_ABORTED) {
                throw new IOException("Operation finished with errorcode: " + sar.m_nErrorCode);
            }


            return sar.m_nReadWritten;
        }
        
        public override int EndRead(IAsyncResult asyncResult) {
            return EndOperation(asyncResult, true);
        }
        
        public override void EndWrite(IAsyncResult asyncResult) {
            EndOperation(asyncResult, false);
        }


        public int EndWriteEx(IAsyncResult asyncResult) {
            return EndOperation(asyncResult, false);
        }


        public override int Read(byte[] buffer, int offset, int count) {
            return EndRead(BeginRead(buffer, offset, count, null, null));
        }


        public override void Write(byte[] buffer, int offset, int count) {
            EndWrite(BeginWrite(buffer, offset, count, null, null));
        }


        public int WriteEx(byte[] buffer, int offset, int count) {
            return EndWriteEx(BeginWrite(buffer, offset, count, null, null));
        }


        public int Read(byte[] buffer) {
            return EndRead(BeginRead(buffer, 0, buffer.Length, null, null));
        }


        public int Write(byte[] buffer) {
            return EndOperation(BeginWrite(buffer, 0, buffer.Length, null, null), false);
        }


        public override void Flush() {
            FlushFileBuffers(m_hFile);
        }


        public bool PurgeRead() {
            return PurgeComm(m_hFile, PURGE_RXCLEAR);
        }


        public bool PurgeWrite() {
            return PurgeComm(m_hFile, PURGE_TXCLEAR);
        }


        public bool Purge() {
            return PurgeRead() && PurgeWrite();
        }


        public bool CancelRead() {
            return PurgeComm(m_hFile, PURGE_RXABORT);
        }


        public bool CancelWrite() {
            return PurgeComm(m_hFile, PURGE_TXABORT);
        }


        public bool CancelAll() {
            return CancelRead() && CancelWrite();
        }


        public override void SetLength(long nLength) {
            throw new NotSupportedException("SetLength isn't supported on serial ports.");
        }


        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException("Seek isn't supported on serial ports.");
        }


        public void SetTimeouts(int ReadIntervalTimeout,
                                int ReadTotalTimeoutMultiplier,
                                int ReadTotalTimeoutConstant, 
                                int WriteTotalTimeoutMultiplier,
                                int WriteTotalTimeoutConstant) {
            SerialTimeouts Timeouts = new SerialTimeouts(ReadIntervalTimeout,
                                                         ReadTotalTimeoutMultiplier,
                                                         ReadTotalTimeoutConstant, 
                                                         WriteTotalTimeoutMultiplier,
                                                         WriteTotalTimeoutConstant);
            unsafe { SetCommTimeouts(m_hFile, ref Timeouts); }
        }


        public bool SetPortSettings(uint baudrate) {
            return SetPortSettings(baudrate, FlowControl.Hardware);
        }


        public bool SetPortSettings(uint baudrate, FlowControl flowControl) {
            return SetPortSettings(baudrate, flowControl, Parity.None);
        }


        public bool SetPortSettings(uint baudrate, FlowControl flowControl, Parity parity) {
            return SetPortSettings(baudrate, flowControl, parity, 8, StopBits.One);
        }


        public bool SetPortSettings(uint baudrate, FlowControl flowControl, Parity parity, byte databits, StopBits stopbits) {
            unsafe {
                DCB dcb = new DCB();
                dcb.DCBlength = sizeof(DCB);
                dcb.BaudRate = baudrate;
                dcb.ByteSize = databits;
                dcb.StopBits = (byte)stopbits;
                dcb.Parity = (byte)parity;
                dcb.fParity = (parity > 0)? 1U : 0U;
                dcb.fBinary = dcb.fDtrControl = dcb.fTXContinueOnXoff = 1;
                dcb.fOutxCtsFlow = dcb.fAbortOnError = (flowControl == FlowControl.Hardware)? 1U : 0U;
                dcb.fOutX = dcb.fInX = (flowControl == FlowControl.XOnXOff)? 1U : 0U;
                dcb.fRtsControl = (flowControl == FlowControl.Hardware)? 2U : 1U;
                dcb.XonLim = 2048;
                dcb.XoffLim = 512;
                dcb.XonChar = 0x11; // Ctrl-Q
                dcb.XoffChar = 0x13; // Ctrl-S
                return SetCommState(m_hFile, ref dcb);
            }
        }


        public bool SetPortSettings(DCB dcb) {
            return SetCommState(m_hFile, ref dcb);
        }


        public bool GetPortSettings(out DCB dcb) {
            unsafe {
                DCB dcb2 = new DCB();
                dcb2.DCBlength = sizeof(DCB);
                bool ret = GetCommState(m_hFile, ref dcb2);
                dcb = dcb2;
                return ret;
            }
        }


        public bool SetXOn() {
            return EscapeCommFunction(m_hFile, SETXON);
        }


        public bool SetXOff() {
            return EscapeCommFunction(m_hFile, SETXOFF);
        }


        private unsafe void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) {
            SerialAsyncResult sar = (SerialAsyncResult)Overlapped.Unpack(pOverlapped).AsyncResult;


            sar.m_nErrorCode = errorCode;
            sar.m_nReadWritten = (int)numBytes;
            sar.m_bCompleted = true;


            if (sar.Callback != null)
                sar.Callback.Invoke(sar);


            Overlapped.Free(pOverlapped);
        }


        #endregion Methods


        #region Constants


        private const uint PURGE_TXABORT = 0x0001;  // Kill the pending/current writes to the comm port.
        private const uint PURGE_RXABORT = 0x0002;  // Kill the pending/current reads to the comm port.
        private const uint PURGE_TXCLEAR = 0x0004;  // Kill the transmit queue if there.
        private const uint PURGE_RXCLEAR = 0x0008;  // Kill the typeahead buffer if there.


        private const uint SETXOFF  = 1;    // Simulate XOFF received
        private const uint SETXON   = 2;    // Simulate XON received
        private const uint SETRTS    = 3;    // Set RTS high
        private const uint CLRRTS    = 4;    // Set RTS low
        private const uint SETDTR    = 5;    // Set DTR high
        private const uint CLRDTR    = 6;    // Set DTR low
        private const uint SETBREAK    = 8;    // Set the device break line.
        private const uint CLRBREAK    = 9;    // Clear the device break line.


        private const uint MS_CTS_ON  = 0x0010;
        private const uint MS_DSR_ON  = 0x0020;
        private const uint MS_RING_ON = 0x0040;
        private const uint MS_RLSD_ON = 0x0080;


        private const uint FILE_FLAG_OVERLAPPED = 0x40000000;


        private const uint OPEN_EXISTING = 3;


        private const int  INVALID_HANDLE_VALUE = -1;


        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;


        private const uint ERROR_SUCCESS = 0;
        private const uint ERROR_OPERATION_ABORTED = 995;
        private const uint ERROR_IO_PENDING = 997;


        #endregion Constants


        #region Enums


        public enum Parity {None, Odd, Even, Mark, Space};
        public enum StopBits {One, OneAndHalf, Two};
        public enum FlowControl {None, XOnXOff, Hardware};


        #endregion Enums


        #region Classes


        [StructLayout(LayoutKind.Sequential)]
        public struct DCB {


            #region Attributes


            public int DCBlength;
            public uint BaudRate;
            public uint Flags;
            public ushort wReserved;
            public ushort XonLim;
            public ushort 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;
            public ushort wReserved1;


            #endregion Attributes


            #region Properties


            public uint fBinary { get { return Flags&0x0001; } 
                                  set { Flags = Flags & ~1U | value; } }
            public uint fParity { get { return (Flags>>1)&1; }
                                  set { Flags = Flags & ~(1U >2)&1; }
                                  set { Flags = Flags & ~(1U >3)&1; }
                                  set { Flags = Flags & ~(1U >4)&3; }
                                  set { Flags = Flags & ~(3U >6)&1; }
                                  set { Flags = Flags & ~(1U >7)&1; }
                                  set { Flags = Flags & ~(1U >8)&1; }
                                  set { Flags = Flags & ~(1U >9)&1; }
                                  set { Flags = Flags & ~(1U >10)&1; }
                                  set { Flags = Flags & ~(1U >11)&1; }
                                  set { Flags = Flags & ~(1U >12)&3; }
                                  set { Flags = Flags & ~(3U >14)&1; }
                                  set { Flags = Flags & ~(1U << 14) | (value << 14); } }


            #endregion Properties


            #region Methods


            public override string ToString() {
                return "DCBlength: " + DCBlength + "\r\n" +
                    "BaudRate: " + BaudRate + "\r\n" +
                    "fBinary: " + fBinary + "\r\n" +
                    "fParity: " + fParity + "\r\n" +
                    "fOutxCtsFlow: " + fOutxCtsFlow + "\r\n" +
                    "fOutxDsrFlow: " + fOutxDsrFlow + "\r\n" +
                    "fDtrControl: " + fDtrControl + "\r\n" +
                    "fDsrSensitivity: " + fDsrSensitivity + "\r\n" +
                    "fTXContinueOnXoff: " + fTXContinueOnXoff + "\r\n" +
                    "fOutX: " + fOutX + "\r\n" +
                    "fInX: " + fInX + "\r\n" +
                    "fErrorChar: " + fErrorChar + "\r\n" +
                    "fNull: " + fNull + "\r\n" +
                    "fRtsControl: " + fRtsControl + "\r\n" +
                    "fAbortOnError: " + fAbortOnError + "\r\n" +
                    "XonLim: " + XonLim + "\r\n" +
                    "XoffLim: " + XoffLim + "\r\n" +
                    "ByteSize: " + ByteSize + "\r\n" +
                    "Parity: " + Parity + "\r\n" +
                    "StopBits: " + StopBits + "\r\n" +
                    "XonChar: " + XonChar + "\r\n" +
                    "XoffChar: " + XoffChar + "\r\n" +
                    "EofChar: " + EofChar + "\r\n" +
                    "EvtChar: " + EvtChar + "\r\n";
            }


            #endregion Methods
        }


        private class SerialAsyncResult : IAsyncResult, IDisposable {


            #region Attributes


            internal bool m_bEndOperationCalled = false;
            internal bool m_bIsRead;
            internal int m_nReadWritten = 0;
            internal bool m_bCompleted = false;
            internal bool m_bCompletedSynchronously = false;
            internal uint m_nErrorCode = ERROR_SUCCESS;


            private object m_AsyncObject;
            private object m_StateObject;
            private ManualResetEvent m_WaitHandle = new ManualResetEvent(false);
            private AsyncCallback m_Callback;
            private GCHandle m_gchBuffer;


            #endregion Attributes


            #region Properties


            internal bool EndOperationCalled { get { return m_bEndOperationCalled; } }


            public bool IsCompleted { get { return m_bCompleted; } }


            public bool CompletedSynchronously { get { return m_bCompletedSynchronously; } }


            public object AsyncObject { get { return m_AsyncObject; } }


            public object AsyncState { get { return m_StateObject; } }


            public WaitHandle AsyncWaitHandle { get { return m_WaitHandle; } }
            internal ManualResetEvent WaitHandle { get { return m_WaitHandle; } }


            public AsyncCallback Callback { get { return m_Callback; } }


            #endregion Properties


            #region Constructors


            public SerialAsyncResult(object asyncObject,
                object stateObject, 
                AsyncCallback callback, 
                bool bIsRead, 
                GCHandle gchBuffer) {


                m_AsyncObject = asyncObject;
                m_StateObject = stateObject;
                m_Callback = callback;
                m_bIsRead = bIsRead;
                m_gchBuffer = gchBuffer;
            }


            #endregion Constructors


            #region Methods


            public void Dispose() {
                m_WaitHandle.Close();
                m_gchBuffer.Free();
            }


            #endregion Methods
        }


        #endregion Classes


        #region Imports


        [DllImport("kernel32.dll", EntryPoint="CreateFileW",  SetLastError=true,
            CharSet=CharSet.Unicode, ExactSpelling=true)]
        static extern IntPtr CreateFile(string filename, uint access, uint sharemode, uint security_attributes, uint creation, uint flags, uint template);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool CloseHandle(IntPtr handle);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern unsafe bool ReadFile(IntPtr hFile, byte* lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern unsafe bool WriteFile(IntPtr hFile, byte* lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, NativeOverlapped* lpOverlapped);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool SetCommTimeouts(IntPtr hFile, ref SerialTimeouts lpCommTimeouts);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool SetCommState(IntPtr hFile, ref DCB lpDCB);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool GetCommState(IntPtr hFile, ref DCB lpDCB);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool BuildCommDCB(string def, ref DCB lpDCB);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern int GetLastError();


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool FlushFileBuffers(IntPtr hFile);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool PurgeComm(IntPtr hFile, uint dwFlags);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool EscapeCommFunction(IntPtr hFile, uint dwFunc);


        [DllImport("kernel32.dll", SetLastError=true)]
        static extern bool GetCommModemStatus(IntPtr hFile, out uint modemStat);


        #endregion Imports
    }


    [StructLayout(LayoutKind.Sequential)]
    public struct SerialTimeouts {


        #region Attributes


        public int ReadIntervalTimeout;
        public int ReadTotalTimeoutMultiplier;
        public int ReadTotalTimeoutConstant;
        public int WriteTotalTimeoutMultiplier;
        public int WriteTotalTimeoutConstant;


        #endregion Attributes


        #region Constructors


        public SerialTimeouts(int r1, int r2, int r3, int w1, int w2) {
            ReadIntervalTimeout = r1;
            ReadTotalTimeoutMultiplier = r2;
            ReadTotalTimeoutConstant = r3;
            WriteTotalTimeoutMultiplier = w1;
            WriteTotalTimeoutConstant = w2;
        }


        #endregion Constructors


        #region Methods


        public override string ToString() {
            return "ReadIntervalTimeout: " + ReadIntervalTimeout + "\r\n" +
                   "ReadTotalTimeoutMultiplier: " + ReadTotalTimeoutMultiplier + "\r\n" +
                   "ReadTotalTimeoutConstant: " + ReadTotalTimeoutConstant + "\r\n" +
                   "WriteTotalTimeoutMultiplier: " + WriteTotalTimeoutMultiplier + "\r\n" +
                   "WriteTotalTimeoutConstant: " + WriteTotalTimeoutConstant + "\r\n";
        }


        #endregion Methods
    }
}






using System;
using System.IO;
using System.Threading;


using LoMaN.IO;


namespace SerialStreamReader {


    class App {


        // The main serial stream
        static SerialStream ss;


        [STAThread]
        static void Main(string[] args) {


            // Create a serial port
            ss = new SerialStream();
            try {
                ss.Open("COM3");
            }
            catch (Exception e) {
                Console.WriteLine("Error: " + e.Message);
                return;
            }


            // Set port settings
            ss.SetPortSettings(9600);


            // Set timeout so read ends after 20ms of silence after a response
            ss.SetTimeouts(20, 0, 0, 0, 0);


            // Create the StreamWriter used to send commands
            StreamWriter sw = new StreamWriter(ss, System.Text.Encoding.ASCII);


            // Create the Thread used to read responses
            Thread responseReaderThread = new Thread(new ThreadStart(ReadResponseThread));
            responseReaderThread.Start();


            // Read all returned lines
            for (;;) {
                // Read command from console
                string command = Console.ReadLine();


                // Check for exit command
                if (command.Trim().ToLower() == "exit") {
                    responseReaderThread.Abort();
                    break;
                }


                // Write command to modem
                sw.WriteLine(command);
                sw.Flush();
            }
        }


        // Main loop for reading responses
        static void ReadResponseThread() {
            StreamReader sr = new StreamReader(ss, System.Text.Encoding.ASCII);
            try {
                for (;;) {
                    // Read response from modem
                    string response = sr.ReadLine();
                    Console.WriteLine("Response: " + response);
                }
            }
            catch (ThreadAbortException) {
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值