一、串口助手是什么?
通过电脑串口(包括USB口)收发数据并且显示的应用软件,一般用于电脑与嵌入式系统的通讯,借助于它来调试串口通讯或者系统的运行状态。也可以用于采集其他系统的数据,用于观察系统的运行情况。
简单的串口助手只能用来接收可发送串口数据,但是当你学会了如何开发串口助手,就可以根据通信协议开发上位机控制硬件和查看硬件的状态,先介绍如何开发一个简单的串口助手。
二、开发环境
- Visual Stdio
- C#
开发串口助手会用到Winform的相关知识,想要学习Winform的相关知识可以点击链接学习:http://c.biancheng.net/csharp/winform/
三、开发步骤
1. 串口参数
要连接上串口让并实现串口通信我们首先要搜索并选择需要进行通信的串口号,然后设置好波特率、数据位、停止位和检验位,串口号、波特率、数据位、停止位和检验位相当于打开特定串口的简单的密码,设置好之后就能打开串口进行通信。
名称作用常用数值波特率串口通信的速率(发送和接收各数据位的间隔时间)4800
9600
14400
19200
38400
56000
57600
115200
128000
256000数据位表示一组数据实际包含的数据位数5
6
7
8停止位停止位在最后,用以标志一个字符传送的结束,表示数据帧之间的间隔1
1.5
2
None校验位用于数据验证Even
Odd
None数据位概述图
2.UI设计
一个串口助手界面主要由参数设置、接收框、发送框三个部分以及一些控制按钮组成,参数设置使用ComboBox组件实现,接收和发送数据框使用RichTextBox组件实现,按钮使用Button组件实现,还有串口可以使用自带的SerialPort组件。
3.创建工程
(1)点击新建项目->选择Windows窗体应用->点击下一步->填写好项目名称和什么位置->点击创建
(2)实现UI
创建好项目后左边是工具栏,可以直接拖拉组件,里面有我们要用到的Label、ComboBox、RichTextBox、Button组件,可以根据字母排序查找需要的组件。右边是用来设置组件参数,按F4可以出现该框,可以设置组件名称、文件内容、颜色、大小、位置、字体、图标等参数,点击闪电标志里面可以设置绑定事件。
注意UI实现的过程中要设置好组件的Name,下面是我设置的一些Name参数
组件NameComboBoxcomboBox_PortNames
comboBox_BaudRate
comboBox_DataBit
comboBox_StopBit
comboBox_Parity
RichTextBoxrichTextBox_Receive
richTextBox_SendButtonbutton_Serach
button_OpenOrClose
button_CLEAR
button_Send串口波特率、数据位、停止位和校验位参数可以预先设置好,这里给出波特率参数设置,其他参数设置的方法一样SerialPort组件使用方法基本实现好的UI(审美不在线),大家可以自行优化点击启动的运行效果(3)功能代码实现代码要实现的功能主要有:
- 1.初始化函数 编写初始化函数时要在Form1.Designer.cs文件的Windows 窗体设计器生成的代码里面添加
this.Load += new System.EventHandler(this.Form1_Load);
如下图所示
/**************************************************************************/#region 初始化窗体privatevoidForm1_Load(object sender,EventArgs e)//加载界面程序{try{string[] str = SerialPort.GetPortNames();//获取连接到电脑的串口号并存进数组
comboBox_PortNames.Items.Clear();//清除串口号下拉框的内容
comboBox_PortNames.Items.AddRange(str);//将串口号添加到下拉框if(str.Length >0){
comboBox_PortNames.SelectedIndex =0;//设置ComboBox框的初始值
comboBox_BaudRate.SelectedIndex =7;
comboBox_DataBit.SelectedIndex =3;
comboBox_StopBit.SelectedIndex =1;
comboBox_Parity.SelectedIndex =2;}else{
MessageBox.Show("当前无串口连接!");}}catch{
MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************/
- 2.搜索串口
/**************************************************************************/#region 搜索串口(与初始化函数一样)privatevoidbutton_Serach_Click(object sender,EventArgs e){try{string[] str = SerialPort.GetPortNames();
comboBox_PortNames.Items.Clear();
comboBox_PortNames.Items.AddRange(str);if(str.Length >0){
comboBox_PortNames.SelectedIndex =0;
comboBox_BaudRate.SelectedIndex =7;
comboBox_DataBit.SelectedIndex =3;
comboBox_StopBit.SelectedIndex =1;
comboBox_Parity.SelectedIndex =2;}else{
MessageBox.Show("当前无串口连接!");}}catch{
MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************/
- 3.打开/关闭串口
/**************************************************************************/#region 打开串口privatevoidbutton_OpenOrClose_Click(object sender,EventArgs e){if(!serialPort1.IsOpen){if(comboBox_PortNames.SelectedItem ==null){
MessageBox.Show("请选择正确的串口","提示");return;}//设置串口参数
serialPort1.PortName = comboBox_PortNames.Text.ToString();//serialPort1是serialPort组件的Name
serialPort1.BaudRate = Convert.ToInt32(comboBox_BaudRate.SelectedItem.ToString());
serialPort1.DataBits = Convert.ToInt32(comboBox_DataBit.SelectedItem.ToString());//设置停止位if(comboBox_StopBit.Text =="One"){
serialPort1.StopBits = StopBits.One;}elseif(comboBox_StopBit.Text =="Two"){
serialPort1.StopBits = StopBits.Two;}elseif(comboBox_StopBit.Text =="OnePointFive"){
serialPort1.StopBits = StopBits.OnePointFive;}elseif(comboBox_StopBit.Text =="None"){
serialPort1.StopBits = StopBits.None;}//设置奇偶校验位if(comboBox_Parity.Text =="Odd"){
serialPort1.Parity = Parity.Odd;}elseif(comboBox_Parity.Text =="Even"){
serialPort1.Parity = Parity.Even;}elseif(comboBox_Parity.Text =="None"){
serialPort1.Parity = Parity.None;}try{//禁止操作组件
comboBox_PortNames.Enabled =false;
comboBox_BaudRate.Enabled =false;
comboBox_DataBit.Enabled =false;
comboBox_StopBit.Enabled =false;
comboBox_Parity.Enabled =false;
button_Serach.Enabled =false;
serialPort1.Open();//设置完参数后打开串口
button_OpenOrClose.Text ="Close";//更改Open按钮文本内容}catch{
MessageBox.Show("串口打开失败!");}
serialPort1.DataReceived +=newSerialDataReceivedEventHandler(SerialDataReceive);//打开串口后绑定数据接收}elseif(button_OpenOrClose.Text =="Close"){try{//允许操作组件
comboBox_PortNames.Enabled =true;
comboBox_BaudRate.Enabled =true;
comboBox_DataBit.Enabled =true;
comboBox_StopBit.Enabled =true;
comboBox_Parity.Enabled =true;
button_Serach.Enabled =true;
serialPort1.DiscardInBuffer();//清除缓冲区的数据
serialPort1.Close();
button_OpenOrClose.Text ="Open";//更改Close按钮文本内容}catch{
MessageBox.Show("串口打开失败!");}}}#endregion/**************************************************************************/
- 4.接收数据
/**************************************************************************/#region 接收串口数据/*
开辟缓存区 根据具体协议内容获取一帧数据进行接收中断处理
*///变量List<byte> sp_buffer =newList<byte>(4096);//串口缓存区int sp_buffer_max =4096;//串口缓存区最大缓存字节数privatevoidSerialDataReceive(object sender,SerialDataReceivedEventArgs e){if(serialPort1.IsOpen ==false){
serialPort1.Close();return;}int Byte_len = serialPort1.BytesToRead;//读取缓存的数据长度byte[] Rc_byte =newbyte[Byte_len];
serialPort1.Read(Rc_byte,0,Byte_len);//将缓存数据存储进字节数组里面if(sp_buffer.Count > sp_buffer_max)//缓存超过字节数 先丢弃前面的字节
sp_buffer.RemoveRange(0,sp_buffer_max);//丢弃前面的字节0到sp_buffer_max
sp_buffer.AddRange(Rc_byte);//存入缓存区byte[] ruffer =newbyte[9192];//用来存放缓冲区的数据流//对数据流进行筛选,缓冲区每一组数据个数大于4则为我们想要的数据流if(sp_buffer.Count >4){
sp_buffer.CopyTo(0,ruffer,0,sp_buffer.Count);
Task.Run(()=>printf_data(ruffer, sp_buffer.Count,1));//打印数据流}}#endregion/**************************************************************************/
- 5.打印数据流
/**************************************************************************/#region 打印数据流voidprintf_data(byte[] Frame,int Length,int T_R)//打印串口数据{Int16 i_len;StringBuilder s =newStringBuilder();if(T_R ==0)
s.Append("发送:");else
s.Append("接收:");for(i_len =0; i_len < Length; i_len++)//打印字符串{
s.Append(Frame[i_len].ToString("X2"));
s.Append(" ");}
s.Append("["+ DateTime.Now.ToString("HH:mm:ss fff")+"]");
s.Append("\r\n");string str_show = s.ToString();MethodInvoker mi =newMethodInvoker(()=>{if(richTextBox_ReceiveBox.Lines.Count()>20)
richTextBox_ReceiveBox.Clear();
richTextBox_ReceiveBox.AppendText(str_show);});BeginInvoke(mi);/*
textBox_com_data.Focus(); //获取焦点
textBox_com_data.Select(textBox_com_data.TextLength, 0);//光标
textBox_com_data.ScrollToCaret(); //滚动条*/}#endregion/**************************************************************************/
- 6.发送数据
/**************************************************************************/#region 发送数据privatevoidbutton_Send_Click(object sender,EventArgs e){if(serialPort1.IsOpen){string[] sendbuff = richTextBox1.Text.Split();//分割输入的字符串,判断有多少个字节需要发送int Buff_Len = sendbuff.Length;byte[] buff =newbyte[Buff_Len];for(int i =0; i < sendbuff.Length; i++){
buff[i]=byte.Parse(sendbuff[i], System.Globalization.NumberStyles.HexNumber);//格式化字符串为十六进制数值}try{
serialPort1.Write(buff,0, buff.Length);//写数据
Task.Run(()=>printf_data(buff, Buff_Len,0));}catch{
MessageBox.Show("发送失败!!");}}else{
MessageBox.Show("串口未打开!");}}#endregion/**************************************************************************/
- 7.清除接收框
/**************************************************************************/#region 清除输出框privatevoidClear_Click(object sender,EventArgs e){
richTextBox_ReceiveBox.Clear();//清楚数据接收框数据}#endregion/**************************************************************************/
功能函数写好后绑定按钮和函数
(4)全部代码整合
usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;usingSystem.IO.Ports;usingSystem.IO;namespaceSerial{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}/**************************************************************************/#region 初始化窗体privatevoidForm1_Load(object sender,EventArgs e)//加载界面程序{try{string[] str = SerialPort.GetPortNames();//获取连接到电脑的串口号并存进数组
comboBox_PortNames.Items.Clear();//清除串口号下拉框的内容
comboBox_PortNames.Items.AddRange(str);//将串口号添加到下拉框if(str.Length >0){
comboBox_PortNames.SelectedIndex =0;//设置ComboBox框的初始值
comboBox_BaudRate.SelectedIndex =7;
comboBox_DataBit.SelectedIndex =3;
comboBox_StopBit.SelectedIndex =0;
comboBox_Parity.SelectedIndex =2;}else{
MessageBox.Show("当前无串口连接!");}}catch{
MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************//**************************************************************************/#region 搜索串口(与初始化函数一样)privatevoidbutton_Serach_Click(object sender,EventArgs e){try{string[] str = SerialPort.GetPortNames();
comboBox_PortNames.Items.Clear();
comboBox_PortNames.Items.AddRange(str);if(str.Length >0){
comboBox_PortNames.SelectedIndex =0;
comboBox_BaudRate.SelectedIndex =7;
comboBox_DataBit.SelectedIndex =3;
comboBox_StopBit.SelectedIndex =0;
comboBox_Parity.SelectedIndex =2;}else{
MessageBox.Show("当前无串口连接!");}}catch{
MessageBox.Show("无串口设备!/r/n请检查是否连接设备!/r/n请检查设备驱动!");}}#endregion/**************************************************************************//**************************************************************************/#region 打开串口privatevoidbutton_OpenOrClose_Click(object sender,EventArgs e){if(!serialPort1.IsOpen){if(comboBox_PortNames.SelectedItem ==null){
MessageBox.Show("请选择正确的串口","提示");return;}//设置串口参数
serialPort1.PortName = comboBox_PortNames.Text.ToString();//serialPort1是serialPort组件的Name
serialPort1.BaudRate = Convert.ToInt32(comboBox_BaudRate.SelectedItem.ToString());
serialPort1.DataBits = Convert.ToInt32(comboBox_DataBit.SelectedItem.ToString());//设置停止位if(comboBox_StopBit.Text =="One"){
serialPort1.StopBits = StopBits.One;}elseif(comboBox_StopBit.Text =="Two"){
serialPort1.StopBits = StopBits.Two;}elseif(comboBox_StopBit.Text =="OnePointFive"){
serialPort1.StopBits = StopBits.OnePointFive;}elseif(comboBox_StopBit.Text =="None"){
serialPort1.StopBits = StopBits.None;}//设置奇偶校验位if(comboBox_Parity.Text =="Odd"){
serialPort1.Parity = Parity.Odd;}elseif(comboBox_Parity.Text =="Even"){
serialPort1.Parity = Parity.Even;}elseif(comboBox_Parity.Text =="None"){
serialPort1.Parity = Parity.None;}try{//禁止操作组件
comboBox_PortNames.Enabled =false;
comboBox_BaudRate.Enabled =false;
comboBox_DataBit.Enabled =false;
comboBox_StopBit.Enabled =false;
comboBox_Parity.Enabled =false;
button_Serach.Enabled =false;
serialPort1.Open();//设置完参数后打开串口
button_OpenOrClose.Text ="Close";//更改Open按钮文本内容}catch{
MessageBox.Show("串口打开失败!");}
serialPort1.DataReceived +=newSerialDataReceivedEventHandler(SerialDataReceive);//打开串口后绑定数据接收}elseif(button_OpenOrClose.Text =="Close"){try{//允许操作组件
comboBox_PortNames.Enabled =true;
comboBox_BaudRate.Enabled =true;
comboBox_DataBit.Enabled =true;
comboBox_StopBit.Enabled =true;
comboBox_Parity.Enabled =true;
button_Serach.Enabled =true;
serialPort1.DiscardInBuffer();//清除缓冲区的数据
serialPort1.Close();
button_OpenOrClose.Text ="Open";//更改Close按钮文本内容}catch{
MessageBox.Show("串口打开失败!");}}}#endregion/**************************************************************************//**************************************************************************/#region 接收串口数据/*
开辟缓存区 根据具体协议内容获取一帧数据进行接收中断处理
*///变量List<byte> sp_buffer =newList<byte>(4096);//串口缓存区int sp_buffer_max =4096;//串口缓存区最大缓存字节数privatevoidSerialDataReceive(object sender,SerialDataReceivedEventArgs e){if(serialPort1.IsOpen ==false){
serialPort1.Close();return;}int Byte_len = serialPort1.BytesToRead;//读取缓存的数据长度byte[] Rc_byte =newbyte[Byte_len];
serialPort1.Read(Rc_byte,0, Byte_len);//将缓存数据存储进字节数组里面if(sp_buffer.Count > sp_buffer_max)//缓存超过字节数 先丢弃前面的字节
sp_buffer.RemoveRange(0, sp_buffer_max);//丢弃前面的字节0到sp_buffer_max
sp_buffer.AddRange(Rc_byte);//存入缓存区byte[] ruffer =newbyte[9192];//用来存放缓冲区的数据流//对数据流进行筛选,缓冲区每一组数据个数大于4则为我们想要的数据流if(sp_buffer.Count >4){
sp_buffer.CopyTo(0, ruffer,0, sp_buffer.Count);
Task.Run(()=>printf_data(ruffer, sp_buffer.Count,1));//打印数据流}}#endregion/**************************************************************************//**************************************************************************/#region 打印数据流voidprintf_data(byte[] Frame,int Length,int T_R)//打印串口数据{Int16 i_len;StringBuilder s =newStringBuilder();if(T_R ==0)
s.Append("发送:");else
s.Append("接收:");for(i_len =0; i_len < Length; i_len++)//打印字符串{
s.Append(Frame[i_len].ToString("X2"));
s.Append(" ");}
s.Append("["+ DateTime.Now.ToString("HH:mm:ss fff")+"]");
s.Append("\r\n");string str_show = s.ToString();MethodInvoker mi =newMethodInvoker(()=>{if(richTextBox_ReceiveBox.Lines.Count()>20)
richTextBox_ReceiveBox.Clear();
richTextBox_ReceiveBox.AppendText(str_show);});BeginInvoke(mi);/*
textBox_com_data.Focus(); //获取焦点
textBox_com_data.Select(textBox_com_data.TextLength, 0);//光标
textBox_com_data.ScrollToCaret(); //滚动条*/}#endregion/**************************************************************************//**************************************************************************/#region 发送数据privatevoidbutton_Send_Click(object sender,EventArgs e){if(serialPort1.IsOpen){string[] sendbuff = richTextBox_Send.Text.Split();//分割输入的字符串,判断有多少个字节需要发送int Buff_Len = sendbuff.Length;byte[] buff =newbyte[Buff_Len];for(int i =0; i < sendbuff.Length; i++){
buff[i]=byte.Parse(sendbuff[i], System.Globalization.NumberStyles.HexNumber);//格式化字符串为十六进制数值}try{
serialPort1.Write(buff,0, buff.Length);//写数据
Task.Run(()=>printf_data(buff, Buff_Len,0));}catch{
MessageBox.Show("发送失败!!");}}else{
MessageBox.Show("串口未打开!");}}#endregion/**************************************************************************//**************************************************************************/#region 清除输出框privatevoidClear_Click(object sender,EventArgs e){
richTextBox_ReceiveBox.Clear();//清楚数据接收框数据}#endregion/**************************************************************************/}}
(5)实现效果
到这我们就完整地做一个简单的串口助手了,我们可以进行二次开发,添加按钮绑定特定的的指令,每次可以通过点击按钮控制硬件,或者对接收到的数据流进行解析并显示出来,这样就能够做出项目需要的上位机了。
版权归原作者 坚强的头发 所有, 如有侵权,请联系我们删除。