为什么要实现软件定时器:
在芯片平台上,地址空间也是相当宝贵的,如果保留了更多的硬件定时器的话,就需要更多的地址空间,那么我们能不能作个折中方案呢?答案是肯定的,我们可以使用一个硬件定时器,来模拟实现一个软件定时器,可以满足更多的定时需求,需要注意的一点就是软件定时器精度可能会有稍微误差,因为会涉及到任务调度、锁中断等,在对定时精度要求不高的场景,可以考虑使用软件定时器。Linux内核中的timer_list精度为10ms,这里我们来实现一套精度为1ms的软件定时器(当然可以实现精度为微秒级的,但是没有必要,微秒级的直接使用硬件定时器了)。
涉及知识点:任务创建与调度、信号量同步、锁中断、中断处理、回调函数、链表操作
设计思想:
对外提供创建定时器、删除定时器、添加定时器、修改定时器超时时间四个接口,创建定时器时,根据用户传入时间折算成物理定时器定时时间,同时做一些软件定时器其他必要操作;添加定时器时,根据超时时间长短添加到超时链表中,一旦超时则处理超时的定时器(通常是链表头部的第一个节点);删除定时器操作把不需要的定时器从超时链表中删除;修改定时器接口主要完成修改超时时间的功能;
在软件定时器模块初始化时,创建超时处理任务,挂接物理定时器中断处理函数,初始化链表、信号量等;超时后中断处理函数释放信号量来激活任务,任务来处理超时链表中的超时定时器。
实现:
1、要实现软件定时器,首先我们需要定义一个结构体来定义对象使用
typedef void (*timer_func)(u32); /*定义回调函数类型*/
struct softtimer_list{
timer_func func; /*超时回调*/
u32 para;/*回调函数参数*/
u32 time_out;/*超时时间,毫秒级别*/
char* timer_name;/*给定时器起个名字,方便维护*/
struct list_head entry;/*链表操作使用*/
................ /*其他需要的成员变量,视情况添加*/
};
2、定义接口
模块内部接口
1)static int timer_init(void){
初始化信号量、互斥锁、创建任务、挂接中断等;
return 0;
}
2)static int softtimer_task_func(void *data)
{
等待物理定时器超时释放信号量,处理超时定时器
return 0;
}
3)static irqreturn_t softtimer_interrupt_cb(int irq,void *dev)
{
清中断,释放信号量
}
对外接口:
1)int softtimer_create(struct softtimer_list *timer)
{
参数检查,分配timer id,折算超时时间
}
2)int softtimer_add(struct softtimer_list *timer)
{
根据超时时间,插入到超时链表中,注意插入头部、尾部、中间三种情况的超时时间更新操作;
}
3)int softtimer_delete(struct softtimer_list *timer)
{
删除不需要的定时器,注意删除头部、尾部、中部定时器时需要分别处理。
}
4)int softtimer_mod(struct softtimer_list *timer)
{
更新定时器超时时间。
}
好了,我们的软件定时器设计完成,在节省物理timer资源的同时,满足了对定时要求不高的任务的定时需求,一举两得。