乐鑫笔试题目之软件定时器实现
2021年秋招乐鑫面试题目:用硬件定时器实现多个软件timer。
1、题目介绍
1.1、已知硬件定时器API
1、u32 get_now();获取系统当前时间,可以理解为硬件tick。(已经实现好,可以直接调用)
2、void set_timer_start(u32 expire_time); 开启硬件定时器,一段时间之后到达expire_time时调用硬件timer函数。(已经实现好,可以直接调用)
3、void timer_fired();硬件中断服务函数
1.2、软件API
1、void timer_start(struct soft_timer T); 用户传入结构T,可以供多个用户调用,结构体T 需要实现。
1.3、求实现:
1、结构体T的定义;
2、timer_start() 的实现;
3、timer_fired() 的实现;
总的下来意思就是使用一个硬件timer实现多个软件timer,如果每个任务都使用硬件timer,那么只有一个硬件timer,根本没法支持多个一起使用,后面的硬件timer调用,可能导致前面的timer调用失效。
所以就是用数据结构来管理硬件timer,实现多个软件timer 共用一个硬件timer。
2、代码实现
2.1、软件timer数据结构定义如下
- interval:定时间隔,一般定时器需要该参数指明定时间隔
- expire_time:截止时间,getnow + interval 来填充,方便调用 set_timer_start 函数,
- soft_timer_callback ,args:回调函数以及参数,
- is_repeat:是否是重复的timer函数
- next:指明下一个执行的timer函数,管理使用timer的函数。
当然如果硬件timer API的实现是如下:
void set_timer_start(u32 interval),也可以实现,看下面介绍
typedef (*soft_timer_callback)(void*);
struct soft_timer
{
u32 interval;
u32 expire_time;
soft_timer_callback callback;
void *args;
bool_t is_repeat;
struct soft_timer* next;
};
2.1、硬件timer中断函数实现
主要是调用用户的回调函数,以及启动下一个定时任务函数。
void timer_fired()
{
struct soft_timer* temp_timer = soft_timer_ready_list;
soft_timer_ready_list = soft_timer_ready_list->next;
(*temp_timer->callback)(temp_timer->args); //call the min interval user callback func
if(temp_timer->is_repeat) // add the ready list
{
temp_timer->expire_time = get_now() + temp_timer->interval;
timer_add_ready_list(temp_timer);
}
if(soft_timer_ready_list->next)
{
set_timer_start(soft_timer_ready_list->expire_time - get_now()); // start the next user timer
}
}
当然笔者是在中断中调用用户函数,如果时间执行时间较长,可能影响其他任务或者中断,优化之后,可以在主函数中去执行,中断里面只是做标志位检查,然后主函数判断标志位,并去执行。
bool_t interrupt_flag = false;
void timer_fired()
{
interrupt_flag = true;
}
void soft_timer_excution_func()
{
if(true == interrupt_flag )
{
interrupt_flag = false;
struct soft_timer* temp_timer = soft_timer_ready_list;
soft_timer_ready_list = soft_timer_ready_list->next;
(*temp_timer->callback)(temp_timer->args); //call the min interval user callback func
if(temp_timer->is_repeat) // add the ready list
{
temp_timer->expire_time = get_now() + temp_timer->interval;
timer_add_ready_list(temp_timer);
}
if(soft_timer_ready_list->next)
{
set_timer_start(soft_timer_ready_list->expire_time - get_now()); // start the next user timer
}
}
}
int main()
{
....
while(1)
{
....
soft_timer_excution_func();
....
}
}
仔细在思考一下上面的代码,假如两个timer同时到达,会发生什么情况,我们只处理了第一个timer,调用了callback,
然后start 第二个 函数,其实第二个timer的截止时间已经到了,那么可能立即又中断,也可能出现异常,取决于硬件timer的健壮性,因为设置的时间为0,timer间隔的时间。
所以需要增加一个循环来判断一下,来对上面代码进行优化一下。
void soft_timer_excution_func()
{
if(true == interrupt_flag )
{
interrupt_flag = false;
while((soft_timer_ready_list != NULL) && (soft_timer_ready_list->expire_time <= get_now())) //loop check timer interval is ok
{
struct soft_timer* temp_timer = soft_timer_ready_list;
soft_timer_ready_list = soft_timer_ready_list->next;
(*temp_timer->callback)(temp_timer->args); //call the min interval user callback func
if(temp_timer->is_repeat) // add the ready list
{
temp_timer->expire_time = get_now() + temp_timer->interval;
timer_add_ready_list(temp_timer);
}
}
if(soft_timer_ready_list) //start the first timer func
{
set_timer_start(soft_timer_ready_list->expire_time - get_now()); // start the next user timer
}
}
}
当然中断函数也可以进行优化,但是就不太合适,因为中断函数里面不太适合放较多的函数,尤其是while循环这种。
2.2、就绪软件timer 链表插入
链表有序插入用户的软件timer节点,管理用户软件timer的执行。
需要注意:
- 当用户传的节点为空时,以及有序链表头节点为空时
- 插入到第一个节点时,即当前的任务间隔最小
- 插入到中间时,需要记录上一个节点,然后完成节点之间的连接
- 插入到末尾时,next = null
struct soft_timer* soft_timer_ready_list;
void timer_add_ready_list(struct soft_timer* T)
{
struct soft_timer* ready_list_temp = soft_timer_ready_list;
if((NULL == T)) // user node is null
{
/* nothing process */
}
else if(NULL == ready_list_temp) //read list is null or header node is null
{
ready_list_temp = T;
ready_list_temp->next = LS_NULL;
}
else if(T->expire_time <= ready_list_temp->expire_time) // first node
{
T->next = ready_list_temp;
ready_list_temp = T;
}
else
{
struct soft_timer* last_timer_node = ready_list_temp;
while((NULL != ready_list_temp) && (ready_list_temp->expire_time < T->expire_time))
{
last_timer_node = ready_list_temp;
ready_list_temp = ready_list_temp->next;
}
if(NULL == ready_list_temp) // the last node
{
last_timer_node->next = T;
T->next = LS_NULL;
}
else // the middle node
{
last_timer_node->next = T;
T->next = ready_list_temp;
}
}
}
2.3、timer 启动函数
timer start 函数,就是负责传入用户的软件timer函数信息,然后开始执行时间间隔最小的定时。
void timer_start(struct soft_timer T)
{
timer_add_ready_list(&T);
if(NULL != soft_timer_ready_list)
{
set_timer_start(soft_timer_ready_list->expire_time);
}
}
当然如果硬件timer set_timer_start(int interval),那么此时的函数就应该如下这样写:
void timer_start(struct soft_timer T)
{
timer_add_ready_list(&T);
if(NULL != soft_timer_ready_list)
{
set_timer_start(soft_timer_ready_list->expire_time - get_now());
}
}
上面的中断函数timer_fired() 里面的调用,同样也是需要减去 get_now()。