引言
在现代各种工业控制和长距离数据传输等通信系统中,实现计算机与单片机的串行通信是比较普遍的需求,这就需要在windows 操作系统下实现串口通信。目前对于上位机串口通信编程多采用串口控件mscomm,或者直接调用api函数的形式。其中mscomm控件使用非常简单,但由于其对串口的封装,在一些有特殊要求的情况下使用起来不够灵活。而api函数都是以独立的全局函数的形式存在,使用起来比较零散,不够系统,容易出错。因此,本文在主要研究api串口操作函数的基础上创建一个串口通信的类,并公开了所有接口,利于串口通信程序的继承和使用,具有较高的可移植性。
windows 9x/nt是抢先式的多任务操作系统,程序对cpu的占用时间由系统决定。多任务指的是系统可以同时运行多个进程,每个进程又可以同时执行多个线程。进程是应用程序的运行实例,拥有自己的地址空间。每个进程拥有一个主线程,同时还可以建立其他的线程。线程是操作系统分配cpu时间的基本实体,每个线程占用的cpu时间由系统分配,系统不停地在线程之间切换。进程中的线程共享进程的虚拟地址空间,可以访问进程的资源,处于并行执行状态,这就是多线程的基本概念<sup style="font-family: ">[1]</sup>。在串口通信的应用中大多数情况下是要实现双工通信,操作系统在随时准备接受数据和发送数据,为了达到更高的效率和复合工业现场的实时性,本文将实现多线程的串口通信。
多线程的程序设计
2.1 多线程的数据安全共享
由于同一进程的所有线程共享进程的虚拟地址空间,并且线程的中断是汇编语言级的,所以可能会发生两个线程同时访问同一个对象(包括全局变量、共享资源、api函数和mfc对象等)的情况,这有可能导致程序错误。例如,如果一个线程在未完成对某一大尺寸全局变量的读操作时,另一个线程又对该变量进行了写操作,那么第一个线程读入的变量值可能是一种修改过程中的不稳定值。属于不同进程的线程在同时访问同一内存区域或共享资源时,也会存在同样的问题。因此,在多线程应用程序中,常常需要采取一些措施来同步线程的执行。线程同步是解决数据共享的有效方法。实现线程同步的方法有:原子访问、关键代码段、事件、信标、互斥量等。其中前两者是用户模式中的同步方法,后三者是利用内核对象来实现线程同步。在本设计中选用了原子访问方式。原子访问不用线程从用户模式转换到内核模式,因此可以节约大约1000个cpu周期的时间,原子访问是指线程在访问资源时确保其它线程不在同一时间内访问相同的资源。原子访问是通过api函数来实现的,本类api函数有:interlocked exchange add(plong*p,long q)此函数实现:*p=*p+q的原子操作。以及interlocked exchange(plong p,long u)完成*p=u的原子操作。可以利用互锁函数 interlocked exchange来控制工作者线程的退出。
2.2 vc++对多线程的支持
使用mfc开发是较普遍的vc++编程方法。在vc++6.0下,mfc应用程序的线程由cwinthread对象表示。vc++把线程分为两种:用户界面线程和工作者线程。用户界面线程能够提供界面和用户交互,通常用于处理用户输入并相应各种事件和消息;而工作者线程主要用来处理程序的后台任务。
程序一般不需要直接创建cwinthread对象,而是创建一个cwinthread的指针,通过调用afxbeginthread()函数创建一个cwinthread对象,从而开始一个线程。创建上述的两种线程都利用这个函数。线程的终止取决于下列事件之一:线程函数返回、线程调用exitthread()退出、异常情况下用线程的句柄调用terminatethread()退出、线程所属的进程被终止。另外vc++还对内核同步方法进行了封装。如本文用到cevent类就是对event内核句柄的封装。
多线程串口通信类设计
3.1 串行通信的基本原理
串行端口的本质功能是作为cpu和串行设备间的编码转换器。当数据从 cpu经过串行端口发送出去时,字节数据转换为串行的位。在接收数据时,串行的位被转换为字节数据。 在windows环境(windows nt、win98、windows2000)下,串口是系统资源的一部分。应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请要求(打开串口),在打开串口后还要进行一系列的配置操作。如:波特率、校验位等。还要设置串口消息、消息表等,应用程序在waitcommevent中得到消息,利用这个消息来触发读串口的操作。通信完成后必须释放资源(关闭串口)。
3.2 串口通信类实现</font> 为了能让用户方便使用串口通信类,而不必关心内部如何实现,将串口通信类做成通信库。这也同时大大地增加了该串口通信库的可移植性。该通信库包含两部分,一是库文件(lib),一是提供接口的头文件(.h),库文件用于具体功能的实现,头文件用于接口的声明。以下是库的接口,其中主要函数功能如下:portconfig进行串口的配置;
readcallback设置读回调;sendcallback设置写回调;startlisten启动线程;senddata发送数据。
3.3 多线程串口通信实现
本程序一共采用了三个线程:
(1) 程序主线程显示线程;
(2) 发数工作者线程;
(3) 收数工作者线程。
发数工作的线程和收数工作的线程均由线程在用户的命令下开启。基于研究的目的,在设计时收、发数据一次均以1k为限。在用户发出发数命令时,主线程首先判断串口是否打开。如果开启,则开启发数线程,发数线程就将缓冲区中的数据全部的发送出去。在数据发送完毕后,线程自动退出。在用户发出监测命令时,线程在串口已开启的情况下开启收数线程。收数线程进去后就开始等待串口事件。当有串口事件发生(如,有字符到达),该线程就读串口,并将数据存入缓冲区,并在窗口中显示。当用户发出停止监测命令时,线程退出
。通信流程如附图所示。
附图
通信流程图
结束语
本文设计编写了通用串口通信类,可适用于与计算机通信的各种工程领域。采用用vc++编写,程序简单易懂。
visual c++串口通信技术与工程实现
串行通信开发指南
mcs-51系列单片机实用接口技术