C# 上位机串口调试工具实时显示

来源:互联网 发布:淘宝店铺市场 编辑:程序博客网 时间:2024/06/06 09:47

转载连接: http://blog.csdn.net/paofuhemakalong/article/details/70840361?locationNum=2&fps=1


大家好~我又来啦~最近没有继续更新leetcode上的题目啊,深感愧疚,最近在忙着做一个上位机PC软件,功能是能够正常接收串口传输来的数据并解析这些数据中对我们有用的数据,另外,为了更加直观的观察这些数据代表的意义,我们还有一个目的就是讲这些数据实时地绘画出来,也就是动态显示。

        忙活了不到一个月,终于把这个给搞定了,心情无比的开心,因为这个我自己一个人完成的,当然也要感谢帮助我的老师和网上的资源,我也要将我的心得写出来希望能够帮助其他人,哈哈,努力追上大神们的脚步。今天我就花两个小时来写下我的心得,有不对的地方还请大家指教哈!

        首先声明一下,我在做这个之前是对C#完全不懂的,我甚至对C++都不懂,只是刚刚开始接触C++,我只是一个只会一点儿C的人,但是我一点点的跟着别人学习也弄懂了一些关于C++的知识。体会最深的就是对面向过程和面向对象的区别,之前我在网上查了很多资料讲这个的,但是都给我看的云里雾里的,完全不知所云,通过这个小项目我来讲讲我的认识吧。C中面向过程的意思就是要实现一个最终目的要分成很好顺序的步骤,第一步干嘛第二步干嘛,都是顺序执行的,中间该调用函数就调用函数,但是这并不影响面向过程的本质,但是C++中的面向对象的意思是它把很多步骤集合成一个整体(也就是所谓的对象),然后这些步骤的完成就是对这个整体的操作。打个比方吧,我要实现一个最终目的的步骤中需要一个人的一些行为,需要他跳100米,跑100米,走100米,如果C来实现的话就是顺序执行这些动作,然后每个动作都写一个函数,顺序调用就可以了,这就是所谓的面向过程的含义,但是C++不会这样,它会将这个人“封装”起来,然后将着三个动作(或者更多)添加到“人”(也就是“类”的含义)这个整体中,当我需要这个人跳的时候,就相当于直接对这个整体(也就是这个人)进行操作,是不是简单了许多呢。做完这个小项目之后,我忽然发现C++真的是很有用的一门语言,我喜欢哈哈。

        这个小项目我把它划分为了三个主要部分,1、实现实时串口通信;2、实现绘图过程并实时显示;3、保存解析的有用数据到指定的TXT文件(这个部分特别的简单,摆渡一下就知道只需要添加几行代码就可以啦!)这里面比较难的就是绘图了,这里我用的是GDI+直接用笔绘图,没有使用专门绘图的控件,只是在界面上加了一个可以画图的面板Panel。下面我将分为三个部分来讲。

        一、实现实时串口通信

        其实我在这里还花了挺长时间的,因为一些细节问题(还是因为自己太懒啦哈哈)得不到解决就拖了很久才解决。其实说实话我从一开始做这个的时候我都无从下手,首先就是用什么平台的问题,我们之前已经用Matlab做过串口通信,只是用Matlab比较麻烦而且不能封装不能直接使用,所以想要做一个上位机软件。C#,VC,Labview,Delphi,Java,QT等等都是一些方法,但是我最选择了比较简单的C#,并不接近底层且封装性较好的一种方法。哦对,比较常见的就是VC的MFC,但是这个已经很老了。。。平台使用的是VS2015,使用的是C#控制台程序。从刚开始的一无所知都不知道怎么建立显示界面等等到做成需要的是合理利用网上资源并自己去悟,在文章的最后我会贴出对我有帮助的网址,有需要的可以去看。这里我想谈几点我在这个过程中遇到的问题。

(1)最主要的问题还是在于接收串口数据和解析串口数据上吧。这是一个非常重要的问题,因为如果你的解析过程非常的复杂,又与接收串口数据在同一个线程上那就很麻烦了,因为在接收的过程中是有一定的触发事件的条件的,如果在处理数据的过程中用掉了很多的时间,串口数据又不能及时的接收,程序就会出现问题,所以应该设立一个公用缓冲区,将接收到的串口数据放到公共缓冲区,然后另外开启一个线程去处理数据,这样两个过程就互不耽误了。开启新的线程需要使用新的命名空间using System.Threading,其他的具体细节就不说啦!

(2)我在调试程序的时候发现一个问题,就是SerialPort类中有一个属性是ReceivedBytesThreshold,我发现在实际的使用过程中,这个值并不能发挥它本身的意义,它的作用是当接收缓冲区达到这个数的时候就触发事件,但是我通过打印出的缓冲区中的数据个数的消息发现并不是这样的,有时候多有时候少(我也不知道为什么会是这样,有明白的大神希望告诉我啊谢谢,可能我使用的方法有问题也不一定),而且它默认的值是1,但是实际过程中也并不是这样的。所以如果像我一样接收到的数据是有一定的顺序的就不要使用这种方法了,这里提供一个思路就是“拼包”,就是每次都将接收缓冲区的数据读空,然后依次放入到接收缓冲区中,然后在处理数据的线程中设置处理条件就可以了。

(3)在调试的过程中还有一个问题,刚开始的时候我是将所有的数据都按字符串的形式输出到界面中的TextBox(这个输出过程是在接收数据线程中),结果会出现这样的情况:接收缓冲区中的数据越来越多,直到达到它的最大值时程序就会卡住,最后问了大牛才知道“打印输出”这个过程是很费时间的,所以不要将所有的数据都显示在TextBox中,这样就不会出现这样的情况了。

        二、实现绘图过程并实时显示

        我觉得这里面比较有挑战性的一部分就在这里了。一会儿我会在下面贴一些我参考的网址。这一部分我是参照在网上下载的一个随机数实时显示绘图的程序的思想的,其实也就是GDI+的方法,下面是我的一些心得。

