上一篇 文章介绍了一个可以用在单片机上的事件处理机制。其一个事件循环是这样的。
int main()
{
int event;
eventHandler func;
while(1){
event = Event(); //事件检测,可以检测到按键按下
if(event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数
//TODO 事件处理完毕后,需要清除event标志
}
}
}
在我们已经完成的代码中,没有事件清除部分,这样做是不完整的,我们补充上,注意我们在代码中添加了clearBit
函数。
typedef void (*eventHandler)(); //函数指针的声明
static int EVENT = 0; //int为16bit
static int firstBitEqualOne(int event); //返回event中第一个等于1的bit位
static void setBit(int event,int pos); //将event的第pos位置1
static void clearBit(int event,int pos)//将event的第pos位置0
#define KEYDOWN 3 //按键按下的事件为3
static eventHandler[16] = {NULL,NULL,NULL,LedOn,NULL ....};//LedOn的数组下标为3
void keyDownEvent() //在中断中实现
{
if(isKeyDown())
setBit(EVENT,KEYDONW); //设置事件
}
static void LedOn(); \\点亮一个LED
eventHandler findEventHandler(int event) //根据event编号,寻找eventHandler
{
return eventHandler[event];
}
int Event()
{
if(EVENT != 0)
return firstBitEqualOne(EVENT) //有事件产生,返回事件编号
return -1; //没有事件产生,返回-1
}
int main()
{
int event;
eventHandler func;
while(1){
event = Event(); //事件检测,可以检测到按键按下
if(event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数
clearBit(EVENT,event) //清除事件,Event函数不再能检测到按键事件
}
}
}
这样的话,事件处理机制已经基本完善。可思考一个问题,当func函数调用期间,又有按键事件发生,会产生什么结果
void keyDownEvent() //在中断中实现
{
if(isKeyDown())
setBit(EVENT,KEYDONW); //设置事件
}
while(1){
event = Event(); //事件检测,可以检测到按键按下
if(event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数,期间又有按键中断产生
clearBit(EVENT,event) //清除事件,Event函数不再能检测到按键事件
//清除了按键事件,这样的话会造成按键事件的丢失。
}
}
很明显,当两次按键中断时间间隔比较小时,容易造成事件的丢失。可应该怎么避免这种现象呢?
- 产生这种现象的根源是,第一次按键中断处理还没有执行完毕,紧接着又发生了一次按键中断
- 如果这种按键中断一直这么频繁发生,系统是承受不了的,要么我们需要换高性能单片机,要么减少中断发生的频率
- 我们可以采取做缓冲的方法解决这个问题。先把事件暂存起来,等下一次处理。
现在,我们修改原来的代码解决这个问题
void fifoPush(int event) //先入先出队列,数据入队
int fifoPop() //出队,无数据返回 -1
int Event()
{
reutrn fifoPop() //没有事件时,返回 -1
}
typedef void (*eventHandler)(); //函数指针的声明
static void LedOn(); \\点亮一个LED
#define KEYDOWN 3 //按键按下的事件为3
static eventHandler[16] = {NULL,NULL,NULL,LedOn,NULL ....};//LedOn的数组下标为3
void keyDownEvent() //在中断中实现
{
if(isKeyDown())
fifoPush(KEYDONW); //设置事件
}
while(1){
event = Event(); //事件检测,可以检测到按键按下,完成了事件的出队列
if(event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数
}
}
现在我们再来思考一个问题,假设系统中有多种事件,那么不同事件的优先级是怎么样的呢?
对于fifoPush、fifoPop
方案来讲,事件是按照发生顺序的先入先出处理,但对于setBit clearBit
方案,由于firstBitEqualOne
函数,事件便有了优先级,假设KEY1DOWN=3
,KEY2DOWN=4
,则KEY1DOWN
的优先级就比KEY2DOWN
的优先级要高。
至于需不需要优先级,优先级应该怎么设置,我们需要在设计代码时考虑到运行的负载。
- 事件源是什么,发生频率有多高,事件处理怎么做,能不能及时处理
- 事件之间有没有竞争关系,竞争关系是怎么产生的,有没有必要避免,怎么避免,
其实,只要实时性要求不高的软件,一般不需要考虑优先级。
现在我们再来看另一个问题
static void LedOn()
{
LED = ON // 点亮LED灯
delay(3) //延迟3秒
LED = OFF //关闭LED灯
}
while(1){
event = Event(); //事件检测,可以检测到按键按下,完成了事件的出队列
if(event != -1){
func = findEventHandler(event); //事件匹配,可以匹配到LedOn函数
func(); //事件处理,调用LedOn函数,点亮3秒后关闭
}
}
很明显,LedOn函数里面有延时操作,会严重的干扰其他事件的处理,遇到这种情况我们应该怎么做呢??
我们可以修改LedOn
函数
#define KEYDOWN 3 //按键按下的事件为3
#define TIMER 4 //定时器事件
static void LedOn()
{
LED = ON; // 点亮LED灯
TimerOn(3); //开启一个3s的定时器
}
static void LedOff()
{
LED = OFF;
}
static eventHandler[16] = {NULL,NULL,NULL,LedOn,LedOff ....};//LedOn的数组下标为3
void keyDownEvent() //在中断中实现
{
if(isKeyDown())
setBit(EVENT,KEYDONW); //设置事件
}
void TimerEvent() //时钟中断中实现
{
setBit(EVENT,TIMER);
}
我们看到,为了解决一个延时问题,LedOn
的功能逻辑已经变得非常不清晰,同时延时功能使用又比较频繁,我们会在下一节,设计一个解决方案
这是一个免费,开源的教程,如果你喜欢可以转发,也可以打赏奖励。 欢迎关注微信公众号小站练兵
网友评论