系统时钟定时器(转自小峰)

来源:互联网 发布:绘制等高线地形图软件 编辑:程序博客网 时间:2024/05/08 18:04
一、系统时钟定时器
1.定时器类型:
  一类:是硬件定时器,对应CC2430的几个timer,系统时钟定时器为硬件定时器,定时时间为“TICK_TIME—系统时间片”。
  另一类:是软件定时器,通过osal_start_timerEx添加到软定时器链表,再由系统时钟统一减数。
2.时间管理
    对事件进行时间管理,OSAL采用了链表的方式进行,有时发生一个要被处理的事件,就启动一个逻辑上的定时器,并将此定时器添加到定时器链表当中。
    利用硬件定时器作为时间操作的基本单元。设置时间操作的最小精度为1ms,每1ms硬件定时器便产生一个时间中断,在时间中断处理程序中去更新定时器链表。每次更新,就将链表中的每一项时间计数减1,如果发现定时器链表中有某一表项时间计数已减到0,则将这个定时器从链表中删除,并设置相应的事件标志。这样任务调度程序便可以根据事件标志进行相应的事件处理。
3.时间管理函数
    extern byte osal_start_timerEx( byte task_id, UINT16 event_id, UINT16 timeout_value );
   这个函数为事件event_id设置超时等待时间timeout_value。一旦等待结束,便为task_id所对应的任务设置相应的事件发生标记,进行相应处理。
4.何为系统时间片?系统时间片是什么?
1).引用outman的一段话,每个操作系统都有一个节拍“tick”,就像每个人都有心跳一样。
2).在OnBoard.h文件中定义:
  /* OSAL timer defines */
   #define TICK_TIME   1000   // Timer per tick - in micro-sec
   1000us,即1ms,
5.系统时钟如何初始化?
void InitBoard( byte level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
    ChkReset();


  /* Timer2 for Osal timer
   * This development board uses ATmega128 Timer/Counter3 to provide
   * system clock ticks for the OSAL scheduler. These functions perform
   * the hardware specific actions required by the OSAL_Timers module.
   */
   OnboardTimerIntEnable = FALSE;
  HalTimerConfig (OSAL_TIMER,                        // 8bit timer2
                  HAL_TIMER_MODE_CTC,                 // Clear Timer on Compare
                  HAL_TIMER_CHANNEL_SINGLE,           // Channel 1 - default
                  HAL_TIMER_CH_MODE_OUTPUT_COMPARE,   // Output Compare mode
                  OnboardTimerIntEnable,              // Use interrupt
                  Onboard_TimerCallBack);             // Channel Mode
}…………
6.系统时钟对应CC2430的哪个定时器?
从上面的初始化可以看到Timer2 for Osal timer,但事实上,就像outman说的一样,要和halTimerRemap()函数对应起来。
 Maps API HAL_TIMER_ID to HW Timer ID.
 *          HAL_TIMER_0 --> HW Timer 3
 *          HAL_TIMER_2 --> HW Timer 4
 *          HAL_TIMER_3 --> HW Timer 1
 Timer2映射到硬件上去即为Timer4
7.系统时钟有什么用?
    系统时钟定时器由osal_timer_activate()开启,溢出时调用其回调函数(中间这一系列流程后面具体记录),回调函数在InitBoard()被初始化为Onboard_TimerCallBack(),而Onboard_TimerCallBack()调用了osal_update_timers(),而osal_update_timers()调用osalTimerUpdate(),osalTimerUpdate()对系统软定时器链表中的每一项定时器时间计数减1ms,如果有软定时器计数减到0,删除这个软定时器并调用osal_set_event()设置相应事件发生标志。
    总结起来就是每1ms系统时钟都会跑到软件定时器链表中去把各定时器的计数值减1.
8.系统时钟定时器溢出时会不会产生中断进入中断函数。
答:不会
在InitBoard()函数中有如下定义:OnboardTimerIntEnable = FALSE;//对系统定时器来说,默认为非中断模式。
在HalTimerConfig函数中,hwtimerid = halTimerRemap (timerId);
                        halTimerRecord[hwtimerid].intEnable= intEnable;
因此,把系统定时器映射到这里,halTimerRecord[HW Timer 4].intEnable= intEnable=FALSE
因此,当系统定时器溢出时,不会产生中断,判断有没有溢出,是在  HalTimerTick();里的halProcessTimer4 ();进行的,当溢出时,则调用halTimerSendCallBack ()这个函数。
9.看下osalTimerUpdate这个函数
/*********************************************************************
 * @fn      osalTimerUpdate
 *
 * @brief   Update the timer structures for a timer tick.
 *
 * @param   none
 *
 * @return  none
 *********************************************************************/
