zephyr SMF 状态机框架学习

zephyr 学习 状态机框架

概述

状态机中每个状态需要注册三个callback 即 进入状态、执行状态、 退出状态时执行的动作

默认情况下 创建的状态机中的状态没有父状态 也叫平面状态机

zephyr也支持使用分层状态机 即某些状态有父状态 需要打开 CONFIG_SMF_ANCESTOR_SUPPORT

要想使用状态机 首先需要定义用户自定义的结构体 所有自定义的结构体的第一个必须是 struct smf_ctx ctx 因为zephyr的通过宏取用户自定义结构体中状态机的句柄 SMF_CTX(&user_obj) (用户变量的首地址是 smf_ctx 的地址)

这个用户自定义结构体的指针会被传入每个事件进入、执行、退出的callback

struct user_object {
   struct smf_ctx ctx;
   /* All User Defined Data Follows */
};

状态机创建

可以使用宏 SMF_CREATE_STATE 创建状态机的各状态

每个状态需要注册 进入 退出的条件 以及运行的callback

enum demo_state { S0, S1, S2 };
const struct smf_state demo_states[] = {
   [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit),
   [S1] = SMF_CREATE_STATE(s1_entry, s1_run, s1_exit),
   [S2] = SMF_CREATE_STATE(s2_entry, s2_run, s2_exit)
};

如果想注册分层状态机

enum demo_state { S0, S1, S2 };
const struct smf_state demo_states[] = {
   [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit, parent_s0),
   [S1] = SMF_CREATE_STATE(s1_entry, s1_run, s1_exit, parent_s12),
   [S2] = SMF_CREATE_STATE(s2_entry, s2_run, s2_exit, parent_s12)
};

使用函数 void smf_set_initial(smf_ctx *ctx, smf_state *state) 设置初始状态(上述创建的状态中的一个)

使用函数 void smf_set_state(smf_ctx *ctx, smf_state *state) 进行状态转移

注意 状态机运行的时候 必须在进入 运行的callback里执行状态转移 如果在退出状态的时候执行状态转移 会产生warning

状态机运行

在应用代码中 执行 int32_t smf_run_state(smf_ctx *ctx) 函数

如果返回非0值 表示状态机需要停止运行

状态机终止

可以在每个状态的进入 运行 退出的callback里执行状态机中止函数 void smf_terminate(smf_ctx *ctx, int32_t val)

平面状态机示例

在这里插入图片描述

#include <zephyr/smf.h>

/* Forward declaration of state table */
static const struct smf_state demo_states[];

/* List of demo states */
enum demo_state { S0, S1, S2 };

/* User defined object */
struct s_object {
        /* This must be first */
        struct smf_ctx ctx;

        /* Other state specific data add here */
} s_obj;

/* State S0 */
static void s0_entry(void *o)
{
        /* Do something */
}
static void s0_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
}
static void s0_exit(void *o)
{
        /* Do something */
}

/* State S1 */
static void s1_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
}
static void s1_exit(void *o)
{
        /* Do something */
}

/* State S2 */
static void s2_entry(void *o)
{
        /* Do something */
}
static void s2_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
}

/* Populate state table */
static const struct smf_state demo_states[] = {
        [S0] = SMF_CREATE_STATE(s0_entry, s0_run, s0_exit),
        /* State S1 does not have an entry action */
        [S1] = SMF_CREATE_STATE(NULL, s1_run, s1_exit),
        /* State S2 does not have an exit action */
        [S2] = SMF_CREATE_STATE(s2_entry, s2_run, NULL),
};

int main(void)
{
        int32_t ret;

        /* Set initial state */
        smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);

        /* Run the state machine */
        while(1) {
                /* State machine terminates if a non-zero value is returned */
                ret = smf_run_state(SMF_CTX(&s_obj));
                if (ret) {
                        /* handle return code and terminate state machine */
                        break;
                }
                k_msleep(1000);
        }
}

分层状态机示例

下图中 S0 和 S1 共享一个父状态,S0 是初始状态。

  1. 父状态进入在子状态进入前执行 比如先执行parent_entry 后执行s0_entry

  2. 子状态间的状态转移不会触发父状态的进入和退出callback

  3. 父状态退出在子状态退出后执行

  4. **只有子状态执行 child_run 时 没有产生状态转移 父状态才会执行 parent_run (**比如s0运行时 状态转移条件没有达成 会执行parent_run 如果本次有状态转移 那就不会执行了)

在这里插入图片描述

#include <zephyr/smf.h>

/* Forward declaration of state table */
static const struct smf_state demo_states[];

