美文网首页单片机学习
嵌入式开发系列教程(五) 嵌入式系统软件设计(中)

嵌入式开发系列教程(五) 嵌入式系统软件设计(中)

作者: qianlihu | 来源:发表于2017-04-12 10:57 被阅读88次

    上一篇 文章介绍了一个可以用在单片机上的事件处理机制。其一个事件循环是这样的。

    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的功能逻辑已经变得非常不清晰,同时延时功能使用又比较频繁,我们会在下一节,设计一个解决方案

    这是一个免费,开源的教程,如果你喜欢可以转发,也可以打赏奖励。 欢迎关注微信公众号小站练兵

    相关文章

      网友评论

        本文标题:嵌入式开发系列教程(五) 嵌入式系统软件设计(中)

        本文链接:https://www.haomeiwen.com/subject/jizpattx.html