本文主要写的是:将需要定时的事件作为一个链表节点添加到链表中。所写代码是从LWIP源码中复制出来的,稍作修改。
当阅读到lwip源码timers.c文件中的sys_timeout
函数时,觉得非常适合如下一种情况:
step1();
delay(100);
step2()
delay(100);
***
stepn();
上面这段代码是阻塞的,代码运行的时候这种耗费CPU的延时会导致其它代码得不到执行,一般不能写成这种方式。 对于嵌入式开发来说如果有操作系统支持,可以把这一部分分离成一个任务,如果没有操作系统的支持,需要我们自己自己通过状态机或是其它的方法实现,实现的方法有很多种。本文介绍的就是其中的一种解决方案。个人认为这种方案比较优雅。将每个step写成一个事件(函数)。然后挂载到链表上。时间到后执行回调函数。
time_list.h代码如下:
#ifndef __TIME_LIST_H
#define __TIME_LIST_H
typedef unsigned int uint32_t;
typedef void(*time_callback)(void*arg);
typedef struct node
{
struct node *next;
uint32_t time;
time_callback callback;
void *arg;
}Sys_timeo;
#endif
time_list.c代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "time_list.h"
#include "time.h"
#define NULL 0
#define ONE "ONE"
#define TOW "TOW"
#define THREE "THREE"
#define FOUR "FOUR"
void test_function(void *arg)
{
time_t t;
time(&t);
printf("%u\t", t);
printf("%s\r\n", arg);
}
Sys_timeo *sys_timeo_list;
void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)
{
Sys_timeo *timeout = NULL,*temp_node = NULL;
timeout = malloc(sizeof(Sys_timeo));
if (timeout == NULL)
return;
timeout->next = NULL;
timeout->time = msecs;
timeout->callback = callback;
timeout->arg = arg;
if (sys_timeo_list == NULL)
{
sys_timeo_list = timeout;
return;
}
if (opt == 1)
{
for (temp_node = sys_timeo_list; temp_node->next != NULL; temp_node = temp_node->next)
{
}
temp_node->next = timeout;
return;
}
if (msecs < sys_timeo_list->time)
{
sys_timeo_list->time -= msecs;
timeout->next = sys_timeo_list;
sys_timeo_list = timeout;
}
else
{
for (temp_node = sys_timeo_list; temp_node != NULL; temp_node = temp_node->next)
{
timeout->time -= temp_node->time;
if ((temp_node->next == NULL) || (timeout->time < temp_node->next->time))
{
if (temp_node->next != NULL)
{
temp_node->next->time -= timeout->time;
}
timeout->next = temp_node->next;
temp_node->next = timeout;
break;
}
}
}
}
int main()
{
Sys_timeo * temp_node = NULL;
time_t current_time, last_time, difference_time;
sys_timeout(10, test_function, ONE, 1);
sys_timeout(15, test_function, TOW, 1);
sys_timeout(20, test_function, THREE, 1);
sys_timeout(25, test_function, FOUR, 1);
time(¤t_time);
last_time = current_time;
printf("start_time = %u\r\n",current_time);
while(1)
{
if (sys_timeo_list == NULL)
{
printf("exti\r\n");
return 0;
}
time(¤t_time);
difference_time = current_time - last_time;
if (difference_time)
{
last_time = current_time;
if (sys_timeo_list->time >= difference_time)
sys_timeo_list->time -= difference_time;
else
sys_timeo_list->time = 0;
}
if (sys_timeo_list->time == 0)
{
sys_timeo_list->callback(sys_timeo_list->arg);
temp_node = sys_timeo_list;
sys_timeo_list = sys_timeo_list->next;
free(temp_node);
}
}
return 0;
}
先看一下Sys_timeo
结构体:
typedef struct node
{
struct node *next;
uint32_t time;
time_callback callback;
void *arg;
}Sys_timeo;
要声明的一点是:结构中time字段延时的时间是相对于上一个节点执行过后还需要延时的时间。
搞清楚这一点后就好办了,我们在中断服务函数中只需要对链表当前的首节点时间做减操作即可。当减到0的时候,执行回调函数,并把链表的首节点释放,下一个节点就是节点了,以此类推。这样就会按既定的时间延时。
函数void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)
会根据需要延时的时间和延时的选项在链表中找到一个合适的位置,将链表节点插入到该位置。介绍一下opt选项,如果为1就是相对于当前链表的尾部节点再延时msecs个时间单位,如果为0就是相对于当前系统时间延时msecs个时间单位。
因为我是在win32环境编写和下测试的,所以就是在主循环中比较当前时间和上次时间的差,以此方法对头节点的时间做减法操作如果在嵌入式中可以在中断服务函数中对链表头部的时间做减法。
在实际使用的时候,在任意时刻需要一个延时时间就调用sys_timeout
函数,向sys_timeo_list
链表中添加一个延时事件即可。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)