系统时钟不准确,久了会死机的疑问?

YaoHui   2009-10-13 11:54 楼主
大家好:
遇到了一个问题,向各位请教:
   我在APP中用SetTimer(1,1000,NULL),然后在Ontimer做响应处理,目的是一秒钟一次操作,但我的系统Timer设置有问题: Timer时快时慢,而且过一段时间后,就会死机。不知道这是什么原因。-
---------------------
我google了一下:
http://www.cnemb.com/forum/read.php?tid=23044   这个帖子和我的问题一样,

我的BSP中是把Timer4预留给系统的,
这个是我的代码:
  1. static DWORD dwPartialCurMSec = 0;                // Keep CPU-specific sub-millisecond leftover.
  2. void
  3. OEMIdle( DWORD dwIdleParam )
  4. {
  5.         DWORD dwIdleMSec;
  6.         DWORD dwPrevMSec = *pCurMSec;
  7.        
  8.         // Use for 64-bit math
  9.         ULARGE_INTEGER currIdle = { curridlelow, curridlehigh };

  10.         if ((int) (dwIdleMSec = dwReschedTime - dwPrevMSec) <= 0)
  11.         {
  12.                 return;                                // already time to wakeup
  13.         }

  14.         // just idle till tick if profiling or running iltiming
  15.         if (bProfileTimerRunning || fIntrTime)        // fIntrTime : Interrupt Latency timeing.
  16.         {
  17.                 // idle till end of 'tick'
  18.                 CPUEnterIdle(dwIdleParam);

  19.                 // Update global idle time and return
  20.                 currIdle.QuadPart += RESCHED_PERIOD;
  21.                 curridlelow = currIdle.LowPart;
  22.                 curridlehigh = currIdle.HighPart;
  23.         
  24.                 return;
  25.         }
  26.         RETAILMSG(1,(TEXT("OEMIdle ->RESCHED_PERIOD=%d \r\n"),RESCHED_PERIOD));  
  27.         //
  28.         // Since OEMIdle( ) is being called in the middle of a normal reschedule
  29.         // period, CurMSec, dwPartialCurMSec, and CurTicks need to be updated accordingly.
  30.         // Once we reach this point, we must re-program the timer (if we ever did)
  31.         // because dwPartialCurMSec will be modified in the next function call.
  32.         //
  33.         CPUGetSysTimerCountElapsed(RESCHED_PERIOD, pCurMSec, &dwPartialCurMSec, pCurTicks);

  34.         if ((int) (dwIdleMSec -= *pCurMSec - dwPrevMSec) > 0)
  35.         {
  36.                 dwPrevMSec = *pCurMSec;

  37.                 //
  38.                 // The system timer may not be capable of arbitrary timeouts. Get the
  39.                 // CPU-specific highest possible timeout available.
  40.                 //
  41.                 dwIdleMSec = CPUGetSysTimerCountMax(dwIdleMSec);

  42.                 //
  43.                 // Set the timer to wake up much later than usual, if needed.
  44.                 //
  45.                 CPUSetSysTimerCount(dwIdleMSec);
  46.                 CPUClearSysTimerIRQ( );

  47.                 //
  48.                 // Enable wakeup on any interrupt, then go to sleep.
  49.                 //
  50. //                DEBUGMSG(1, (TEXT("OEMIDle  \r\n")));
  51.                 CPUEnterIdle(dwIdleParam);
  52.                 INTERRUPTS_OFF( );
  53.         
  54.                 //
  55.                 // We're awake! The wake-up ISR (or any other ISR) has already run.
  56.                 //
  57.                 if (dwPrevMSec != *pCurMSec)
  58.                 {
  59.                         //
  60.                         // We completed the full period we asked to sleep.  Update the counters.
  61.                         //
  62.                         *pCurMSec  += (dwIdleMSec - RESCHED_PERIOD); // Subtract resched period, because ISR also incremented.
  63.                         CurTicks.QuadPart += (dwIdleMSec - RESCHED_PERIOD) * dwReschedIncrement;

  64.                         currIdle.QuadPart += dwIdleMSec;
  65.                 } else {
  66.                         //
  67.                         // Some other interrupt woke us up before the full idle period was
  68.                         // complete.  Determine how much time has elapsed.
  69.                         //
  70.                         currIdle.QuadPart += CPUGetSysTimerCountElapsed(dwIdleMSec, pCurMSec, &dwPartialCurMSec, pCurTicks);
  71.                 }
  72.         }

  73.         // Re-arm counters
  74.         CPUSetSysTimerCount(RESCHED_PERIOD);
  75.         CPUClearSysTimerIRQ( );

  76.         // Update global idle time
  77.         curridlelow = currIdle.LowPart;
  78.         curridlehigh = currIdle.HighPart;

  79.         return;
  80. }

  81. ------------------------------------------------------------------------------------------------------
  82. void
  83. InitClock(void)
  84. {
  85.     volatile PWMreg *s2440PWM =(PWMreg *)PWM_BASE;
  86.     volatile INTreg *s2440INT = (INTreg *)INT_BASE;  
  87.     DWORD ttmp;  


  88.         // Timer4 as OS tick and disable it first.
  89.     s2440INT->rINTMSK |= BIT_TIMER4;                // Mask timer4 interrupt.
  90.     s2440INT->rSRCPND = BIT_TIMER4;                        // Clear pending bit
  91.     s2440INT->rINTPND = BIT_TIMER4;

  92.         // Operating clock : PCLK=101500000 (101.5 Mhz)   
  93.         // IMPORTANT : MUST CHECK S2440.H DEFINITIONS !!!!
  94.         s2440PWM->rTCFG1  = 0x8080;        //;;; SHL
  95.         s2440PWM->rTCFG0 &= ~(0xff << 8);                /* Prescaler 1's Value                        */
  96.         s2440PWM->rTCFG0 |= (PRESCALER << 8);        // prescaler value=15       

  97.         RETAILMSG(1, (_T("InitClock_PRESCALER=%d\r\n"),PRESCALER));
  98.         /* Timer4 Divider field clear */
  99.         s2440PWM->rTCFG1 = 0x11111;        // ;;; SHL
  100.         s2440PWM->rTCFG1 &= ~(0xf << 16);
  101.        
  102. #if( SYS_TIMER_DIVIDER == D2 )
  103.                   s2440PWM->rTCFG1  |=  (D1_2   << 16);                /* 1/2                                                        */
  104. #elif ( SYS_TIMER_DIVIDER == D4 )
  105.                 s2440PWM->rTCFG1  |=  (D1_4   << 16);                /* 1/4                                                        */
  106. #elif ( SYS_TIMER_DIVIDER == D8 )
  107.                   s2440PWM->rTCFG1  |=  (D1_8   << 16);                /* 1/8                                                        */
  108. #elif ( SYS_TIMER_DIVIDER == D16 )
  109.                   s2440PWM->rTCFG1  |=  (D1_16   << 16);                /* 1/16                                                        */
  110. #endif
  111.         s2440PWM->rTCNTB4 = RESCHED_INCREMENT;        //((RESCHED_PERIOD * OEM_CLOCK_FREQ) / 1000)         
  112.         ttmp = s2440PWM->rTCON & (~(0xf << 20));

  113.         s2440PWM->rTCON = ttmp | (2 << 20);                /* update TCVNTB4, stop                                        */
  114.         s2440PWM->rTCON = ttmp | (1 << 20);                /* one-shot mode,  start                                */

  115.     // Number of timer counts for reschedule interval
  116.     dwReschedIncrement = RESCHED_INCREMENT;

  117.         // Set OEM timer count for 1 ms
  118.     OEMCount1ms = OEM_COUNT_1MS;
  119.    
  120.     // Set OEM clock frequency
  121.     OEMClockFreq = OEM_CLOCK_FREQ;

  122.     s2440INT->rINTMSK &= ~BIT_TIMER4;               

  123.     return;
  124. }