static void osalTimerUpdate( uint16 updateTime )
{
  halIntState_t intState;
  osalTimerRec_t *srchTimer;
  osalTimerRec_t *prevTimer;
  osalTimerRec_t *saveTimer;


  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.


  // Update the system time系统时间,1ms往上加。
  osal_systemClock += updateTime;


  // Look for open timer slot
  if ( timerHead != NULL )
  {
    // Add it to the end of the timer list
    srchTimer = timerHead;
    prevTimer = (void *)NULL;


    // Look for open timer slot
    while ( srchTimer )
    {
      // Decrease the correct amount of time小于等于
      if (srchTimer->timeout <= updateTime)
        srchTimer->timeout = 0;
      else//大于
        srchTimer->timeout = srchTimer->timeout - updateTime;//减1ms


      // When timeout, execute the task
      if ( srchTimer->timeout == 0 )
      {
        osal_set_event( srchTimer->task_id, srchTimer->event_flag );//设置事件发生标志


        // Take out of list
…………
osalTimerUpdate函数以ms为单位对软定时器链表中的软定时器进行减计数,溢出时,即调用osal_set_event触发事件。
10.系统时钟定时器什么时候开启?
初始化定时器函数osalTimerInit();中,
void osalTimerInit( void )
{
  // Initialize the rollover modulo
  tmr_count = TICK_TIME;
  tmr_decr_time = TIMER_DECR_TIME;


  // Initialize the system timer
  osal_timer_activate( false );
  timerActive = false;


  osal_systemClock = 0;
}
   可以看到默认 osal_timer_activate( false );是没有开启的。
   我看到过一篇文章《ZigBee学习之35—按键部分及系统调用时钟的分析》,里面也讨论了系统时钟什么时候开启的问题,最终的最终结果是:系统时钟是在osal_start_timerEx()这个函数里面被开启的。具体请参考这篇文章。
byte osal_start_timerEx( byte taskID, UINT16 event_id, UINT16 timeout_value )

 ……
    // Add timer
  newTimer = osalAddTimer( taskID, event_id, timeout_value );
……
    // Does the timer need to be started?
    if ( timerActive == FALSE )
    {
      osal_timer_activate( TRUE );
    }
……
 }
    综上所述,个人认为,系统什么时候开始运行第一个osal_start_timerEx(),就什么时候开启系统时钟。即当系统的软定时器链表中添加第一个软定时器时(软定时器的添加在osal_start_timerEx中进行),就开启系统时钟。
11.系统时钟运行流程
    有上述可知,系统时钟定时器采用非中断模式。
    在系统主循环osal_start_system()中调用了Hal_ProcessPoll(),进而调用了HalTimerTick(),HalTimerTick这个函数就是系统对定时器的轮询,下面进入这个函数:
void HalTimerTick (void)
{
  if (!halTimerRecord[HW_TIMER_1].intEnable)//判断定时器是否允许中断
  {
    halProcessTimer1 ();                     //不允许中断,则调用此函数
  }


  if (!halTimerRecord[HW_TIMER_3].intEnable)
  {
    halProcessTimer3 ();
  }


  if (!halTimerRecord[HW_TIMER_4].intEnable)   //系统时钟定时器默认intEnable=False
  {
    halProcessTimer4 ();
  }
}
注释:
*************************************************
 (
 这里分析下如果定时器采用的是中断方式的情况。
Timer1/3/4如果采用中断,当溢出时就会跳到中断处理函数,各定时器的中断函数为(1/3/4):
HAL_ISR_FUNCTION( halTimer1/3/4Isr, T1/3/4_VECTOR )
{
  halProcessTimer1/3/4 ();
}
可以看到进入中断函数后也是跳到halProcessTimer()执行,因此接下来的流程跟后面的非中断方式一致.
)
*************************************************
    HalTimerTick()函数判断定时器是否允许中断,不允许,则调用halProcessTimer()函数,在halProcessTimer函数里,判断定时器是否有溢出,溢出的话,则调用halTimerSendCallBack()来发送消息给相应硬件定时器的回调函数并调用。进入halTimerSendCallBack函数。
void halTimerSendCallBack (uint8 timerId, uint8 channel, uint8 channelMode)
{
  uint8 hwtimerid;


  hwtimerid = halTimerRemap (timerId);


  if (halTimerRecord[hwtimerid].callBackFunc)
    (halTimerRecord[hwtimerid].callBackFunc) (timerId, channel, channelMode);
}           
   看到函数最终调用相应定时器的回调函数.系统时钟定时器的回调函数在InitBoard()初始化为Onboard_TimerCallBack()。
void Onboard_TimerCallBack ( uint8 timerId, uint8 channel, uint8 channelMode)
{
   //#define OSAL_TIMER  HAL_TIMER_2
  if ((timerId == OSAL_TIMER) && (channelMode == HAL_TIMER_CH_MODE_OUTPUT_COMPARE))
  {
    osal_update_timers();
  }
   可以看到回调函数中调用了osal_update_timers(),而osal_update_timers()调用osalTimerUpdate(),由osalTimerUpdate来以ms为单位对“软定时器”减计数,溢出时便调用osal_set_event()触发事件。
    以上是系统时钟定时器的运行流程,具体参考了outman的OSAL系统框架.pdf(22日笔记)
12.定时器非中断函数调用流程
      osal_timer_activate( TRUE )开启系统时钟定时器——系统主循环函数osal_start_system()——调用Hal_ProcessPoll()轮询硬件——调用HalTimerTick()轮询定时器——定时器采用非中断方式则调用halProcessTimer()判断定时器是否溢出——溢出则调用halTimerSendCallBack()来发送消息给相应定时器的回调函数——调用各定时器的回调函数,如系统时钟定时器的Onboard_TimerCallBack()——调用osal_update_timers()来更新软件定时器链表中各定时器的计数值(每次减1ms)——如有软件定时器溢出调用osal_set_event()触发事件。

原创粉丝点击