C#上位机开发——串口通信,读取并处理数据
目标:实现一个将串口传输的16进制数据转换成需要的数据的上位机,并且显示数据。重力传感器通过串口发送12位16进制数据,将其转换为带有正负的重量。选择视图——工具箱,将所需控件添加到幕布上。工具:vs2022,串口调试器。右击控件属性修改控件名称、内容。在form1.cs中编辑代码。
文章共3,968字 · 阅读需要大约14分钟
一键AI生成摘要,助你高效阅读
问答
·
目标:实现一个将串口传输的16进制数据转换成需要的数据的上位机,并且显示数据
工具:vs2022,串口调试器
一、新建一个windows窗体应用
选择视图——工具箱,将所需控件添加到幕布上
右击控件属性修改控件名称、内容
二、打开串口
在form1.cs中编辑代码
/// <summary>
/// 打开串口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOpen_Click(object sender, EventArgs e)
{
try
{
//安全打开串口。
OpenPort();
if (serialport.IsOpen)
{
btnClose.Enabled = true;
btnOpen.Enabled = false;
lblComStat.Text = "已打开 " + Convert.ToString(serialport.PortName) + "," + Convert.ToString(serialport.BaudRate) + "," + Convert.ToString(serialport.DataBits) + "," + Convert.ToString(serialport.Parity) + "," + Convert.ToString(serialport.StopBits);
}
}
catch (Exception er)
{
//安全关闭当前串口。
ClosePort();
MessageBox.Show("串口打开失败!" + er.Message, "串口调试助手");
lblComStat.Text = "已关闭";
}
}
三、接收指令
/// <summary>
/// 发送指令
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
//自动打开串口。
if (!serialport.IsOpen)
{
btnOpen_Click(null, null);
}
}
/// <summary>
/// 端口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxPort_MouseDown(object sender, MouseEventArgs e)
{
try
{
cbxPort.Items.Clear();
//自动获取串行口名称
foreach (string port in SerialPort.GetPortNames())
{
this.cbxPort.Items.Add(port);
}
}
catch
{
MessageBox.Show("找不到通讯串口!", "串口调试助手");
}
}
四、处理数据
/// <summary>
/// 接收数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void serialport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// *** 正在接收状态指示。
IsReceving = true;
int Len = serialport.BytesToRead;
byte[] data = new byte[Len];
serialport.Read(data, 0, Len);
int check_high = data[9] - 0x30;//校验低位
int check_low = data[10] - 0x30;//校验高位
int Xor_Value = data[1];//异或校验初始值,从符号位开始
for (int i = 2; i < 9; i++)
{
Xor_Value ^= data[i];
}//通过循环实现第二位至第九位的异或
DateTime dt = DateTime.Now;
string receiveTime = dt.ToString();
// 检查数据开头是否为02
if (data[0] != 0x02)
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据开头不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据长度是否为12
if (data.Length != 12)
{
// 数据长度不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据长度不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据结尾是否为03
if (data[11] != 0x03)
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据结尾不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据是否和校验位一致
if (check_low != (Xor_Value & 0x0F) || check_high != (Xor_Value >> 4))
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据校验失败\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 重量为0时不显示数据
if (data[2] == 0x30 && data[3] == 0x30 && data[4] == 0x30 && data[5] == 0x30 && data[6] == 0x30 && data[7] == 0x30)
{
return; // 如果数据为0,则不进行后续处理
}
// 如果数据符合要求,则继续处理
string Hex = BitConverter.ToString(data).Replace("-", " ");
Hex = Hex.ToUpper();
rtbxRecHex.Invoke(new EventHandler(delegate
{
// *** 正在处理十六进制数据。
DoingHex = true;
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + Hex + "\n");
if (rtbxRecHex.Text.Length > 10000)
{
rtbxRecHex.Text = string.Empty;
}
// 将控件的内容滚动到当前位置。
rtbxRecHex.ScrollToCaret();
// *** 完成处理十六进制数据。
DoingHex = false;
}));
//添加小数点
int InsertIndex = data[8] - 0x30;
List<byte> modifiedData = new List<byte>(data);
modifiedData.Insert(8 - InsertIndex, (byte)'.');
data = modifiedData.ToArray();
//删除数组里多余元素,分两次删除是因为每删除一次数组长度就会发生变化,元素对应位置也随着而变
List<byte> delDate = new List<byte>(data);
delDate.RemoveRange(9,4);
data= delDate.ToArray();
List<byte> delDate1 = new List<byte>(data);
delDate1.RemoveAt(0);
data = delDate1.ToArray();
LastDatartbxRecHex.Invoke(new EventHandler(delegate
{
// *** 正在处理字符串。
DoingStr = true;
LastDatartbxRecHex.AppendText("[" + receiveTime + "]" + ":" + Encoding.GetEncoding("GB2312").GetString(data)+"\n");
if (LastDatartbxRecHex.Text.Length > 10000)
{
LastDatartbxRecHex.Text = string.Empty;
}
// 将控件的内容滚动到当前位置。
LastDatartbxRecHex.ScrollToCaret();
// *** 完成处理字符串。
DataCount++;
DoingStr = false;
}));
while (DoingStr || DoingHex)
{
// 处理串口接收事件及其它系统消息。
Application.DoEvents();
}
// *** 接收完成状态指示。
IsReceving = false;
}
catch (Exception Err)
{
MessageBox.Show(Err.Message, "串口调试助手");
}
}
重力传感器通过串口发送12位16进制数据,将其转换为带有正负的重量。
完整代码
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using Sunisoft.IrisSkin;
//using System.IO.Ports;
using System.Drawing;
using System.Data;
using System.Text;
using System.IO;
using System;
using System.IO.Ports;
using NPOI.HSSF.UserModel;
using NPOI.XSSF.UserModel;
using System.Text.RegularExpressions;
namespace SerialComm
{
public partial class Form1 : Form
{
#region 全局变量
//===================================================
//申明全局变量。
/// <summary>
/// 皮肤引擎
/// </summary>
private SkinEngine se = new SkinEngine();
/// <summary>
/// 指令发送指针初始为0。
/// </summary>
public int CmdIdx = 0;
/// <summary>
/// 接收数据标志。
/// </summary>
bool IsReceving = false;
/// <summary>
/// 处理字符串标志。
/// </summary>
bool DoingStr = false;
/// <summary>
/// 处理十六进制标志。
/// </summary>
bool DoingHex = false;
//===================================================
#endregion
#region 构造函数
//===================================================
public Form1()
{
InitializeComponent();
}
//===================================================
#endregion
#region 页面初始化
//===================================================
/// <summary>
/// 页面初始化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
//加载串口
BindPort();
}
//===================================================
#endregion
#region 调用方法
//===================================================
/// <summary>
/// 加载串口
/// </summary>
private void BindPort()
{
try
{
//自动获取串行口名称
foreach (string port in SerialPort.GetPortNames())
{
this.cbxPort.Items.Add(port);
}
cbxPort.SelectedIndex = 0;
}
catch
{
MessageBox.Show("找不到通讯串口!", "串口调试助手");
}
}
/// <summary>
/// 清除空格
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string DelSpace(string str)
{
string TempStr = string.Empty;
int Len = str.Length;
for (int i = 0; i < Len; i++)
{
if (str[i] != ' ')
{
TempStr += str[i];
}
}
//Hex 位数检查。
Len = TempStr.Length;
if (Len % 2 != 0)
{
TempStr = '0' + TempStr;
}
return TempStr;
}
/// <summary>
/// 重新打开串口
/// </summary>
private void ReOpenPort()
{
try
{
if (serialport.IsOpen)
{
btnClose_Click(null, null);
//重新打开通讯串口。
if (!serialport.IsOpen)
{
btnOpen_Click(null, null);
}
}
}
catch (Exception Err)
{
MessageBox.Show(Err.Message, "串口调试助手");
}
}
/// <summary>
/// 打开串口
/// </summary>
private void OpenPort()
{
//***避免串口死锁***
//写超时,如果底层串口驱动效率问题,能有效的避免死锁。
serialport.WriteTimeout = 1000;
//读超时,同上。
serialport.ReadTimeout = 1000;
//回车换行。
serialport.NewLine = "\r\n";
//注册事件。
serialport.DataReceived += new SerialDataReceivedEventHandler(this.serialport_DataReceived);
//***避免串口死锁***
serialport.PortName = cbxPort.Text;
serialport.BaudRate = int.Parse(cbxBaudRate.Text);
serialport.DataBits = int.Parse(cbxDataBit.Text);
serialport.Parity = (Parity)Enum.Parse(typeof(Parity), cbxParity.Text);
serialport.StopBits = (StopBits)Enum.Parse(typeof(StopBits), cbxStopBit.Text);
serialport.Open();
}
/// <summary>
/// 关闭串口
/// </summary>
private void ClosePort()
{
//安全关闭当前串口。
//***避免串口死锁***
//注销串口中断接收事件,避免下次再执行进来,造成死锁。
serialport.DataReceived -= this.serialport_DataReceived;
while (IsReceving)
{
//处理串口接收事件及其它系统消息。
Application.DoEvents();
}
//现在没有死锁,可以关闭串口。
serialport.Close();
//***避免串口死锁***
}
//===================================================
#endregion
#region 事件函数
//===================================================
/// <summary>
/// 打开串口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOpen_Click(object sender, EventArgs e)
{
try
{
//安全打开串口。
OpenPort();
if (serialport.IsOpen)
{
btnClose.Enabled = true;
btnOpen.Enabled = false;
lblComStat.Text = "已打开 " + Convert.ToString(serialport.PortName) + "," + Convert.ToString(serialport.BaudRate) + "," + Convert.ToString(serialport.DataBits) + "," + Convert.ToString(serialport.Parity) + "," + Convert.ToString(serialport.StopBits);
}
}
catch (Exception er)
{
//安全关闭当前串口。
ClosePort();
MessageBox.Show("串口打开失败!" + er.Message, "串口调试助手");
lblComStat.Text = "已关闭";
}
}
/// <summary>
/// 发送指令
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
//自动打开串口。
if (!serialport.IsOpen)
{
btnOpen_Click(null, null);
}
}
/// <summary>
/// 端口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxPort_MouseDown(object sender, MouseEventArgs e)
{
try
{
cbxPort.Items.Clear();
//自动获取串行口名称
foreach (string port in SerialPort.GetPortNames())
{
this.cbxPort.Items.Add(port);
}
}
catch
{
MessageBox.Show("找不到通讯串口!", "串口调试助手");
}
}
/// <summary>
/// 接收数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void serialport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// *** 正在接收状态指示。
IsReceving = true;
int Len = serialport.BytesToRead;
byte[] data = new byte[Len];
serialport.Read(data, 0, Len);
int check_high = data[9] - 0x30;//校验低位
int check_low = data[10] - 0x30;//校验高位
int Xor_Value = data[1];//异或校验初始值,从符号位开始
for (int i = 2; i < 9; i++)
{
Xor_Value ^= data[i];
}//通过循环实现第二位至第九位的异或
DateTime dt = DateTime.Now;
string receiveTime = dt.ToString();
// 检查数据开头是否为02
if (data[0] != 0x02)
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据开头不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据长度是否为12
if (data.Length != 12)
{
// 数据长度不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据长度不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据结尾是否为03
if (data[11] != 0x03)
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据结尾不符合要求\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 检查数据是否和校验位一致
if (check_low != (Xor_Value & 0x0F) || check_high != (Xor_Value >> 4))
{
// 数据开头或结尾不符合要求,显示错误或者返回
// 可以选择直接显示错误消息
rtbxRecHex.Invoke(new EventHandler(delegate
{
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + "错误:数据校验失败\n");
rtbxRecHex.ScrollToCaret();
}));
return;
}
// 重量为0时不显示数据
if (data[2] == 0x30 && data[3] == 0x30 && data[4] == 0x30 && data[5] == 0x30 && data[6] == 0x30 && data[7] == 0x30)
{
return; // 如果数据为0,则不进行后续处理
}
// 如果数据符合要求,则继续处理
string Hex = BitConverter.ToString(data).Replace("-", " ");
Hex = Hex.ToUpper();
rtbxRecHex.Invoke(new EventHandler(delegate
{
// *** 正在处理十六进制数据。
DoingHex = true;
rtbxRecHex.AppendText("[" + receiveTime + "]" + ":" + Hex + "\n");
if (rtbxRecHex.Text.Length > 10000)
{
rtbxRecHex.Text = string.Empty;
}
// 将控件的内容滚动到当前位置。
rtbxRecHex.ScrollToCaret();
// *** 完成处理十六进制数据。
DoingHex = false;
}));
//添加小数点
int InsertIndex = data[8] - 0x30;
List<byte> modifiedData = new List<byte>(data);
modifiedData.Insert(8 - InsertIndex, (byte)'.');
data = modifiedData.ToArray();
//删除数组里多余元素,分两次删除是因为每删除一次数组长度就会发生变化,元素对应位置也随着而变
List<byte> delDate = new List<byte>(data);
delDate.RemoveRange(9,4);
data= delDate.ToArray();
List<byte> delDate1 = new List<byte>(data);
delDate1.RemoveAt(0);
data = delDate1.ToArray();
LastDatartbxRecHex.Invoke(new EventHandler(delegate
{
// *** 正在处理字符串。
DoingStr = true;
LastDatartbxRecHex.AppendText("[" + receiveTime + "]" + ":" + Encoding.GetEncoding("GB2312").GetString(data)+"\n");
if (LastDatartbxRecHex.Text.Length > 10000)
{
LastDatartbxRecHex.Text = string.Empty;
}
// 将控件的内容滚动到当前位置。
LastDatartbxRecHex.ScrollToCaret();
// *** 完成处理字符串。
DataCount++;
DoingStr = false;
}));
while (DoingStr || DoingHex)
{
// 处理串口接收事件及其它系统消息。
Application.DoEvents();
}
// *** 接收完成状态指示。
IsReceving = false;
}
catch (Exception Err)
{
MessageBox.Show(Err.Message, "串口调试助手");
}
}
/// <summary>
/// 串口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxPort_SelectedIndexChanged(object sender, EventArgs e)
{
//重新打开串口
ReOpenPort();
}
/// <summary>
/// 波特率
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxBaudRate_SelectedIndexChanged(object sender, EventArgs e)
{
//重新打开串口
ReOpenPort();
}
/// <summary>
/// 数据位
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxDataBit_SelectedIndexChanged(object sender, EventArgs e)
{
//重新打开串口
ReOpenPort();
}
/// <summary>
/// 校验位
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxParity_SelectedIndexChanged(object sender, EventArgs e)
{
//重新打开串口
ReOpenPort();
}
/// <summary>
/// 停止位
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cbxStopBit_SelectedIndexChanged(object sender, EventArgs e)
{
//重新打开串口
ReOpenPort();
}
/// <summary>
/// 关闭串口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClose_Click(object sender, EventArgs e)
{
//安全关闭串口。
ClosePort();
if (!serialport.IsOpen)
{
btnOpen.Enabled = true;
btnClose.Enabled = false;
lblComStat.Text = "已关闭";
}
}
/// <summary>
/// 退出
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnQuit_Click(object sender, EventArgs e)
{
btnClose_Click(null, null);
this.Close();
}
private void rtbxRecStr_TextChanged(object sender, EventArgs e)
{
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("wwww!", "串口调试助手");
}
private void Form1_oad(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
//清除接收
rtbxRecHex.Text = string.Empty;
}
private void LastDataClr_Click(object sender, EventArgs e)
{
//清除接收
DataCount=0;
LastDatartbxRecHex.Text = string.Empty;
}
private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)
{
}
private void TimeStr_Tick(object sender, EventArgs e)
{
}
private void label6_Click(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
DateTime dt = System.DateTime.Now;
label1.Text = dt.ToString();
}
//===================================================
#endregion
private void label1_Click(object sender, EventArgs e)
{
object Systemtime = null;
_ = Systemtime;
}
private void gpPortSet_Enter(object sender, EventArgs e)
{
}
}
}
测试结果
最终可成功获得数据
更多推荐
已为社区贡献1条内容
所有评论(0)