关于串口通信文本框不显示或显示慢的解决办法
作者:互联网
C# 关于串口通信文本框不显示或显示慢的解决办法)
笔者写了一个串口通信类,却碰到了一个问题就是显示速度非常的慢,文本框要四五分钟的时间才会显示串口数据的变化,查了几百篇文章都没有提到这个问题,于是在这里记录一下也许会帮到你,有网友私信给我让我加QQ号,在这里如果有什么问题可以加我的QQ:778576519以便共同提高。
先上图
可以看到是一个物联网汽车信号采集卡上传的信号
接收数据格式为
注意:不支持项显示 井号,这里一但要使用井号就会0与1的变化,如<左转灯>如果使用就会有变化开灯是<1>关灯<0>如果不使用就一直是井号。
串口类代码
由于笔者注释的比较详细直接看代码吧
下面展示一些 内联代码片
。
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using Accord.Statistics;
namespace VehicleCollector.CalssDocuments
{
class SerialPortCaptrue
{
#region 串口连接方法
private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
public SerialPort sd;
public bool SerialPortConnect(string portName, string baudRate, string dataBits, string parity, string stopBits)
{
Parity iParity_1;
switch (parity)
{
case "奇校验": iParity_1 = Parity.Odd; break;
case "偶校验": iParity_1 = Parity.Even; break;
case "无校验": iParity_1 = Parity.None; break;
default: iParity_1 = Parity.None; break;
}
StopBits istopBits_1;
switch (stopBits)
{
case "1": istopBits_1 = StopBits.One; break;
case "1.5": istopBits_1 = StopBits.OnePointFive; break;
case "2": istopBits_1 = StopBits.Two; break;
default: istopBits_1 = StopBits.One; break;
}
sd = new SerialPort();
sd.PortName = portName;//串口名称
sd.BaudRate = Convert.ToInt32(baudRate);//波特率
sd.DataBits = Convert.ToInt32(dataBits);//数据位
sd.Parity = iParity_1;//校验位
sd.StopBits = istopBits_1;//停止位
sd.ReceivedBytesThreshold = 1;//设置有多少个字节后才触发接收事件
sd.ReadTimeout = -1;//读超时
sd.ReadBufferSize = 4096;//设置缓存区的大小
sd.DataReceived += new SerialDataReceivedEventHandler(PortReceived);//订阅串口接收事件
//sd.DtrEnable = true;
//sd.RtsEnable = false;
bool stat = false;
//sd.Open();
if (sd.IsOpen)
{
stat = true;
}
else
{
stat = false;
}
return stat;//返回串口是否打开状态 如是是False 没打开 如果True则打开
}
#endregion
public string receive = "";//数据接收
public string ReadLineStr;
private static readonly object objLock = new object();//加锁
#region 串口接收事件
/// <summary>
///串口接收事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PortReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Closing) return; //如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环
try
{
Thread.Sleep(100); //(毫秒)由于线程读取速度太快必须等待一定时间
//否则文本框显示会等四五分钟时间才会看到变化
//这个地方笔者搞了两个星期没有找到原因最初值是(5ms)现在改为100ms
if (sd.BytesToRead > 0)
{
ReadLineStr = "";
//ReadLineStr = sd.ReadLine();
ReadLineStr = sd.ReadExisting(); //数据接收内容
if (ReadLineStr[0] != '$')
{
return;
}
if (ReadLineStr.EndsWith("\n"))
{
lock (objLock)//加锁
{
string[] Serial_Info = ReadLineStr.Split(','); //按照逗号分隔把$ODB各种信号分隔到字符数组中
receive = string.Join(",", Serial_Info); //数组转成字符串
if (receive != "")
{
Task tsk = Task.Run(() =>
{
Receiving_show(receive);
});
}
}
}
sd.DiscardInBuffer(); //清空缓存区数据
}
}
catch (Exception ex)
{
throw ex;
}
}
public string Receiving_show(string receive_str)
{
string str;
string re = "";
int NN = receive_str.IndexOf('\n');//取字符串最后\n的索引值如果为空返回值是-1
if (receive_str == null && NN < 39 && NN == -1)//如果为空容易报错这里加上返回防止报错
{
return "";
}
str = receive_str;
if (receive != null && NN > 0)
{
string st = receive_str.Substring(0, 7);//取字符串前7个字符从O开始
if (st == "$OBD-RT")
{
string s = receive_str.Substring(0, NN);//取字符串回车符前所有的字符
string[] StrArray = s.Split(new Char[] { ',' });//将字符装入字符串数组中
//这里只能一个一个的取字符没有规律
PublicCons.K_KeyOnOff_Str = StrArray[1]; //钥匙状态
PublicCons.K_EngineSpeed_Str = StrArray[3]; //发动机转速
PublicCons.K_RunningSpeed_Str = StrArray[4]; //行驶速度
PublicCons.K_Gears_Str = StrArray[6]; //档位信号A0 绿色线
PublicCons.K_SteeringCorner_Str = StrArray[7]; //方向盘转角
PublicCons.K_Gas_Str = StrArray[8]; //油门加速踏板
PublicCons.K_StopMove_Str = StrArray[13]; //当前故障码数量转闯动信号
PublicCons.K_MainDoor_Str = StrArray[15]; //主驾驶车门
PublicCons.K_CopilotBrake_Str = StrArray[24]; //定义天窗转副刹信号
PublicCons.K_SafetyBelt_Str = StrArray[26]; //主驾驶安全带
PublicCons.K_Clutch_Str = StrArray[28]; //离合信号
PublicCons.K_Speaker_Str = StrArray[29]; //喇叭信号
PublicCons.K_HandBrake_Str = StrArray[31]; //手刹信号
PublicCons.K_BrakeSignal_Str = StrArray[32]; //刹车信号
PublicCons.K_LeftLight_Str = StrArray[33]; //左转灯
PublicCons.K_RightLight_Str = StrArray[34]; //右转灯
PublicCons.K_ParkLight_Str = StrArray[35]; //位置灯
PublicCons.K_DippedHeadlight_Str = StrArray[36]; //近光灯
PublicCons.K_HighBeam_Str = StrArray[37]; //远光灯
PublicCons.K_FrontFogLamp_Str = StrArray[38]; //前雾灯
PublicCons.K_RearFogLamp_Str = StrArray[39]; //后雾灯
re = string.Join(",", StrArray); //数组转成字符串
PublicCons.Receive_Str = re;
}
}
return re;
}
#region 打开串口
public bool SerialOpen()
{
try
{
if (sd.PortName != PublicCons.PortStr_1)
{
MessageBox.Show(string.Format("端口不存在请检查设置{0}", PublicCons.PortStr_1.ToString()));
return false;
}
if (!sd.IsOpen)//如果没有打开
{
sd.Open();
sd.Handshake = Handshake.None;//通信协议
sd.DtrEnable = false;//如果为 true,则启用数据终端就绪 (DTR);否则为 false。
//默认为 false。如果用TRUE程序假死不会返回取数据
//Thread.Sleep(50);
//sd.DtrEnable = true;
/*sd.RtsEnable = true;
Thread.Sleep(50);*/
sd.RtsEnable = false;
}
if (sd.IsOpen)//如果打开串口
{
sd.Close();
Thread.Sleep(100); //(毫秒)等待一定时间,
sd.Open();
sd.Handshake = Handshake.None;//通信协议
sd.DtrEnable = false;//如果为 true,则启用数据终端就绪 (DTR);否则为 false。
//默认为 false。如果用TRUE程序假死不会返回取数据
//Thread.Sleep(50);
//sd.DtrEnable = true;
/*sd.RtsEnable = true;
Thread.Sleep(50);*/
sd.RtsEnable = false;
}
}
catch (Exception e)
{
//throw e;
MessageBox.Show("错误" + e.Message);
}
return sd.IsOpen;
}
#endregion
private bool Listening = false;//等待监听线程结束
public void SerialClose()
{
Closing = true;
while (Listening) Application.DoEvents();
//sd.DtrEnable = true;
//sd.RtsEnable = false;
sd.Close();
Closing = false;
}
#endregion
}
}
刚开始笔者以为是多线程的原因造成的,于是在多线程上花了很多的时间可是问题却不是出在多线程上,大多数博客上说访问ui资源,所以需要使用invoke方式同步ui笔者也没有错啊那问题出在那里。
现在看笔者调用文本框的代码:
下面展示一些 内联代码片
。
private static readonly object objLock_Txt = new object();//加锁
private void timer_SerialPort_Tick(object sender, EventArgs e)//串口时钟
{
if (isConnect)
{
DateTime dt = DateTime.Now;
lab_SerialPortConn.Text = "采集卡连接成功";
lock (objLock_Txt)//加锁
{
if (txt_SerialPortRecevideData.InvokeRequired)//如果不在一个线程上InvokeRequired返回值为真
{
this.BeginInvoke(new Action(() =>//C# 3.0以后代替委托的新方法
{
txt_SerialPortRecevideData.AppendText(dt.ToString("yyyy-M-d-HH:mm:ss ") + PublicCons.Receive_Str + "\r\n"); //向文本添加数据+ "\r\n"; //注意:回车换行必须这样写,单独使用"\r"和"\n"都不会有效果
if (txt_SerialPortRecevideData.TextLength > 20000)//文本框超过20000个字节清空
{
txt_SerialPortRecevideData.Clear();
}
}));
}
else
{
txt_SerialPortRecevideData.AppendText(dt.ToString("yyyy-M-d-HH:mm:ss ") + PublicCons.Receive_Str + "\r\n"); //向文本添加数据+ "\r\n"; //注意:回车换行必须这样写,单独使用"\r"和"\n"都不会有效果
if (txt_SerialPortRecevideData.TextLength > 20000)//文本框超过20000个字节清空
{
txt_SerialPortRecevideData.Clear();
}
}
ShowTxt();
//txt_Refresh();
}
}
else
{
lab_SerialPortConn.Text = "采集卡未连接";
}
}
txt_SerialPortRecevideData 是文本框
PublicCons.Receive_Str 是静态变量。
由于解析出来的完整数据要被多个地方调用这里必须要加锁(lock)不加锁会出现争夺资源的现象影响执行效率问题,搞不好还会假死。
费了两个星期的时间又是用事件,委托等方法都没有解决在文本框显示慢的问题,以至于笔者差点放弃。最终笔者无意间调整了线程睡眠时间突然一下子好了。最初笔者调整线程睡眠时间为5ms 无意间调整为500ms反正没事调着玩没有想到的是发现可以正常使用了,后慢慢调整到100ms.问题最终得到解决。
下面展示一些 内联代码片
。
private void PortReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Closing) return; //如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环
try
{
Thread.Sleep(100); //(毫秒)由于线程读取速度太快必须等待一定时间
//否则文本框显示会等四五分钟时间才会看到变化
//这个地方笔者搞了两个星期没有找到原因最初值是(5ms)现在改为100ms
标签:解决办法,PublicCons,StrArray,文本框,Str,串口,string,sd 来源: https://blog.csdn.net/weixin_43727933/article/details/115016991