前后台系统和多任务操作系统,在简单功能上差别不大,唯一不顺手的就是前后台系统没有合适的软件定时器。封装一层软件定时器接口,对后续功能开发事半功倍。
定义结构体数组,其基础成员如下:
static uint32_t hw_interrupt_timer_ticks = 0;//timer ticks
typedef void (*timer_callback)(void);
//软件定时器最大个数
#define SW_TIMER_MAX 10
typedef struct
{
uint32 delay; //延迟时间,实际是当前时间+期望延时的时间后的tick
uint32 auto_repeat; //执行后自动重载时间,为0表示只执行一次
timer_callback callback; //回调函数
} timer_struct;
timer_struct my_timer[SW_TIMER_MAX] = {
0}; //定时器参数数组,最大支持10个
//启动定时器,index可按需求定为枚举类型,必须小于SW_TIMER_MAX
void timer_start(uint8 index, uint32 delay, bool loop, timer_callback callback);
void timer_stop(uint8 index);//停止定时器,delay清零,回调函数置为NULL
bool timer_is_running(uint8 index);//当前定时器是否在计时运行中,判断delay是否非0
对应用提供的启动定时器接口实现可以参考如下:
static void timer_start(uint8 index, uint32 delay, bool loop, timer_callback callback)
{
if((index >= SW_TIMER_MAX) || (delay == 0) || (callback == NULL))
{
return;
}
my_timer[index].delay = hw_timer_ticks + delay;
if(loop)//自动重载
{
my_timer[index].auto_repeat = delay;
}
else
{
my_timer[index].auto_repeat = 0;
}
my_timer[index].callback = callback;
}
使用硬件定时中断,例如1ms中断一次,每次中断hw_timer_ticks自增一次。主程序中查询hw_timer_ticks 大于delay表示当前定时器任务超时,执行callback。这种在8位单片机系统合适,如果硬件资源有限或者系统时钟太低,可以调整硬件定时中断的精度。这样多个定时器时事件的执行只需要一个硬件定时器。
static void timer_loop(void)
{
uint8 i = 0, n = 0, min = 0;
for(i = 0 ; i < SW_TIMER_MAX ; i++)
{
if(my_timer[i].delay > 0 && my_timer[i].callback != NULL)
{
if(hw_timer_ticks >= my_timer[i].delay)
{
if(my_timer[i]->auto_repeat > 0)
{
my_timer[i].delay = hw_timer_ticks + my_timer[i]->auto_repeat;
}
else
{
my_timer[i].delay = 0; //stop
}
my_timer[i].callback();
}
}
}
}
实现基础框架后,应用层只需要简单的操作就是实现,例如定时每60秒读取一次ADC结果,和延时30秒后关闭LED显示。
static void timer_start(READ_ADC_TIMER_ID, 60*1000, TRUE, read_adc_callback);
static void timer_start(LED_ON_TIMEROUT_TIMER_ID, 30*1000, FALSE, led_on_timeout_callback);
这种方案的缺点是my_timer数组下标index与应用绑定,即使没有全部使用,主程序每次都要查询全部数组成员;11个定时任务即使明确最多只有9个同时在运行,该接口也无法满足需求。
增加申请定时器资源的接口,在10个定时器池中获取没有被使用的,将index下标返回,再传入timer_statrt,同样的定义,可以满足10个定时任务同时运行,提高了定时器的利用率。
因为主程序轮流查询定时器状态,加上前面的回调函数执行的时间,实际执行回调时间与期望的延时时间存在一定误差。在硬件资源充足的情况下,可以将数组改为链表,按delay从小到大排序,新启动的定时器按顺序插入到合适的位置,这样主程序查询速度会加快,在最近的软件定时器,也就是第一个软件定时器超时前可以休眠省电。或者分为多个数组映射不同的优先级和时间精度,这样提高回调函数执行的时间准确度。
工欲善其事,必先利其器,基础件完美才能支撑复杂多变的应用层。