因为需要移植nrf51822的程序到普通单片机上,于是分析了协议栈里的软件定时器,用链表+函数指针+定时器中断的方法实现了软件定时器的功能。
下面介绍代码和使用方法
1、函数指针和链表初始化
typedef void (*app_timer_timeout_handler_t)(void);
typedef struct app_timer
{
u8 id;
u16 time;
u16 interval_time;
app_timer_timeout_handler_t p_timeout_handler;
struct app_timer *next; //指向下一节点的指针
bool isrunning ;
}app_timer;
2、为链表申请空间
typedef struct app_timer_t
{
uint32_t data[sizeof(uint32_t) ];
} app_timer_t;
/**@brief Timer ID type.
* Never declare a variable of this type, but use the macro @ref APP_TIMER_DEF instead.*/
typedef app_timer_t * app_timer_name_t;
/**
* @brief Create a timer identifier and statically allocate memory for the timer.
*
* @param timer_name Name of the timer identifier variable that will be used to control the timer.
*/
#define APP_TIMER_DEF(timer_name)
static app_timer_t timer_name##_data = { {0} };
static app_timer_name_t timer_name = &timer_name##_data
3、创建定时器
参数 定时器名字 定时器id 定时器工作时长 定时器回调函数
u32 app_timer_create(app_timer_name_t const * p_timer_name,
u8 id,
u16 time,
app_timer_timeout_handler_t timeout_handler)
{
app_timer * p_node = (app_timer *)*p_timer_name;
p_node->id = id;
p_node->time = time;
p_node->interval_time = time; //记录下定时器的触发时间
p_node->p_timeout_handler = timeout_handler ;
p_node->isrunning = FALSE;
p_head = Insert(p_head,p_node);
}
4、将节点插入链表的最后
参数: 链表头指针 节点指针
返回:指向链表表头的指针
struct app_timer *Insert (struct app_timer *head,struct app_timer *node)
{
struct app_timer *p1; //p1保存当前需要检查的节点的地址
//当头节点为空时,将传入的节点作为头节点,返回头节点
if (head == NULL)
{
head = node;
node->next = NULL;
return head;
}
p1 = head;
while(p1->next != NULL)
{
p1 = p1->next; //后移一个节点
}
if(p1->next == NULL) //将该节点插入链表的末尾
{
p1->next = node;
node->next = NULL;
}
else
{
}
return head;
}
5、遍历链表 计算是否有定时器超时
将这个函数放入到定时器中断中,每进入一次定时器中断(软件定时器),将设定的软件定时器值-1,减为0之后就调用一开始创建定时器时注册的函数。
void TraverseList(void)
{
struct app_timer *p1 = p_head;
while( p1!= NULL) //下一个节点如果不为空
{
if(p1->isrunning == TRUE)
{
p1->time--;
}
else
{
p1->time = p1->interval_time;
}
if(p1->time == 0 )
{
p1->time = p1->interval_time;
p1->p_timeout_handler();
}
if(p1->next != NULL)
{
p1 = p1->next;
}
else
{
return ;
}
}
}
6、修改定时器工作状态
可以让指定id的定时器启动或停止
参数 定时器id 定时器使能
void app_timer_isrunning(int id, bool isrunning)
{
struct app_timer *p1 = p_head;
while((p1->id != id) && (p1->next != NULL))
{
p1 = p1->next; //后移一个节点
}
if (p1->id==id) //找到了
{
p1->isrunning = isrunning;
}
else
{
return ;
}
}
7、使用方法
1)通过宏定义来设置id和定时时间
//定时器id设置
#define MAIN_TIMER_ID 1
//定时器初值设置
#define MAIN_TIMER_INTERVAL 5000
2)通过宏预申请链表空间
APP_TIMER_DEF(main_timer);
3)创建定时器
app_timer_create(&main_timer,MAIN_TIMER_ID,MAIN_TIMER_INTERVAL,main_task_timeout_handler);
4)使能定时器
app_timer_isrunning(MAIN_TIMER_ID,TRUE);
5)初始化硬件定时器,将TraverseList()函数放入硬件定时器中断中