打印

GD32实战__状态机(转载)

[复制链接]
883|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2019-6-21 14:30 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
引子

​        上面的点灯例子中,如果想要实现如下功能,使用状态机可以把代码写的简洁通透。

  • 按一下全亮
  • 再按一下亮度降低50%
  • 再按一下跑马灯
  • 长按3秒熄灭

使用特权

评论回复
沙发
wiba|  楼主 | 2019-6-21 14:30 | 只看该作者
状态机设计

​        我们把上面的功能在分解下,如下:

  • 按键检测,如图

    • 按下,低电平
    • 弹起,高电平
    • 按下时间长,可用作区分短按还是长按
    • 触发方式,高电平触发,低电平触发,下降沿触发,上升沿触发,根据经验,牵扯到时长判断,触发方式最好选择上升沿触发



使用特权

评论回复
板凳
wiba|  楼主 | 2019-6-21 14:30 | 只看该作者

2. 灯状态

  • 全亮
  • 50%亮度
  • 跑马灯状态
  • 全灭

总结,可以设计如下图的两个小状态机相互切换。


使用特权

评论回复
地板
wiba|  楼主 | 2019-6-21 14:30 | 只看该作者
功能实现
按键检测
​        状态迁移如上图,代码如下,功能与上图描述一一对应。

#define APP_KEY_JITTERTIME 50 /* 50ms */
#define APP_KEY_LONGPRESSTIME 3000 /* 3s */

typedef enum
{
    KEY_SMSTATUS_UP = 0,
    KEY_SMSTATUS_UPING,
    KEY_SMSTATUS_DOWN,
    KEY_SMSTATUS_DOWNING,
    KEY_SMSTATUS_BUTT
}KeySmStatus_e;

typedef struct
{
    KeySmStatus_e smStatus;     /* up-->downing-->down-->uping-->up */
    U64 downingMoment;  
    U64 jitterTimeBegin;
}KeySm_t;

static KeySm_t gKeySm;


使用特权

评论回复
5
wiba|  楼主 | 2019-6-21 14:31 | 只看该作者
static VOID KEY_SmStatusUp(VOID)
{
    U8 keyStatus = 0;

    if (KEY_SMSTATUS_UP != gKeySm.smStatus)
    {
        return;
    }

    keyStatus = DRV_KEY_GetStatus(DRV_KEY3);
    if (DRV_KEY_DOWN == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_DOWNING;
        gKeySm.jitterTimeBegin = APP_TimeMs();
        APP_TRACE("up --> downing");
    }
}


使用特权

评论回复
6
wiba|  楼主 | 2019-6-21 14:31 | 只看该作者
static VOID KEY_SmStatusDowning(VOID)
{
    U64 currentTime = 0;
    U8 keyStatus = 0;

    if (KEY_SMSTATUS_DOWNING != gKeySm.smStatus)
    {
        return;
    }

    currentTime = APP_TimeMs();
    if (currentTime < (gKeySm.jitterTimeBegin + APP_KEY_JITTERTIME))
    {
        return;
    }

    keyStatus = DRV_KEY_GetStatus(DRV_KEY3);
    if (DRV_KEY_DOWN == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_DOWN;
        gKeySm.downingMoment = APP_TimeMs();
        APP_TRACE("downing --> down");
    }
    else if (DRV_KEY_UP == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_UP;
        APP_TRACE("downing --> up");
    }
    else
    {
        APP_ERROR("");
    }
}


使用特权

评论回复
7
wiba|  楼主 | 2019-6-21 14:32 | 只看该作者
static VOID KEY_SmStatusDown(VOID)
{
    U8 keyStatus = 0;

    if (KEY_SMSTATUS_DOWN != gKeySm.smStatus)
    {
        return;
    }

    keyStatus = DRV_KEY_GetStatus(DRV_KEY3);
    if (DRV_KEY_UP == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_UPING;
        gKeySm.jitterTimeBegin = APP_TimeMs();
        APP_TRACE("down --> uping");
    }
}


使用特权

