C#上位机编程完成串口通讯,然后实时数据处理

青山渺渺
音视频领域新星创作者
2018-05-07 05:09:50
小白一枚,按照老师要求现在完成了数据的接收和处理。因为是5ms接收一个数据,接受完收据之后处理了还要显示到一个新的窗体里,现在基本功能完成了,不过程序在运行一会儿之后会变得很卡,看了有异步委托等方法,不知道该怎么用到程序里。还尝试的在一个新的线程中开的窗体,没什么用,望大神指点指点。

因为C#学了不多,很多知识不清楚,所以还望各位高手能够耐心指点


界面如下,点击调试数据之后就可以把接收的数据显示出来,但是用了一会儿就会很卡



下面是代码
public partial class MainInterface : Form
{
bool maglevStatus = false;//悬浮状态
bool flagRPM0 = false;//转速为0标志位
string rec;

Form2 fr2 = new Form2();//实例化绘图窗口
Form3 fr3 = new Form3();//实例化测试窗体的对象,来访问其中的textbox

public MainInterface()
{
InitializeComponent();
}

private void MainInterface_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;

//开启委托
sp1.DataReceived += new SerialDataReceivedEventHandler(DataReceive);

Init init = new Init(this);//创建一个Init对象
init.FormInit();//窗体初始化

#region 自动搜索串口
try
{
PublicVar.portName = SerialPort.GetPortNames();
cmbPort.Items.AddRange(PublicVar.portName);
cmbPort.SelectedItem = cmbPort.Items[0];
}
catch
{
MessageBox.Show("未搜索到串口");
}
if (cmbPort.Items.Count == 0)
{
btnOpenOrClose.Enabled = false;
}
else
{
btnOpenOrClose.Enabled = true;
}
#endregion
}

/// <summary>
/// 接收数据
/// 参数设置存在paraRec中,调试数据存在debugRec中
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DataReceive(object sender, SerialDataReceivedEventArgs e)
{
try
{
//sp1.ReadLine();
//sp1.ReadExisting
int count = sp1.BytesToRead;//实际接收到的字节数
byte[] buffer = new byte[count];
sp1.Read(buffer, 0, count);//把读取的数据存到缓冲字节数组中
if (buffer[0] == 0x5B && buffer[buffer.Length - 3] == 0x5D) //转义字符\r\n只算r和n的字节数
{
rec = Encoding.Default.GetString(buffer);
PublicVar.paraRec.Add(rec);//把接收到的参数设置的数据存到paraRec中

HandData(rec);//即时处理数据
sp1.DiscardInBuffer();
}
else
{
sp1.DiscardInBuffer();
}
}
catch { }
}

