C#编写上位机遇到的关于serialPort的若干问题

  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";

这是错误的。

原文地址:https://www.cnblogs.com/young-dalong/p/13718040.html