评论回复
8
wiba|  楼主 | 2019-6-21 14:33 | 只看该作者
static VOID KEY_SmStatusUping(VOID)
{
    U64 currentTime = 0;
    U8 keyStatus = 0;

    if (KEY_SMSTATUS_UPING != gKeySm.smStatus)
    {
        return;
    }

    currentTime = APP_TimeMs();
    if (currentTime < (gKeySm.jitterTimeBegin + APP_KEY_JITTERTIME))
    {
        return;
    }

    keyStatus = DRV_KEY_GetStatus(DRV_KEY3);
    if (DRV_KEY_DOWN == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_DOWN;
        APP_TRACE("uping --> down");
    }
    else if (DRV_KEY_UP == keyStatus)
    {
        gKeySm.smStatus = KEY_SMSTATUS_UP;
        currentTime = APP_TimeMs();
        if (currentTime >= (gKeySm.downingMoment + APP_KEY_LONGPRESSTIME))
        {
            APP_DEBUG("long press.");
            APP_LED_SmSwitch4LongPress();
        }
        else
        {
            APP_DEBUG("short press.");
            APP_LED_SmSwitch4ShortPress();
        }

        APP_TRACE("uping --> up");
    }
    else
    {
        APP_ERROR("");
    }
}


使用特权

评论回复
9
wiba|  楼主 | 2019-6-21 14:33 | 只看该作者


VOID APP_KEY_Loop(VOID)
{
    KEY_SmStatusUp();
    KEY_SmStatusDowning();
    KEY_SmStatusDown();
    KEY_SmStatusUping();
}


使用特权

评论回复
10
wiba|  楼主 | 2019-6-21 14:34 | 只看该作者
按键通知LED接口
VOID APP_LED_SmSwitch4LongPress(VOID)
{
    if (LED_SMSTATUS_OFF != gLedSm.smStatus)
    {
        gLedSm.smStatus = LED_SMSTATUS_OFF;
    }
}


使用特权

评论回复
11
wiba|  楼主 | 2019-6-21 14:34 | 只看该作者



VOID APP_LED_SmSwitch4ShortPress(VOID)
{
    switch (gLedSm.smStatus)
    {
        case LED_SMSTATUS_OFF:
            gLedSm.smStatus = LED_SMSTATUS_ON;
            break;
        case LED_SMSTATUS_ON:
            LED_DoHalfBrightInit();
            gLedSm.smStatus = LED_SMSTATUS_HALFBRIGHT;
            break;
        case LED_SMSTATUS_HALFBRIGHT:
            LED_DoWaterfallBrightInit();
            gLedSm.smStatus = LED_SMSTATUS_WATERFALL;
            break;
        case LED_SMSTATUS_WATERFALL:
            gLedSm.smStatus = LED_SMSTATUS_ON;
            break;
        default:
            APP_ERROR("error sm status.");
            break;
    }
}

使用特权

评论回复
12
wiba|  楼主 | 2019-6-21 14:35 | 只看该作者
LED灯状态迁移


typedef enum
{
    LED_SMSTATUS_OFF = 0,
    LED_SMSTATUS_ON,
    LED_SMSTATUS_HALFBRIGHT,
    LED_SMSTATUS_WATERFALL,
    LED_SMSTATUS_BUTT
}LedSmStatus_e;


使用特权

评论回复
13
wiba|  楼主 | 2019-6-21 14:35 | 只看该作者


typedef struct
{
    LedSmStatus_e smStatus;
    LedSmStatus_e currentStatus;
}LedSm_t;

static LedSm_t gLedSm;



使用特权

评论回复
14
wiba|  楼主 | 2019-6-21 14:36 | 只看该作者
static VOID LED_LightOn(VOID)
{
    if ((LED_SMSTATUS_ON != gLedSm.smStatus) ||
        (LED_SMSTATUS_ON == gLedSm.currentStatus))
    {
        return;
    }

    APP_TRACE("light on.");
    DRV_LED_On(DRV_LED1);
    DRV_LED_On(DRV_LED2);
    DRV_LED_On(DRV_LED3);
    DRV_LED_On(DRV_LED4);

    gLedSm.currentStatus = LED_SMSTATUS_ON;
}




使用特权

评论回复
15
wiba|  楼主 | 2019-6-21 14:36 | 只看该作者
static VOID LED_HalfBright(VOID)
{
    if (LED_SMSTATUS_HALFBRIGHT != gLedSm.smStatus)
    {
        return;
    }

    APP_TRACE("light half.");
    LED_DoHalfBright();
    gLedSm.currentStatus = LED_SMSTATUS_HALFBRIGHT;
}




