单片机学习嵌入式软件开发

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

2017-04-17  本文已影响76人  qianlihu

在上一节中,提到了关于延时操作的处理方案

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函数,延时阻塞在这里
    }
}

在事件处理中,我们不能直接调用delay,会严重影响事件循环

#define KEYDOWN     3   //按键按下的事件为3
#define TIMER       4   //定时器事件
static eventHandler[16] = {NULL,NULL,NULL,LedOn,LedOff ....};//LedOn的数组下标为3

static void LedOn()
{
    LED = ON;   // 点亮LED灯
    TimerOn(3);    //开启一个3s的定时器
}
void keyDownEvent()    //在中断中实现
{
    if(isKeyDown())
        setBit(EVENT,KEYDONW);   //设置事件
}
void TimerEvent() //时钟中断中实现
{
    setBit(EVENT,TIMER);
}

为了避免影响事件循环,我们引入了定时器事件,但代码逻辑已经非常不清楚。
解决这种问题,大概有三种方案,OS的线程,协程,回调。三种方法各有利弊,我们先介绍回调方案,至于其他解决方案,留到后面的章节再去讨论

我们先实现一个普适的定时器

typdef void (*timerHandler)();
static uint Tick  = 0;  //当前系统时钟滴答

struct timerEvent{
    uint tick;     //时钟滴答
    timerHandler handler;  //定时器事件处理函数
};

struct timerEventNode{
    struct timerEvent timer;
    struct timerEventNode *next;
    struct timerEventNode *prev; 
};

struct timerEventHead{
    struct timerEventNode *next;
    struct timerEventNode *prev;
}
struct timerEventHead Head = {
    .next = NULL;
    .prev = NULL;
};

void setTimerEvent(int ticks,timerHandler handler) //tick为要延时的滴答数
{
    struct timerEventNode *node = malloc(sizeof(*node));
    node->timer.tick = Tick + ticks;    //请思考一下回滚问题 
    node->timer.handler = handler;
  
    if(Head.next == NULL && Head.prev == NULL){  //定时器链表操作
        ...
    }else{
        ...
    }
}

void execTimerEvent()
{
    struct timerEventNode *node = NULL;
    for(node = Head.next;node != NULL;node = node->next){
        if(node.timer.tick == Tick){ //定时器到了
            node.timer.handler() //调用定时处理函数
            //TODO     从定时器链表中删掉自己
        }
    }
}

void timerEvent()  //中断中实现,定时器中断若被设置为1ms间隔,则滴答计数器的精度为1ms
{
    Tick++;    //系统滴答计数加一,
    execTimerEvent(); //寻找到时的计时器,并执行相应处理函数
}


void timerInit()
{
    //TODO  //设置定时器
}

根据上面伪代码的实现,我们可以了解到这个定时器,有以下特点

你可以思考怎么优化上面提出的问题,这里我们不再讨论。

我们再回头看一下LED定时闪烁的问题

void LedFlash()   //led 闪烁,相比来讲代码结果已经比较清晰
{
    if(LED == ON)
        LED = OFF;
    else
        LED = ON;
    setTimerEvent(3000,LedFlash); //led 3000ms(tick) 闪烁一次
}

我们把这段代码结合我们前面提到的定时器放到我们的事件循环中


void fifoPush(int event)      //先入先出队列,数据入队
int  fifoPop()                //出队,无数据返回 -1

int Event()
{
    reutrn fifoPop()     //没有事件时,返回 -1
}
typedef void (*eventHandler)(); //函数指针的声明

#define KEYDOWN     3   //按键按下的事件为3
#define TIMER       4   //定时器事件

static eventHandler[16] = {NULL,NULL,NULL,LedFlash,timerEvent ....};//LedFlash的数组下标为3

void keyDownEvent()    //在中断中实现
{
    if(isKeyDown())
        fifoPush(KEYDONW);   //设置事件
}

void LedFlash()   //led 闪烁,相比来讲代码结果已经比较清晰
{
    if(LED == ON)
        LED = OFF;
    else
        LED = ON;
    setTimerEvent(3000,LedFlash); //led 3000ms(tick) 闪烁一次
}

int main()
{
    int event;
    eventHandler func;
    timerInit();
    while(1){
        event = Event();     //事件检测,可以检测到按键按下,完成了事件的出队列
        if(event != -1){
            func = findEventHandler(event);  //事件匹配,可以匹配到LedFlash函数
            func();                           //事件处理,调用LedFlash函数
        }
    }
}

我们看到,对于LED闪烁函数,其代码结构已经变得比较清晰。
请思考一下上面代码中,timerEvent的执行过程,setTimerEvent的执行过程,以及事件是怎么挂载和卸载的。

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

上一篇下一篇

猜你喜欢

热点阅读