欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > PPT文档下载  

    单个串口设备数据的连续接收.ppt

    • 资源ID:6362255       资源大小:621.50KB        全文页数:42页
    • 资源格式: PPT        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    单个串口设备数据的连续接收.ppt

    工控程序设计,学习情景2 串口设备的数据采集,学习情景2.3 单个串口设备数据的连续接收,2.3.1 学习要点 1.知识点:线程的概念,委托的概念和使用方法,线程的创建和启动,工作者线程和用户界面线程之间的数据传递,线程同步技 2.技能点:工作者线程的创建,串口数据接收和处理操作的封装2.3.2 任务描述 1.在前一个情景中完成了接收和处理单一串口设备数据的工作任务。实际应用中,上位机需要连续地接收和处理下位机发送的数据,而且在等待和接收数据的时候,用户界面不能停止响应。接收数据和响应用户输入这两个工作在宏观上是同时进行的,为了满足该需求,必须采用多线程模式来进行程序设计。2.该教学情景通过“在工作者线程中接收HSDZC电能综合测试仪的”“HSDZC电能综合测试仪数据接收和处理操作的封装”这两个实施步骤达到连续接收接收单个串口设备(下位机)数据的目的。,学习情景2.3 单个串口设备数据的连续接收,2.3.3 相关知识1 多线程技术概述(1)线程的概念Windows是一个抢占式多任务操作系统,在系统内核中提供了对多线程的支持,多线程技术可以让应用程序在一个耗时的操作中能够及时对用户操作进行响应,并且从宏观上达到多个任务“齐头并进”的目的进程是应用程序的一个运行例程,是应用程序的一次动态执行过程。线程是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。每个进程至少需要一个执行线程,由系统自动创建,程序设计者可以根据需要创建其它线程。由于多个线程共享进程中的全局变量和系统资源,所以线程间的切换比较容易,通信也比较方便。,学习情景2.3 单个串口设备数据的连续接收,(2).NET Framework对多线程的支持.NET平台库提供了Thread类对线程进行处理,该类包含在System.Threading命名空间中,程序中需要包含语句“using System.Threading;”。编程人员可以通过创建一个Thread类的实例来创建一个线程,并通过Thread类提供的方法对线程进行管理。Thread类的常用属性和方法如下表:表2.3.1 Thread类的常用属性和方法,学习情景2.3 单个串口设备数据的连续接收,2 委托的概念和使用方法C#中的委托的作用相当于C/C+中的函数指针,函数指针是一个函数的入口地址。必修使用函数指针的场合是:程序员A编写了函数f,该函数中需要执行另外一个参数和返回值已经确定,但名字暂时不能确定函数,所以给函数设置一个函数指针类型的形式参数;当程序员B使用函数f时,定义函数g,并把g的入口地址作为实际参数传递给函数f,这样就可以在f中执行函数g了。线程的启动是使用函数指针的一个典型例子,在启动线程之前,先要给操作系统指明线程启动后执行哪一函数中包含的代码,这时就要把函数的指针传递给创建线程的函数。回调(完成后通知)是使用函数指针的另外一个典型例子。如程序员A编写了负责接收网络数据的函数f1,程序员B编写了负责处理数据的函数f2,那么就可以把f2的函数指针作为参数传递给函数f1,当f1接收数据完毕后,自动调用f2。所以回调的特点是:站在程序员B的角度看,函数由自己编写,但是不由自己调用,且不知道什么时候被调用(因为网络速度有快有慢),函数编写者要做的就是把函数指针传递出去。,学习情景2.3 单个串口设备数据的连续接收,定义委托的关键字是delegate,它是从System.Delegate类派生出来的。例如:delegate int SomeDelegate(int p1,string p2);/int p1和string p2是被引用函数的参数类型和名称。其中参数类型和参数的个数必须和被引用函数的类型与个数一致。3 创建和启动线程一个线程必须和一个方法的入口(委托)关联起来,线程启动后,自动从该入口进入,执行函数体中包含的内容。C#应用程序启动时,自动创建主线程,并进入Main方法开始执行,其它线程需要在程序里自己定义和启动。由于委托可以代表一个方法的入口,学习情景2.3 单个串口设备数据的连续接收,所以创建线程实例时只需要在Thread类的构造方法里传入一个委托实例即可,这个委托名叫ThreadStart,已经在线程命名空间中定义作了定义:public delegate void ThreadStart();所以创建线程方式如下:ThreadStart functionEntrance=new ThreadStart(threadFunction);Thread t=new Thread(functionEntrance);在委托ThreadStart的构造方法里面传入的是方法名,这个方法可以是静态方法,也可以是某个对象的方法。线程对象创建后,我们就可以调用其Start方法开始线程的执行了。,学习情景2.3 单个串口设备数据的连续接收,我们可以在主线程里建立线程,也可以在线程里再创建线程,线程启动后会自动执行委托实例代表的方法,线程执行完后会自动销毁并释放其占用的资源。在一个新线程中执行带参数的函数,操作步骤如下:定义线程函数:private void paramThreadFunction(object param)/函数体 用ParameterizedThreadStart委托封装线程函数:,学习情景2.3 单个串口设备数据的连续接收,ParameterizedThreadStart functionEntrance=new ParameterizedThreadStart(paramThreadFunction);创建线程对象hread t=new Thread(functionEntrance);启动线程 t.Start(param);/param为传入的参数,可以是任意对象,学习情景2.3 单个串口设备数据的连续接收,4 线程同步技术多线程应用程序中的的线程启动后,执行的先后顺序是无法预知的,通常情况下多个线程会交错执行。但是在多个线程访问共享数据的情况下,必须对数据的访问进行同步。好比有两路车,一路自东向西,一路自南向北运行,在一个十字路口交汇。在十字路口以外的区域可以看着私有区域,而十字路口则是共有区域,需要红绿灯或交警来维护秩序,即确保在同一时刻只能有一路车进入,而另外一路车必须等待,这就是现实生活中的线程同步问题。,学习情景2.3 单个串口设备数据的连续接收,下面的例子展示了一个读数据线程和一个写数据线程同时运行的情况:private static int a=new int5;static void Main(string args)Thread t1=new Thread(new ThreadStart(threadFun1);Thread t2=new Thread(new ThreadStart(threadFun2);t1.Start();t2.Start();private static void threadFun1()/线程函数1 while(true),学习情景2.3 单个串口设备数据的连续接收,for(int i=0;i a.Length;i+)/将数组元素全部输出 System.Console.Write(ai+);();private static void threadFun2()/线程函数2 int flag=0,i;while(true)for(i=0;i a.Length;i+)/将数组元素全部改为0或1 ai=flag;flag=flag=0?1:0;,学习情景2.3 单个串口设备数据的连续接收,下面采用Monitor类来进行线程同步,使数据读、写操作称为原子操作。即达到这样的目的:在线程2写数据时,线程1等待,在线程1读数据时,线程2等待,使每次输出的结果全部为0或全部为1。当调用Monitor类的Enter(Object obj)方法时,会获取对象obj的独占权,直到调用Exit(Object obj)方法时,才会释放对obj的独占权。注意调用Enter方法的次数要和,调用Exit方法的次数相等。Monitor类还提供了TryEnter方法,该方法尝试获取obj对象的独占权,当获取独占权失败时,将返回false。实现代码如下:,学习情景2.3 单个串口设备数据的连续接收,private static int a=new int5;private static object obj=new object();static void Main(string args)Thread t1=new Thread(new ThreadStart(threadFun1);Thread t2=new Thread(new ThreadStart(threadFun2);t1.Start();t2.Start();private static void threadFun1()while(true)Monitor.Enter(obj);/线程1进入临界区活动时,线程2等待 for(int i=0;i a.Length;i+)System.Console.Write(ai+);();,学习情景2.3 单个串口设备数据的连续接收,Monitor.Exit(obj);/线程1出临界区后,线程2才可以进入 private static void threadFun2()int flag=0,i;while(true)Monitor.Enter(obj);/线程2进入临界区活动时,线程1等待 for(i=0;i a.Length;i+)ai=flag;flag=flag=0?1:0;Monitor.Exit(obj);/线程2出临界区后,线程1才可以进入,学习情景2.3 单个串口设备数据的连续接收,5 工作者线程向用户界面线程传递数据用户界面线程简称UI线程,其主要特点是能响应Windows消息,主要负责接收用户输入和向用户展示程序执行结果。为了及时响应用户的输入,UI线程中不应执行费时的运算,更不能被阻塞。工作者线程一般用于在后台进行费时运算或和慢速设备打交道,这种线程不响应Windows消息。在通信程序中,数据的发送和接收耗费的时间不确定。为了在通信过程中能够响应用户输入,通常在建立一个或多个工作者线程,在后台完成通信任务。工作者线程向运行在UI线程中的用户控件传递数据时,不能直接对对控件的属性和方法进行调用,而要先定义一个委托,再用控件的Invoke方法,切换到UI线程去执行委托所指向的函数,来更新控件显示的内容。在下面的程序中,工作线程每循环完一次,就更新UI线程中的控件属性,向用户报告当前步骤。程序界面和后台代码如下:,学习情景2.3 单个串口设备数据的连续接收,图2.3.1 工作者线程向UI线程传递数据private void btnRun_Click(object sender,EventArgs e)ThreadStart funEntrance=new ThreadStart(threadFun);Thread t=new Thread(funEntrance);t.IsBackground=true;t.Start();,学习情景2.3 单个串口设备数据的连续接收,private delegate void crossThreadDelegate(int i);/定义委托void showValue(int i)lblReport.Text=执行到了第+i+步;private void threadFun()crossThreadDelegate cdt=new crossThreadDelegate(showValue);for(int i=1;i=5;i+)/Invoke方法将当前线程切换到UI线程,再执行委托指向的函数 this.Invoke(cdt,i);/this指代当前窗口 Thread.Sleep(1000);/延时,便于看清中间执行过程,学习情景2.3 单个串口设备数据的连续接收,下面的程序在工作者线程的线程函数中直接使用lblReport.Text属性,而没有用Invoke方法:private void threadFun()for(int i=1;i=5;i+)lblReport.Text=执行到了第+i+步;Thread.Sleep(1000);在运行时会捕获到图所示的异常:,学习情景2.3 单个串口设备数据的连续接收,图2.3.2 后台线程向UI线程传递数据,学习情景2.3 单个串口设备数据的连续接收,使用委托和Invoke方法会使代码复杂度增加,在实际应用中,可以用匿名委托来简化代码:private delegate void crossThreadDelegate();private void threadFun()for(int i=1;i=5;i+)crossThreadDelegate cdt=delegate/匿名委托(无函数名showValue)lblReport.Text=执行到了第+i+步;this.Invoke(cdt);/指向委托指向的函数 Thread.Sleep(1000);,学习情景2.3 单个串口设备数据的连续接收,2.3.4 任务实施1 在工作者线程中接收HSDZC电能综合测试仪的数据(1)设计界面在转机性能测试中需要读取HSDZC电能综合测试仪采集到的3个数值:输入功率、输出功率和功率因数,以测试电机效率,此时测试仪的是选择测量方式1。程序设计时的界面如图,用3个文本框分别显示输入功率、输出功率和功率因数,控件名称分别为txtSrgl、txtScgl和txtGlys。该界面是整个钻机性能测试系统主界面的一部份,为了便于观察和调试,在界面的右边以十六进制形式显示收到的数据帧,程序运行时的界面如图所示。,学习情景2.3 单个串口设备数据的连续接收,图2.3.3 电机效率测试程序设计时界面,学习情景2.3 单个串口设备数据的连续接收,图2.3.4 电机效率测试程序运行时界面,学习情景2.3 单个串口设备数据的连续接收,private void btnStart_Click(object sender,EventArgs e)/启动工作者线程 ThreadStart funEntrance=new ThreadStart(threadFun);Thread t=new Thread(funEntrance);t.IsBackground=true;stop=false;t.Start();(3)编写在工作者线程中执行的函数点击“开始读数”按钮后时,启动一个工作者线程,线程执行函数threadFun中的内容。threadFun在执行时先打开串口,设置超时毫秒数,并创建数据接收队列recvBuf,然后就进入while循环开始不断地接收串口数据并进行分析和显示。while循环受到bool型变量stop的控制,可以通过设置该变量值为true,来结束线程中的循环。其中用到recvBuf对象和getBlock方法,它们的用法在前一节已经进行了详细说明。,学习情景2.3 单个串口设备数据的连续接收,private void threadFun()SerialPort sp=new SerialPort(COM1,9600,Parity.None,8,StopBits.One);try sp.Open();catch(Exception ex)MessageBox.Show(打开串口失败!);return;byte first;byte bRecv;/HSDZC在工作方式1时,数据帧长度为46字节,学习情景2.3 单个串口设备数据的连续接收,private void threadFun()SerialPort sp=new SerialPort(COM1,9600,Parity.None,8,StopBits.One);try sp.Open();catch(Exception ex)MessageBox.Show(打开串口失败!);return;byte first;byte bRecv;/HSDZC在工作方式1时,数据帧长度为46字节,学习情景2.3 单个串口设备数据的连续接收,(4)编写显示数据到控件的showData方法在线程函数中调用了showData方法显示数据,代码如下:private delegate void crossThreadDelegate();private void showData(byte b)float v;/跨线程访问UI控件 crossThreadDelegate cdt=delegate/匿名委托 if(b=null)/没有接收到数据 txtSrgl.Text=Error;txtScgl.Text=Error;txtGlys.Text=Error;else,学习情景2.3 单个串口设备数据的连续接收,/以十六进制形式在txtData文本框中显示数据帧的内容 txtData.Text=CCheck.BinaryToHexString(b);/对浮点数进行解码(用HexToFloat函数),并分别显示在3个文本框中 v=HexToFloat(b4+9*3,b4+9*3+1,b4+9*3+2);txtSrgl.Text=v.ToString(0.00);/输入功率(第9个数值)v=HexToFloat(b4+12*3,b4+12*3+1,b4+12*3+2);txtScgl.Text=v.ToString(0.00);/输出功率(第12个数值)v=HexToFloat(b4+6*3,b4+6*3+1,b4+6*3+2);txtGlys.Text=v.ToString(0.00);/功率因数(第6个数值);this.Invoke(cdt);,学习情景2.3 单个串口设备数据的连续接收,(5)编写代码处理“停止读数”按钮的点击事件:private void btnStop_Click(object sender,EventArgs e)stop=true;/设置标志,使线程中的循环自然结束2 HSDZC电能综合测试仪数据接收和处理操作的封装在前一个步骤中已经实现了串口数据接收、解析和显示的功能,并且在工作者线程中接收和处理数据,在此期间前台的UI线程能响应用户输入。从功能上看,已经能够满足用户需求,但是从代码的组织和管理角度看,该程序还有比较大问题:负责接收和处理数据的代码和负责显示的代码混杂在一起,不能重复使用,若增加一个同类设备,很多代码还要重写,而且给查看和调试程序也带来较大困难。下面用面向对象方法对程序进行重新设计。,学习情景2.3 单个串口设备数据的连续接收,(1)创建抽象类CPassiveCOMHelper,public class CPassiveCOMHelper public int readTimeOut,recvBufLength;/读取超时毫秒数,接收队列大小 protected CRecvBuf recvBuf;/接收队列 private bool stopFlag;/线程循环结束标志 private string portName;/串口名称 private int baudRate,dataBits;/波特率,数据位数 private StopBits stopBits;/停止位 private byte dataBlock;/一个完整的数据帧,学习情景2.3 单个串口设备数据的连续接收,public delegate void DataReceivedHandler();public event DataReceivedHandler DataReceived;/数据接收完毕事件/构造方法 public CPassiveCOMHelper(string portName,int baudRate,int dataBits,StopBits stopBits)this.portName=portName;this.baudRate=baudRate;this.dataBits=dataBits;this.stopBits=stopBits;this.readTimeOut=500;/默认500毫秒读取超时 this.recvBufLength=100;/默认接收队列大小为100字节,学习情景2.3 单个串口设备数据的连续接收,public void start()/启动线程 Thread t=new Thread(new ThreadStart(threadFun);t.IsBackground=true;stopFlag=false;t.Start();public void stop()/停止线程循环,自然终止线程 stopFlag=true;protected virtual byte getBlock(List li)/需要在派生类中重写该方法,学习情景2.3 单个串口设备数据的连续接收,public byte getData()byte tmp=null;Monitor.Enter(this);/线程同步:在进行get操作时,不准进行set操作 if(dataBlock!=null)tmp=new bytedataBlock.Length;dataBlock.CopyTo(tmp,0);Monitor.Exit(this);return tmp;private void setData(byte tmp),Monitor.Enter(this);/线程同步:在进行set操作时,不准进行get操作 dataBlock=tmp;Monitor.Exit(this);private void threadFun()recvBuf=new CRecvBuf(recvBufLength);SerialPort sp=new SerialPort(portName,baudRate,Parity.None,dataBits,stopBits);try if(sp.IsOpen)sp.Close();sp.Open();,学习情景2.3 单个串口设备数据的连续接收,catch return;byte first;byte bRecv;while(!stopFlag)try sp.ReadTimeout=readTimeOut;first=(byte)sp.ReadByte();if(sp.BytesToRead+1 recvBuf.maxLength)/堆积数据太多,学习情景2.3 单个串口设备数据的连续接收,(2)派生出具体类CHSDZC在CPassiveCOMHelper类中实现了串口打开、关闭、线程创建、数据接收等基础操作,其中有一个虚方法getBlock,需要在派生类中根据具体情况实现,下面实现专门采集HSDZC电能综合测试仪数据的类CHSDZC:class CHSDZC:CPassiveCOMHelper/构造方法 public CHSDZC(string portName,int baudRate,int dataBits,StopBits stopBits):base(portName,baudRate,dataBits,stopBits)/覆盖基类的getBlock方法,专门针对HSDZC获取数据,学习情景2.3 单个串口设备数据的连续接收,protected override byte getBlock(List li)byte b=null;if(li.Count=p+46)if(lip+1=0 xFF/已经找到,接收循环 if(p!=-1)/p为数据帧的起始位置,学习情景2.3 单个串口设备数据的连续接收,b=new byte46;li.CopyTo(p,b,0,46);/拷贝数据帧到b数组 return b;/返回找到的完整数据帧(3)在UI模块中使用CHSDZC类实现数据采集在前面所作工作的基础上编写UI模块就非常轻松了。在UI模块中不用关心串口通信、线程操作等复杂问题,只需要创建CHSDZC对象并启动它即可,当有完整的数据帧收到时,会引发DataReceived事件,并自动调用hsdzc_DataReceived方法处理数据。不过这里需要注意的是,hsdzc_DataReceived是个回调函数,虽然包含在Form6类中,但是在工作者线程中进行调用,需要跨线程操作UI线程中的文本框等控件,因此把操作控件的代码封装到一个匿名委托中,并用this.Invoke进行调用。,学习情景2.3 单个串口设备数据的连续接收,2.3.5 考核要点,学习情景2.3 单个串口设备数据的连续接收,2.3.6 能力拓展1.什么是委托,委托在什么场合使用,如何使用委托?2.在什么情况下需要使用多线程技术,使用Thread类要引入什么命名空间,如何创建和启动线程?3.如何将工作者线程中的数据显示在UI线程的控件中?4.安照说明书里规定的数据格式,设计一个HSDZC电能综合测试仪的模拟程序(包含两种测量方式)。5.在图的窗体上增加一个Timer控件,并结合小节的任务二,将采集到的输入功率、输出功率、功率因数值存储到Params集合中(不立即显示在文本框中),每隔1秒钟用Params集合中的值刷新文本框的内容,

    注意事项

    本文(单个串口设备数据的连续接收.ppt)为本站会员(牧羊曲112)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开