这节教给用户使用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里面的数据
无
奇校验
偶校验
----------------------------------以上需要录制演示视频
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#常用事件对应值
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