回复评论 (11)

帮顶,up
点赞  2009-10-13 13:35
忘了说我的平台是:S3C2440+WINCE5.0
点赞  2009-10-13 13:42
顶一下
点赞  2009-10-13 13:45
1. 系统时钟(system tick)概念
  
  系统时钟是内核需要的唯一中断(IRQ0),系统时钟每毫秒产生一个中断,当发生中断时内核在ISR中累计,到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数,还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL,系统时钟是第一个必须做的事。
  
  2. X86平台系统时钟中断的处理工作 系统时钟由InitClock函数负责初始化工作,一般是在OEMInit函数中调用。当发生中断时,ISR首先用下列语句累计计数:
  
  CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1
  
  然后根据下列语句判断应该返回什么值:
  
  if ((int) (dwReschedTime – CurMSec) >= 0)
  return SYSINTR_RESCHED; ///重新调度
  else
  return SYSINTR_NOP; ///不再执行任何操作
  
  上述代码中全局变量dwReschedTime在schedule.c中定义,也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数,当没有线程准备运行时OEMIdle被调用,OEMIdle函数将CPU置于空闲模式,但在空闲模式下仍然要累计系统时钟。
-------以上资料来源--[/url]-----

在[url=http://www.cnemb.com/forum/read.php?tid=23044]
该贴LZ说是OEMIdle函数的影响,导致时钟时快时慢,但现在不知道究竟是什么影响了, 继续研究
点赞  2009-10-13 14:40
引用: 引用 4 楼 trueman_onlyme 的回复:
1. 系统时钟(system tick)概念
  
  系统时钟是内核需要的唯一中断(IRQ0),系统时钟每毫秒产生一个中断,当发生中断时内核在ISR中累计,到1000的倍数就是过了一秒钟。在处理系统时钟的ISR中不仅要累计计数,还要决定是否通知内核开始重新调度当前所有的线程。要实现一个OAL,系统时钟是第一个必须做的事。
  
  2. X86平台系统时钟中断的处理工作 系统时钟由InitClock函数负责初始化工作,一般是在OEMInit函数中调用。当发生中断时,ISR首先用下列语句累计计数:
  
  CurMSec += SYSTEM_TICK_MS; /////SYSTEM_TICK_MS = 1
  
  然后根据下列语句判断应该返回什么值:
  
  if ((int) (dwReschedTime – CurMSec) >= 0)
  return SYSINTR_RESCHED; ///重新调度
  else
  return SYSINTR_NOP; ///不再执行任何操作
  
  上述代码中全局变量dwReschedTime在schedule.c中定义,也就是由内核的调度模块决定在何时开始重新调度线程。CurMSec累计了从WindowsCE启动到当前总共产生了多少个system tick。实现系统时钟后还要实现OEMIdle函数,当没有线程准备运行时OEMIdle被调用,OEMIdle函数将CPU置于空闲模式,但在空闲模式下仍然要累计系统时钟。
-------以上资料来源--http://motion.chinaitlab.com/WINCE/30568.html-----

在http://www.cnemb.com/forum/read.php?tid=23044 该贴LZ说是OEMIdle函数的影响,导致时钟时快时慢,但现在不知道究竟是什么影响了, 继续研究

采用动态tick的情况下,如果OEMIdle函数没有处理好,会影响到系统的tick
但是lz采用了动态tick么?
点赞  2009-10-13 20:34
1.同意楼上的,是不是采用的vartick?可以用fixedtick试试
2.注意2440的Timer2、3、4的设置clock第一次分频是共用一个TCFG,会不会你在其他地方修改了这个TCFG?造成timer4不准??
点赞  2009-10-13 22:47
引用: 引用 6 楼 skynet000 的回复:
1.同意楼上的,是不是采用的vartick?可以用fixedtick试试
2.注意2440的Timer2、3、4的设置clock第一次分频是共用一个TCFG,会不会你在其他地方修改了这个TCFG?造成timer4不准??


vartick  and fixedticK?
      这个没有研究过,请问可否在详细说下,  明天来看看
----
其他的设置,触摸屏用的是Timer1,  其他的Timer不知道哪里在用?   
----
     
点赞  2009-10-13 23:03
如果上层有线程在runing,oem_idle不会使用到,可以不用理会var tick
点赞  2009-10-14 16:47
主要是APP中用到SetTimer了,  从InitClock(void)
中的
// Set OEM timer count for 1 ms
    OEMCount1ms = OEM_COUNT_1MS;
可以看出  是1ms  !

但真的好像是Tick 在变,
点赞  2009-10-14 16:51
什么是stubs?
Stubs are routines that do not contain executable code. They act as placeholders for functions that need to be fully implemented later.
   When implementing stubs, you can leave comments that describe what you eventually need to do to add functionality to your function. By scanning existing sample OAL code, you can easily obtain the function header, such as return type and arguments, for each of these functions.
   You can either create the stub functions from scratch or copy an existing boot loader or OAL that is of similar CPU type and board design, and then use #if 0 and #endif tags for the function contents.
To create a stub function
1、Obtain the function header definition for the function to be stubbed by referring to a sample OAL that supports similar hardware. The function header includes a return type and a series of function arguments.
2、From the function header definition, create an empty function implementation.
3、If the function header specifies a return type, add a placeholder return value to satisfy the compiler.
For example, if the function returns a Boolean value, add either return (TRUE); or return (FALSE); to satisfy the compiler. A successful function call is usually signaled by a TRUE or non-zero value, while failure is usually signaled by a FALSE or 0 value. However, this depends on the function and the data being returned.
The following code example shows the stub for the OEMReadData function:
BOOL OEMReadData (DWORD cbData, LPBYTE pbData) { return(TRUE); }
这个链接
[url=http://www.cnemb.com/forum/read.php?tid=23044][/url]
上的解决办法是
“找到了问题出在哪里了,OEMIdle函数中的处理应该是有问题,用Stub代替之后正常了。 ”
stub 具体是什么,怎么用,  怎么用它来代替OEMIdle?
看文档的介绍有点费解,  
点赞  2009-10-15 12:12
我想stubs 的意思应该是,将暂时不实现的function , 直接做return TRUE或return FALSE 处理,居然是这个意思! 汗,
//------------------------------------------------------------------------------
BOOL
OEMGetExtensionDRAM(
    LPDWORD lpMemStart,
    LPDWORD lpMemLen
    )
{
    return FALSE; // no extension DRAM
}
------------
点赞  2009-10-15 13:46
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复