基于工控机的数控系统的实时控制设计
发布时间:2009-01-26
作者:
许燕萍 杨代华
来源:万方数据
分析了Windows的定时器原理,设计了一种在Windows平台下执行的实时控制程序,实现了系统微秒级的定时,可满足数控系统运动时对实时性的要求。
0 概 述
计算机数控机床(CNC)是20世纪70年代发展起来的机床控制新技术,它综合了计算机、自动控制、测量技术、机械制造等领域的最新成就,使机器工具的生产效率和加工精度得到了极大提高。数控技术的先进与否直接代表了一个国家的机械工业水平,数控产业对于国家的工业现代化,乃至于国家经济安全和国防安全都具有超越其巨大经济价值的战略意义。
数控系统是先进制造装备实现控制功能的核心部件。国内对数控系统的研究由于起步较晚,在技术上还落后于国外一到两代。目前数控产业的高端市场主要由西门子、发那克、GE等大公司主导,我国每年会进口上万台高档数控机床。因此,加强数控技术的研发,发展自主知识产权的数控系统势在必行。
实时控制是数控系统开发的关键技术之一,数控机床的精度一般可达到微米级,本设计主要用作教学实践用,对精度的要求不用太高,设计时可定为0.01 mm,此级精度对于普通的零件加工也是可以满足的。数控机床的速度一般为(2~20)m/min,若要达到0.01姗的定位,系统的响应需达到(0.03~0.3)脚,即需要微秒级的响应。不光是定位,在基于PC机的数控软件中,为了保证对控制对象的实时性处理,包括数据采集、速度处理、插补及位置伺服控制、事件处理等,每一项任务都要在规定的时间内完成。因此,对于底层控制程序的设计是非常重要的。
1 Windows的定时器原理
Windows是一个消息驱动式的操作系统,Windows消息提供了应用程序与应用程序之间、应用程序与Windows系统之间进行通讯的手段。应用程序要实现的功能由消息来触发,并靠对消息的响应和处理来完成。但由于消息传递是非抢先性的,不论事件消息的急与缓,总是按到达的先后顺序排队,这就使得一些外部实时事件可能得不到及时的处理,容易造成实时系统性能不稳定。对实时控制系统而言,在精确的时间段内及时响应消息是实时系统的根本,如果不能保证系统的实时性,实时控制系统也就失去了实际意义。在计算机数控系统开发中,数据插补是一件实时性很强的工作,它要求在给定的时间段内,必须进行插补工作。插补的品质直接决定了系统的加工速度和加工精度。在Windows环境下如何实现实时中断和控制是计算机数控系统开发中的难点之一。
2 实时控制解决方案
在各种实时控制系统中,实时时钟的控制都是关键技术。因为各种控制过程、控制任务都由实时时钟来推进。在Windows环境下,常用的实时时钟获取方法有下面几种:
a)设置Win32定时器并响应WM_TIMER消息来进行实时处理:这种方法是最简单的一类方法,在Windows环境下,各种可视化开发工具如VC,Delphi,C++ Builder等,都提供定时器控件Timer。通过设定控件的属性,并响应WM_TMER消息,可以实现一定的定时功能。但是由于Windows提供的定时器是建立在D0sICH中断的基础上,该中断每秒钟发生18.2次,即定时周期为54.945ms,该定时精度远不能满足数控系统对实时性的要求。另外,函数SetTimer()中指定的计时周期虽以ms为单位,但这个值要转化为54.945ms的整数倍;定时器发送的WM_TIMER消息的优先级相当低,在应用程序的消息队列里要等高优先级的消息处理完后才能被处理。而且Windows在应用程序的消息队列里只为一个定时器保存一条WM_TIMER消息。因此,WM_TIMER消息的处理带有很大的不确定性和非实时性,在多任务操作系统中,定时器消息往往不能得到及时响应,容易造成系统实时处理的不稳定,不能满足实时控制环境下的应用。因此这种方法只适合实时性要求不高的应用场合。
b)采用Windows多媒体定时器,通过设置回调函数进行实时处理:Win32函数提供的实时多媒体定时器,在多媒体扩展库MMSYSTEM.DLL中提供了高精度的定时服务。定时器的定时精度由多媒体时钟函数TimeBeginPeriod()设置,SetTimeEvent()函数设置定时器定时间隔并启动定时器。但资料表明,定时间隔在20ms以上的定时任务中,精度可保持在1ms左右;定时间隔在(7~20)ms之间,精度要视Windows系统的整体工作量和应用程序的任务而定,误差在(1~3)ms;7ms以下的软件定时一般难以实现,因为Windows系统本身的刷新任务及其消息循环等系统开销需要占用时间。并且这种方法占用了系统大量宝贵的资源,随着程序的运行,系统速度会变慢,而且当定时信号由用户提供或需要更高的中断频率时,这种方法就无能为力了。所以多媒体定时器只可满足一般数控系统对定时精度的要求,而不能满足高精度定时的要求。
c)使用QueryPerformanceCounter()函数结合多线程来实现:QueryPerformanceFrequency()和QueryPerformaneeCounter()函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。
QueryPerformaneeFrequency()函数和QueryPerformanceCounter()函数的原型如下:
BOOL QueryPerfonnanceFrequency(LARGE_INTEGER * lpFrequency);
BOOL QueryPerformaneeCounter(LARGE_INTEGER * lpCount);
数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:
typedef union_LARGE_INTEGER
{
struet
{
DWORD LowPart;//4字节整型数
LONG HighPart;//4字节整型数
};
LONGLONG QuadPart;//8字节整型数
}LARGE_INTEGER;
在进行定时之前,先调用QueryPerformaneeFrequeney()函数获得机器内部定时器的时钟频率,然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformaneeCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经历的精确时间。
使用时需要自行编写消息循环程序,下列代码实现1μs的精确定时:
LARGE_INTEGER litmp;
LONGLONG QPartl,QPart2;
double dtMinus,dfFreq,dtTim;
QueryPerformanceFrequency(& litmp);
dfFreq=(double)litmp.QuadPart;//获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPartl=litmp.QuadPart;//获得初始值
do
{
QueryPerformanceCounter(& litmp);
QPm2=litmp.QuadPart;//获得中止值
dfMinus=(double)(QPm2-QPartl);
dfTim=dfMinus/dfFreq;//获得对应的时间值。单位为s
}
while(dtTim<O.000001);
经测试,其定时误差一般不超过0.5μs,精度与CPU等机器配置有关。本系统只需要30~300μs的精度,所以用此方法可以满足精度的要求。但是此方法依然存在缺点,程序要不断地运行,并不断地强制CPU中断,使用时会占用很高的CPU,若将其放在主程序中,甚至可能会使程序和系统假死。为了解决此问题,可以使用多线程技术。
用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义。
进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其他系统资源组成。进程在运行时创建的资源随着进程的终止而死亡。线程的基本思想很简单,它是一个独立的执行流,是进程内部的一个独立的执行单元,相当于一个子程序,它对应于Visual C++中的CWinThread类对象。单独一个执行程序运行时,缺省地包含的一个主线程,主线程以函数地址的形式出现,提供程序的启动点,如main()或WinMain()函数等。当主线程终止时,进程也随之终止。根据实际需要,应用程序可以分解成许多独立执行的线程,每个线程并行的运行在同一进程中。
一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某一个时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行,由于每个时间片时间很短,所以对用户来说,仿佛各个线程在计算机中是并行处理的。操作系统是根据线程的优先级来安排CPU的时间,优先级高的线程优先运行,优先级低的线程则继续等待。
线程被分为两种:用户界面线程和工作线程(又称为后台线程)。用户界面线程通常用来处理用户的输入并响应各种事件和消息,其实,应用程序的主执行线程CWinAPP对象就是一个用户界面线程,当应用程序启动时自动创建和启动,同样它的终止也意味着该程序的结束,进程终止。工作线程用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等,它和用户界面线程的区别是它不用从CWinThread类派生来创建,对它来说最重要的是如何实现工作线程任务的运行控制函数。工作线程和用户界面线程启动时要调用同一个函数的不同版本;一个进程中的所有线程共享它们父进程的变量,但同时每个线程可以拥有自己的变量。
使用多线程可以减少程序对CPU的占用,使程序运行更高效、可靠。多线程的函数表述如下:
UINT Work_Proc(LPVOID 1Param);
函数内完成功能的操作,并不断发送消息提示中断,以使CPU去处理实时性的事件。在MFC中,开启线程时使用AfxBeginThread(),开启后线程会能自动执行函数体的程序,主线程还可以继续自己的工作。也可以通过调用CWinThread::SetThreadPriority来设置线程的优先级,用CWinThread::SuspendThread来挂起线程。如果线程被挂起,那么直到调用CWinThread::ResumeThread后线程才开始运行。若要中止线程,可使用ExitThread()函数,若要从外部中止线程,可用TerminateThread()函数。
3 结 论
经实验,使用多线程可以明显提高系统的响应和效率。只是由于中断的频率较高,CPU一直处于满负荷状态,但此方法解决了实时性和系统响应的问题,将实时控制周期减少到了1ms以内,基本满足了数控系统的实时性要求。