嵌入式状态机的几种骚操作_嵌入式状态机的妙用,2024年最新如何在Golang-Studio下进行NDK开发

本文讨论了两种状态机实现方式,包括二维和一维状态转换表,对比了它们的优缺点,强调了事件驱动编程和状态的进入/退出操作。同时还提到了Golang学习资源,尤其是对于初学者和进阶者的内容组织。
摘要由CSDN通过智能技术生成

break;
}
} break;
case TIMING:
{
switch(evt)
{
case UP_EVT: // “+” 按键按下事件
bomb1.code = (bomb1.code <<1) |0x01;
break;
case DOWN_EVT: // “-” 按键按下事件
bomb1.code = (bomb1.code <<1);
break;
case ARM_EVT: // “确认” 按键按下事件
if(bomb1.code == bomb1.defuse_code){
bomb1.state = SETTING;
}
else{
bsp_display(“bomb!”)
}
break;
case TICK_EVT:
if(bomb1.timeout)
{
–bomb1.timeout;
bsp_display(bomb1.timeout);
}
if(bomb1.timeout == 0)
{
bsp_display(“bomb!”)
}
break;
}
}break;
}
}

图片

优点:简单,代码阅读连贯,容易理解

缺点:

  1. 当状态或事件增多时,代码状态函数需要经常改动,状态事件处理函数会代码量会不断增加
  2. 状态机没有进行封装,移植性差。
  3. 没有实现状态的进入和退出的操作。进入和退出在状态机中尤为重要。进入事件:只会在刚进入时触发一次,主要作用是对状态进行必要的初始化。退出事件:只会在状态切换时触发一次 ,主要的作用是清除状态产生的中间参数,为下次进入提供干净环境

4. 状态表

二维状态转换表

状态机可以分为状态和事件 ,状态的跃迁都是受事件驱动的,因此可以通过一个二维表格来表示状态的跃迁。

图片

仅当(code == defuse_code) 时才发生到setting 的转换。

/1.列出所有的状态/
enum
{
SETTING,
TIMING,
MAX_STATE
};
/2.列出所有的事件/
enum
{
UP_EVT,
DOWN_EVT,
ARM_EVT,
TICK_EVT,
MAX_EVT
};

/3.定义状态表/
typedef void (fp_state)(EVT_TYPE evt , void param);
static const fp_state bomb2_table[MAX_STATE][MAX_EVENT] =
{
{setting_UP , setting_DOWN , setting_ARM , null},
{setting_UP , setting_DOWN , setting_ARM , timing_TICK}
};

struct bomb_t
{
const fp_state const state_table; / the State-Table /
uint8_t state; /
the current active state */

uint8_t timeout;
uint8_t code;
uint8_t defuse_code;
};
struct bomb bomb2=
{
.state_table = bomb2_table;
}
void bomb2_init(void)
{
bomb2.defuse_code = 6; // 0110
bomb2.state = SETTING;
}

void bomb2_dispatch(EVT_TYPE evt , void* param)
{
fp_state s = NULL;
if(evt > MAX_EVT)
{
LOG(“EVT type error!”);
return;
}
s = bomb2.state_table[bomb2.state * MAX_EVT + evt];
if(s != NULL)
{
s(evt , param);
}
}
/列出所有的状态对应的事件处理函数/
void setting_UP(EVT_TYPE evt, void* param)
{
if(bomb1.timeout< 60) ++bomb1.timeout;
bsp_display(bomb1.timeout);
}

缺点:函数粒度太小是最明显的一个缺点,一个状态和一个事件就会产生一个函数,当状态和事件较多时,处理函数将增加很快,在阅读代码时,逻辑分散。没有实现进入退出动作。

一维状态转换表

图片

实现原理:图片

typedef void (fp_action)(EVT_TYPE evt,void param);