/// <summary>
/// 处理数据
/// </summary>
/// <param name="data">接收到的数据转换成的字符串</param>
void HandData(string data)
{
//handData处理之后的字符串数组
string[] handData = data.Split(new string[] { "[", " ", "]", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
if (handData[0] == "rpm")
{
rpmtxt.Text = handData[1];
}
else if (handData[0] == "maglev")
{
if (handData[1] == "1")
{
maglevStatus = true;//悬浮状态
magStaPic.Image = Properties.Resources.起浮;
}
else if (handData[1] == "0")
{
maglevStatus = false;//落下状态
magStaPic.Image = Properties.Resources.落下;
}
}
else if (handData[0] == "err")
{
errtxt.Text = handData[1];
}
else if (handData[0] == "x_shift")//将获取到的x y坐标存储到list中,超过500则覆盖
{
fr3.x_shift.Text = handData[1];//把值传入到调试界面
if (PublicVar.x_shift.Count > 500)
{
PublicVar.x_shift.RemoveAt(0);
PublicVar.x_shift.Add(handData[1]);
}
else
{
PublicVar.x_shift.Add(handData[1]);
}
}
else if (handData[0] == "y_shift")
{
fr3.y_shift.Text = handData[1];//把值传入到调试界面
if (PublicVar.y_shift.Count > 500)
{
PublicVar.y_shift.RemoveAt(0);
PublicVar.y_shift.Add(handData[1]);
}
else
{
PublicVar.y_shift.Add(handData[1]);
}
}
else if (handData[0] == "speed_input")
{
fr3.speed_input.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "speed_set")
{
fr3.speed_set.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "speed")
{
fr3.speed.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "speed_output")
{
fr3.speed_output.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "phase")
{
fr3.phase.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "iic")
{
fr3.iic.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "x_offset")
{
fr3.x_offset.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "y_offset")
{
fr3.phase.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "x_offset_feedback")
{
fr3.x_offset_feedback.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "y_offset_feedback")
{
fr3.y_offset_feedback.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "x_shift_average")
{
fr3.x_shift_average.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "y_shift_average")
{
fr3.y_shift_average.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "maglev_flag")
{
fr3.maglev_flag.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "maglev_protect")
{
fr3.maglev_protect.Text = handData[1];//把值传入到调试界面
}
else if (handData[0] == "pwm_protect_tick")
{
fr3.pwm_protect_tick.Text = handData[1];//把值传入到调试界面
}
}
...全文
3909 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复 1
这是 2018 年的帖子啊,不知道谁挖出来的。不过这里有个很大的坑人(坑正在设计入门的程序设计师)的概念还是值得强调一下:所谓“5ms接收一个数据”这就是老师的失职。傻傻地所谓接收概念是纯粹不专业的,而老师的职责就是领你入门,告诉你编程设计的理念就是数据到了时才触发事件委托、你的代码才被调用,事件没到根本不轮询、不阻塞、不胡乱说什么接收。这就好像说你收快递,人家会用支付宝、短信等等即时通讯手段理科通知你,难道是要你每隔10分钟去跑一趟快递点去排队看看自己的快递吗?简单地所谓“接收”概念机制设计,就能看出你学过的编程知识是专业的,还是仅仅是中学生爱好者刚学编程时的那个概念。 稍微专业一点的编程设计,必定要从画流程图、用例图、类库、甬道图,或者时序图之类的任何2、3种蓝图设计入手,而不是只是在那里纠缠什么编程语句。许多坑人的所谓设计,连基本的依赖倒置设计模式都不理解,连为什么自己用了那么多控件、组件都是依赖着事件驱动设计模式都不知道,这样遇到许多所谓的“性能、阻塞”之类的简单问题,也就没法在同一个设计频道上,说什么你也不会很好领会。
  • 打赏
  • 举报
回复
[quote=引用 楼主 壹砂 的回复:] 小白一枚,按照老师要求现在完成了数据的接收和处理。因为是5ms接收一个数据,接受完收据之后处理了还要显示/quote] 这是 2018 年的帖子啊,不知道谁挖出来的。不过这里有个很大的坑人(坑正在设计入门的程序设计师)的概念还是值得强调一下:所谓“5ms接收一个数据”这就是老师的失职。傻傻地所谓接收概念是纯粹不专业的,而老师的职责就是领你入门,告诉你编程设计的理念就是数据到了时才触发事件委托、你的代码才被调用,事件没到根本不轮询、不阻塞、不胡乱说什么接收。这就好像说你收快递,人家会用支付宝、短信等等即时通讯手段理科通知你,难道是要你每隔10分钟去跑一趟快递点去排队看看自己的快递吗? 简单地所谓“接收”概念机制设计,就能看出你学过的编程知识是专业的,还是仅仅是中学生爱好者刚学编程时的那个概念。
小羊i 2019-02-20
  • 打赏
  • 举报
回复
能不能问问你在怎么设置转速的啊
  • 打赏
  • 举报
回复
当你收到消息数据,你要处理分包、粘包,你收到的不一定是一个完整的消息(并且可能是多条消息)。当你识别出一条消息,那么消息的解析和执行应该是异步的,应该立刻释放 I/O 线程去继续处理消息接收动做。而消息的解析,以及操作控件也应该是异步的。仅仅在个别操作控件的语句的代码块上要使用
control.BeginInvoke(....)
注册给 UI 线程去调度。UI 线程仅仅在必要的时候才会优化显示。对于你自己的频繁的数据改变,也可以对要显示的数据进行“抽稀”(也就是说区分开来你收到的数据与你要显示的数据),例如仅仅显示最近300毫秒以内最新的一条数据就够了。但是总之是说,事件回调、异步、减少不必要的显示动做,从细节进行设计,而不是以标题党的简单粗暴方式来设计程序。
junki 2018-05-08
  • 打赏
  • 举报
回复
使用Task编程,进行多线程异步处理
qu121069694 2018-05-08
  • 打赏
  • 举报
回复
5ms刷新UI应该还是够的,但是前提是界面线程单独拿出来用。你的代码里并没有开启新线程和委托执行,这是在界面线程(主线程)中的循环操作,这样一定会卡的,而且你的按键在程序跑起来后基本无效。 把串口放在新开的工作线程中执行提取,在工作线程中委托主线程刷新UI就不会卡了;新开线程 thread ,委托 invoke
liwen9016 2018-05-08
  • 打赏
  • 举报
回复
5MS一“包”数据,你在串口回调函数中处理的内容包括UI刷新吧?如此短的周期,不断的刷新UI,肯定会卡。你可以再起个buf,存储串口接收到的数据,然后再起个线程处理数据,类似任务处理那种。估计效果就会好很多。
青山渺渺 2018-05-08
  • 打赏
  • 举报
回复
引用 3 楼 junki 的回复:
使用Task编程,进行多线程异步处理
谢谢,我去看看Task是怎么使用的,还没学过task
青山渺渺 2018-05-08
  • 打赏
  • 举报
回复
引用 2 楼 qu121069694 的回复:
5ms刷新UI应该还是够的,但是前提是界面线程单独拿出来用。你的代码里并没有开启新线程和委托执行,这是在界面线程(主线程)中的循环操作,这样一定会卡的,而且你的按键在程序跑起来后基本无效。 把串口放在新开的工作线程中执行提取,在工作线程中委托主线程刷新UI就不会卡了;新开线程 thread ,委托 invoke
我就是在开新的窗体的这边用的这个,想试试行不行,因为不太会所以可能是乱用的。 看了你的指导若有所思,不过不太清楚怎么把串口接收的那个委托放到新开的线程当中去,然后在工作线程中怎么同步拿到那个数据去刷新UI?恕我驽钝,望指教!
//在新线程中开启一个窗口
        private void btnDebug_Click(object sender, EventArgs e)
        {
            if (fr2==null||fr2.IsDisposed)
            {
                fr2 = new Form2();
            }
            //Form2 fr2 = new Form2();
            //fr2.Show();
            Thread th1 = new Thread(ThreadPro);
            th1.IsBackground = true;
            th1.Start();
        }
        public void ThreadPro()
        {
            MethodInvoker methInvo = new MethodInvoker(ShowForm2);
            BeginInvoke(methInvo);
        }
        public void ShowForm2()
        {
            fr2.Show();
        }
青山渺渺 2018-05-08
  • 打赏
  • 举报
回复
引用 1 楼 liwen9016 的回复:
5MS一“包”数据,你在串口回调函数中处理的内容包括UI刷新吧?如此短的周期,不断的刷新UI,肯定会卡。你可以再起个buf,存储串口接收到的数据,然后再起个线程处理数据,类似任务处理那种。估计效果就会好很多。
串口回调函数中是包括UI的刷新的,把发送的数据从格式中解析出来,然后显示在UI上。也想过新起一个buf,但是数据的种类比较多,不知道该怎么存储,还要做到实时显示。谢谢指导!
xian_wwq 2018-05-08
  • 打赏
  • 举报
回复
卡是因为主线程被阻塞了 Control.CheckForIllegalCrossThreadCalls = false;不推荐使用 如果使用thread,那么通过委托来刷UI 也可以使用Task来处理

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