`

C#串口通信

 
阅读更多

本文主要是讲述如何通过C#实现串口通信:

 

自己用API写串口通信,可以方便实现自己想要的各种功能,用现成的已经封装好的类库,常见两个串口操作类是JustinIO和SerialStreamReader。介绍JustinIO的使用方法: 

 

1、打开串口: 

函数原型:public void Open() 

说明:打开事先设置好的端口 

using JustinIO; 

static JustinIO.CommPort ss_port = new JustinIO.CommPort(); 
ss_port.PortNum = COM1; //端口号 
ss_port.BaudRate = 19200; //串口通信波特率 
ss_port.ByteSize = 8; //数据位 
ss_port.Parity = 0; //奇偶校验 
ss_port.StopBits = 1;//停止位 
ss_port.ReadTimeout = 1000; //读超时 
try 
{ 
if (ss_port.Opened) 
{ 
ss_port.Close(); 
ss_port.Open(); //打开串口 
} 
else 
{ 
ss_port.Open();//打开串口 
} 
return true; 
} 
catch(Exception e) 
{ 
MessageBox.Show("错误:" + e.Message); 
return false; 
} 

 

 

 2、写串口:

函数原型:public void Write(byte[] WriteBytes) 
WriteBytes 就是你的写入的字节,注意,字符串要转换成字节数组才能进行通信

 

3、读串口: 

函数原型:public byte[] Read(int NumBytes) 

NumBytes 读入缓存数,注意读取来的是字节数组,要实际应用中要进行字符转换

 

4、关闭串口: 

函数原型:ss_port.Close() 

 

5、整合代码

 

using System; 
using System.Runtime.InteropServices; 

namespace JustinIO { 
class CommPort { 

public int PortNum; 
public int BaudRate; 
public byte ByteSize; 
public byte Parity; // 0-4=no,odd,even,mark,space 
public byte StopBits; // 0,1,2 = 1, 1.5, 2 
public int ReadTimeout; 

//comm port win32 file handle 
private int hComm = -1; 

public bool Opened = false; 

//win32 api constants 
private const uint GENERIC_READ = 0x80000000; 
private const uint GENERIC_WRITE = 0x40000000; 
private const int OPEN_EXISTING = 3; 
private const int INVALID_HANDLE_VALUE = -1; 

[StructLayout(LayoutKind.Sequential)] 
public struct DCB { 
//taken from c struct in platform sdk 
public int DCBlength; // sizeof(DCB) 
public int BaudRate; // current baud rate 
/* these are the c struct bit fields, bit twiddle flag to set 
public int fBinary; // binary mode, no EOF check 
public int fParity; // enable parity checking 
public int fOutxCtsFlow; // CTS output flow control 
public int fOutxDsrFlow; // DSR output flow control 
public int fDtrControl; // DTR flow control type 
public int fDsrSensitivity; // DSR sensitivity 
public int fTXContinueOnXoff; // XOFF continues Tx 
public int fOutX; // XON/XOFF out flow control 
public int fInX; // XON/XOFF in flow control 
public int fErrorChar; // enable error replacement 
public int fNull; // enable null stripping 
public int fRtsControl; // RTS flow control 
public int fAbortOnError; // abort on error 
public int fDummy2; // reserved 
*/ 
public uint flags; 
public ushort wReserved; // not currently used 
public ushort XonLim; // transmit XON threshold 
public ushort XoffLim; // transmit XOFF threshold 
public byte ByteSize; // number of bits/byte, 4-8 
public byte Parity; // 0-4=no,odd,even,mark,space 
public byte StopBits; // 0,1,2 = 1, 1.5, 2 
public char XonChar; // Tx and Rx XON character 
public char XoffChar; // Tx and Rx XOFF character 
public char ErrorChar; // error replacement character 
public char EofChar; // end of input character 
public char EvtChar; // received event character 
public ushort wReserved1; // reserved; do not use 
} 

[StructLayout(LayoutKind.Sequential)] 
private struct COMMTIMEOUTS { 
public int ReadIntervalTimeout; 
public int ReadTotalTimeoutMultiplier; 
public int ReadTotalTimeoutConstant; 
public int WriteTotalTimeoutMultiplier; 
public int WriteTotalTimeoutConstant; 
} 

[StructLayout(LayoutKind.Sequential)] 
private struct OVERLAPPED { 
public int Internal; 
public int InternalHigh; 
public int Offset; 
public int OffsetHigh; 
public int hEvent; 
} 

[DllImport("kernel32.dll")] 
private static extern int CreateFile( 
string lpFileName, // file name 
uint dwDesiredAccess, // access mode 
int dwShareMode, // share mode 
int lpSecurityAttributes, // SD 
int dwCreationDisposition, // how to create 
int dwFlagsAndAttributes, // file attributes 
int hTemplateFile // handle to template file 
); 
[DllImport("kernel32.dll")] 
private static extern bool GetCommState( 
int hFile, // handle to communications device 
ref DCB lpDCB // device-control block 
); 
[DllImport("kernel32.dll")] 
private static extern bool BuildCommDCB( 
string lpDef, // device-control string 
ref DCB lpDCB // device-control block 
); 
[DllImport("kernel32.dll")] 
private static extern bool SetCommState( 
int hFile, // handle to communications device 
ref DCB lpDCB // device-control block 
); 
[DllImport("kernel32.dll")] 
private static extern bool GetCommTimeouts( 
int hFile, // handle to comm device 
ref COMMTIMEOUTS lpCommTimeouts // time-out values 
); 
[DllImport("kernel32.dll")] 
private static extern bool SetCommTimeouts( 
int hFile, // handle to comm device 
ref COMMTIMEOUTS lpCommTimeouts // time-out values 
); 
[DllImport("kernel32.dll")] 
private static extern bool ReadFile( 
int hFile, // handle to file 
byte[] lpBuffer, // data buffer 
int nNumberOfBytesToRead, // number of bytes to read 
ref int lpNumberOfBytesRead, // number of bytes read 
ref OVERLAPPED lpOverlapped // overlapped buffer 
); 
[DllImport("kernel32.dll")] 
private static extern bool WriteFile( 
int hFile, // handle to file 
byte[] lpBuffer, // data buffer 
int nNumberOfBytesToWrite, // number of bytes to write 
ref int lpNumberOfBytesWritten, // number of bytes written 
ref OVERLAPPED lpOverlapped // overlapped buffer 
); 
[DllImport("kernel32.dll")] 
private static extern bool CloseHandle( 
int hObject // handle to object 
); 
[DllImport("kernel32.dll")] 
private static extern uint GetLastError(); 

public void Open() { 

DCB dcbCommPort = new DCB(); 
COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS(); 


// OPEN THE COMM PORT. 


hComm = CreateFile("COM" + PortNum ,GENERIC_READ | GENERIC_WRITE,0, 0,OPEN_EXISTING,0,0); 

// IF THE PORT CANNOT BE OPENED, BAIL OUT. 
if(hComm == INVALID_HANDLE_VALUE) { 
throw(new ApplicationException("Comm Port Can Not Be Opened")); 
} 

// SET THE COMM TIMEOUTS. 

GetCommTimeouts(hComm,ref ctoCommPort); 
ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout; 
ctoCommPort.ReadTotalTimeoutMultiplier = 0; 
ctoCommPort.WriteTotalTimeoutMultiplier = 0; 
ctoCommPort.WriteTotalTimeoutConstant = 0; 
SetCommTimeouts(hComm,ref ctoCommPort); 

// SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS. 
GetCommState(hComm, ref dcbCommPort); 
dcbCommPort.BaudRate=BaudRate; 
dcbCommPort.flags=0; 
//dcb.fBinary=1; 
dcbCommPort.flags|=1; 
if (Parity>0) 
{ 
//dcb.fParity=1 
dcbCommPort.flags|=2; 
} 
dcbCommPort.Parity=Parity; 
dcbCommPort.ByteSize=ByteSize; 
dcbCommPort.StopBits=StopBits; 
if (!SetCommState(hComm, ref dcbCommPort)) 
{ 
//uint ErrorNum=GetLastError(); 
throw(new ApplicationException("Comm Port Can Not Be Opened")); 
} 
//unremark to see if setting took correctly 
//DCB dcbCommPort2 = new DCB(); 
//GetCommState(hComm, ref dcbCommPort2); 
Opened = true; 

} 

public void Close() { 
if (hComm!=INVALID_HANDLE_VALUE) { 
CloseHandle(hComm); 
} 
} 
public byte[] Read(int NumBytes) { 
byte[] BufBytes; 
byte[] OutBytes; 
BufBytes = new byte[NumBytes]; 
if (hComm!=INVALID_HANDLE_VALUE) { 
OVERLAPPED ovlCommPort = new OVERLAPPED(); 
int BytesRead=0; 
ReadFile(hComm,BufBytes,NumBytes,ref BytesRead,ref ovlCommPort); 
OutBytes = new byte[BytesRead]; 
Array.Copy(BufBytes,OutBytes,BytesRead); 
} 
else { 
throw(new ApplicationException("Comm Port Not Open")); 
} 
return OutBytes; 
} 

public void Write(byte[] WriteBytes) { 
if (hComm!=INVALID_HANDLE_VALUE) { 
OVERLAPPED ovlCommPort = new OVERLAPPED(); 
int BytesWritten = 0; 
WriteFile(hComm,WriteBytes,WriteBytes.Length,ref BytesWritten,ref ovlCommPort); 
} 
else { 
throw(new ApplicationException("Comm Port Not Open")); 
} 
} 
} 

} 
} 

 

 

6、C#中使用SerialPort类实现串口通信

 

使用SerialPort类的方法:

方法一:

首先要添加

using System.IO;
using System.IO.Ports;

1...在类的内部定义SerialPort com;

2...打开串口

            com = new SerialPort();
            com.BaudRate = 115200;
            com.PortName = "COM1";
            com.DataBits = 8;
            com.Open();//打开串口

3...发送数据

            Byte[] TxData ={1,2,3,4,5,6,7,8 };
            com.Write(TxData, 0, 8);

4...接收数据

     4.1使用事件接收

     this.com.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.OnDataReceived);

private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)

    4.2使用线程接收

     接收数据启动一个线程,使其接收。

在类的内部定义

        Thread _readThread;
        bool _keepReading;

打开串口后启动线程

            _keepReading = true;
            _readThread = new Thread(ReadPort);
            _readThread.Start();

 

线程函数:

 

        private void ReadPort()
        {
            while (_keepReading)
            {
                if (com.IsOpen)
                {
                    byte[] readBuffer = new byte[com.ReadBufferSize + 1];
                    try
                    {
                        // If there are bytes available on the serial port,
                        // Read returns up to "count" bytes, but will not block (wait)
                        // for the remaining bytes. If there are no bytes available
                        // on the serial port, Read will block until at least one byte
                        // is available on the port, up until the ReadTimeout milliseconds
                        // have elapsed, at which time a TimeoutException will be thrown.
                        int count = com.Read(readBuffer, 0, com.ReadBufferSize);
                        String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);
                        if (count != 0)
                            //byteToHexStr(readBuffer);
                            ThreadFunction(byteToHexStr(readBuffer,count));
                    }
                    catch (TimeoutException) { }
                }
                else
                {
                    TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50);
                    Thread.Sleep(waitTime);
                }
            }
        }

 方法二:使用C#自带的SerialPor控件。

 

1...在“工具箱”的“组件”中选择SerialPor控件添加。

2...设置串口并打开

serialPort1.PortName = "COM1";

serialPort1.BaudRate = 9600;

serialPort1.Open();

3...写入数据可以使用Write或者下面的函数

serialPort1.WriteLine(str);

4...添加数据接收的事件

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)

 

使用中的一些常见问题

      C#中SerialPort类中DataReceived事件GUI实时处理方法(来自wanglei_wan@yahoo.com.cn 的看法)
      MSDN:从 SerialPort 对象接收数据时,将在辅助线程上引发 DataReceived 事件。由于此事件在辅助线程而非主线程上引发,因此尝试修改主线程中的一些元素(如 UI 元素)时会引发线程异常。如果有必要修改主 Form 或 Control 中的元素,必须使用 Invoke 回发更改请求,这将在正确的线程上执行.进而要想将辅助线程中所读到的数据显示到主线程的Form控件上时,只有通过Invoke方法来实现 


下面是代码实例: 

 

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
   int SDateTemp = this.serialPort1.ReadByte();
   //读取串口中一个字节的数据
   this.tB_ReceiveDate.Invoke(   
	//在拥有此控件的基础窗口句柄的线程上执行委托Invoke(Delegate)
	//即在textBox_ReceiveDate控件的父窗口form中执行委托.
	new MethodInvoker(            
	/*表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。 在对控件的 Invoke 	方法进行调用时或需要一个简单委托又不想自己定义时可以使用该委托。*/
	delegate{                 
    /*匿名方法,C#2.0的新功能,这是一种允许程序员将一段完整代码区块当成参数传递的程序代码编写技术,通过此种方法可 	以直接使用委托来设计事件响应程序以下就是你要在主线程上实现的功能但是有一点要注意,这里不适宜处理过多的方法,因为C#消息机制是消息流水线响应机制,如果这里在主线程上处理语句的时间过长会导致主UI线程阻塞,停止响应或响应不顺畅,这时你的主form界面会延迟或卡死      */                   
	this.tB_ReceiveDate.AppendText(SDateTemp.ToString());//输出到主窗口文本控件
	this.tB_ReceiveDate.Text += " ";}
    )
    );
}

 

如何知道当前电脑有哪个串口

在窗体上添加一个comboBox控件。

然后使用comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());  或者

string[] portList = System.IO.Ports.SerialPort.GetPortNames();
            for (int i = 0; i < portList.Length; ++i)
            {
                string name = portList[i];
                comboBox1.Items.Add(name);
            }

 

 

博客参考:http://blog.csdn.net/byxdaz/article/details/6748234

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics