基于Windows的精确定时技术.doc_第1页
基于Windows的精确定时技术.doc_第2页
基于Windows的精确定时技术.doc_第3页
基于Windows的精确定时技术.doc_第4页
基于Windows的精确定时技术.doc_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

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

文档简介

基于Windows的精确定时技术(长沙交通学院计算机工程系 李湘江) 在工业生产控制系统中,有许多需要定时完成的操作,如:定时显示当前时间,定时刷新屏幕上的进度条,上位机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的控制系统和数据采集系统中,就更需要精确定时操作。众所周知,Windows是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列中的消息就暂时被挂起,得不到实时处理。因此,不能简单地通过Windows消息引发一个对定时要求严格的事件。另外,由于在Windows中已经封装了计算机底层硬件的访问,所以,要想通过直接利用访问硬件来完成精确定时,也比较困难。所以在实际应用时,应针对具体定时精度的要求,采取相适应的定时方法。 VC的时间操作函数 VC 中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操作。VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常简单,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。 在精度要求较高的情况下,如要求误差不大于1ms时,可以利用GetTickCount()函数。该函数的返回值是DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。下列的代码可以实现50ms的精确定时,其误差小于1ms。 / 起始值和中止值DWORD dwStart, dwStop ;dwStop = GetTickCount(); while(TRUE) / 上一次的中止值变成新的起始值 dwStart = dwStop ; / 此处添加相应控制语句do dwStop = GetTickCount() ; while(dwStop 50 dwStart) ; 对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下: BOOL QueryPerformanceFrequency(LARGE_INTEGER lpFrequency); BOOL QueryPerformanceCounter(LARGE_INTEGER lpCount); 数据类型LARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下: typedef union _LARGE_INTEGER struct / 4字节整型数 DWORD LowPart ; / 4字节整型数 LONGHighPart ; ; / 8字节整型数 LONGLONG QuadPart ; LARGE_INTEGER ; 在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率,然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经历的精确时间。下面的程序用来测试函数Sleep(100)的精确持续时间: LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(litmp); / 获得计数器的时钟频率 dfFreq = (double)litmp.QuadPart; QueryPerformanceCounter(litmp); / 获得初始值 QPart1 = litmp.QuadPart; Sleep(100); QueryPerformanceCounter(litmp); / 获得中止值 QPart2 = litmp.QuadPart; dfMinus = (double)(QPart2 QPart1); / 获得对应的时间值 dfTim = dfMinus / dfFreq; 由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。 使用多媒体定时器 微软公司在其多媒体Windows中提供了精确定时器的底层API支持。利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一个事件、函数或过程的调用。利用多媒体定时器的基本功能,可以通过两种方法实现精确定时。 1.使用timeGetTime()函数 该函数定时精度为ms级,返回从Windows启动开始所经过的时间。由于使用该函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。 2. 使用timeSetEvent()函数 利用该函数可以实现周期性的函数调用。函数的参数说明如下: uDelay:延迟时间; uResolution:时间精度,在Windows中缺省值为1ms; lpFunction:回调函数,为用户自定义函数,定时调用; dwUser:用户参数; uFlags:标志参数; TIME_ONESHOT:执行一次; TIME_PERIODIC:周期性执行。 具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在lpFunction回调函数中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是:任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用timeKillEvent()将之释放。 下面这段代码的主要功能是设置两个时钟定时器,一个间隔是1ms,一个间隔是2s。每执行一次,把当前系统时钟值输入文件“cure.out”中,以比较该定时器的精确度。 /定义1ms和2s时钟间隔,以ms为单位 define ONE_MILLI_SECOND 1 define TWO_SECOND 2000 /定义时钟分辨率,以ms为单位 define TIMER_ACCURACY 1 /定义时间间隔 UINT wTimerRes_1ms,wTimerRes_2s; /定义分辨率 UINT wAccuracy; /定义定时器句柄 UINT TimerID_1ms,TimerID_2s; /打开输出文件“cure.out” CCureApp:CCureApp():fout(“cure.out”, ios:out) / 给时间间隔变量赋值 wTimerRes_1ms = ONE_MILLI_SECOND; wTimerRes_2s = TWO_SECOND; TIMECAPS tc; /利用函数timeGetDevCaps取出系统分辨率的取值范围,如果无错则继续 if(timeGetDevCaps(tc,sizeof(TIMECAPS)=TIMERR_NOERROR) /分辨率的值不能超出系统的取值范围 wAccuracy=min(max(tc.wPeriodMin, TIMER_ACCURACY),tc.wPeriodMax); /调用timeBeginPeriod函数设置定时器的分辨率 timeBeginPeriod(wAccuracy); /设置定时器 InitializeTimer(); CCureApp: CCureApp() /结束时钟 fout “结束时钟”foutms“:1ms:” osBinaryTimeendl; / 加装1ms定时器 void CCureApp:StartOneMilliSecondTimer() if(TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy, / 回调函数 (LPTIMECALBACK) OneMil liSecondProc, / 用户传送到回调函数的数据 (DWORD)this, / 周期调用,只使用一次,用TIME_ONESHOT/ TIME_PERIODIC) = 0) AfxMessageBox(“不能进行定时!”, MB_OK | MB_ICONASTERISK); else /不等于0表明加装成功,返回此定时器的句柄 fout “16ms 计 时:” endl; 利用系统定时中断 在PC机中采用了可编程定时/计数芯片8253,计数器0工作在方式3,用OUT0产生时钟信号。OUT0作为中断请求信号接可编程中断控制器8259A的IR0(系统中IRQ0)。由于fCLK1.19MHz,TCLK840ns,因此8253初值为65536时,大约每840ns6553655ms中断一次。可以读取计数器的当前计数值,计数器值每减一,代表时间840ns,另加上计数器是否计满的判断,则可计算出时间的精确值。 8253的6种工作方式的设置是在初始化时用输出指令写控制字来实现的,其中,方式0为在结束计数时中断。 调用VC运行库函数clock(),可以获得本次程序运行由处理器用去的ms时间,由此可判断出计数器是否计满。具体程序代码如下: / 延时函数 void Ddelay(unsigned long n) unsigned long Told; unsigned long nn,old_Clock, new_Clock, low, high, v_8253; unsigned long old_8253, new_8253, int_Time; / nn单位: 840ns nn =(unsigned long)(float)n1000/840); / 锁定8253计数值 _outp(0x43,0x00); / 读取8253计数值 low = _inp(0x40); high = _inp(0x40); v_8253 = low 256 high; old_8253 = v_8253; old_Clock=clock(); Told=old_Clock; int_Time=0; while(int_Time nn) _outp(0x43,0x00); low = _inp(0x40); high = _inp(0x40); v_8253 = low 256 high; new_8253 = v_8253; new_Clock=clock(); if(old_8253 new_8253 new_ClockTold 55) int_Time=(old_8253new_8253) (new_Clockold_Clock)/551)65536lu; else int_Time=(old_8253new_8253) (new_Clockold_Clock)/55) 65536lu; Told=new_Clock; Windows98下精度为微秒级的定时 南京航空航天大学 刘学军- 目前程序员所能看到的一些计时程序精度为ms级,在精度要求较高的场合不适用。现介绍一种方法可以得到精度为大约840ns的定时程序。 - 在PC机中采用了一片可编程定时/计数芯片8253,计数器0工作在方式3,用OUT0产生时钟信号。OUT0作为中断请求信号接可编程中断控制器8259A的IR0(系统中IRQ0)。由于fCLK1.19MHZ,TCLK840ns,因此8253初值为65536时,此定时时间大约每840ns6553655ms中断一次。可以读取计数器的当前计数值,计数器值每减一,代表时间840ns,另加上计数器是否计满的判断,则可计算出时间的精确值。 - 调用VC+运行库函数clock(),可以获得本次程序运行由处理器用去的时间(单位是ms),由此可判断出计数器是否计满。 - 根据上述方法编写的延时程序在实际应用上具有重要意义,现举二例说明。 - 步进电机是工业过程控制及仪表中的主要控制之一。用PC机控制步进电机原理系统图如下: - 步进电机控制软件中必须解决的一个重要问题,就是产生一个如下图的周期性脉冲序列: - 实现脉冲波的方法可以先输出低电平,然后利用本文所述定时方法延时一段时间,而后输出高电平,再延时。这样可以控制较高工作频率的步进电机工作。具体程序代码如下: void Ddelay(unsigned long n) / 延时函数,n单位:usunsigned long Told;unsigned long nn,old_Clock, new_Clock, low, high, v_8253;unsigned long old_8253, new_8253, int_Time; nn =(unsigned long)(float)n*1000/840); / nn单位: 840ns */_outp(0x43,0x00); / 锁定8253计数值low = _inp(0x40); / 读取8253计数值high = _inp(0x40);v_8253 = low + 256 * high;old_8253 = v_8253;old_Clock=clock();Told=old_Clock;int_Time=0;while(int_Time nn)_outp(0x43,0x00);low = _inp(0x40);high = _inp(0x40);v_8253 = low + 256 * high;new_8253 = v_8253;new_Clock=clock();if(old_8253 new_8253 & new_Clock-Told 55) int_Time=(old_8253-new_8253)+(new_Clock-old_Clock)/55+1)*65536lu; else int_Time=(old_8253-new_8253)+ (new_Clock-old_Clock)/55) * 65536lu; Told=new_Clock;若步进电机工作频率为400HZ,则发送num个脉冲的程序段如下:send_pio=0x00; / 初始化待发送的数据_outp(0x360,0x8a); / 初始化8255for(i=0;i num;i+) send_pio & = 0xfd; /d0位为脉冲输出位 _outp(0x366,send_pio); / 输出低电平 Ddelay(1250); / 延时半个周期 send_pio | = 0x01; _outp(0x366,send_pio); / 输出高电平 Ddelay(1250); / 延时半个周期另利用该方法可以求出某段程序代码精确的执行时间。程序段如下:unsigned t1,t2,t3,T1,T2,T;_outp(0x43,0x00); / 锁定8253计数值t1=_inp(0x40)+256*_inp(0x40); / 读取8253计数值T1=clock(); / 待计时的程序段_outp(0x43,0x00);t2=_inp(0x40)+256*_inp(0x40);T2=clock();if(t155) t3=t1-t2+(T2-T1+1)*65536; else t3=t1-t2+(T2-T1)*65536; T=840*t3; / T即为程序的执行时间基于Windows95的高速数据采集摘要:Windows95的出现给软件业带来了革命性的变化,也对工控软件的设计提出了挑战。Windows95的新特性使其实时性大大降低,WIN95环境下的数据采集速度难以提高。本文提出了一种利用双缓冲和多线程提高采集速度的方法,并在对MTS材料疲劳实验系统的改造中加以运用,取得了非常好的效果。关键词:双缓冲,多线程,中断,实时数据采集High Speed Data Acquisition Under Windows95Abstract:A method to realize high speed data acquisition under Windows95 is introduced.Double buffer and multithread are used to increase acquisition speed.An application using this method is given.Key words:double buffer,multithread,interrupt,real-time data acquisition在工业控制领域,实时数据采集是一个最基本且最重要的环节。如何提高采集速度一直是工控技术人员所关心的问题。作为一个实时操作系统,MS-DOS曾给工控软件提供了一个非常好的平台。在DOS环境下,利用Turbo C或其他语言,可以很方便地开发出具有极高速度的实时数据采集软件。然而,要在DOS下设计出一个图文并茂、操作方便的用户界面,却是一件费时费力的工作。Windows95的出现,给计算机用户展现了一个焕然一新的画面。多窗口,多任务,以鼠标为主的操作方式,这在原先的DOS环境下是无法想象的。同时,也涌现出大量Windows95环境下的软件开发工具,如:Visual C+,Visual Basic,Borland C+,Borland Delphi等。利用这些新的开发工具,可以很方便地设计出符合Windows95标准的用户界面。Turbo C已不再能满足人们的需要。然而,作为代价,WIN95的实时性却大大下降。WIN95的多任务机制使得一个程序不能独占全部的资源,也使得实时数据采集难以实现。因此,许多工控技术人员至今仍在DOS环境下编写数据采集软件。然而,习惯了Windows界面后,很少有用户愿意回到DOS下去面对那枯燥无味的屏幕,许多工控软件用户在协议中直接提出了要在WIN95下运行的要求。因此如何在WIN95下设计出高速数据采集程序已成为工控技术人员关心的焦点。1定时中断存在的问题一个基本的数据采集系统通常包括A/D和D/A两个部分。在许多场合下,A/D和D/A操作都要精确定时。通常的办法是利用计时器中断,先设定定时间隔,然后对计时器中断进行挂钩,在中断程序中进行所需的操作。在数据量较大时,发送和采集的数据都只能以文件的格式存储在硬盘上。中断程序的任务是: 从文件中取出数据;必要时对数据进行转换;通过D/A卡发送数据;从A/D卡读取数据;必要的数据转换;将数据存入文件;其他的数据处理工作。在DOS下设计程序时,我们就是这样做的。在Windows环境下,通过编写设备驱动程序(.VXD),仍然可以实现对定时中断的挂钩。然而,如果将同样的代码放在中断程序中,就会出现问题。中断程序过长,当定时间隔很小,即采样频率较高时,会造成死机。由于Windows95的多任务机制,在中断程序中进行文件处理,会造成系统的崩溃。在DOS下之所以这样设计程序,就是因为DOS是单任务操作系统。而Windows95是一个多任务操作系统,当同时有多个任务存在时,在中断程序中对文件进行操作可能会造成系统函数的重入,导致系统的崩溃。实验证明,这种崩溃是必然的,而且是致命的,只有通过Reset键才能恢复。那么,如何解决这些问题呢?2解决的办法Windows为我们提供了解决的办法:采用Windows提供的定时器代替硬件定时中断。使用这种方法非常安全和简单,可以在定时函数内进行任何操作,决不会造成死机现象。然而,Windows定时器的实时性非常差,既不能保证定时精度,也不能保证中断次数,而且,定时间隔也不能太短(55 ms)。显然,即使对于精度要求很低的数据采集系统,利用Windows定时器作为解决办法也不能令人满意。利用Windows提供的多媒体定时器。顾名思义,这种定时器原本是为多媒体服务而设计的,例如MIDI,然而,也可以将其另作它用。因为多媒体定时器的精度非常高,与硬件中断相当,而且也比较安全,不容易造成死机现象。唯一的缺点是中断频率太低,最高只能达到1 kHz,即每毫秒中断一次。因此无法用于有较高速度要求的系统,当然,对于较低速度的数据采集系统。多媒体定时器仍是一种极好的选择。 利用Windows95 的多线程,并与硬件中断相结合,实现高速数据采集。这正是下面要介绍的方法,利用这种方法,可以实现Windows95下的高速数据采集,并从容地对数据进行处理和保存。3多线程和双缓冲为了保证数据采集的精度和速度,仍然采用了硬件中断的办法,利用硬件定时器(如AD卡上的定时器)产生中断,并编写VXD为其设置相应的中断处理程序。为了提高中断速度以及避免函数重入,去掉了中断过程中一切与系统调用有关的部分,将中断程序缩至最短。这样,中断程序所做的就是读取AD卡上的数据,将其送到一个公用的缓冲区,并维护一个缓冲区指针,而把数据处理和文件存盘的工作留给处理线程去完成。公用缓冲区由两个大小相同、且连续的子缓冲区组成,称为双缓冲,用于暂存AD转换的数据。每次定时中断到达时,中断程序从AD卡上读一个数据放入缓冲区,并将缓冲区指针加1,当两个子缓冲区全部填满后,将缓冲区指针归0,后读入的数据冲掉原来的数据。开始中断采集的同时,由主程序启动一个线程,称为处理线程。处理线程在后台与主程序并行工作,用它监视双缓冲指针。当发现双缓冲指针从子缓冲区1进入子缓冲区2时,说明中断程序已将子缓冲区1填满,这时,处理线程对子缓冲区1中的数据进行处理并存盘。同理,当发现双缓冲指针从子缓冲区2回到子缓冲区1时,对子缓冲区2中的数据进行处理和存盘。只要缓冲区足够大,即使在采集频率非常高的情况下,中断程序填满一个子缓冲区的时间也会比较长,而处理线程进行一次处理和存盘的时间相对比较短。因此,中断程序和处理线程可以并行地访问缓冲区而不会发生交叠,即中断程序访问的子缓冲区与处理线程访问的子缓冲区总是不同的。这样,就可以在不丢失任何一个数据的情况下实现长时间连续高速采集。该方法实现的框图如图1。图1利用双缓冲和多线程提高数据采集速度这实际相当于在PC机的内存中开辟了一片FIFO队列。与硬件FIFO相比,这种方法大大降低了成本,而且,由于将其放在内存中,可以轻易地得到非常大的FIFO队列,

温馨提示

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

评论

0/150

提交评论