(1)通过定时器不断的绘图然后绘在面板上就可以了,这里最重要的一点就是PointF点数组的想法,如果我不看参考程序,我觉得我估计都不会想到这样做,谢谢那个网上的大神啦!就是将PointF的长度设置为你要显示的总的图形的长度,然后不断去移动和改变这个点的数值然后用笔绘制出来就可以了。

(2)我在调试程序的过程中遇到了一个问题就是我的串口数据的值是不稳定的,在0-60000+之间,所以我不可能将纵坐标轴的长度设置成固定的,所以必须是动态变化的,我是每100个点移一次,我将纵坐标轴的最大最小值设置为着100个点中的最大最小值,需要特别注意的是,因为作图的时候是使用的像素值,并不是实际的数据值,所以应该在绘图前也开辟一个专门盛放最大显示长度的点数组,然后绘图的时候将这个点数组的纵坐标全都复制到另一个private的点数组中,这样就会保证你显示的所有的数据的像素值当前都是正确的。唉。。昨天这个问题我想了好久,尝试了好久才成功的呢,呜呜。。

(3)在Graphics这个类中有个属性是Transform,这个属性我愣是看了两三天才看懂在程序中是什么意思啊,现在将心得分享给大家。

Graphics.Transform属性:获取或设置此 Graphics 的几何世界变换的副本。

命名空间:   System.Drawing
程序集:  System.Drawing(位于 System.Drawing.dll)

GDI + 使用三个坐标空间︰世界、 页和设备。 世界坐标是用于建模对特定图形世界的坐标,将传递到.NETFramework 中的方法的坐标。 页坐标是指绘图图面,如窗体或控件所使用的坐标系。 设备坐标是在其上进行绘制,如屏幕或打印机的物理设备使用的坐标系。 Transform 属性表示的世界变换,将世界坐标映射到的页坐标。

因为返回的矩阵和 Transform 属性为几何变换的副本,当不再需要时,应释放的矩阵。

看到MSDN上的说明,表示“什么鬼……”

后来看了一个文档,豁然开朗,花了两天研究这一个属性我也是醉醉的了……不过还好明白是怎么回事儿了!

https://books.google.de/books?id=tQJECXrqapwC&pg=PA285&lpg=PA285&dq=C%23+Graphics.Transform%E7%9A%84%E4%BD%BF%E7%94%A8&source=bl&ots=tl2V_X8qU5&sig=ndLR5-HFDpKwTLpERmU5MNKG2a8&hl=zh-CN&sa=X&ved=0ahUKEwjXic3Ir7TTAhXHTbwKHcMmAYoQ6AEIWjAI#v=onepage&q&f=false(要翻墙!)

直接上代码吧:

上面是画坐标轴的委托函数,第二句中涉及到了Transform属性的使用,之前之所以卡在这里不明白,是因为我以为每一次Transform都是继承上一次的变换的,其实每一次Transform都是基于最原始的Gph1的。


上面是OriginCoordClass类函数。整个过程如下图所示:


第三次的转换也是红色所示的区域。

上面的参考代码就是我对它的理解,这也帮助我找到了我自己代码的实现的方法。这个参考代码的VS程序完整版下载:VS参考代码

三、保存解析的有用数据到指定的TXT文件

       这个功能就更加的简单了,就几句代码的事情,虽然我也不是很懂它的本质意义,但是会用就可以啦!贴一下代码吧!

[csharp] view plain copy
  1. string path = "保存文件的地址";  
  2.                     FileStream fs = new FileStream(path, FileMode.Append); //若文件已存在,往里写数据;若文件不存在,则自动创建txt文件  
  3.                     StreamWriter sw = new StreamWriter(fs);  
  4.                     sw.Write(要保存的数据);  
  5.                     sw.Close();  
  6.                     fs.Close();  

以上就是我的心得啦!最后呢,展示一下我的效果吧,这是我的界面。


我在网上参考的有用的网址:

http://www.cnblogs.com/darrenji/p/3821313.html

http://blog.csdn.net/taoerit/article/details/42612629

http://www.zhangxinxu.com/wordpress/2012/06/css3-transform-matrix-矩阵/

http://blog.csdn.net/mask_soft/article/details/7913739

http://www.cnblogs.com/peterzb/archive/2009/07/19/1526555.html

http://blog.csdn.net/zhoufoxcn/article/details/2682027

http://blog.csdn.net/leaf_130/article/details/51588019

http://blog.sina.com.cn/s/blog_880ab31b01012h75.html

http://blog.sina.com.cn/s/blog_880ab31b01012okp.html

http://download.csdn.net/detail/u012558127/8783301

http://blog.csdn.net/wuyazhe/article/details/5627253

http://bbs.csdn.net/topics/390535051

http://blog.csdn.net/wyd1520/article/details/44153543

http://blog.csdn.net/dangercheng/article/details/8205009

http://blog.csdn.net/kasama1953/article/details/52075709

http://blog.csdn.net/smartsmile2012/article/details/27207061

https://wenku.baidu.com/view/0ab1862c2af90242a895e5c4.html

http://www.cnblogs.com/jxnclyk/archive/2010/06/07/1753317.html

https://q.cnblogs.com/q/67391/

https://wenku.baidu.com/view/792c467a02768e9951e7382f.html

http://www.cnblogs.com/AdaLoong/p/5528917.html


原创粉丝点击