/* List of demo states */
enum demo_state { PARENT, S0, S1, S2 };

/* User defined object */
struct s_object {
        /* This must be first */
        struct smf_ctx ctx;

        /* Other state specific data add here */
} s_obj;

/* Parent State */
static void parent_entry(void *o)
{
        /* Do something */
}
static void parent_exit(void *o)
{
        /* Do something */
}

/* State S0 */
static void s0_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
}

/* State S1 */
static void s1_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S2]);
}

/* State S2 */
static void s2_run(void *o)
{
        smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
}

/* Populate state table */
static const struct smf_state demo_states[] = {
        /* Parent state does not have a run action */
        [PARENT] = SMF_CREATE_STATE(parent_entry, NULL, parent_exit, NULL),
        /* Child states do not have entry or exit actions */
        [S0] = SMF_CREATE_STATE(NULL, s0_run, NULL, &demo_states[PARENT]),
        [S1] = SMF_CREATE_STATE(NULL, s1_run, NULL, &demo_states[PARENT]),
        /* State S2 do ot have entry or exit actions and no parent */
        [S2] = SMF_CREATE_STATE(NULL, s2_run, NULL, NULL),
};

int main(void)
{
        int32_t ret;

        /* Set initial state */
        smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);

        /* Run the state machine */
        while(1) {
                /* State machine terminates if a non-zero value is returned */
                ret = smf_run_state(SMF_CTX(&s_obj));
                if (ret) {
                        /* handle return code and terminate state machine */
                        break;
                }
                k_msleep(1000);
        }
}

事件驱动型状态机

事件不是状态驱动框架中的明确组成部分 (也可以采用轮询的方式 实现满足条件进行状态转移)

同时zephyr也支持使用events实现事件驱动的状态机

在这里插入图片描述

通过 k_event_init(&s_obj.smf_event)初始化一个事件

单独起一个线程 执行状态机的运行 通过 k_event_wait 阻塞状态机的运行

直到另一个线程或者中断 有事件抛出时 才会执行状态机运行 然后在状态的执行callcack中 通过判断抛出的事件类型 比如按键按下事件 EVENT_BTN_PRESS 来完成状态转移

#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/smf.h>

#define SW0_NODE        DT_ALIAS(sw0)

/* List of events */
#define EVENT_BTN_PRESS BIT(0)

static const struct gpio_dt_spec button =
        GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});

static struct gpio_callback button_cb_data;

/* Forward declaration of state table */
static const struct smf_state demo_states[];

/* List of demo states */
enum demo_state { S0, S1 };

/* User defined object */
struct s_object {
        /* This must be first */
        struct smf_ctx ctx;

        /* Events */
        struct k_event smf_event;
        int32_t events;

        /* Other state specific data add here */
} s_obj;

/* State S0 */
static void s0_entry(void *o)
{
        printk("STATE0\n");
}

static void s0_run(void *o)
{
        struct s_object *s = (struct s_object *)o;

        /* Change states on Button Press Event */
        if (s->events & EVENT_BTN_PRESS) {
                smf_set_state(SMF_CTX(&s_obj), &demo_states[S1]);
        }
}

/* State S1 */
static void s1_entry(void *o)
{
        printk("STATE1\n");
}

static void s1_run(void *o)
{
        struct s_object *s = (struct s_object *)o;

        /* Change states on Button Press Event */
        if (s->events & EVENT_BTN_PRESS) {
                smf_set_state(SMF_CTX(&s_obj), &demo_states[S0]);
        }
}

/* Populate state table */
static const struct smf_state demo_states[] = {
        [S0] = SMF_CREATE_STATE(s0_entry, s0_run, NULL),
        [S1] = SMF_CREATE_STATE(s1_entry, s1_run, NULL),
};

void button_pressed(const struct device *dev,
                struct gpio_callback *cb, uint32_t pins)
{
        /* Generate Button Press Event */
        k_event_post(&s_obj.smf_event, EVENT_BTN_PRESS);
}

