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;
}
}
优点:简单,代码阅读连贯,容易理解
缺点:
- 当状态或事件增多时,代码状态函数需要经常改动,状态事件处理函数会代码量会不断增加
- 状态机没有进行封装,移植性差。
- 没有实现状态的进入和退出的操作。进入和退出在状态机中尤为重要。进入事件:只会在刚进入时触发一次,主要作用是对状态进行必要的初始化。退出事件:只会在状态切换时触发一次 ,主要的作用是清除状态产生的中间参数,为下次进入提供干净环境
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语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
存中…(img-E0VZTvUO-1713028473764)]
[外链图片转存中…(img-bSwr1k1c-1713028473765)]
[外链图片转存中…(img-rKhFNrBv-1713028473766)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-uB99ZI5b-1713028473766)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!