VC中如何实现等待5微秒?

RadAsm 2001-07-14 06:38:04
...全文
1514 37 打赏 收藏 转发到动态 举报
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
clarelh 2002-01-21
  • 打赏
  • 举报
回复
guanzhu
richincsdn2 2001-07-17
  • 打赏
  • 举报
回复
gz
Chice_wxg 2001-07-17
  • 打赏
  • 举报
回复

Sleep(5);
全部搞定了~~~~~~~
liqi 2001-07-17
  • 打赏
  • 举报
回复
Win95下高精度定时器的实现

国防科技大学机电工程与仪器系
张云洲 梁科山

---- 在工业控制软件中,数据采集和实时控制是经常性的工作。目前,工业控制软件
已经从DOS和Windows3.x平台,转到了Windows95上。然而,Windows95并不能直接支持
中断,这就意味着在DOS和Windows3.x中的定时器中断不能为我们所用。因此,我们必
须寻求新的定时方式。
---- 在C++ Builder和Delphi等典型的编程语言中,都提供了定时器控件,可以方便地
实现定时和事件响应。此外,Windows95还提供了SetTimer和KillTimer函数来设置和删
除一个定时器,在事件WM_TIMER响应函数中实现处理。然而,遗憾的是,通过这些方式
获得的Win95定时器最小只能精确到55毫秒,对于55毫秒以下的时间精度便无能为力。
这对于Win95下测控软件的开发是十分不利的。幸运的是,多媒体定时器可以解决这一
难题。
---- 以下是一个完整的多媒体定时器设计示例程序,该定时器的精度为1毫秒。该程序
的内容十分简单:按下按钮1,启动定时器,在Edit框中显示定时值(计数值),按下
按钮2则将关闭定时器。此程序在Windows95环境下,采用C++Builder3.0编程并编译通
过,但设计思想同样适用于其它高级语言。按照本程序中的实现方法,读者将可以轻松
实现自己的高精度定时器。
---- 程序清单如下:
#include < vcl.h >
#pragma hdrstop

#include "mmsystem.h" //包含多媒体定时器函数的头文件
#define MilliSecond 1 //定时间隔1毫秒
#define Accuracy 1 //系统允许的分辨率最小值

#define Min(x,y) ((x < y) ? x : y)
#define Max(x,y) ((x > y) ? x : y)

#include "HighTimerU.h"
//------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
UINT TimerID; //定义定时器句柄
int count; //定义一个变量以进行计数
int TimerAccuracy;
TForm1 *Form1;
//------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}

void PASCAL TimerCallProc(UINT
TimerID, UINT msg,DWORD dwUser,
DWORD dwa,DWORD dwb)
//定义定时器事件的调用函数
{
count++;
Form1- >Edit1->Text=count;
//在一个编辑框内显示计数值,即定时值
}

//---------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TIMECAPS timecaps;
int TimerResolution;

//从系统获得关于定时器服务能力的信息,
//分辨率不能超出系统许可值(1到16毫秒)
if (timeGetDevCaps(&timecaps,sizeof(
TIMECAPS))==TIMERR_NOERROR)
TimerAccuracy=Min(Max(timecaps.wPeriodMin,
Accuracy),timecaps.wPeriodMax);

timeBeginPeriod(TimerAccuracy);
//设置定时器分辨率

TimerResolution=1; //设置定时间隔为1毫秒

//产生间隔1毫秒,周期执行的定时器事件;启动定时器
TimerID = timeSetEvent(TimerResolution,TimerAccuracy,
&TimerCallProc,1,TIME_PERIODIC);
}
//-------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
timeKillEvent(TimerID); //删除定时器事件
timeEndPeriod(TimerAccuracy); //清除定时器分辨率
}

---- 多媒体定时器对实现高精度定时是很理想的工具,而且其精度是十分可靠的。但
是,多媒体定时器也并不是完美的。因为它可靠的精度是建立在对系统资源的消耗之上
的。因此,在利用多媒体定时器完成工作的同时,必须注意以下几点:
---- 1. 多媒体定时器的设置分辨率不能超出系统许可范围。
---- 2. 在使用完定时器以后,一定要及时删除定时器及其分辨率,否则系统会越来
越慢。
---- 3. 多媒体定时器在启动时,将自动开辟一个独立的线程。在定时器线程结束之
前,注意一定不能再次启动该定时器,不然将迅速造成死机。


RadAsm 2001-07-17
  • 打赏
  • 举报
回复
Sleep(5)是5微秒而不是5毫秒吗?
RadAsm 2001-07-16
  • 打赏
  • 举报
