《c#代码例程代码.pdf》由会员分享,可在线阅读,更多相关《c#代码例程代码.pdf(23页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、创建 C#串口通信程序之命名空间System.IO.Ports命名空间中最重用的是SerialPort 类。创建 C#串口通信程序之创建SerialPort 对象通过创建SerialPort 对象,我们可以在程序中控制串口通信的全过程。我们将要用到的SerialPort 类的方法:ReadLine():从输入缓冲区读一新行的值,如果没有,会返回NULL WriteLine(string):写入输出缓冲Open():打开一个新的串口连接Close():关闭复制代码代码如下:SerialPort sp=new SerialPort();默认情况下,DataBits 值是 8,StopBits 是
2、1,通信端口是COM1。这些都可以在下面的属性中重新设置:BaudRate:串口的波特率StopBits:每个字节的停止位数量ReadTimeout:当读操作没有完成时的停止时间。单位,毫秒还有不少其它公共属性,自己查阅MSDN。创建 C#串口通信程序之串口的硬件知识在数据传输的时候,每个字节的数据通过单个的电缆线传输。包包括开始位,数据,结束为。一旦开始位传出,后面就会传数据,可能是5,6,7 或 8 位,就看你的设定了。发送和接收必须设定同样的波特率和数据位数。创建 C#串口通信程序之无猫模式没有 Modem 模式的电缆只是简单地交叉传送和接收线。同样 DTR&DSR,和 RTS&CTS
3、也需要交叉。这里,我们三条线。互连2 和 3(一段的2pin 连接 3pin),连接两端的5pin。创建 C#串口通信程序示例程序如果想使用默认属性,按“Save Status”按钮,如果想改变属性按“Property”。设定好之后,可以通信了。主窗口的代码复制代码代码如下:#region Using directives using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Windows.For
4、ms;using System.IO.Ports;#endregion namespace Serialexpample partial class Form1:Form /create instance of property page /property page is used to set values for stop bits and /baud rate PropertyPage pp=new PropertyPage();/create an Serial Port object SerialPort sp=new SerialPort();public Form1()Init
5、ializeComponent();private void propertyButton_Click(object sender,EventArgs e)/show property dialog pp.ShowDialog();propertyButton.Hide();private void sendButton_Click(object sender,EventArgs e)try /write line to serial port sp.WriteLine(textBox.Text);/clear the text box textBox.Text=;catch(System.E
6、xception ex)baudRatelLabel.Text=ex.Message;private void ReadButton_Click(object sender,EventArgs e)try /clear the text box textBox.Text=;/read serial port and displayed the data in text box textBox.Text=sp.ReadLine();catch(System.Exception ex)baudRatelLabel.Text=ex.Message;private void Form1_Load(ob
7、ject sender,EventArgs e)private void Form1_FormClosing(object sender,FormClosingEventArgs e)MessageBox.Show(Do u want to Close the App);sp.Close();private void startCommButton_Click(object sender,EventArgs e)startCommButton.Hide();sendButton.Show();readButton.Show();textBox.Show();/when we want to s
8、ave the status(value)private void saveStatusButton_Click_1(object sender,EventArgs e)/display values /if no property is set the default values if(pp.bRate=&pp.sBits=)dataBitLabel.Text=BaudRate=+sp.BaudRate.ToString();readTimeOutLabel.Text=StopBits=+sp.StopBits.ToString();else dataBitLabel.Text=BaudR
9、ate=+pp.bRate;readTimeOutLabel.Text=StopBits=+pp.sBits;/创建 C#串口通信程序parityLabel.Text=DataBits=+sp.DataBits.ToString();stopBitLabel.Text=Parity=+sp.Parity.ToString();readTimeOutLabel.Text=ReadTimeout=+sp.ReadTimeout.ToString();if(propertyButton.Visible=true)propertyButton.Hide();saveStatusButton.Hide(
10、);startCommButton.Show();try /open serial port sp.Open();/set read time out to 500 ms sp.ReadTimeout=500;catch(System.Exception ex)baudRatelLabel.Text=ex.Message;创建 C#串口通信程序之属性设置对话框代码:复制代码代码如下:#region Using directives using System;using System.Collections.Generic;using System.ComponentModel;using Sy
11、stem.Data;using System.Drawing;using System.Text;using System.Windows.Forms;#endregion namespace Serialexpample partial class PropertyPage:Form /variables for storing values of baud rate and stop bits private string baudR=;private string stopB=;/property for setting and getting baud rate and stop bi
12、ts public string bRate get return baudR;set baudR=value;public string sBits get return stopB;set stopB=value;public PropertyPage()InitializeComponent();private void cancelButton_Click(object sender,EventArgs e)this.bRate=;this.sBits=;/close form this.Close();private void okButton_Click_1(object send
13、er,EventArgs e)/here we set the value for stop bits and baud rate.this.bRate=BaudRateComboBox.Text;this.sBits=stopBitComboBox.Text;/this.Close();我假设读者已经了解了c#的语法,本文是针对刚打算解除串口编程的朋友阅读的,作为串口编程的入门范例,也是我这个系列的基础。我们的开发环境假定为vs2005(虽然我在用vs2010,但避免有些网友用2005,不支持lambda,避免不兼容,就用2005 来做例子)一个基本的串口程序,既然是个程序了。我们就先从功能
14、说起,包含串口选择波特率选择打开关闭接受数据显示发送数据输入发送数据数据量提示以及归零好吧,有了这些功能,我们就先画出界面。例如:这里,波特率就定死几种好了。直接界面上添加2400,4800,9600,19200,38400,57600,115200 comboPortName这里,为了我们的软件能通用所有电脑避免每次查询的效率损失,我们使用微软提供的枚举方式,代码如下:c-sharpview plaincopy1.string ports=SerialPort.GetPortNames();2.Array.Sort(ports);boPortName.Items.AddRange(ports
15、);显然,我们需要定义一个SerialPort对象。添加DataReceived事件响应收到数据,还有一个重点,我们需要记得设置NewLine 属性哦。好想有的版本不设置的时候,WriteLine 和Write 效果一样。所以,我们需要初始化SerialPort对象,例如:c-sharpview plaincopy1./初始化 SerialPort对象m.NewLine=/r/n;m.RtsEnable=true;/根据实际情况吧。4./添加事件注册m.DataReceived+=comm_DataReceived;初始化好串口,简单的编写打开,关闭方法,编写界面响应的是否自动换行,如何复位计
16、数器,发送方法。以及数据处理。因为我已经写了完整注视,我就直接贴代码了。第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort 类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有一半的人真的解决了。另外一半的人就睁只眼闭只眼阿弥佗佛希望不要在客户那里出现这问题了。你看到我的文章,就放心吧,这问题有救了。我们先回顾一下上一篇中的代码c-sharpview plaincopy1.void comm_DataReceived(object sender,SerialDataReceivedEv
17、entArgs e)2.3./先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致4.int n=comm.BytesToRead;5./声明一个临时数组存储当前来的串口数据6.byte buf=new byte n;7./增加接收计数8.received_count+=n;9./读取缓冲数据m.Read(buf,0,n);11./清除字符串构造器的内容12.builder.Clear();13./因为要访问ui 资源,所以需要使用invoke方式同步 ui。14.this.Invoke(EventHandler)(delegate.界面更新,略);15.16.17.priva
18、tevoid buttonOpenClose_Click(object sender,EventArgs e)18.19./根据当前串口对象,来判断操作20.if (comm.IsOpen)21.22./打开时点击,则关闭串口m.Close();/这里就是可能导致软件死掉的地方24.25.else26.27.为什么会死锁呢,并发冲突。我们要了解一下SerialPort 的实现和串口通讯机制,在你打开串口的时候,SerialPort会创建一个监听线程ListenThread,在这个线程中,等待注册的串口中断,当收到中断后,会调用DataReceived事件。调用完成后,继续进入循环等待,直到串口
19、被关闭退出线程。我们的 UI 主线程如何做的呢,首先创建一个窗体,然后执行了Application.Run(窗体实例)。是这样把,这里的Application.Run就是创建了一个消息循环,循环的处理相关的消息。这里我们就有了2 个线程,UI 主线程、串口监听线程。那么你在DataReceived处理数据的时候,就需要线程同步,避免并发冲突,什么是并发冲突?并发冲突就是2 个或多个并行(至少看上去像)的线程运行的时候,多个线程共同的操作某一线程的资源,在时序上同时或没有按我们的预计顺序操作,这样就可能导致数据混乱无序或是彼此等待完成死锁软件。而串口程序大多是后者。为什么呢,看看我们的例子中Da
20、taReceived做了什么?首先读取数据,然后就是调用this.Invoke 方法更新 UI 了。这里 Invoke 的时候,监听线程将等待 UI 线程的标志,等到后,开始操作UI 的资源,当操作完成之前,监听线程也就停在 DataReceived方法的调用这里,如果这个时候。并发了关闭串口的操作会如何呢?SerialPort 的 Close 方法,会首先尝试等待和监听线程一样的一个互斥体、临界区、或是事件(不确定.net 用的哪种)。那这个同步对象什么时候释放呢?每次循环结束就释放,哦。循环为什么不结束呢?因为这一次的循环操作执行到DataReceived之后,执行了 Invoke 去更新
21、界面了,那 Invoke 怎么又没有执行完成呢?看上去很简单的几行代码。虽然我没仔细研读过.net 的 Invoke 原理,但我猜测是通过消息的方式来同步的,这也是为什么这么多的类,只有控件(窗体也是控件的一种,.net 在概念上,颠覆了微软自己的概念,传统的 win32 编程,是说所有的控件都是个window,只是父窗体不同,表现形式不同,但都是基于系统消息队列的,.net 出于更高的抽象,正好反过来了。呵呵)才有 Invoke 方法了。(委托自己的Invoke 和这个不同)我猜测控件/窗体的 Invoke 是 SendMessage方式实现的,那么发送消息后就会等待消息循环来处理消息了。如
22、果你直接去关闭串口了。你点击按钮本身也会被转换成消息WM_CLICK,消息循环在处理按钮的WM_CLICK时候,调用你按钮的OnClick 方法,进而触发调用你的ButtonClose_Click事件,这都是同步调用的,你的主线程,处理消息的过程,停在了这个Click 事件,而你的Click 事件又去调用了SerialPort 的 Close方法,Close 方法又因为和串口监听线程的同步信号量关联在一起需要等待一次的while结束,而这个while 循环中调用了DataReceived方法,这个方法中调用了Invoke,也就是发送了消息到消息队列等待结果,但消息循环正在处理你的关闭按钮事件等
23、待退出。实在太复杂了,这个情况下,你想要真的关闭串口成功,就需要while 中的DataReceived方法调用结束释放同步信号,就需要执行完Invoke,就需要执行消息循环,幸运的是,我们真的有办法执行消息循环来打破僵局。Application.DoEvents()。还好,不幸中的万幸。可是问题又来了,你能让Invoke 结束,但你无法确定是否在你调用消息循环后,你的某一时刻不会再次并发,可能由于单cpu 的串行操作模拟并行中,又把时间片先分给了优先级高的串口监听线程呢?是有可能的。所以,我们就需要一点方法来避免再次invoke 窗体。优化后不会司机的例子如下,我们修改DataReceive
24、d方法,关闭方法,并定义2 个标记 Listening 和 Closing。c-sharpview plaincopy1.using System;2.using System.Collections.Generic;3.using System.ComponentModel;4.using System.Data;5.using System.Drawing;6.using System.Linq;7.using System.Text;8.using System.Windows.Forms;9.using System.IO.Ports;10.using System.Text.Regu
25、larExpressions;11.namespace SerialportSample 12.13.public partial class SerialportSampleForm:Form 14.15.private SerialPort comm=new SerialPort();16.private StringBuilder builder=new StringBuilder();/避免在事件处理方法中反复的创建,定义到外面。17.privatelong received_count=0;/接收计数18.privatelong send_count=0;/发送计数19.privat
26、ebool Listening=false;/是否没有执行完invoke相关操作20.privatebool Closing=false;/是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke21.public SerialportSampleForm()22.23.InitializeComponent();24.25./窗体初始化26.privatevoid Form1_Load(object sender,EventArgs e)27.28./初始化下拉串口名称列表框29.string ports=SerialPort.GetPortNames();30
27、.Array.Sort(ports);boPortName.Items.AddRange(ports);boPortName.SelectedIndex=comboPortName.Items.Count 0?0:-1;boBaudrate.SelectedIndex=comboBaudrate.Items.IndexOf(9600);34./初始化 SerialPort对象m.NewLine=/r/n;m.RtsEnable=true;/根据实际情况吧。37./添加事件注册m.DataReceived+=comm_DataReceived;39.40.void comm_DataReceiv
28、ed(object sender,SerialDataReceivedEventArgs e)41.42.if (Closing)return;/如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环43.try44.45.Listening=true;/设置标记,说明我已经开始处理数据,一会儿要使用系统 UI 的。46.int n=comm.BytesToRead;/先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致47.byte buf=new byte n;/声明一个临时数组存储当前来的串口数据48.received_count+=n;/增加接收计数m.Re
29、ad(buf,0,n);/读取缓冲数据50.builder.Clear();/清除字符串构造器的内容51./因为要访问ui 资源,所以需要使用invoke方式同步 ui。52.this.Invoke(EventHandler)(delegate53.54./判断是否是显示为16 禁止55.if (checkBoxHexView.Checked)56.57./依次的拼接出16 进制字符串58.foreach(byte b in buf)59.60.builder.Append(b.ToString(X2)+);61.62.63.else64.65./直接按 ASCII 规则转换成字符串66.bu
30、ilder.Append(Encoding.ASCII.GetString(buf);67.68./追加的形式添加到文本框末端,并滚动到最后。69.this.txGet.AppendText(builder.ToString();70./修改接收计数71.labelGetCount.Text=Get:+received_count.ToString();72.);73.74.finally75.76.Listening=false;/我用完了,ui 可以关闭串口了。77.78.79.privatevoid buttonOpenClose_Click(object sender,EventArg
31、s e)80.81./根据当前串口对象,来判断操作82.if (comm.IsOpen)83.84.Closing=true;85.while (Listening)Application.DoEvents();86./打开时点击,则关闭串口m.Close();88.Closing=false;89.90.else91.92./关闭时点击,则设置好端口,波特率后打开m.PortName=comboPortName.Text;m.BaudRate=int.Parse(comboBaudrate.Text);m.Open();98.99.catch(Exception ex)100.101./捕获
32、到异常信息,创建一个新的comm对象,之前的不能用了。m=new SerialPort();103./现实异常信息给客户。104.MessageBox.Show(ex.Message);105.106.107./设置按钮的状态108.buttonOpenClose.Text=comm.IsOpen?Close:Open;109.buttonSend.Enabled=comm.IsOpen;110.111./动态的修改获取文本框是否支持自动换行。112.privatevoid checkBoxNewlineGet_CheckedChanged(object sender,EventArgs e)
33、113.114.txGet.WordWrap=checkBoxNewlineGet.Checked;115.116.privatevoid buttonSend_Click(object sender,EventArgs e)117.118./定义一个变量,记录发送了几个字节119.int n=0;120./16进制发送121.if (checkBoxHexSend.Checked)122.123./我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数124.MatchCollection mc=Regex.Matches(txSend.Text,(?i)/da-f2);1
34、25.List buf=new List();/填充到这个临时列表中126./依次添加到列表中127.foreach(Match m in mc)128.129.buf.Add(byte.Parse(m.Value);130.131./转换列表为数组后发送m.Write(buf.ToArray(),0,buf.Count);133./记录发送的字节数134.n=buf.Count;135.136.else/ascii编码直接发送137.138./包含换行符139.if (checkBoxNewlineSend.Checked)m.WriteLine(txSend.Text);142.n=txS
35、end.Text.Length+2;143.144.else/不包含换行符m.Write(txSend.Text);147.n=txSend.Text.Length;148.149.150.send_count+=n;/累加发送字节数151.labelSendCount.Text=Send:+send_count.ToString();/更新界面152.153.privatevoid buttonReset_Click(object sender,EventArgs e)154.155./复位接受和发送的字节数计数器并更新界面。156.send_count=received_count=0;1
36、57.labelGetCount.Text=Get:0;158.labelSendCount.Text=Send:0;159.160.161.至此,不会再出现关闭死锁问题了。希望这篇文章能解你的燃眉之急,非常高兴能与读者分享我层遇到,大多数人都遇到的这个问题。如果说的不明白,欢迎讨论。后续的有关通讯程序底层设计的文章会讲述一个具有丰富扩展性,但有设计简介的万能通讯库,支持网络、蓝牙、串口通讯、并口通讯。但不要指望我都实现出来了,我只是设计出这个框架。C#串口通信程序创建的相关内容就向你介绍到这里,希望对你了解创建C#串口通信程序的步骤和需要注意的事宜。using System;using Sy
37、stem.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.IO.Ports;using System.Text.RegularExpressions;namespace SerialportSample public partial class SerialportSampleForm:Form private Seri
38、alPort comm=new SerialPort();private StringBuilder builder=new StringBuilder();/避免在事件处理方法中反复的创建,定义到外面。private long received_count=0;/接收计数private long send_count=0;/发送计数public SerialportSampleForm()InitializeComponent();/窗体初始化private void Form1_Load(object sender,EventArgs e)/初始化下拉串口名称列表框string ports
39、=SerialPort.GetPortNames();Array.Sort(ports);comboPortName.Items.AddRange(ports);comboPortName.SelectedIndex=comboPortName.Items.Count 0?0:-1;comboBaudrate.SelectedIndex=comboBaudrate.Items.IndexOf(9600);/初始化 SerialPort 对象comm.NewLine=/r/n;comm.RtsEnable=true;/根据实际情况吧。/添加事件注册comm.DataReceived+=comm_
40、DataReceived;void comm_DataReceived(object sender,SerialDataReceivedEventArgs e)int n=comm.BytesToRead;/先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致byte buf=new byten;/声明一个临时数组存储当前来的串口数据received_count+=n;/增加接收计数comm.Read(buf,0,n);/读取缓冲数据builder.Clear();/清除字符串构造器的内容/因为要访问ui 资源,所以需要使用invoke 方式同步ui。this.Invoke(E
41、ventHandler)(delegate /判断是否是显示为16 禁止if(checkBoxHexView.Checked)/依次的拼接出16 进制字符串foreach(byte b in buf)builder.Append(b.ToString(X2)+);else /直接按 ASCII 规则转换成字符串builder.Append(Encoding.ASCII.GetString(buf);/追加的形式添加到文本框末端,并滚动到最后。this.txGet.AppendText(builder.ToString();/修改接收计数labelGetCount.Text=Get:+recei
42、ved_count.ToString(););private void buttonOpenClose_Click(object sender,EventArgs e)/根据当前串口对象,来判断操作if(comm.IsOpen)/打开时点击,则关闭串口comm.Close();else /关闭时点击,则设置好端口,波特率后打开comm.PortName=comboPortName.Text;comm.BaudRate=int.Parse(comboBaudrate.Text);try comm.Open();catch(Exception ex)/捕获到异常信息,创建一个新的comm 对象,之
43、前的不能用了。comm=new SerialPort();/现实异常信息给客户。MessageBox.Show(ex.Message);/设置按钮的状态buttonOpenClose.Text=comm.IsOpen?Close:Open;buttonSend.Enabled=comm.IsOpen;/动态的修改获取文本框是否支持自动换行。private void checkBoxNewlineGet_CheckedChanged(object sender,EventArgs e)txGet.WordWrap=checkBoxNewlineGet.Checked;private void b
44、uttonSend_Click(object sender,EventArgs e)/定义一个变量,记录发送了几个字节int n=0;/16 进制发送if(checkBoxHexSend.Checked)/我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数MatchCollection mc=Regex.Matches(txSend.Text,(?i)/da-f2);List buf=new List();/填充到这个临时列表中/依次添加到列表中foreach(Match m in mc)buf.Add(byte.Parse(m.Value);/转换列表为数组后发送com
45、m.Write(buf.ToArray(),0,buf.Count);/记录发送的字节数n=buf.Count;else/ascii 编码直接发送/包含换行符if(checkBoxNewlineSend.Checked)comm.WriteLine(txSend.Text);n=txSend.Text.Length+2;else/不包含换行符 comm.Write(txSend.Text);n=txSend.Text.Length;send_count+=n;/累加发送字节数labelSendCount.Text=Send:+send_count.ToString();/更新界面 private void buttonReset_Click(object sender,EventArgs e)/复位接受和发送的字节数计数器并更新界面。send_count=received_count=0;labelGetCount.Text=Get:0;labelSendCount.Text=Send:0;
限制150内