一、系统时钟定时器
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()触发事件。
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()触发事件。