C#串口操作

前一阵,从国外网站看到一个用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("COM4"); //我对猫进行了调用

}

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) {

}

}

}

}

程序运行后,你可以对设备进行指令操作。(具体设备接受的指令)