回复
ahphone(阿丰)推荐的办法是通过取得cpu速度后进行空循环并控制循环数来实现的。
那样将占用CPU时间,有出让CPU时间的短暂延迟方法吗?
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
来了
ahphone 2001-07-14
  • 打赏
  • 举报
回复
分!
http://www.csdn.net/dev/Visual%20C++/source%20code/Misc/microsecond_delay.shtml.htm
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
4. 高精确的时序
4.1 延迟时间
首先, 我会说不保证你在使用者模式 (user-mode) 中执行的行程 (process) 能够精确地控制时序因为 Linux 是个多工的作业环境. 你在执行中的行程 (process) 随时会因为各种原因被暂停大约 10 毫秒到数秒 (在系统负荷非常高的时候). 然而, 对于大多数使用 I/O 埠的应用而言, 这个延迟时间实际上算不了什么. 要缩短延迟时间, 你得使用函数 nice 将你在执行中的行程 (process ) 设定成高优先权(请参考 nice(2) 使用说明文件) 或使用即时排程法 (real-time scheduling) (请看下面).

如果你想获得比在一般使用者模式 (user-mode) 中执行的行程 (process) 还要精确的时序, 有一些方法可以让你在使用者模式 (user-mode) 中做到 `即时' 排程的支持. Linux 2.x 版本的核心中有软件方式的即时排程支持; 详细的说明请参考 sched_setscheduler(2) 使用说明文件. 有一个特殊的核心支持硬件的即时排程; 详细的信息请参考网页 http://luz.cs.nmt.edu/~rtlinux/


休息中 (Sleeping) : sleep() 与 usleep()
现在, 让我们开始较简单的时序函数调用. 想要延迟数秒的时间, 最佳的方法大概是使用函数 sleep() . 想要延迟至少数十毫秒的时间 (10 ms 似乎已是最短的延迟时间了), 函数 usleep() 应该可以使用. 这些函数是让出 CPU 的使用权给其他想要执行的行程 (processes) (``自己休息去了''), 所以没有浪费掉 CPU 的时间. 细节请参考 sleep(3) 与 usleep(3) 的说明文件.

如果让出 CPU 的使用权因而使得时间延迟了大约 50 毫秒 (这取决於处理器与机器的速度, 以及系统的负荷), 就浪费掉 CPU 太多的时间, 因为 Linux 的排程器 (scheduler) (单就 x86 架构而言) 在将控制权发还给你的行程 (process) 之前通常至少要花费 10-30 毫秒的时间. 因此, 短时间的延迟, 使用函数 usleep(3) 所得到的延迟结果通常会大於你在参数所指定的值, 大约至少有 10 ms.



nanosleep()
在 Linux 2.0.x 一系列的核心发行版本中, 有一个新的系统调用 (system call), nanosleep() (请参考 nanosleep(2) 的说明文件), 他让你能够休息或延迟一个短的时间 (数微秒或更多).

如果延迟的时间 <= 2 ms, 若(且唯若)你执行中的行程 (process) 设定了软件的即时排程 (就是使用函数 tt/sched_setscheduler()/), 调用函数 nanosleep() 时不是使用一个忙碌循环来延迟时间; 就是会像函数 usleep() 一样让出 CPU 的使用权休息去了.

这个忙碌循环使用函数 udelay() (一个驱动程序常会用到的核心内部的函数) 来达成, 并且使用 BogoMips 值 (BogoMips 可以准确量测这类忙碌循环的速度) 来计算循环延迟的时间长度. 其如何动作的细节请参考 /usr/include/asm/delay.h).


使用 I/O 埠来延迟时间
另一个延迟数微秒的方法是使用 I/O 埠. 就是从埠地址 0x80 输入或输出任何 byte 的数据 (请参考前面) 等待的时间应该几乎只要 1 微秒这要看你的处理器的型别与速度. 如果要延迟数微秒的时间你可以将这个动作多做几次. 在任何标准的机器上输出数据到该埠地址应该不会有不良的後果□对 (而且有些核心的设备驱动程序也在使用他). {in|out}[bw]_p() 等函数就是使用这个方法来产生时间延迟的 (请参考文件 asm/io.h).

实际上, 一个使用到埠地址范围为 0-0x3ff 的 I/O 埠指令几乎只要 1 微秒的时间, 所以如果你要如此做, 例如, 直接使用并口, 只要加上几个 inb() 函数从该埠地址范围读入 byte 的数据即可.


使用组合语言来延迟时间
如果你知道执行程序所在机器的处理器型别与时钟速度, 你可以执行某些组合语言指令以便获得较短的延迟时间 (但是记住, 你在执行中的行程 (process) 随时会被暂停, 所以有时延迟的时间会比实际长). 如下面的表格所示, 内部处理器的速度决定了所要使用的时钟周期数; 如, 一个 50 MHz 的处理器 (486DX-50 或 486DX2-50), 一个时钟周期要花费 1/50000000 秒 (=200 奈秒).


指令 i386 时钟周期数 i486 时钟周期数
nop 3 1
xchg %ax,%ax 3 3
or %ax,%ax 2 1
mov %ax,%ax 2 1
add %ax,0 2 1

(对不起, 我不知道 Pentiums 的数据, 或许与 i486 接近吧. 我无法在 i386 的数据上找到只花费一个时钟周期的指令. 如果能够就请使用花费一个时钟周期的指令, 要不然就使用管线技术的新式处理器也是可以缩短时间的.)

上面的表格中指令 nop 与 xchg 应该不会有不良的後果. 指令最后可能会改变旗号暂存器的内容, 但是这没关系因为 gcc 会处理. 指令 nop 是个好的选择.

想要在你的程序中使用到这些指令, 你得使用 asm("instruction"). 指令的语法就如同上面表格的用法; 如果你想要在单一的 asm() 叙述中使用多个指令, 可以使用分号将他们隔开. 例如, asm("nop ; nop ; nop ; nop") 会执行四个 nop 指令, 在 i486 或 Pentium 处理器中会延迟四个时钟周期 (或是 i386 会延迟 12 个时钟周期).

gcc 会将 asm() 翻译成单行组合语言程序代码, 所以不会有调用函数的负荷.

在 Intel x86 架构中不可能有比一个时钟周期还短的时间延迟.


在 Pentiums 处理器上使用函数 rdtsc
对于 Pentiums 处理器而言, 你可以使用下面的 C 语言程序代码来取得自从上次重新开机到现在经过了多少个时钟周期:



--------------------------------------------------------------------------------

extern __inline__ unsigned long long int rdtsc()
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}


--------------------------------------------------------------------------------

你可以询问参考此值以便延迟你想要的时钟周期数.


4.2 时间的量测
想要时间精确到一秒钟, 使用函数 time() 或许是最简单的方法. 想要时间更精确, 函数 gettimeofday() 大约可以精确到微秒 (但是如前所述会受到 CPU 排程的影响). 至於 Pentiums 处理器, 使用上面的程序代码片断就可以精确到一个时钟周期.

如果你要你执行中的行程 (process) 在一段时间到了之后能够被通知 (get a signal), 你得使用函数 setitimer() 或 alarm() . 细节请参考函数的使用说明文件.

===========================
不会Linux+gcc可以做到的事情Windows+VC做不到吧?
大虾们,该出手时就出手,指点一下吧!
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
GetTickCount也不行
coolxiao 2001-07-14
  • 打赏
  • 举报
回复
不是说windows系统是毫秒级的吗?
oliver_liyei 2001-07-14
  • 打赏
  • 举报
回复
用GetTickCount()吧
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
weikeming(丘丘): 请问cpu的定时指令如何用?叫什么名字?
weikeming 2001-07-14
  • 打赏
  • 举报
回复
用asm也不行。
得用cpu的定时指令。
就算你用多媒体定时器和系统定时器最多也只精确到25毫秒。
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
我本想用asm的空循环来延迟,但总觉得那不是正道
Mp_Hsie 2001-07-14
  • 打赏
  • 举报
回复
的却,是我疏忽了,献丑献丑 :(
creative 2001-07-14
  • 打赏
  • 举报
回复
it is imposible to wait for such a fixed tiny time except that you are programming the WDM device driver. In 98 ddk, 2000 ddk or later, there is a function called KeStallExecutionProcessor, it can stall the proceesor for a tiny time, and the unit is microsecond.
wjyasd 2001-07-14
  • 打赏
  • 举报
回复
solar(solar) 有道理,还是硬件解决吧!
RadAsm 2001-07-14
  • 打赏
  • 举报
回复
Mp_Hsie(都泥丸) 大虾的这段代码也是在毫秒级呀
Mp_Hsie 2001-07-14
  • 打赏
  • 举报
回复
这样应该更好一点儿:
void Wait5MicroSec(ULONG delay)
{
SYSTEMTIME st;
FILETIME ft;
ULONG ui1=0L,ui2=0L;
GetSystemTime(&st);
SystemTimeToFileTime(&st,&ft);
memcpy(&ui1,&ft,sizeof(ft));
do
{
GetSystemTime(&st);
SystemTimeToFileTime(&st,&ft);
memcpy(&ui2,&ft,sizeof(ft));
}while(ui2-ui1<delay);
}

加载更多回复(17)

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