C#用于开发上位机非常的方便,因为这属于可视化编程,很多时候我们需要做的只是把我们需要用到的控件拉到窗口界面,双击控件就会自动生成代码框架,我们再把其中的一些逻辑调通了就可以。因为一般情况下C#不会用到指针,所以编写程序就会变得更加简单,阅读起来也很通俗易懂。
最近做的项目有涉及到串口通讯的,为了方便测试,我用C#写了一个上位机作为测试工具。其中有一个控件必不可少,那就是serialPort控件。我们只需要在工具箱找到这个控件,然后把它拖到我们的窗口界面,但是它并不会像其他控件一样出现在界面上,而是出现在界面下方。
1、设置属性
接着我们需要设置一些相关的参数,比如波特率,数据位,停止位,校验位等等,设置的方法有两种,第一是选中这个控件,然后 在属性里面直接修改:
第二种是在Form_Load()函数里面修改,相关的一些属性介绍如下:
.PortName 串口名称,COM1, COM2等。
.BaudRate 波特率,也就是串口通讯的速度,进行串口通讯的双方其波特率需要相同,如果用PC连接其他非PC系统,一般地,波特率由非PC系统决定。
.Parity 奇偶校验。可以选取枚举Parity中的值
.DataBits 数据位
.StopBits 停止位,可以选取枚举StopBits中的值. .Handshake 握手方式,也就是数据流控制方式,可以选取枚举Handshake中的值
属性说明
名 称 |
说 明 |
BaseStream |
获取 SerialPort 对象的基础 Stream 对象 |
BaudRate |
获取或设置串行波特率 |
BreakState |
获取或设置中断信号状态 |
BytesToRead |
获取接收缓冲区中数据的字节数 |
BytesToWrite |
获取发送缓冲区中数据的字节数 |
CDHolding |
获取端口的载波检测行的状态 |
CtsHolding |
获取“可以发送”行的状态 |
DataBits |
获取或设置每个字节的标准数据位长度 |
DiscardNull |
获取或设置一个值,该值指示 Null 字节在端口和接收缓冲区之间传输时是否被忽略 |
DsrHolding |
获取数据设置就绪 (DSR) 信号的状态 |
DtrEnable |
获取或设置一个值,该值在串行通信过程中启用数据终端就绪 (DTR) 信号 |
Encoding |
获取或设置传输前后文本转换的字节编码 |
Handshake |
获取或设置串行端口数据传输的握手协议 |
IsOpen |
获取一个值,该值指示 SerialPort 对象的打开或关闭状态 |
NewLine |
获取或设置用于解释 ReadLine( )和WriteLine( )方法调用结束的值 |
Parity |
获取或设置奇偶校验检查协议 |
ParityReplace |
获取或设置一个字节,该字节在发生奇偶校验错误时替换数据流中的无效字节 |
PortName |
获取或设置通信端口,包括但不限于所有可用的 COM 端口 |
ReadBufferSize |
获取或设置 SerialPort 输入缓冲区的大小 |
ReadTimeout |
获取或设置读取操作未完成时发生超时之前的毫秒数 |
ReceivedBytesThreshold |
获取或设置 DataReceived 事件发生前内部输入缓冲区中的字节数 |
RtsEnable |
获取或设置一个值,该值指示在串行通信中是否启用请求发送 (RTS) 信号 |
StopBits |
获取或设置每个字节的标准停止位数 |
WriteBufferSize |
获取或设置串行端口输出缓冲区的大小 |
WriteTimeout |
获取或设置写入操作未完成时发生超时之前的毫秒数 |
二、打开串口
设置好属性之后应该打开串口,调用serialPort.Open();即可。
三、发送和接收数据
发送数据时可以采用serialPort.Write()方法,或者serialPort.WriteLine()方法,接受数据采用serialPort.Read()和serialPort.ReadLine()方法。
serialPort.WriteLine()和serialPort.Write()两者的区别是前者是阻塞式的,如果接收方没有及时读取数据,就会引起TimeoutException异常,这跟serialPort.ReadLine()是一样的,serialPort.ReadLine()和serialPort.WriteLine()都是以换行符作为结束的,如果serialPort.ReadLine()在读取数据的时候一直没有读取到换行符,那么在等待ReadTimeout时间后,抛出一个TimeoutException。因为这两个方法是阻塞性的,所以在使用的时候一般交由其他线程进行读写,避免因为阻塞而导致程序不响应。
对于字节或字符数据,用Read()方法来读数据,该方法需要一个字节或字符数组作为参数来保存读取的数据,结果返回实际读取的字节或字符数。写数据使用Write()方法,该方法可以将字节数组、字符数据或字符串发送给另一方。如果通讯双方交换的数据位字节流数据,要构建一个使用的串口通讯程序,那么双方应该定义数据帧格式。通常数据桢由帧头和帧尾来界定。发送数据比较简单,只需要将构造好的数据用Write()方法发送出去即可。接收数据则比较复杂,通讯是以字节流的形式到达的,通过调用一次Read()方法并不能确保所读取的数据就是完整一帧。因此需要将每次读取的数据整合在一起,对整合后的数据进行分析,按照定义的帧格式,通过帧头和帧尾,将帧信息从字节流中抽取出来,这样才能获取有意义的信息。除了利用Read()方法来读数据,还可以使用ReadExisting()方法来读取数据。该方法读取当前所能读到的数据,以字符串的形式返回。
四、事件
SerialPort 提供了DataReceived事件。当有数据进入时,该事件被触发。该事件的触发由操作系统决定,当有数据到达时,该事件在辅助线程中被触发。辅助线程的优先级比较低,因此并不能确保每个字节的数据到达时,该事件都被触发。在使用该事件接收数据时,最好对定义通讯协议格式,添加桢头和桢尾。在DataReceived事件中接收数据时,把数据放在数组中或字符串中缓冲起来,当接收的包含桢头和桢尾的完整数据时,在进行处理,另外,为了有效地接收数据,可以在每次读取数据后,加入System.Threading.Thread.Sleep方法进行演示。
示例代码:
首先要在Form_Load()函数里手动添加事件处理程序:
1 serialPort1.DataReceived += new SerialDataReceivedEventHandler(SerialPortDataReceivedEventHandler);
处理程序的具体内容:
1 private void SerialPortDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e) 2 { 3 byte[] readBuffer = null; 4 int n = serialPort1.BytesToRead; 5 byte[] buf = new byte[n]; 6 serialPort1.Read(buf, 0, n); 7 buffer.AddRange(buf); 8 9 while (buffer.Count >= 5) 10 { 11 if ((buffer[0] == 0x0A)&&(buffer[4] == 0xff)) //帧头和帧尾接收正确 12 { 13 if (buffer[1] == 0x01) //命令字 14 { 15 ... //处理数据内容 16 } 17 buffer.RemoveRange(0, 5); //从缓冲区中清除 18 } 19 } 20 21 }
五、方法说明
方 法 名 称 |
说 明 |
Close |
关闭端口连接,将 IsOpen 属性设置为False,并释放内部 Stream 对象 |
Open |
打开一个新的串行端口连接 |
Read |
从 SerialPort 输入缓冲区中读取数据字节数 |
ReadByte |
从 SerialPort 输入缓冲区中同步读取一个字节 |
ReadChar |
从 SerialPort 输入缓冲区中同步读取一个字符 |
ReadLine |
一直读取到输入缓冲区中的 NewLine 值 |
ReadTo |
一直读取到输入缓冲区中指定 value 的字符串 |
Write |
已重载。将数据写入串行端口输出缓冲区 |
WriteLine |
将指定的字符串和 NewLine 值写入输出缓冲区 |
DiscardInBuffer DiscardOutBuffer |
清空接收缓冲区数据 清空输出缓冲去数据 |
参考:https://www.cnblogs.com/fx2008/p/4317302.html
六、this.invoke()的作用和用法
Invoke()的作用是:在应用程序的主线程上执行指定的委托。一般应用:在辅助线程中修改UI线程( 主线程 )中对象的属性时,调用this.Invoke();
正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。
如我在辅助线程里修改TextBox.Text的内容,应该这样写:
1 this.Invoke(new Action(() => 2 { 3 TextBox1.Text = "L"; 4 }));
而不能直接写成:
1 TextBox1.Text = "L";
这是错误的。