bqmcu 发表于 2011-4-8 22:50:21

从零开始学VC系列教程之 浅谈额外知识点1(延时函数的实现)

习惯了MCU写法的朋友们往往对VC很不顺手,通常我们的MCU裸机程序都是单线程的,按流程执行。于是,用到的延时程序也是阻塞式的,也就是说,MCU可以在某个地方弄一个死循环等在那里什么也不干,而VC之中是不行的,例如我们弄了以下的函数用于延时,会造成一些不好的后果。
void Delayms(unsigned int nms)
{
    unsigned int i = 0;
    while(--nms)
    {
         //假定这个for循环正好延时1us
         for(i=0;i<1000;i++);
    }
}

    延时一段时间看看?会发现程序好像死了,鼠标都不动了。延时完成后,又正常了。原因是windows是消息机制的,鼠标的移动会通过一个消息传输,当主线程阻塞时(例如开个死循环),会导致消息不能及时传输。当然,VC里面还有一个可以实现MS级延时的函数,Sleep(nms);使用该函数可以让出本线程时间给其它线程,但如果这个函数用在主线程里面,也是不行的,除非需要一个几毫秒的延时。基于以上问题,不难看出,如果让消息能顺利传输,就会好多了,至少不会看起来好像系统死机了。
    怎么实现呢?有一个函数PeekMessage可以取得消息队列中的消息,同时,可以通过TranslateMessage传输出去。所以,可以把延时函数改一下,试试以下的函数。

void Delayms(DWORD wDelayms)
{
    MSG m_Msg;
    while(--wDelayms)
    {
      //让出时间给其它线程
      Sleep(1);
      PeekMessage(&m_Msg,NULL,0,0,PM_REMOVE);
      TranslateMessage(&m_Msg);
      DispatchMessage(&m_Msg);
    }
}

    这样就可以让消息传输出去了,同时也可以实现延时,当然,如果程序在系统中的优先级不够,这种延时也不一定准确,延时时间越长,会误差越大。另外,我们也可以用定时器来延时,让一些延时执行的函数在定时器中调用。开定时器很简单SetTimer();这个定时器很像MCU中的硬件定时器,MCU的定时器时间到时,可以直接引发中断,在中断函数中执行一些功能或者实现计数,而SetTimer设定时间到后,则会有一个消息发出,同时,也有一个函数OnTimer自动响应。VC中的定时器消息优先级非常低,很多消息都可以优先执行,所以其实这个定时器也不会非常准确。

    以下是一个例子了,网上很多,利用CPU的频率来延时,理论上是非常准确的,可以精确到us 当然,虽然这个函数可以实现us延时,那也是理想状态,因为操作系统不会让时间全部花在一个函数上面,所以时间查询可以认为是精确的,但延时后面的函数能否得到立即执行,还要依赖于很多的东西,这里就不再细论了。

void Delayms(DWORD wInterval)
{
        LARGE_INTEGER frequence,lInterval;

        //取高精度运行计数器的频率,若硬件不支持则返回FALSE
        if(!QueryPerformanceFrequency( &frequence))
                AfxMessageBox("Not Support!");

        QueryPerformanceFrequency(&frequence);
        lInterval.QuadPart = frequence.QuadPart * wInterval * 1000 / 1000000;
        LARGE_INTEGER privious,current;

        QueryPerformanceCounter(&privious);
        current=privious;

        MSG m_Msg;

        while(current.QuadPart-privious.QuadPart < lInterval.QuadPart)
        {
                QueryPerformanceCounter(¤t);

                if(wInterval > 10) //大于10ms
                {
                        //传递系统消息
                        PeekMessage(&m_Msg,NULL,0,0,PM_REMOVE);
                        TranslateMessage(&m_Msg);
                        DispatchMessage(&m_Msg);
                        //让出时间给其它线程
                        Sleep(1);
                }
        }
}

以上一些,限于本人知识不足,不一定非常专业,仅抛砖引玉

281229961 发表于 2011-4-8 23:52:49

./emotion/em025.gif

treeyan 发表于 2011-4-9 02:16:54

呵呵,windows 上通过应用程序完成us的延时,很难实现。倒不是因为不能做到,而是无法保证短延时的精确性,分时多任务操作系统本质决定了。
Sleep(1) 看上去是延时1个ms,其实最短也是超过10个ms的。
msdn上有说,比方Sleep(100),那么Windows只能保证延时的时间一定大于或等于100ms,但不保证正好100个ms就会调度到您进程继续执行。
所以,实际上边一段代码跟 SetTimer 并没有本质上的不同。

chxumengyang 发表于 2011-12-7 13:00:59

楼主的教程怎么不写了啊

haohan007 发表于 2011-12-7 14:20:46

顶楼主,楼主写的东西,通俗易懂,希望继续发表你的教程。

newywx 发表于 2012-9-18 10:33:13

楼主好久不见了~

bbsview 发表于 2012-9-25 16:06:22

呵呵,期待楼主继续带来cool帖

ruan18278816371 发表于 2013-11-19 22:53:38

没学过VC,我电脑有个雅琦880想学来做串口,不知道比VC容易入门否?
页: [1]
查看完整版本: 从零开始学VC系列教程之 浅谈额外知识点1(延时函数的实现)