工控程序设计之串口设备的数据采集(ppt 43页).ppt_第1页
工控程序设计之串口设备的数据采集(ppt 43页).ppt_第2页
工控程序设计之串口设备的数据采集(ppt 43页).ppt_第3页
工控程序设计之串口设备的数据采集(ppt 43页).ppt_第4页
工控程序设计之串口设备的数据采集(ppt 43页).ppt_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

工控程序设计 学习情景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 NETFramework对多线程的支持 NET平台库提供了Thread类对线程进行处理 该类包含在System Threading命名空间中 程序中需要包含语句 usingSystem Threading 编程人员可以通过创建一个Thread类的实例来创建一个线程 并通过Thread类提供的方法对线程进行管理 Thread类的常用属性和方法如下表 表2 3 1Thread类的常用属性和方法 学习情景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类派生出来的 例如 delegateintSomeDelegate intp1 stringp2 intp1和stringp2是被引用函数的参数类型和名称 其中参数类型和参数的个数必须和被引用函数的类型与个数一致 3创建和启动线程一个线程必须和一个方法的入口 委托 关联起来 线程启动后 自动从该入口进入 执行函数体中包含的内容 C 应用程序启动时 自动创建主线程 并进入Main方法开始执行 其它线程需要在程序里自己定义和启动 由于委托可以代表一个方法的入口 学习情景2 3单个串口设备数据的连续接收 所以创建线程实例时只需要在Thread类的构造方法里传入一个委托实例即可 这个委托名叫ThreadStart 已经在线程命名空间中定义作了定义 publicdelegatevoidThreadStart 所以创建线程方式如下 ThreadStartfunctionEntrance newThreadStart threadFunction Threadt newThread functionEntrance 在委托ThreadStart的构造方法里面传入的是方法名 这个方法可以是静态方法 也可以是某个对象的方法 线程对象创建后 我们就可以调用其Start方法开始线程的执行了 学习情景2 3单个串口设备数据的连续接收 我们可以在主线程里建立线程 也可以在线程里再创建线程 线程启动后会自动执行委托实例代表的方法 线程执行完后会自动销毁并释放其占用的资源 在一个新线程中执行带参数的函数 操作步骤如下 定义线程函数 privatevoidparamThreadFunction objectparam 函数体 用ParameterizedThreadStart委托封装线程函数 学习情景2 3单个串口设备数据的连续接收 ParameterizedThreadStartfunctionEntrance newParameterizedThreadStart paramThreadFunction 创建线程对象hreadt newThread functionEntrance 启动线程t Start param param为传入的参数 可以是任意对象 学习情景2 3单个串口设备数据的连续接收 4线程同步技术多线程应用程序中的的线程启动后 执行的先后顺序是无法预知的 通常情况下多个线程会交错执行 但是在多个线程访问共享数据的情况下 必须对数据的访问进行同步 好比有两路车 一路自东向西 一路自南向北运行 在一个十字路口交汇 在十字路口以外的区域可以看着私有区域 而十字路口则是共有区域 需要红绿灯或交警来维护秩序 即确保在同一时刻只能有一路车进入 而另外一路车必须等待 这就是现实生活中的线程同步问题 学习情景2 3单个串口设备数据的连续接收 下面的例子展示了一个读数据线程和一个写数据线程同时运行的情况 privatestaticint a newint 5 staticvoidMain string args Threadt1 newThread newThreadStart threadFun1 Threadt2 newThread newThreadStart threadFun2 t1 Start t2 Start privatestaticvoidthreadFun1 线程函数1 while true 学习情景2 3单个串口设备数据的连续接收 for inti 0 i a Length i 将数组元素全部输出System Console Write a i System Console WriteLine privatestaticvoidthreadFun2 线程函数2 intflag 0 i while true for i 0 i a Length i 将数组元素全部改为0或1a i flag flag flag 0 1 0 学习情景2 3单个串口设备数据的连续接收 下面采用Monitor类来进行线程同步 使数据读 写操作称为原子操作 即达到这样的目的 在线程2写数据时 线程1等待 在线程1读数据时 线程2等待 使每次输出的结果全部为0或全部为1 当调用Monitor类的Enter Objectobj 方法时 会获取对象obj的独占权 直到调用Exit Objectobj 方法时 才会释放对obj的独占权 注意调用Enter方法的次数要和 调用Exit方法的次数相等 Monitor类还提供了TryEnter方法 该方法尝试获取obj对象的独占权 当获取独占权失败时 将返回false 实现代码如下 学习情景2 3单个串口设备数据的连续接收 privatestaticint a newint 5 privatestaticobjectobj newobject staticvoidMain string args Threadt1 newThread newThreadStart threadFun1 Threadt2 newThread newThreadStart threadFun2 t1 Start t2 Start privatestaticvoidthreadFun1 while true Monitor Enter obj 线程1进入临界区活动时 线程2等待for inti 0 i a Length i System Console Write a i System Console WriteLine 学习情景2 3单个串口设备数据的连续接收 Monitor Exit obj 线程1出临界区后 线程2才可以进入 privatestaticvoidthreadFun2 intflag 0 i while true Monitor Enter obj 线程2进入临界区活动时 线程1等待for i 0 i a Length i a i 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线程传递数据privatevoidbtnRun Click objectsender EventArgse ThreadStartfunEntrance newThreadStart threadFun Threadt newThread funEntrance t IsBackground true t Start 学习情景2 3单个串口设备数据的连续接收 privatedelegatevoidcrossThreadDelegate inti 定义委托voidshowValue inti lblReport Text 执行到了第 i 步 privatevoidthreadFun crossThreadDelegatecdt newcrossThreadDelegate showValue for inti 1 i 5 i Invoke方法将当前线程切换到UI线程 再执行委托指向的函数this Invoke cdt i this指代当前窗口Thread Sleep 1000 延时 便于看清中间执行过程 学习情景2 3单个串口设备数据的连续接收 下面的程序在工作者线程的线程函数中直接使用lblReport Text属性 而没有用Invoke方法 privatevoidthreadFun for inti 1 i 5 i lblReport Text 执行到了第 i 步 Thread Sleep 1000 在运行时会捕获到图2 4 2所示的异常 学习情景2 3单个串口设备数据的连续接收 图2 3 2后台线程向UI线程传递数据 学习情景2 3单个串口设备数据的连续接收 使用委托和Invoke方法会使代码复杂度增加 在实际应用中 可以用匿名委托来简化代码 privatedelegatevoidcrossThreadDelegate privatevoidthreadFun for inti 1 i 5 i crossThreadDelegatecdt delegate 匿名委托 无函数名showValue lblReport Text 执行到了第 i 步 this Invoke cdt 指向委托指向的函数Thread Sleep 1000 学习情景2 3单个串口设备数据的连续接收 2 3 4任务实施1在工作者线程中接收HSDZC电能综合测试仪的数据 1 设计界面在转机性能测试中需要读取HSDZC电能综合测试仪采集到的3个数值 输入功率 输出功率和功率因数 以测试电机效率 此时测试仪的是选择测量方式1 程序设计时的界面如图2 3 3 用3个文本框分别显示输入功率 输出功率和功率因数 控件名称分别为txtSrgl txtScgl和txtGlys 该界面是整个钻机性能测试系统主界面的一部份 为了便于观察和调试 在界面的右边以十六进制形式显示收到的数据帧 程序运行时的界面如图2 3 4所示 学习情景2 3单个串口设备数据的连续接收 图2 3 3电机效率测试程序设计时界面 学习情景2 3单个串口设备数据的连续接收 图2 3 4电机效率测试程序运行时界面 学习情景2 3单个串口设备数据的连续接收 privatevoidbtnStart Click objectsender EventArgse 启动工作者线程ThreadStartfunEntrance newThreadStart threadFun Threadt newThread funEntrance t IsBackground true stop false t Start 3 编写在工作者线程中执行的函数点击 开始读数 按钮后时 启动一个工作者线程 线程执行函数threadFun中的内容 threadFun在执行时先打开串口 设置超时毫秒数 并创建数据接收队列recvBuf 然后就进入while循环开始不断地接收串口数据并进行分析和显示 while循环受到bool型变量stop的控制 可以通过设置该变量值为true 来结束线程中的循环 其中用到recvBuf对象和getBlock方法 它们的用法在前一节已经进行了详细说明 学习情景2 3单个串口设备数据的连续接收 privatevoidthreadFun SerialPortsp newSerialPort COM1 9600 Parity None 8 StopBits One try sp Open catch Exceptionex MessageBox Show 打开串口失败 return bytefirst byte bRecv HSDZC在工作方式1时 数据帧长度为46字节 学习情景2 3单个串口设备数据的连续接收 privatevoidthreadFun SerialPortsp newSerialPort COM1 9600 Parity None 8 StopBits One try sp Open catch Exceptionex MessageBox Show 打开串口失败 return bytefirst byte bRecv HSDZC在工作方式1时 数据帧长度为46字节 学习情景2 3单个串口设备数据的连续接收 4 编写显示数据到控件的showData方法在线程函数中调用了showData方法显示数据 代码如下 privatedelegatevoidcrossThreadDelegate privatevoidshowData byte b floatv 跨线程访问UI控件crossThreadDelegatecdt 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 b 4 9 3 b 4 9 3 1 b 4 9 3 2 txtSrgl Text v ToString 0 00 输入功率 第9个数值 v HexToFloat b 4 12 3 b 4 12 3 1 b 4 12 3 2 txtScgl Text v ToString 0 00 输出功率 第12个数值 v HexToFloat b 4 6 3 b 4 6 3 1 b 4 6 3 2 txtGlys Text v ToString 0 00 功率因数 第6个数值 this Invoke cdt 学习情景2 3单个串口设备数据的连续接收 5 编写代码处理 停止读数 按钮的点击事件 privatevoidbtnStop Click objectsender EventArgse stop true 设置标志 使线程中的循环自然结束 2HSDZC电能综合测试仪数据接收和处理操作的封装在前一个步骤中已经实现了串口数据接收 解析和显示的功能 并且在工作者线程中接收和处理数据 在此期间前台的UI线程能响应用户输入 从功能上看 已经能够满足用户需求 但是从代码的组织和管理角度看 该程序还有比较大问题 负责接收和处理数据的代码和负责显示的代码混杂在一起 不能重复使用 若增加一个同类设备 很多代码还要重写 而且给查看和调试程序也带来较大困难 下面用面向对象方法对程序进行重新设计 学习情景2 3单个串口设备数据的连续接收 1 创建抽象类CPassiveCOMHelper publicclassCPassiveCOMHelper publicintreadTimeOut recvBufLength 读取超时毫秒数 接收队列大小protectedCRecvBufrecvBuf 接收队列privateboolstopFlag 线程循环结束标志privatestringportName 串口名称privateintbaudRate dataBits 波特率 数据位数privateStopBitsstopBits 停止位privatebyte dataBlock 一个完整的数据帧 学习情景2 3单个串口设备数据的连续接收 publicdelegatevoidDataReceivedHandler publiceventDataReceivedHandlerDataReceived 数据接收完毕事件 构造方法publicCPassiveCOMHelper stringportName intbaudRate intdataBits StopBitsstopBits this portName portName this baudRate baudRate this dataBits dataBits this stopBits stopBits this readTimeOut 500 默认500毫秒读取超时this recvBufLength 100 默认接收队列大小为100字节 学习情景2 3单个串口设备数据的连续接收 publicvoidstart 启动线程 Threadt newThread newThreadStart threadFun t IsBackground true stopFlag false t Start publicvoidstop 停止线程循环 自然终止线程 stopFlag true protectedvirtualbyte getBlock Listli 需要在派生类中重写该方法 学习情景2 3单个串口设备数据的连续接收 publicbyte getData byte tmp null Monitor Enter this 线程同步 在进行get操作时 不准进行set操作if dataBlock null tmp newbyte dataBlock Length dataBlock CopyTo tmp 0 Monitor Exit this returntmp privatevoidsetData byte tmp Monitor Enter this 线程同步 在进行set操作时 不准进行get操作dataBlock tmp Monitor Exit this privatevoidthreadFun recvBuf newCRecvBuf recvBufLength SerialPortsp newSerialPort portName baudRate Parity None dataBits stopBits try if sp IsOpen sp Close sp Open 学习情景2 3单个串口设备数据的连续接收 catch return bytefirst byte bRecv while stopFlag try sp ReadTimeout readTimeOut first byte sp ReadByte if sp BytesToRead 1 recvBuf maxLength 堆积数据太多 学习情景2 3单个串口设备数据的连续接收 2 派生出具体类CHSDZC在CPassiveCOMHelper类中实现了串口打开 关闭 线程创建 数据接收等基础操作 其中有一个虚方法getBlock 需要在派生类中根据具

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论