kmain中会调用timer_init来初始timer_queue,并通过
void timer_init(void)
{
list_initialize(&timer_queue);
/* register for a periodic timer tick */
platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
}
并通过platform_set_periodic_timer 设定timer每10ms中断一次,中断callback是timer_tick
我们看一个平台的实现
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, time_t interval)
{
enter_critical_section();
t_callback = callback;
*REG(PIT_CLEAR) = 1;
*REG(PIT_INTERVAL) = interval;
*REG(PIT_START_PERIODIC) = 1;
unmask_interrupt(INT_PIT);
exit_critical_section();
return NO_ERROR;
}
将callback 赋值给t_callback,并写寄存器设定中断间隙为10ms
void platform_init_timer(void)
{
register_int_handler(INT_PIT, &platform_tick, NULL);
}
我们会在提前调用platform_init_timer,在其中调用register_int_handler 来设定timer的irq number INT_PIT的callback函数是platform_tick。
static enum handler_return platform_tick(void *arg)
{
*REG(PIT_CLEAR_INT) = 1;
if (t_callback) {
return t_callback(arg, current_time());
} else {
return INT_NO_RESCHEDULE;
}
}
最终在platform_tick这个函数中调用t_callback
最终结论就是在timer的中断函数中最后会调用timer_tick函数.
static enum handler_return timer_tick(void *arg, time_t now)
{
timer_t *timer;
enum handler_return ret = INT_NO_RESCHEDULE;
#if THREAD_STATS
thread_stats.timer_ints++;
#endif
for (;;) {
/* see if there's an event to process */
timer = list_peek_head_type(&timer_queue, timer_t, node);
if (likely(!timer || now < timer->scheduled_time))
break;
/* process it */
DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
list_delete(&timer->node);
//
timer = list_remove_head_type(&timer_queue, timer_t, node);
//
ASSERT(timer);
#if THREAD_STATS
thread_stats.timers++;
#endif
//
TRACEF("firing callback %p, arg %p\n", timer->callback, timer->arg);
if (timer->callback(timer, now, timer->arg) == INT_RESCHEDULE)
ret = INT_RESCHEDULE;
}
/* let the scheduler have a shot to do quantum expiration, etc */
if (thread_timer_tick() == INT_RESCHEDULE)
ret = INT_RESCHEDULE;
return INT_RESCHEDULE;
}
timer_tick 是个死循环,会从变量timer_queue 中是否有到期的时间
if (likely(!timer || now < timer->scheduled_time))
如果有的话,就从timer_queue 中删除,
list_delete(&timer->node);
并调用这个timer 到期时间需要执行的callback
if (timer->callback(timer, now, timer->arg) == INT_RESCHEDULE)
然后在坚持当前thread的时间片是否用完了,如果完了,就要重新调度.
timer系统部分运作原理解讲完了,我们下来看看是否使用timer
我们以gpio_keypad.c 为例
timer_initialize(&keypad->timer);
timer_set_oneshot(&keypad->timer, 0, gpio_keypad_timer_func, NULL);
可以看到先调用timer_initialize 初始化一个timer_t 结构体
void timer_initialize(timer_t *timer)
{
timer->magic = TIMER_MAGIC;
list_clear_node(&timer->node);
timer->scheduled_time = 0;
timer->periodic_time = 0;
timer->callback = 0;
timer->arg = 0;
}
在调用timer_set_oneshot 来加入到timer_queue 中
void timer_set_oneshot(timer_t *timer, time_t delay, timer_callback callback, void *arg)
{
time_t now;
//
TRACEF("delay %d, callback %p, arg %p\n", delay, callback, arg);
DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
if (list_in_list(&timer->node)) {
panic("timer %p already in list\n", timer);
}
now = current_time();
timer->scheduled_time = now + delay;
timer->periodic_time = 0;
timer->callback = callback;
timer->arg = arg;
//
TRACEF("scheduled time %u\n", timer->scheduled_time);
enter_critical_section();
insert_timer_in_queue(timer);
exit_critical_section();
}
设定到期时间和callback
timer->scheduled_time = now + delay;
timer->periodic_time = 0;
timer->callback = callback;
然后插入到timer_queue list中
static void insert_timer_in_queue(timer_t *timer)
{
timer_t *entry;
list_for_every_entry(&timer_queue, entry, timer_t, node) {
if (entry->scheduled_time > timer->scheduled_time) {
list_add_before(&entry->node, &timer->node);
return;
}
}
/* walked off the end of the list */
list_add_tail(&timer_queue, &timer->node);
}
可见timer_queue中是按scheduled_time 来从小到大排序的
下来接timer到期,然后再timer中的callback函数中调用gpio_keypad_timer_func。
如果需要continue time的话,就在gpio_keypad_timer_func 中继续调用timer_set_oneshot。
static enum handler_return
gpio_keypad_timer_func(struct timer *timer, time_t now, void *arg)
{
kp->current_output = out;
if (out < kpinfo->noutputs) {
gpio = kpinfo->output_gpios[out];
if (kpinfo->flags & GPIOKPF_DRIVE_INACTIVE)
gpio_set(gpio, polarity);
else
gpio_config(gpio, polarity ? GPIO_OUTPUT : 0);
timer_set_oneshot(timer, kpinfo->settle_time,
gpio_keypad_timer_func, NULL);
goto done;
}
}