分享:用10行代码实现的C语言状态机,支持超时机制

admin 2017-6-8 3997


代码如下,是ansi c编写的demo,大家可以在vc货mdk中编译看看。

这个简单的状态机使用c语言函数指针实现,从我的一个STM32工程中摘出来的;

可以支持状态转换、状态超时。

反正我用这种方法,实现了自动售货机售卖、收钱、找钱、上位机通信等完整功能,已经商用一段时间,还算稳定。已经把这种思想推行到其他项目中。

实现原理简介:

每一个状态就是一个void func(void) 类型的函数,每个函数只需要知道两件事:

1)自己要做什么(业务)

2)自己的下一个状态是什么(状态切换)

就像接力棒跑一样,每个人拿到棒子后全力跑完自己那一段(业务),把棒子递给下一个人(状态迁移)。

用我这种方法实现,比switch实现的方案,好在没有集中的状态转换,而是把状态转换分散到每一个状态中,最大的优点有两个:

1)无论多复杂的业务流程,状态切换的代码都只需要负责几个分支,不用通盘考虑。

2)对流程变化更友好:哪些流程变化,修改对应状态的函数即可;没有涉及到的内容完全无需考虑,无需担心引入bug。

相比较其他框架,这个方案的唯一优点就是简单,就几行代码而已嘛,随时根据自己的需求,添加特殊代码,实现特殊功能,对g_state_timeout_milliseconds的使用就是一个典型实例。

归根结底,最重要的还是对业务需求的分析,合理的状态划分,具体实现方案,只是个工具而已

核心代码是“状态机 BEGIN ”到“状态机 END”中的那10句话。

#include <time.h>
#include <stdio.h>
void delay_ms(int ms)
{
    //系统延时函数
}
unsigned int systemMs(void)
{
    clock_t c = clock()+100;
    return c;
}
/************ 状态机 BEGIN *****************/
typedef void (*state_func_t)(void);
static unsigned int g_state_timeout_milliseconds=0;
static state_func_t g_state_func = NULL;
void main_setNextState(state_func_t func)
{
    /*
     * g_state_timeout_milliseconds
     * 可以用于判断是否进入一个新状态;
     * 还可以用于判断进入某个状态多长时间了。
     */
    g_state_timeout_milliseconds = 0;
    g_state_func = func;
}
state_func_t main_getNextState(void)
{
    return g_state_func;
}
/************ 状态机 END *****************/
/************ 状态函数 BEGIN *************/
void main_state1(void);
void main_state2(void);
void main_state1(void)
{
    if(0 == g_state_timeout_milliseconds) {
        g_state_timeout_milliseconds = systemMs();
        printf("enter state1\n");
    }
    /* 5000毫秒超时 */
    if(systemMs() - g_state_timeout_milliseconds > 5*1000) {
        printf("exit state1\n");
        main_setNextState(main_state2);
        return;
    }
    /*
     * TODO 这里实现业务逻辑
     * 比如到达某种条件,进入其他状态
     * 比如收到某条消息,进入其他状态
     */
}
void main_state2(void)
{
    if(0 == g_state_timeout_milliseconds) {
        g_state_timeout_milliseconds = systemMs();
        printf("enter state2\n");
    }
    /* 2000毫秒超时 */
    if(systemMs() - g_state_timeout_milliseconds > 2*1000) {
        printf("exit state2\n");
        main_setNextState(main_state1);
        return;
    }
    /*
     * TODO 这里实现业务逻辑
     * 比如到达某种条件,进入其他状态
     * 比如收到某条消息,进入其他状态
     */
}
/************ 状态函数 END *************/
int main(int argc, char *argv[])
{
    printf("Hello, state machine!\n");
    /*
     * 设置初始状态
     */
    main_setNextState(main_state1);
    /*
     * 状态驱动
     * 每一个状态,处理完自己的事情后,负责指定下一个状态(使用main_setNextState函数)
     */
    while(1) {
        (*main_getNextState())();
        delay_ms(1);
    }
    return 0;
}

systemMs()函数的作用,就是返回系统从启动到现在,经过了多少个毫秒

完整C代码

上传的附件:
最新回复 [1]
  • ChinaMobile 2019-10-27
    0 2
    好的思想...
返回