int main(void)
{
        int ret;

        if (!device_is_ready(button.port)) {
                printk("Error: button device %s is not ready\n",
                        button.port->name);
                return;
        }

        ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
        if (ret != 0) {
                printk("Error %d: failed to configure %s pin %d\n",
                        ret, button.port->name, button.pin);
                return;
        }

        ret = gpio_pin_interrupt_configure_dt(&button,
                GPIO_INT_EDGE_TO_ACTIVE);
        if (ret != 0) {
                printk("Error %d: failed to configure interrupt on %s pin %d\n",
                        ret, button.port->name, button.pin);
                return;
        }

        gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
        gpio_add_callback(button.port, &button_cb_data);

        /* Initialize the event */
        k_event_init(&s_obj.smf_event);

        /* Set initial state */
        smf_set_initial(SMF_CTX(&s_obj), &demo_states[S0]);

        /* Run the state machine */
        while(1) {
                /* Block until an event is detected */
                s_obj.events = k_event_wait(&s_obj.smf_event,
                                EVENT_BTN_PRESS, true, K_FOREVER);

                /* State machine terminates if a non-zero value is returned */
                ret = smf_run_state(SMF_CTX(&s_obj));
                if (ret) {
                        /* handle return code and terminate state machine */
                        break;
                }
        }
}

探究状态机框架的实现方式

状态机句柄结构体smf_ctx用来管理状态机 其包含:当前状态 、上一次状态 、状态停止标志位 、状态机运行情况的变量的地址

/** Defines the current context of the state machine. */
struct smf_ctx {
    /** Current state the state machine is executing. */
    const struct smf_state *current;
    /** Previous state the state machine executed */
    const struct smf_state *previous;
    /**
     * This value is set by the set_terminate function and
     * should terminate the state machine when its set to a
     * value other than zero when it's returned by the
     * run_state function.
     */
    int32_t terminate_val;
    /**
     * The state machine casts this to a "struct internal_ctx" and it's
     * used to track state machine context
     */
    uint32_t internal;
};

internal变量 用来存放状态机运行情况信息 也就是 internal_ctx结构体 表示当前状态机是首次创建 停止 还是退出了

比如调用 smf_set_terminate 会将 terminate标志置1 然后会将 terminate_val 设置为用户自动的非零值

在下一次执行 smf_run_state 就会直接返回 terminate_val 这样就直到状态机停止是因为用户调用了中止函数

再比如用 exit 标志 来防止在退出状态时执行了状态转移

struct internal_ctx {
    bool new_state : 1;
    bool terminate : 1;
    bool exit      : 1;
};

smf_state用来表示每个状态的结构体 包含 进入 执行 退出 三个函数指针 此外 还有其父状态的地址

这里可以看出 一个父状态可以有多个子状态 但一个子状态只能有一个父状态

/** General state that can be used in multiple state machines. */
struct smf_state {
    /** Optional method that will be run when this state is entered */
    const state_execution entry;
    /**
     * Optional method that will be run repeatedly during state machine
     * loop.
     */
    const state_execution run;
    /** Optional method that will be run when this state exists */
    const state_execution exit;
    /**
     * Optional parent state that contains common entry/run/exit
     *  implementation among various child states.
     *  entry: Parent function executes BEFORE child function.
     *  run:   Parent function executes AFTER child function.
     *  exit:  Parent function executes AFTER child function.
     *
     *  Note: When transitioning between two child states with a shared parent,
     *  that parent's exit and entry functions do not execute.
     */
    const struct smf_state *parent;
};

smf_run_state 函数中 会首先判断本次状态机是否要中止 然后就会一直运行当前状态的执行callback

int32_t smf_run_state(struct smf_ctx *const ctx)
{
    struct internal_ctx * const internal = (void *) &ctx->internal;

    /* No need to continue if terminate was set */
    if (internal->terminate) {
        return ctx->terminate_val;
    }

    if (ctx->current->run) {
        ctx->current->run(ctx);
    }

    if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
        if (smf_execute_ancestor_run_actions(ctx)) {
            return ctx->terminate_val;
        }
    }

    return 0;
}

如果通过kconfig定义了 CONFIG_SMF_ANCESTOR_SUPPORT 宏 会进入smf_execute_ancestor_run_actions函数

如果 new_state 是false 即未发生状态转移 会执行父状态的 run函数

可以看到这里用到了for循环遍历父状态 也就是说 父状态是支持嵌套的

/**
 * @brief Execute all ancestor run actions
 *
 * @param ctx State machine context
 * @param target The run actions of this target's ancestors are executed
 * @return true if the state machine should terminate, else false
 */
__unused static bool smf_execute_ancestor_run_actions(struct smf_ctx *ctx)
{
    struct internal_ctx * const internal = (void *) &ctx->internal;
    /* Execute all run actions in reverse order */

    /* Return if the current state switched states */
    if (internal->new_state) {
        internal->new_state = false;
        return false;
    }

    /* Return if the current state terminated */
    if (internal->terminate) {
        return true;
    }

    /* Try to run parent run actions */
    for (const struct smf_state *tmp_state = ctx->current->parent;
         tmp_state != NULL;
         tmp_state = tmp_state->parent) {
        /* Execute parent run action */
        if (tmp_state->run) {
            tmp_state->run(ctx);
            /* No need to continue if terminate was set */
            if (internal->terminate) {
                return true;
            }

            if (internal->new_state) {
                break;
            }
        }
    }

    internal->new_state = false;
    /* All done executing the run actions */

    return false;
}

