C#串口调试助手

说明

 这节教给用户使用C#做一个串口调试助手,

 用户可在此基础上依据自己的需求做修改

 本人的其它关于串口通信的上位机也会在此基础上做修改,

 请用户认真学习本节.

技术图片

 

 

 

 

 

新建工程

技术图片

 

 

 

布局串口配置页面

1.拖拽上来一个 GroupBox

技术图片

 

 

 

 2.重新命名

技术图片

 

 

 

 3.拖拽上去6个Label,位置和显示如下

技术图片

 

 

 

4. 拖拽上去5个ComboBox,1个Button,位置如下

设置Button的显示改为打开串口

技术图片

 

 

 

 

-----------------------------------------------------------------------------------------------以上需要嵌入视频演示

 

5. 添加ComboBox显示的内容

5.1:以波特率的ComboBox详细叙述

技术图片

 

 

 

技术图片

 

 

 

13824009216004608002560002304001280001152007680057600430003840019200144009600
4800
1200


运行一下

技术图片

 

 

 

 

5.2:设置停止位的ComboBox里面的数据

 

 

技术图片

 

 

 

 

1
1.5
2

 

5.3:设置数据位的ComboBox里面的数据

 技术图片

 

 

8
7
6
5

 

5.4:设置校验位的ComboBox里面的数据

技术图片

 

 

 


奇校验
偶校验

 

----------------------------------以上需要录制演示视频

 

 

 

拖拽串口控件

技术图片

 

 

 

 

技术图片

 

 

 

 

获取电脑上可用串口,并显示在端口号的ComboBox里面

1.双击窗体,进入窗体加载回调函数

技术图片

 

 

 

2.在页面加载函数中填写以下程序

技术图片

 

 

 

 string[] ports = System.IO.Ports.SerialPort.GetPortNames();//获取电脑上可用串口号 comboBox1.Items.AddRange(ports);//给comboBox1添加数据 comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//如果里面有数据,显示第0个

 

 

警告:请选择对应的comboBox

comboBox1 

技术图片

 

 

 

3.启动

comboBox里面显示当前电脑上可用串口号

技术图片

 

4. 设置下默认显示的配置

技术图片

 

 

 

 comboBox2.Text = "115200";/*默认波特率:115200*/ comboBox3.Text = "1";/*默认停止位:1*/ comboBox4.Text = "8";/*默认数据位:8*/ comboBox5.Text = "无";/*默认奇偶校验位:无*/

 

5.启动

技术图片

 

 

 

 

打开关闭串口

1.双击按钮进入按钮点击事件

技术图片

 

 

 

2.按钮点击事件函数内部程序如下

技术图片

 

 

 

