我们知道在Arduino中是没有线程这个概念的,如果需要同时按照时间间隔执行多个事件,使用系统自带的delay()进行阻塞势必会影响到后面的事件。例如在loop()中我们需要不间断的向电机发送脉冲信号使之行走,同时每间隔1s向控制端发送一次数据。如果采用delay(1000)的方法,电机则会每隔1s才获得一次脉冲信号,这不是我们想要的。所以,我们需要为此自定义一个事件定时器。
事件定时器类的C++代码如下:
EventTimer.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #pragma once #include "Arduino.h" typedef void (*LPFUNC)();//typedef定义一个函数指针 class EventTimer { public: EventTimer(void); ~EventTimer(void); void Init();//初始化 void Looper();//事件循环 void AddEvent(LPFUNC fun,int delay,int repeat);//添加一个事件 private: /*事件结构体*/ struct EventNode{ LPFUNC fun;//事件函数体 int delay;//执行延时 int repeat;//执行次数 int lasttime;//上一个时间 EventNode * next,* prior;//上一级、下一级指针 }; EventNode *head; EventNode *tail; }; |
EventTimer.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | #include "EventTimer.h" EventTimer::EventTimer(void) { } EventTimer::~EventTimer(void) { } /* 初始化队列 */ void EventTimer::Init(){ head = NULL; tail = NULL; } /* 添加循环事件 */ void EventTimer::AddEvent(LPFUNC fun,int delay,int repeat){ EventNode *p = new EventNode; p->fun = fun;//事件函数体 p->delay = delay;//执行延时 p->repeat = repeat;//执行次数 p->lasttime = 0; p->next = NULL; p->prior = NULL; if(head == NULL){//如果是空表 head = p; tail = p; }else{//将节点与头结点链接 tail->next = p; p->prior = tail; tail = p; } } /* 事件循环 */ void EventTimer::Looper(){ if (head == NULL)return; EventNode *i = head; while(true) { if (millis() - i->lasttime >= i->delay && i->repeat > 0) { i->lasttime = millis();// millis()获取程序当前执行毫秒数 i->fun(); if (i->repeat < 99)i->repeat--;//默认设置循环数大于99为无限循环 if(i->next != NULL){ i = i->next; }else{ break; } } //删除到期事件 if(i->repeat <= 0){ if(i->prior == NULL && i->next == NULL){//only head 最后一个节点被删除 free(i); head = NULL; tail = NULL; break; } else if(i->prior == NULL){//head 被删节点为头部 head = i->next; i->next->prior = NULL; EventNode *del = i; i = i->next; free(del); } else if(i->next != NULL ){//mid 被删节点为中间 i->prior->next = i->next; i->next->prior = i->prior; EventNode *del = i; i = i->next; free(del); } else //tail 被删节点为尾部 { tail = i->prior; i->prior->next = NULL; free(i); break; } } } } |
上述代码简单的实现了一个以双向链表为基础的事件循环队列,当用户调用AddEvent时,程序向队列尾部添加进一个事件节点。在Looper()循环队列中,将依次移动节点指针,并根据相应参数判断是执行函数体还是跳过或者到达执行最大次数将目标节点移出队列。
这个类在Arduino中和C++中略有不同,如若要在PC端控制台运行,将Arduino显示当前程序运行毫秒数的millis()函数 修改为GetTickCount()即可。
EventTimer在Arduino的主函数体中的调用方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include "EventTimer.h" EventTimer eventTimer; void setup() { /* add setup code here */ eventTimer.Init();//初始化定时器 eventTimer.AddEvent(func,1000,5); //添加事件至定时器,1000毫秒执行一次,共执行5次 } void loop() { /* add main program code here */ eventTimer.Looper();//事件定时器循环 } void func(){ //这里添加需要定时执行的代码 } |
至此在Arduino中,我能也拥有了一个简单的事件循环队列定时器。就目前单线程需求来说,已经基本足够。
以上为此EventTimer的全部代码,这里就不打包成library文件包了,有需要的同学自己编译一下。
欢迎提出bug及修改优化建议。
本篇到此,谢谢关注。
BeiTown
2013.04.20
Tags: Arduino, C++, EventTimer, GetTickCount(), millis(), 定时器