在 状态转移 smf_set_state 函数中 首先执行当前状态的退出callback 然后将下一个状态指定位当前状态 然后执行当前状态的进入callback

可以看到 使用了 exit 标志位来防止在退出callback又执行状态转移函数 从而导致状态转移进入循环嵌套的情况

void smf_set_state(struct smf_ctx *const ctx, const struct smf_state *target)
{
    struct internal_ctx * const internal = (void *) &ctx->internal;

    /*
     * It does not make sense to call set_state in an exit phase of a state
     * since we are already in a transition; we would always ignore the
     * intended state to transition into.
     */
    if (internal->exit) {
        LOG_WRN("Calling %s from exit action", __func__);
        return;
    }

    internal->exit = true;

    /* Execute the current states exit action */
    if (ctx->current->exit) {
        ctx->current->exit(ctx);

        /*
         * No need to continue if terminate was set in the
         * exit action
         */
        if (internal->terminate) {
            return;
        }
    }

    if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
        internal->new_state = true;

        if (smf_execute_ancestor_exit_actions(ctx, target)) {
            return;
        }
    }

    internal->exit = false;

    /* update the state variables */
    ctx->previous = ctx->current;
    ctx->current = target;

    if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) {
        if (smf_execute_ancestor_entry_actions(ctx, target)) {
            return;
        }
    }

    /* Now execute the target entry action */
    if (ctx->current->entry) {
        ctx->current->entry(ctx);
        /*
         * If terminate was set, it will be handled in the
         * smf_run_state function
         */
    }
}

如果通过kconfig定义了 CONFIG_SMF_ANCESTOR_SUPPORT

子状态退出后 如有父状态 会执行 smf_execute_ancestor_exit_actions 遍历并执行父状态的退出callback

/**
 * @brief Execute all ancestor exit actions
 *
 * @param ctx State machine context
 * @param target The exit actions of this target's ancestors are executed
 * @return true if the state machine should terminate, else false
 */
__unused static bool smf_execute_ancestor_exit_actions(
        struct smf_ctx *const ctx, const struct smf_state *target)
{
    struct internal_ctx * const internal = (void *) &ctx->internal;

    /* Execute all parent exit actions in reverse order */

    for (const struct smf_state *tmp_state = ctx->current->parent;
         tmp_state != NULL;
         tmp_state = tmp_state->parent) {
        if (!share_paren(target->parent, tmp_state) && tmp_state->exit) {
            tmp_state->exit(ctx);

            /* No need to continue if terminate was set */
            if (internal->terminate) {
                return true;
            }
        }
    }
    return false;
}

在进入子状态的entry callback 前 会执行smf_execute_ancestor_entry_actions 遍历并执行父状态的entry callback

/**
 * @brief Execute all ancestor entry actions
 *
 * @param ctx State machine context
 * @param target The entry actions of this target's ancestors are executed
 * @return true if the state machine should terminate, else false
 */
__unused static bool smf_execute_ancestor_entry_actions(
        struct smf_ctx *const ctx, const struct smf_state *target)
{
    struct internal_ctx * const internal = (void *) &ctx->internal;

    for (const struct smf_state *to_execute = get_last_of(target);
         to_execute != NULL && to_execute != target;
         to_execute = get_child_of(target, to_execute)) {
        /* Execute parent state's entry */
        if (!last_state_share_paren(ctx, to_execute) && to_execute->entry) {
            to_execute->entry(ctx);

            /* No need to continue if terminate was set */
            if (internal->terminate) {
                return true;
            }
        }
    }

    return false;
}

ct smf_ctx *const ctx, const struct smf_state *target)
{
struct internal_ctx * const internal = (void *) &ctx->internal;

for (const struct smf_state *to_execute = get_last_of(target);
     to_execute != NULL && to_execute != target;
     to_execute = get_child_of(target, to_execute)) {
    /* Execute parent state's entry */
    if (!last_state_share_paren(ctx, to_execute) && to_execute->entry) {
        to_execute->entry(ctx);

        /* No need to continue if terminate was set */
        if (internal->terminate) {
            return true;
        }
    }
}

return false;

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值