冯 正 韩 焱 王黎明
摘 要:GPS导航系统需要实时获取来自接收机串口的定位数据,在对串口实时监控的同时还可以在前台进行一些其他的操作,利用基于多线程的串口通信编程思想方法可以很好地解决这一问题。介绍多线程的基本概念和串口通信编程技术,分析了GPS导航系统的功能和需求,着重阐述了采用基于多线程的CSerialPort类的串口通信方法来获取定位数据,并且给出了CSerialPort类的使用方法。经过调试,程序运行平稳。
关键词:多线程;串口通信;CSerialPort;GPS导航
中图分类号:TP399文献标识码:B
文章编号:1004-373X(2009)05-028-03
Application of Multi-thread Serial Port Communication Technology in GPS Navigation System
FENG Zheng1,HAN Yan2,WANG Liming2
(1.National Key Laboratory of Electronic Testing Technology,North University of China,Taiyuan,030051,China;
2.Information Detection and Treatment Institute of Technology,North University of China,Taiyuan,030051,China)
Abstract:The GPS navigation system needs not only gaining real-time orientation data from the receiver serial port but also operating other somethings,the way that based on the multi-thread and the serial communication programming technology theory can solve this question.The multi-thread basic concept and the serial port communication programming technology are introduced,and the GPS navigation system′s function and the requirement are analysed,real-time orientation data by using CSerialPort class based on the multi-thread,and the application method of CSerialPort class are given.After the debugging,the program runs steady.
Keywords:multi-thread;serial port communication;CSerialPort;GPS navigation
0 引 言
GPS(Global Position System)具有全球性、全天候性优势的定位、定时、测速系统,用户利用GPS接收机接收卫星发射的信号,从而获取当前位置的大地坐标、高程和时间等信息,达到定位、导航或测量高程的目的。卫星导航定位技术被广泛应用于海洋勘测、海洋工程、海洋开发和军事作战中,高精度、快捷方便、全天候等优良特性,使其越来越受到人们的青睐[1]。在GPS导航中,需要实时采集遵循NMEA0183协议的GPS数据,对数据进行处理后,通过ODBC接口将用户的位置、时间、速度等信息存到数据库,为以后在电子地图上实时显示目标位置提供依据。为了避免由于一直等待串口I/O操作而引起的线程阻塞,要求程序在对串行端口进行实时监控的同时,可以在前台进行数据提取、保存、显示等操作。为了解决实时性和多任务处理,避免某项任务长时间占用CPU,多线程编程是一个比较理想的选择。
1 多线程概述
1.1 基本概念
进程是程序在计算机上的一个执行实例,线程是程序中的一条执行分支,多线程就是在同一个程序中可以同时执行多个任务。每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其他线程,多个线程并发地运行于同一个进程中。
一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源[2]。
1.2 VC++环境对多线程技术的支持
Visual C++ 6.0中,MFC类库提供了对多线程编程的支持,使得多线程编程更加方便。MFC中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。工作者线程通常用来执行后台计算和维护任务。用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。
1.3 线程创建、挂起、恢复、终止
在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。SuspendThread() 和ResumeThread()分别用于挂起指定的线程和恢复用SuspendThread()挂起的线程。ExitThread(DWORD dwExitCode)用于线程终结自身的执行。
1.4 线程同步
线程之间经常要同时访问一些资源,因此对共享资源进行访问引起冲突是不可避免的。为了解决这种资源冲突问题,必须引入线程同步的概念。Win32 API提供了多种同步控制对象来解决共享资源访问冲突,包括使用临界区、使用互斥对象、使用信号量、使用事件[3]。
2 串口通信编程
目前,在Windows下编程时,常用的串口通信主要有3种方法:用MSComm通信控件;用Windows API进行编程;使用第三方提供的一些串口通讯类进行编写[4]。
(1) MSComm控件
利用MSComm控件会使编程快捷简单。然而,由于做了大量的封装,降低了编程的可控性和灵活性,因此在多线程多串口编程时,需要做许多复杂的处理。
(2) Windows API
利用Windows API编写串口程序,特别是复杂的多线程串口程序时,对于程序员的编程能力要求较高。除了需要程序员熟练掌握和使用众多的API函数,能编写很多底层代码之外,还必须熟悉线程的编程方法。
(3) 第三方串口通信类
利用第三方的串口通信类进行串口编程时,既可以使编程效率高,程序可控性强,又比Window API编程简单,其中应用最多的第三方的串口通信类是CSerialPort。它基于多线程,是一个Win32 API的打包类,对处理串口的Win32 API类进行了封装,借助这个类可以很方便地对串口进行操作,容易实现多线程的串口通信,编写的程序在Windows 98/NT/2000/XP操作系统下可很好地运行。
比较3种串口通信方式,可以发现使用第三方串口通信类CSerialPort是实现Windows下多线程串口编程的较好选择。
3 多线程编程技术在GPS数据采集系统中的应用
3.1 GPS导航系统功能分析
GPS导航是通过GPS定位技术实时给出用户所在的位置,这就要求需要实时接收来自GPS接收机串口的定位数据,在实时监视串口的同时还需要进行数据存储、显示等,利用多线程串口通信技术将很好地解决这个问题。通过对GPS导航系统分析,将程序分成以下几个线程:
主线程:负责处理用户界面的消息处理,按照预定义流程调度其他线程处理数据。
串口监视线程:监视串口,采集数据并将数据保存到一个缓冲区 。
入库线程:从缓冲区读取数据进行相应处理并将处理好的数据存入数据库。
显示线程:通过地图匹配算法将用户实时位置显示在电子地图上。
GPS导航系统框图如图1所示。
3.2 具体实现
系统首先对线程在相应的头文件中说明,然后在程序初始化时加入创建程序代码,这样创建后,线程就可以和主线程并发执行了。主线程、入库线程、显示线程与一般的编程处理相同,所以下面着重说明串口监视线程。
对串口的操作采用基于多线程编程的CSerialPort类,其工作流程如下:首先设置好串口参数,再开启串口监测工作线程。串口监测工作线程监测到串口接收到的数据流、控制事件或其他串口事件后,就以消息方式通知主程序,激发消息处理函数进行数据处理,这是对接收数据而言的;发送数据可直接向串口发送[5]。应用程序流程如图2所示。
编程步骤如下:
(1) 建立程序
建立一个基于单文档的MFC应用程序CSerialPortTest,其他步骤保持缺省状态。
(2) 添加类文件
将SerialPort.h和SerialPort.cpp两个类文件复制到工程文件夹中,用Project-Add to Project-Files命令将上述两个文件加入工程,并在任何要调用这个类的模块中加上#include SerialPort.h文件。
在视类头文件中定义串口类的对象:CSerialPort m_Port。
(3) 人工增加串口消息响应函数OnCommunication(WPARAM ch,LPARAM port)
首先在CSerialPortTestView.h中添加串口字符接收消息WM_COMM_RXCHAR(串口接收缓冲区内有一个字符)的响应函数声明,即:
afx_msg LONG OnCommunication(WPARAM ch,LPARAM port);
然后在CSerialPortTestView.cpp文件中进行WM_COMM_RXCHAR消息映射:
ON_MESSAGE(WM_COMM_RXCHAR,OnCommunication)
接着在CSerialPortTestView.cpp中加入函数的实现,即:
LONG CSerialPortTestView::OnCommunication(WPARAM ch,LPARAM port)
{ … }
(4) 初始化串口并开启串口监视线程
在视创建时初始化串口,首先利用ClassWizard生成OnInitialUpdate()函数。代码如下:
Void CSerialPortTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
if(m_Port.InitPort(this,1,4 800,′N′,8,1,EV_RXFLAG | EV_RXCHAR,512))
//设置端口为COM1,波特率4 800 b/s,数据位8,停止位1,检验位N,缓冲区512
{
m_Port.StartMonitoring();
//启动串口监视线程,利用WaitCommEvent函数对串口上发生的事件进行获取并根据事件的不同
类型进行相应的处理,利用WaitForMultipleObjects函数对串口相关的用户控制事件进行等待并做相应处理
}
else
{
AfxMessageBox(“没有发现此串口”);
}
}
在StartMonitoring()这个成员函数内部调用AfxBeginThread创建了一个工作线程,它的函数申明如下:
BOOL CSerialPort::StartMonitoring()
{
if(!(m_Thread=AfxBeginThread(CommThread,this)))
return FALSE;
TRACE("Thread started\n");
return TRUE;
}
在主线程初始化串口后创建CommThread函数进入死循环,线程一直监视串口事件,当读串口事件发生,读取串口接收到的数据,向主线程发自定义消息WM_COMM_RXCHAR,通知主线程在相应的消息响应函数中进行数据处理,当收到主线程的写串口命令时,将缓存中的数据写到串口。
(5) 在OnCommunication()函数中进行数据处理
每当串口接收缓冲区内有一个字符时,就产生一个WM_COMM_RXCHAR消息,触发OnCommunication函数。这时就可以在函数中进行相应数据处理,提取出时间、经纬度、速度等定位的关键数据,然后将这些数据保存到数据库。
LONG CMainFrame::OnCommunication(WPARAM ch,LPARAM port){
m_strReceived+=(char)ch;
while((m_strReceived.Find(0x0d)!=-1)&&(m_strReceived.Find(0x0a)!=-1)){
int startLF=m_strReceived.Find(0x0a);
//此行表示找到换行的位置并返回值
int endCR=m_strReceived.Find(0x0d);
if(startLF>endCR)
endCR=m_strReceived.Find(0x0d,startLF);
//从startLF位置开始找回车符
…………
}
4 结 语
串行通讯在通讯领域被广泛应用。利用基于多线程的第三方串口通信类CSerialPort很好地解决了由于串口长时间占用CPU而引起的线程堵塞等问题,编程简单、方便、可移植性强,对于其他类型的串口通信问题均可采用。该程序由Microsoft Visual C++ 6.0编译,在Windows XP下运行通过。
参考文献
[1]吴自银.一种基于电子地图的GPS导航定位技术 [J].海洋通报,2001,20(6):65-71.
[2]吴先亮,刘春生.基于多线程的串口通信软件的设计与实现[J].控制工程,2004,11(2):171-174.
[3]赵素林.利用多线程实现串口数据的实时图形化显示 [J].计算机技术与发展,2006,16 (6):124-126.
[4]蒋大奎.串口通信实现实时数据高速采集[J].北华航天工业学院学报,2007,17(4):15-16.
[5]龚建伟,熊光明.Visual C++/Turbo C串口通信编程实践[M].北京:电子工业出版社,2004.
[6]黄凌.基于单片机的GPS信息处理系统.现代电子技术,2007,30(21):60-61,75.
作者简介 冯 正 男,1980年出生,山西忻州人,硕士。主要研究方向为遥控遥测技术。
韩 焱 男,1957年出生,山西汶水人,教授、博士生导师。主要研究领域为信号与信息处理、无线通信。
王黎明 男,1974年出生,山西运城人,教授、硕士研究生导师。主要研究方向为兵器无损检测技术。