/转换表基础结构/
struct tran_evt_t
{
EVT_TYPE evt;
uint8_t next_state;
};
/状态的描述/
struct fsm_state_t
{
fp_action enter_action; //进入动作
fp_action exit_action; //退出动作
fp_action action;

tran_evt_t* tran; //转换表
uint8_t tran_nb; //转换表的大小
const char* name;
}
/状态表本体/
#define ARRAY(x) x,sizeof(x)/sizeof(x[0])
const struct fsm_state_t state_table[]=
{
{setting_enter , setting_exit , setting_action , ARRAY(set_tran_evt),“setting” },
{timing_enter , timing_exit , timing_action , ARRAY(time_tran_evt),“timing” }
};

/构建一个状态机/
struct fsm
{
const struct state_t * state_table; /* the State-Table /
uint8_t cur_state; /
the current active state */

uint8_t timeout;
uint8_t code;
uint8_t defuse_code;
}bomb3;

/初始化状态机/
void bomb3_init(void)
{
bomb3.state_table = state_table; //指向状态表
bomb3.cur_state = setting;
bomb3.defuse_code = 8; //1000
}
/状态机事件派发/
void fsm_dispatch(EVT_TYPE evt , void* param)
{
tran_evt_t* p_tran = NULL;

/获取当前状态的转换表/
p_tran = bomb3.state_table[bomb3.cur_state]->tran;

/判断所有可能的转换是否与当前触发的事件匹配/
for(uint8_t i=0;i<x;i++)
{
if(p_tran[i]->evt == evt)//事件会触发转换
{
if(NULL != bomb3.state_table[bomb3.cur_state].exit_action){
bomb3.state_table[bomb3.cur_state].exit_action(NULL); //执行退出动作
}
if(bomb3.state_table[_tran[i]->next_state].enter_action){
bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//执行进入动作
}
/更新当前状态/
bomb3.cur_state = p_tran[i]->next_state;
}
else
{
bomb3.state_table[bomb3.cur_state].action(evt,param);
}
}
}
/*************************************************************************
setting状态相关
***********************************************************************/
void setting_enter(EVT_TYPE evt , void
param)
{

}
void setting_exit(EVT_TYPE evt , void* param)
{

}
void setting_action(EVT_TYPE evt , void* param)
{

}
tran_evt_t set_tran_evt[]=
{
{ARM , timing},
}
/timing 状态相关/

优点:

  • 各个状态面向用户相对独立,增加事件和状态不需要去修改先前已存在的状态事件函数。
  • 实现了状态的进入和退出
  • 容易根据状态跃迁图来设计 (状态跃迁图列出了每个状态的跃迁可能,也就是这里的转换表)
  • 实现灵活,可实现复杂逻辑,如上一次状态,增加监护条件来减少事件的数量。可实现非完全事件驱动

缺点:

  • 函数粒度较小(比二维小且增长慢),可以看到,每一个状态需要至少3个函数,还需要列出所有的转换关系。

5. QP嵌入式实时框架

事件驱动型编程

好莱坞原则:和传统的顺序式编程方法例如“超级循环”,或传统的RTOS 的任务不同。绝大多数的现代事件驱动型系统根据好莱坞原则被构造(Don’t call me; I’ll call you.)

面向对象

类和单一继承:

工具

QM :一个通过UML类图来描述状态机的软件,并且可以自动生成C代码

QS软件追踪工具:

6. QEP实现有限状态机Fsm

/* qevent.h ----------------------------------------------------------------/
typedef struct QEventTag
{
QSignal sig;
uint8_t dynamic_;
} QEvent;
/
qep.h -------------------------------------------------------------------/
typedef uint8_t QState; /
status returned from a state-handler function */
typedef QState (*QStateHandler) (void *me, QEvent const e); / argument list /
typedef struct QFsmTag /
Finite State Machine /
{
QStateHandler state; /
current active state */
}QFsm;

#define QFsm_ctor(me_, initial_) ((me_)->state = (initial_))
void QFsm_init (QFsm *me, QEvent const *e);
void QFsm_dispatch(QFsm *me, QEvent const *e);

#define Q_RET_HANDLED ((QState)0)
#define Q_RET_IGNORED ((QState)1)
#define Q_RET_TRAN ((QState)2)
#define Q_HANDLED() (Q_RET_HANDLED)
#define Q_IGNORED() (Q_RET_IGNORED)

#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler) (target_),Q_RET_TRAN)

