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 是初始状态。
-
父状态进入在子状态进入前执行 比如先执行parent_entry 后执行s0_entry
-
子状态间的状态转移不会触发父状态的进入和退出callback
-
父状态退出在子状态退出后执行
-
**只有子状态执行 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;
}