用链表+函数指针+定时器中断实现的一个软件定时器(试用于所有单片机)

2019-04-15 12:43发布

因为需要移植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()函数放入硬件定时器中断中