enum QReservedSignals
{
Q_ENTRY_SIG = 1,
Q_EXIT_SIG,
Q_INIT_SIG,
Q_USER_SIG
};

/* file qfsm_ini.c ---------------------------------------------------------/
#include “qep_port.h” /
the port of the QEP event processor /
#include “qassert.h” /
embedded systems-friendly assertions */
void QFsm_init(QFsm *me, QEvent const *e)
{
(me->state)(me, e); / execute the top-most initial transition /
/
enter the target /
(void)(me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]);
}
/
file qfsm_dis.c ---------------------------------------------------------
/
void QFsm_dispatch(QFsm *me, QEvent const e)
{
QStateHandler s = me->state; /
save the current state */
QState r = (s)(me, e); / call the event handler /
if (r == Q_RET_TRAN) /
transition taken? */
{
(void)(s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); / exit the source */
(void)(me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/enter target/
}
}
实现上面定时器例子
#include “qep_port.h” /
the port of the QEP event processor /
#include “bsp.h” /
board support package */

enum BombSignals /* all signals for the Bomb FSM /
{
UP_SIG = Q_USER_SIG,
DOWN_SIG,
ARM_SIG,
TICK_SIG
};
typedef struct TickEvtTag
{
QEvent super; /
derive from the QEvent structure /
uint8_t fine_time; /
the fine 1/10 s counter */
}TickEvt;

typedef struct Bomb4Tag
{
QFsm super; /* derive from QFsm /
uint8_t timeout; /
number of seconds till explosion /
uint8_t code; /
currently entered code to disarm the bomb /
uint8_t defuse; /
secret defuse code to disarm the bomb */
} Bomb4;

void Bomb4_ctor (Bomb4 *me, uint8_t defuse);
QState Bomb4_initial(Bomb4 *me, QEvent const e);
QState Bomb4_setting(Bomb4 me, QEvent const e);
QState Bomb4_timing (Bomb4 me, QEvent const e);
/
--------------------------------------------------------------------------
/
/
the initial value of the timeout /
#define INIT_TIMEOUT 10
/
/
void Bomb4_ctor(Bomb4 me, uint8_t defuse) {
QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial);
me->defuse = defuse; /
the defuse code is assigned at instantiation /
}
/
/
QState Bomb4_initial(Bomb4 me, QEvent const e) {
(void)e;
me->timeout = INIT_TIMEOUT;
return Q_TRAN(&Bomb4_setting);
}
/
/
QState Bomb4_setting(Bomb4 me, QEvent const e) {
switch (e->sig){
case UP_SIG:{
if (me->timeout < 60) {
++me->timeout;
BSP_display(me->timeout);
}
return Q_HANDLED();
}
case DOWN_SIG: {
if (me->timeout > 1) {
–me->timeout;
BSP_display(me->timeout);
}
return Q_HANDLED();
}
case ARM_SIG: {
return Q_TRAN(&Bomb4_timing); /
transition to “timing” /
}
}
return Q_IGNORED();
}
/
/
void Bomb4_timing(Bomb4 *me, QEvent const e) {
switch (e->sig) {
case Q_ENTRY_SIG: {
me->code = 0; /
clear the defuse code */
return Q_HANDLED();
}
case UP_SIG: {
me->code <<= 1;
me->code |= 1;
return Q_HANDLED();
}
case DOWN_SIG: {
me->code <<= 1;
return Q_HANDLED();
}
case ARM_SIG: {
if (me->code == me->defuse) {
return Q_TRAN(&Bomb4_setting);
}
return Q_HANDLED();
}
case TICK_SIG: {
if (((TickEvt const )e)->fine_time == 0) {
–me->timeout;
BSP_display(me->timeout);
if (me->timeout == 0) {
BSP_boom(); /
destroy the bomb */
}
}
return Q_HANDLED();
}
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

存中…(img-E0VZTvUO-1713028473764)]
[外链图片转存中…(img-bSwr1k1c-1713028473765)]
[外链图片转存中…(img-rKhFNrBv-1713028473766)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-uB99ZI5b-1713028473766)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值