复制代码
 if (button1.Text == "打开串口"){//如果按钮显示的是打开串口 try{//防止意外错误 serialPort1.PortName = comboBox1.Text;//获取comboBox1要打开的串口号 serialPort1.BaudRate = int.Parse(comboBox2.Text);//获取comboBox2选择的波特率 serialPort1.DataBits = int.Parse(comboBox4.Text);//设置数据位 /*设置停止位*/ if (comboBox3.Text == "1") { serialPort1.StopBits = StopBits.One; } else if (comboBox3.Text == "1.5") { serialPort1.StopBits = StopBits.OnePointFive; } else if (comboBox3.Text == "2") { serialPort1.StopBits = StopBits.Two; } /*设置奇偶校验*/ if (comboBox5.Text == "无") { serialPort1.Parity = Parity.None; } else if (comboBox5.Text == "奇校验") { serialPort1.Parity = Parity.Odd; } else if (comboBox5.Text == "偶校验") { serialPort1.Parity = Parity.Even; } serialPort1.Open();//打开串口 button1.Text = "关闭串口";//按钮显示关闭串口 } catch (Exception err) { MessageBox.Show("打开失败"+ err.ToString(), "提示!");//对话框显示打开失败 } } else{//要关闭串口 try{//防止意外错误 serialPort1.Close();//关闭串口 } catch (Exception){} button1.Text = "打开串口";//按钮显示打开 }
复制代码

 

提示:serialPort1

技术图片

 

 

 

 

 

3.启动测试

技术图片  技术图片

 

 

检测串口热插拔

1.在串口模块拔插的时候,重新更新下串口comboBox的内容

然后:

如果热插拔的是用户刚打开的串口,则按钮显示 "打开串口",comboBox显示初始化的串口号

如果热插拔的不是用户刚打开的串口,则comboBox还是显示咱刚才的串口号

 

2.用一个变量记录用户打开的串口号

技术图片

 

 

 

 

 

 

String serialPortName;

 

 

 

 

serialPortName = comboBox1.Text;

 

 

 

2.添加系统函数 protected override void WndProc(ref Message m)

技术图片

 

 

 

复制代码
 protected override void WndProc(ref Message m) { if (m.Msg == 0x0219){//设备改变 if (m.WParam.ToInt32() == 0x8004){//usb串口拔出 string[] ports = System.IO.Ports.SerialPort.GetPortNames();//重新获取串口 comboBox1.Items.Clear();//清除comboBox里面的数据 comboBox1.Items.AddRange(ports);//给comboBox1添加数据 if (button1.Text == "关闭串口"){//用户打开过串口 if (!serialPort1.IsOpen){//用户打开的串口被关闭:说明热插拔是用户打开的串口 button1.Text = "打开串口"; serialPort1.Dispose();//释放掉原先的串口资源 comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号 } else{ comboBox1.Text = serialPortName;//显示用户打开的那个串口号 } } else{//用户没有打开过串口 comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号 } } else if (m.WParam.ToInt32() == 0x8000){//usb串口连接上 string[] ports = System.IO.Ports.SerialPort.GetPortNames();//重新获取串口 comboBox1.Items.Clear(); comboBox1.Items.AddRange(ports); if (button1.Text == "关闭串口"){//用户打开过一个串口 comboBox1.Text = serialPortName;//显示用户打开的那个串口号 } else{ comboBox1.SelectedIndex = comboBox1.Items.Count > 0 ? 0 : -1;//显示获取的第一个串口号 } } } base.WndProc(ref m); }
复制代码

 

提示:C#常用事件对应值

技术图片 
View Code

 

 

3.启动测试 :用户自己测试热插拔

 

串口接收数据

1.拖拽上来一个TextBox

技术图片

 

 

 

 

2.点击TextBox上面的箭头,选择MultiLine (允许显示多行)

技术图片

 

技术图片

 

 

 2.拖拽TextBox使其达到自己满意的长宽,

设置TextBox的Scrollbars属性为Vertical  (显示竖直滚动条,可以滚动显示接收数据)

技术图片

 

 

 

 

 

 

3.为了更好看些,设置TextBox的背景色为黑色 (Black) 

显示的字体颜色为草绿色 (LawnGreen)

技术图片

 

 

 

4.选择serialPort1 -> 选择事件 -> 双击DataReceived

生成串口接收回调函数

技术图片

 

 

 

 

技术图片

 

 

 

5.在回调函数中接收数据,然后把接收数据显示在TextBox

技术图片

 

 

 

复制代码
 int len = serialPort1.BytesToRead;//获取可以读取的字节数 byte[] buff = new byte[len];//创建缓存数据数组 serialPort1.Read(buff, 0, len);//把数据读取到buff数组 string str = Encoding.Default.GetString(buff);//Byte值根据ASCII码表转为 String Invoke((new Action(() => //C# 3.0以后代替委托的新方法 { textBox1.AppendText(str);//对话框追加显示数据 })));
复制代码

 

 

6.测试接收数据

为了测试接收数据,可以安装虚拟串口软件

https://qqqqqbucket.oss-cn-beijing.aliyuncs.com/LearnC%23/VSPD6.9.zip

安装好以后

技术图片

 

 

 

技术图片

 

 

提示:

按照上面的操作以后

电脑上便会有了COM1和COM2

COM1发送的数据将会自动发送给COM2

COM2发送的数据将会自动发送给COM1

 

7.测试

技术图片

 

 

 

 

技术图片

 

 

8.增加选择16进制显示(CheckBox),增加数据清除按钮

技术图片

 

 

 

 

 

9.关于显示字符串和显示16进制说明

 

在上面的测试接收中COM1那个串口调试助手发送的

数据为字符串 123456

实际传输是按照ASCii码表传输的对应的16进制

技术图片

 

 

 

实际上buff数组

buff[0] = 49 = 0x31

执行 string str = Encoding.Default.GetString(buff); 以后

str = 0 (字符串0)

所以接收显示为:123456

 

 

当COM1选择16进制传输的时候

假设传输的是0x01 0x02 0x03 0x04 0x05 0x06

技术图片

 

 

COM1实际发送也是:0x01 0x02 0x03 0x04 0x05 0x06

不过ASCII码表中显示的字符是乱码

技术图片

 

 

 

所以咱们需要另外添加转换程序

程序如下:

复制代码
 /// <字节数组转16进制字符串> /// <param name="bytes"></param> /// <returns> String 16进制显示形式</returns> public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try { if (bytes != null) { for (int i = 0; i < bytes.Length; i++) { returnStr += bytes[i].ToString("X2"); returnStr += " ";//两个16进制用空格隔开,方便看数据 } } return returnStr; } catch (Exception) { return returnStr; } }
复制代码

 

实际上就一句话:

bytes[i].ToString("X2");  

如果变量是 0x01

则经过函数以后会返回 "01" ,字符串形式

 

 

10.如果用户选择16进制,则接收的数据按照16进制显示

技术图片

 

 

 

复制代码
 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { int len = serialPort1.BytesToRead;//获取可以读取的字节数 byte[] buff = new byte[len];//创建缓存数据数组 serialPort1.Read(buff, 0, len);//把数据读取到buff数组 Invoke((new Action(() =>{//C# 3.0以后代替委托的新方法 if (checkBox1.Checked){//16进制显示 textBox1.AppendText(byteToHexStr(buff)); } else{ textBox1.AppendText(Encoding.Default.GetString(buff));//对话框追加显示数据 } }))); } /// <字节数组转16进制字符串> /// <param name="bytes"></param> /// <returns> String 16进制显示形式</returns> public static string byteToHexStr(byte[] bytes) { string returnStr = ""; try{ if (bytes != null){ for (int i = 0; i < bytes.Length; i++){ returnStr += bytes[i].ToString("X2"); returnStr += " ";//两个16进制用空格隔开,方便看数据 } } return returnStr; } catch (Exception){ return returnStr; } }
复制代码

 

 

 

11.测试

技术图片

 

 

技术图片

 

 

12.双击清除接收按钮,生成按钮点击事件

写上 textBox1.Clear(); //清除接收对话框显示的数据

技术图片

 

 

技术图片

 

 

 

 

 

 

串口发送数据

1.页面布局如下:

技术图片

 

 

 

 

2.双击点击发送按钮,生成按钮点击事件

技术图片

3.事件函数中,程序如下

技术图片

 

 

复制代码
 String Str = textBox2.Text.ToString();//获取发送文本框里面的数据 try { if (Str.Length > 0) { serialPort1.Write(Str);//串口发送数据 } } catch (Exception){ }
复制代码

 

 

4.测试发送数据

技术图片

 

 技术图片

 

 

 

5.发送16进制数据说明

首先需要明确

技术图片

 

 

 

假设文本框里面填写的是:01 02 03 04 05 06

Str获取的是01 02 03 04 05 06  是字符串

咱所希望的是把01转为0x01

02转为0x02

.

.

.

然后发送

 

首先先说思路

默认把文本框每隔两个字符作为一个16进制数

假设文本框中是

010203040506   (偶数个数据)

则01 ,02 ,03 ,04 ,05 ,06 分别作为16进制

 

如果文本框中的字符个数是奇数个

1115268

则默认先把前面每隔两个作为一个16进制数

最后一个数前面补零

0x11,0x15,0x26,0x08

 

 

封装的解析转换程序如下:

复制代码
 /// <字符串转16进制格式,不够自动前面补零> /// /// </summary> /// <param name="hexString"></param> /// <returns></returns> private static byte[] strToToHexByte(String hexString) { int i; hexString = hexString.Replace(" ", "");//清除空格 if ((hexString.Length % 2) != 0)//奇数个 { byte[] returnBytes = new byte[(hexString.Length + 1) / 2]; try { for (i = 0; i < (hexString.Length - 1) / 2; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, ‘0‘), 16); } catch { MessageBox.Show("含有非16进制字符", "提示"); return null; } return returnBytes; } else { byte[] returnBytes = new byte[(hexString.Length) / 2]; try { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { MessageBox.Show("含有非16进制字符", "提示"); return null; } return returnBytes; } }
复制代码

 

 

6.完善发送16进制程序

技术图片

 

 

 

 

 

复制代码
 private void button3_Click(object sender, EventArgs e) { String Str = textBox2.Text.ToString();//获取发送文本框里面的数据 try { if (Str.Length > 0) { if (checkBox2.Checked)//16进制发送 { byte[] byt = strToToHexByte(Str); serialPort1.Write(byt, 0, byt.Length); } else { serialPort1.Write(Str);//串口发送数据 } } } catch (Exception){ } } /// <字符串转16进制格式,不够自动前面补零> /// /// </summary> /// <param name="hexString"></param> /// <returns></returns> private static byte[] strToToHexByte(String hexString) { int i; hexString = hexString.Replace(" ", "");//清除空格 if ((hexString.Length % 2) != 0)//奇数个 { byte[] returnBytes = new byte[(hexString.Length + 1) / 2]; try { for (i = 0; i < (hexString.Length - 1) / 2; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, ‘0‘), 16); } catch { MessageBox.Show("含有非16进制字符", "提示"); return null; } return returnBytes; } else { byte[] returnBytes = new byte[(hexString.Length) / 2]; try { for (i = 0; i < returnBytes.Length; i++) { returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } } catch { MessageBox.Show("含有非16进制字符", "提示"); return null; } return returnBytes; } }
复制代码

 

 

 

7.测试

技术图片

 

 

 

 

技术图片

 

 

 

 

 

8.双击 清除发送 按钮,生成按钮点击事件

textBox2.Clear();//清除发送文本框里面的内容

技术图片

 技术图片

 

 

 

提醒

提醒

提醒

技术图片

 

 

 

 

C#的串口中断回调函数也是不定个数的进入

假设另一个串口发送的数据为 123456

int len = serialPort1.BytesToRead;//获取可以读取的字节数
byte[] buff = new byte[len];//创建缓存数据数组

serialPort1.Read(buff, 0, len);//把数据读取到buff数组

 

len 可能等于 2

则存入buff 的数据为 

buff [0] = ‘1‘

buff [1] = ‘2‘

 

然后接着又进来这个函数

len 可能等于 4

则存入buff 的数据为 

buff [0] = ‘3‘

buff [1] = ‘4‘

buff [2] = ‘5‘

buff [3] = ‘6‘

 

如若想接收到一条完整的数据以后再进行处理

可参考: https://www.cnblogs.com/yangfengwu/p/11669373.html

 

增加一个定时器,用于空闲时间检测

 

 

转载自:https://www.cnblogs.com/yangfengwu/p/12382103.html

相关文章