本文主要是讲述如何通过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")); } } } } }
使用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
相关推荐
C#串口通讯的完整源码,简单,清楚 每行代码都有注释,一步一步的写出来的,适合初学者借鉴学习,下载直接可用
以网上某位大虾的源码为版本,沿用了其可用的代码部分。对其余欠妥部分进行了改造。不要资源分,是大家不用为积分而烦恼。出来混都不容易,希望大家相互照应!
c#串口通讯,c#编写的串口通讯,功能很齐全
C#打开含有对应字符的串口,并发送字符串
C#串口通信、发送、监听串口数据,可以使用windows api或者C#自带serialPort通信。
c#编写串口通讯代码多线程实现,逻辑与界面分离。发送与接收都为单独线程
C#串口通讯时,已经实现正确发送数据包,正确得到回复;如何实现若发送失败最多发送5次,并且发送多个数据包时,发送数据和回复数据在界面的显示不串行;我尝试做过,会出现数据串行,或者一帧数据已成功发送、回复...
C#串口通信(解决中文传输和实现换行),该版本解决了传输中文变成乱码、敲回车键不换行等问题
C#串口通信源代码,可以实现串口通信,实现字符串和16进制的发送以及接受
这个类是上位机串口编程的框架类,用单例模式多线程...类里面是我开发使用的命令,使用者可以参考这个类实现自己的串口通讯框架类,包括数据的发送和接收,事件的触发,错误的处理,单例模式的实现等,希望对您有帮助.
串口通信的实现,编程环境为C#,实现技术采用了多线程方式
C#串口通信程序.pdf 很详细的C#写的具体事例啊!
基于NET2.0的串口通信类库,和应用实例; 基本能满足工程应用;
C#串口通讯CRC校验码计算类 本文将对C#串口通讯CRC校验码计算类进行详细的解读和分析,以帮助读者更好地理解该类的设计和实现。 首先,让我们来了解什么是CRC校验码。CRC(Cyclic Redundancy Check)是一种常用的...
c# 串口通信,用于打开、传输数据在串口上。
本人自己开发的串口调试工具。在同步线程的基础上,自动接收串口数据,自动计算CRC16校验,可选择CRC是否发送到下位机。可以定时发送数据到下位机,精确到毫秒。功能很强大,欢迎与我交流。QQ :281274071
基于VS2015版的C#窗体设计串口通讯
C# 串口通讯的简单例子,特别适合初学者
用C#编写的用ASCII码发送的串口通信程序