相信很多网友在学习C#编程时,遇到串口通讯的一系列问题。目前我也在学习C#串口编程类的基础知识,在网上也找了一些资料,但都存在一些问题,现在他们基础上再进行一定的修改,且更详细的表达如何实现串口编程,实现串口的发送与接收。本文目的在于:
1)总结C# 编程串口的属性定义、成员变量以及方法等问题;
2)掌握串口的发送与接收,利用虚拟串口来进行通讯,体验串口通讯;
实施步骤如下:
1、程序主界面如下:
2、启动VS2005编程工具,新建一个项目,并自拟定项目名称和存放路径。
3、按主界面样式设计好各个控件。
4、代码实施(简单介绍——具体实验请参见源码,下文可下载):
(1)在Form1.cs程序头文件处添加代码:
using System.Threading;//用于启用线程类;
using System.IO.Ports;//用于调用串口类函数
(2)添加成员变量
串口的基本参数有:串口号,波特率,数据位,停止位等,如图所示:
public string iPort = "com1"; //默认为串口1
public int iRate = 9600; //波特率1200,2400,4800,9600
public byte bSize = 8; //8 bits
public int iTimeout = 1000; //延时时长
public SerialPort serialPort1 = new SerialPort();//定义一个串口类的串口变量
string serialReadString; //用于串口接收数据
public bool IsCirlce;//判断是否选用循环发送数据
public Thread Thd_Send; //开辟一个专用于发送数据的线程
public byte[] recb; //用于存放接收数据的数组
(3)程序开启,串口初始化
private void Form1_Load(object sender, EventArgs e)
{
Microsoft.VisualBasic.Devices.Computer cmbCOM = new Microsoft.VisualBasic.Devices.Computer();
foreach (string s in cmbCOM.Ports.SerialPortNames)
{
cmb_port.Items.Add(s);
}
IsCirlce = false;
}
(4)打开/关闭串口按钮代码
//打开/关闭串口
private void button5_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
{
serialPort1.Close();
btnOpenCloseCOM.Text = "开启串口";
msg.AppendText("串口被关闭……\r\n");
}
else
{
Parity myParity = Parity.None; //奇偶校验对象
StopBits MyStopBits = StopBits.One; //停止位对象
serialPort1.PortName = cmb_port.Text;
serialPort1.BaudRate = iRate; //波特率
serialPort1.DataBits = bSize; //数据位的长度
serialPort1.Parity = myParity; //奇偶校验位
serialPort1.StopBits = MyStopBits; //停止位
serialPort1.ReadTimeout = iTimeout; //读取超时【从有数据开始,读取的时间超过这个设置的值,则引发异常】
serialPort1.Open();
btnOpenCloseCOM.Text = "关闭串口";
msg.AppendText("串口成功开启……\r\n");
}
}
//开串口
public bool OpenCom()
{
try
{
if (serialPort1.IsOpen)
{
//mycom1.Close();
//mycom1.Open(); //打开串口
msg.AppendText("串口已打开\r\n");
}
else
{
serialPort1.Open();//打开串口
}
return true;
}
catch (Exception e)
{
MessageBox.Show("错误:" + e.Message);
return false;
}
}
(5)发送按钮(主程序)
//发送按钮
private void button1_Click(object sender, EventArgs e)
{
if (t_send.Text == "")
{ MessageBox.Show("发送数据为空!"); return; }
Thd_Send = new Thread(new ThreadStart(SendData));
Thd_Send.Start();
}
//发送数据函数
public void SendData() //主要看SubSendData函数,这个函数本身不用太过于关注,无法是控件的异步调用修改控件的属性
{
if (System.Convert.ToBoolean(btnSend.Text == "发送"))
{
if (IsCirlce)
{
btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text="停止"; }));
//MethodInvoker是无参数的系统定义好的简单方法委托,这里使用了匿名方法,此句执行结果是修改按钮的文本为“停止”
while (true)
{
if (IsCirlce && serialPort1.IsOpen && (System.Convert.ToBoolean(btnSend.Text =="停止")))
{
SubSendData();
}
else
{ goto L1; }
}
}
else
{
SubSendData();
}
}
else
{
btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text = "发送"; }));
}
L1:
btnSend.Invoke(new MethodInvoker(delegate { btnSend.Text = "发送"; }));
}
//发送数据子函数,实际上是在新开的线程中执行这个函数
public void SubSendData()
{
//byte[] ZhengHeHouData = new byte[mycom1.ReadBufferSize];
//定义一个无空格二位16进制排列的数组,这样可控制用户输入
byte[] ZhengHeHouData = NoSpace_2Hex_SendData_ZhengHe(); //这里是整合后的发送的数组,每二位组成一个16进制的数排列【sting转换为Byte类型】
int SendNumB = 0;
try
{
serialPort1.WriteLine(Dis_Package(ZhengHeHouData)); //Dis_Package函数是Byte类型转换为string类型【通过ASC2码把字符串自动转为字节数组发送】
SendNumB = serialPort1.BytesToWrite;
msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n发送数据!(" + serialPort1.BytesToWrite + "):" + Dis_Package(ZhengHeHouData)); }));
Thread.Sleep(System.Convert.ToInt32(txtTimeGas.Text));
}
//mycom1.Read(ZhengHeHouData,1,1);
//recb = mycom1.BytesToRead;
//msg.AppendText("\r\n接收到数据包:" + Dis_Package(recb));
//mycom1.Read(ZhengHeHouData, 0, ZhengHeHouData.Length);
//if(recb.Length!=0)
//msg.AppendText("\r\n接收到数据包:" + Dis_Package(recb));
catch
{
//msg.AppendText("\r\n发送失败!");
msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n发送失败!"); }));
return;
}
}
//去掉发送数组中的空格
public string Delspace(string txt_SendData)
{
string Infact_SendData = "";
for (int i = 0; i < txt_SendData.Length; i++) //txt_SendData.Length为发送框内的数据
{
if (txt_SendData[i] != ' ')
Infact_SendData += txt_SendData[i];
}
return Infact_SendData;
}
//提取数据包--数组函数,返回整合后的数据,无空格二位16进制排列的数组
public byte[] NoSpace_2Hex_SendData_ZhengHe()
{
string NoSpace_SendData = Delspace(t_send.Text); //去掉所有空格,整合数据
byte[] SendData = new byte[50];
int j = 0;
for (int i = 0; i < NoSpace_SendData.Length; i = i + 2, j++) //将无空格发送的数据每二位组成一个数,存放在新的数组SendData中
SendData[j] = Convert.ToByte(NoSpace_SendData.Substring(i, 2), 16);
byte[] Last_SendData = new byte[j]; //Last_SendData最终返回的发送数据
Array.Copy(SendData, Last_SendData, j); //复制整合后的数据,放入最终返回的数组里,以便调用
return Last_SendData; //最终返回的发送数据
}
//显示包信息,以每二位加一个空格显示数据
public string Dis_Package(byte[] reb)
{
string temp = "";
foreach (byte b in reb)
temp += b.ToString("X2") + " "; //以每二位加一个空格显示数据
return temp;
}
(6) 清空列表显示
private void button3_Click(object sender, EventArgs e)
{
t_send.Text = string.Empty; //清空发送端
msg.Text = string.Empty; //清空信息列表框
}
(7)应用设置按钮的代码
//应用设置
private void button2_Click(object sender, EventArgs e) //串口初始化,对应图中“应用设置”按钮
{
Parity myParity = Parity.None;
StopBits MyStopBits = StopBits.One;
if (serialPort1.IsOpen)
{
//msg.AppendText("串口处于开启状态,若要重新更换端口,请先关闭!\r\n");
serialPort1.Close();
}
if (cmb_port.Text == "")
{
msg.AppendText("串口不能为空,请选择串口!\r\n");
}
else
{
serialPort1.PortName = Convert.ToString(cmb_port.Text); //1,2,3,4
serialPort1.BaudRate = Convert.ToInt16(t_rate.Text); //1200,2400,4800,9600
serialPort1.DataBits = Convert.ToByte(t_bytesize.Text, 10); //8 bits
serialPort1.Parity = myParity; // 0-4=no,odd,even,mark,space
serialPort1.StopBits = MyStopBits;
//iTimeout=3;
if (this.OpenCom())
msg.AppendText("串口初始化成功……\r\n");
else
msg.AppendText("串口初始化失败!\r\n");
}
}
(8) 程序关闭,结束串口
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
serialPort1.Close();
}
(9) 用于显示接收数据,这个事件处理程序会自动开一个线程然后执行
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
serialReadString = "";
serialReadString += serialPort1.ReadExisting(); //读取所有数据,返回字符串【字节数组通过ASC2码翻译成字符串】
msg.Invoke(new MethodInvoker(delegate { msg.AppendText("\r\n接收数据:" + serialReadString); }));
}
(10)控制输入
//发送文本框内限制输入,须偶数位
private void t_send_TextChanged(object sender, EventArgs e)
{
btnSend.Enabled = false;
string SendDataLength = Delspace(t_send.Text); //去掉所有空格,整合数据
if (SendDataLength.Length % 2 != 0)
{
t_send.Focus();
}
else
{
btnSend.Enabled = true;
}
}
(11)复选框程序(是否启用无限循环)
private void chkCircle_CheckedChanged(object sender, EventArgs e)
{
if (IsCirlce)
{
IsCirlce = false;
}
else
{
IsCirlce = true;
}
}
5、虚拟串口配置(可下载此软件,按照下载后的软件进行配置说明即可使用)
6、打开UartAssist.exe软件,运行如下所示:
7、在配置好VSPM后,虚拟串口有3、4、5、6共四个串口,启动VSPM后,再运行串口通讯选择串口4,此时串口通讯程序与上面的 UartAssist.exe软件串口3进行了互联,如下图所示。选择串口4后,点击[应用设置]按钮,此时文本框显示:串口初始化成功....
8、发送测试,在设置数据包文本框内输入:12345678
9、接收数据,如图:
10、源码下载。见原文