使用特权

评论回复
16
wiba|  楼主 | 2019-6-21 14:39 | 只看该作者
static VOID LED_WaterfallBright(VOID)
{
    if (LED_SMSTATUS_WATERFALL != gLedSm.smStatus)
    {
        return;
    }

    APP_TRACE("light waterfall.");
    LED_DoWaterfallBright();
    gLedSm.currentStatus = LED_SMSTATUS_WATERFALL;
}





使用特权

评论回复
17
wiba|  楼主 | 2019-6-21 14:39 | 只看该作者
static VOID LED_LightOff(VOID)
{
    if ((LED_SMSTATUS_OFF != gLedSm.smStatus) ||
        (LED_SMSTATUS_OFF == gLedSm.currentStatus))
    {
        return;
    }

    APP_TRACE("light off.");
    DRV_LED_Off(DRV_LED1);
    DRV_LED_Off(DRV_LED2);
    DRV_LED_Off(DRV_LED3);
    DRV_LED_Off(DRV_LED4);

    gLedSm.currentStatus = LED_SMSTATUS_OFF;
}



使用特权

评论回复
18
wiba|  楼主 | 2019-6-21 14:40 | 只看该作者
VOID APP_LED_Loop(VOID)
{
    LED_LightOn();
    LED_HalfBright();
    LED_WaterfallBright();
    LED_LightOff();
}

使用特权

评论回复
19
wiba|  楼主 | 2019-6-21 14:40 | 只看该作者
LED灯亮度控制
​        通过控制LED快速闪烁,调节亮灭的时间占空比实现的,如下

static VOID LED_DoHalfBright(VOID)
{
    U64 time = 0;

    time = APP_TimeMs();
    if (time > gLightOnMoment + APP_LED_HALFLIGHT_TIME)
    {
        gLightOnMoment = APP_TimeMs();
        gLightCount++;
    }
    else
    {
        return;
    }

    if (1 == gLightCount)
    {
        DRV_LED_On(DRV_LED1);
        DRV_LED_On(DRV_LED2);
        DRV_LED_On(DRV_LED3);
        DRV_LED_On(DRV_LED4);
    }
    else
    {
        DRV_LED_Off(DRV_LED1);
        DRV_LED_Off(DRV_LED2);
        DRV_LED_Off(DRV_LED3);
        DRV_LED_Off(DRV_LED4);
    }

    /* 调节亮度 */
    if (3 == gLightCount)
    {
        gLightCount = 0;
    }
}


使用特权

评论回复
20
wiba|  楼主 | 2019-6-21 14:40 | 只看该作者
跑马灯
​        即每个灯一次亮灭,如下

static VOID LED_DoWaterfallBright(VOID)
{
    U64 time = 0;

    time = APP_TimeMs();
    if (time > gLightOnMoment + APP_LED_WATERFALL_TIME)
    {
        gLightOnMoment = APP_TimeMs();
        gLightCount++;
    }
    else
    {
        return;
    }

    if (1 == gLightCount)
    {
        DRV_LED_On(DRV_LED1);
        DRV_LED_Off(DRV_LED2);
        DRV_LED_Off(DRV_LED3);
        DRV_LED_Off(DRV_LED4);
    }

    if (2 == gLightCount)
    {
        DRV_LED_Off(DRV_LED1);
        DRV_LED_On(DRV_LED2);
        DRV_LED_Off(DRV_LED3);
        DRV_LED_Off(DRV_LED4);
    }

    if (3 == gLightCount)
    {
        DRV_LED_Off(DRV_LED1);
        DRV_LED_Off(DRV_LED2);
        DRV_LED_On(DRV_LED3);
        DRV_LED_Off(DRV_LED4);
    }

    if (4 == gLightCount)
    {
        DRV_LED_Off(DRV_LED1);
        DRV_LED_Off(DRV_LED2);
        DRV_LED_Off(DRV_LED3);
        DRV_LED_On(DRV_LED4);
        gLightCount = 0;
    }
}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

3305

帖子

3

粉丝