在嵌入式软件中,常常采用顺序式的编程结构。有一些需要定时执行的逻辑在主程序中不断的进行轮询时间,时间到了执行程序。这样程序中会存在大量的时间变量不方便统一处理,以一种比较分散的方式进行计时以及时间清零的操作。本文介绍一个好用的定时器模块,将计时服务进行统一管理,通过回调函数的方式执行定时的相关逻辑。
原作者0x1abin,源代码https://github.com/0x1abin/MultiTimer
致敬原作者!
本文在arduino UNO开发板上试用了定时模块代码,进行了略微的修改,模块代码包含一个.h一个.cpp文件,如下:
.h文件
#ifndef _MULTI_TIMER_H_
#define _MULTI_TIMER_H_
#include <stdint.h>
#include <stddef.h>
/*
It means 1 tick for 1ms.
Your can configurate for your tick time such as 5ms/10ms and so on.
*/
#define CFG_TIMER_1_TICK_N_MS 1
typedef struct Timer {
uint32_t cur_ticks; /* Record current timer start tick */
uint32_t cur_expired_time; /* Record current timer expired time */
uint32_t timeout; /* Delay (xx ms) time to start tiemr */
uint32_t repeat; /* Timer interval expired time (xx ms) */
void* arg; /* Input argument for timeout_cb function */
void (*timeout_cb)(void* arg); /* Timer expired callback function */
struct Timer* next; /* Pointer to next timer */
} Timer;
#ifdef __cplusplus
extern "C" {
#endif
typedef void(*Timeout_CB)(void* arg);
/**
* @brief Initializes the timer struct handle.
* @param handle: the timer handle strcut.
* @param timeout_cb: timeout callback.
* @param timeout: delay to start the timer.
* @param repeat: repeat interval time.
* @param arg: the input argument for timeout_cb fucntion.
* @retval None
*/
void timer_init(struct Timer* handle, Timeout_CB, \
uint32_t timeout, uint32_t repeat, void* arg);
/**
* @brief Start the timer work, add the handle into work list.
* @param btn: target handle strcut.
* @retval 0: succeed. -1: already exist.
*/
int timer_start(struct Timer* handle);
/**
* @brief Stop the timer work, remove the handle off work list.
* @param handle: target handle strcut.
* @retval 0: succeed. -1: timer not exist.
*/
int timer_stop(struct Timer* handle);
/**
* @brief background ticks, timer repeat invoking interval nms.
* @param None.
* @retval None.
*/
void timer_ticks(void);
/**
* @brief main loop.
* @param None.
* @retval None
*/
void timer_loop(void);
#ifdef __cplusplus
}
#endif
#endif
.cpp文件:
#include <stdio.h>
#include "MultiTimer.h"
//timer handle list head.
static struct Timer* head_handle = NULL;
//Timer ticks
//static uint32_t _timer_ticks = (1 << 32)- 1000; // only for test tick clock overflow
static uint32_t _timer_ticks = 0;
/**
* @brief Initializes the timer struct handle.
* @param handle: the timer handle strcut.
* @param timeout_cb: timeout callback.
* @param timeout: delay to start the timer.
* @param repeat: repeat interval time.
* @param arg: the input argument for timeout_cb fucntion.
* @retval None
*/
void timer_init(struct Timer* handle, Timeout_CB timeout_cb, \
uint32_t timeout, uint32_t repeat, void* arg)
{
// memset(handle, sizeof(struct Timer), 0);
handle->timeout_cb = timeout_cb;
handle->timeout = timeout;
handle->repeat = repeat;
handle->cur_ticks = _timer_ticks;
handle->cur_expired_time = handle->timeout;
handle->arg = arg;
//printf("cur_ticks: %u, cur_expired_time: %u, _timer_ticks: %u, timeout: %u\r\n",
// handle->cur_ticks, handle->cur_expired_time, _timer_ticks, timeout);
}
/**
* @brief Start the timer work, add the handle into work list.
* @param btn: target handle strcut.
* @retval 0: succeed. -1: already exist.
*/
int timer_start(struct Timer* handle)
{
struct Timer* target = head_handle;
while (target) {
if (target == handle) {
return -1; //already exist.
}
target = target->next;
}
handle->next = head_handle;
head_handle = handle;
return 0;
}
/**
* @brief Stop the timer work, remove the handle off work list.
* @param handle: target handle strcut.
* @retval 0: succeed. -1: timer not exist.
*/
int timer_stop(struct Timer* handle)
{
struct Timer** curr;
for (curr = &head_handle; *curr;) {
struct Timer* entry = *curr;
if (entry == handle) {
*curr = entry->next;
//free(entry);
return 0; // found specified timer
}
else {
curr = &entry->next;
}
}
return 0;
}
/**
* @brief main loop.
* @param None.
* @retval None
*/
void timer_loop(void)
{
struct Timer* target;
for (target = head_handle; target; target = target->next) {
if (_timer_ticks - target->cur_ticks >= target->cur_expired_time) {
//printf("cur_ticks: %u, cur_expired_time: %u, _timer_ticks: %u\r\n",
// target->cur_ticks, target->cur_expired_time, _timer_ticks);
if (target->repeat == 0) {
timer_stop(target);
}
else {
target->cur_ticks = _timer_ticks;
target->cur_expired_time = target->timeout;
}
target->timeout_cb(target->arg);
}
}
}
/**
* @brief background ticks, timer repeat invoking interval nms.
* @param None.
* @retval None.
*/
void timer_ticks(void)
{
_timer_ticks += CFG_TIMER_1_TICK_N_MS;
}
模块提供的接口如下:
定时器的注册:
void timer_init(struct Timer* handle, Timeout_CB, \
uint32_t timeout, uint32_t repeat, void* arg);
定时器启动和停止:
int timer_start(struct Timer* handle);
int timer_stop(struct Timer* handle);
和底层代码的接口:
将timer_ticks放在1ms中断中执行,驱动定时器模块的计数
void timer_ticks(void);
定时器任务:
将timer_loop放在主程序中执行,检查链表中的所有定时器是否触发即使周期。
void timer_loop(void);
使用示例,本文在arduino开发板上使用:
创建一个定时器timer1,编写一个定时器回调函数
#include "userLed.h"
#include "MultiTimer.h"
struct Timer timer1;
void Timer1CallBack(void* arg)
{
static unsigned char state = HIGH;
digitalWrite(LED, state);
state = (HIGH == state) ? LOW : HIGH;
}
在任务初始化中注册timer1,并打开定时器,主程序中调用timer_loop
void TaskTimer::setup() {
MsTimer2::set(1, timer_ticks);
MsTimer2::start();
timer_init(&timer1, Timer1CallBack, 500, 500,0);
timer_start(&timer1);
}
void TaskTimer::loop() {
timer_loop();
}
该定时器模块通过链表来定时器的管理,可以很方便的添加定时器。需要注意的是,定时器的更新和回调函数的执行都是在timer_loop函数中实现的,回调函数中不应该有过于复杂的逻辑或者延时逻辑,否则将可能出现某些定时器无法正常更新,栈空间的资源占用过多。