Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >状态机编程实例-状态表法

状态机编程实例-状态表法

作者头像
xxpcb
发布于 2023-08-22 01:32:31
发布于 2023-08-22 01:32:31
43900
代码可运行
举报
运行总次数:0
代码可运行

本篇,继续介绍状态机编程的第二种方法:状态表法,来实现炸弹拆除小游戏的状态机编程。

1 状态表法

状态表法,顾名思义,就是通过一个状态表,来实现状态机中的状态转换,下面就先介绍下状态表的基础知识。

1.1 状态表

状态表 ,最常用的是使用一个2维状态表:

  • 水平方向是各个事件
  • 竖直方向是各个状态
  • 单元的内容是通过(执行动作,下一状态)来表示各种转换关系

结合上一篇设计炸弹拆除小游戏的状态图(2个状态和4个事件):

可以设计出对应的状态表,如下图:

  • 水平方向的4种事件:UP、DOWN和ARM按键事件,TICK事件
  • 竖直方向的2种状态:设置状态和倒计时状态
  • 单元的内容表示执行指定动作后,下一状态是什么。比如设置状态时按下UP键,执行setting_UP函数中的动作后,下一状态还是留在设置状态

注意:

  • (*):仅当(me->code == me->defuse),即密码输入正确时,才进行状态转换至“设置状态”
  • (**):仅当(me->fine_time == 0)和(me->timeout != 0),即每过一秒且倒计时未减到0时,才进行状态转换至“倒计时状态”

1.2 事件处理器

由于状态表法可以使用一个非常有规律的数据结构(状态表)来表现一个状态机,因此编程时可以编写一个通用的“事件处理器”来实现状态机功能。

如下图,通用的状态表事件处理器,包含两个主要结构:

  • 一个外部转换的StateTable结构
  • 一个带有事件参数和没有事件参数的Event结构

此外,StateTable结构有两个相关的函数:

  • init()函数用于触发状态机的初始转换
  • dispatch()函数用于派送一个事件给状态机处理

需体会的是,StateTable结构是一个抽象的结构,按照UML类图的画法,这是一个抽象类(使用《abstract》或斜体类名表示),需要通过派生出一个实例类,如图中的Bomb2,来实现具体的业务功能。

在状态机的应用程序中,状态表仅包含执行转换函数的指针,即函数指针,而不是(执行动作,下一状态)的形式,使用这种方式,实际就是把状态改变的逻辑,放到了转换函数中,这样做,使得编程更加灵活,因为状态函数能方便地判断某些监护条件并随之改变。

2 状态表法的实现

上面介绍了状态表法的基础知识,下面就来通过代码来介绍状态表法的具体实现。

2.1 通用状态表事件处理器

上面说到,状态表法可以使用一个非常有规律的状态表数据结构来表现一个状态机,因而在程序设计时,可以编写一个通用的状态表事件处理器。

2.1.1 接口定义

通用的状态表事件处理器,先来通过接口定义,看下它的功能。

注意上面提到的它包含两个主要结构:

  • 一个外部转换的StateTable结构
  • 一个带有事件参数和没有事件参数的Event结构

以及StateTable结构的两个相关的函数:

  • init()函数:用于触发状态机的初始转换
  • dispatch()函数:用于派送一个事件给状态机处理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 用于进行状态转换的宏
#define TRAN(target) (((StateTable *)me)->state = (uint8_t)(target))

typedef struct EventTag
{
  uint16_t sig; // 事件的信号
} Event;

struct StateTableTag; //提前声明此变量

// 函数指针
typedef void (*Tran)(struct StateTableTag *me, Event const *e);

// 状态表数据结构
typedef struct StateTableTag
{
  uint8_t state;           //当前状态
  Tran const *state_table; //状态表
  uint8_t n_states;        //状态的个数
  uint8_t n_signals;       //事件(信号)的个数
  Tran initial;            //初始转换
} StateTable;

void StateTable_ctor(StateTable *me, Tran const *table, uint8_t n_states, uint8_t n_signals, Tran initial);
void StateTable_init(StateTable *me);
void StateTable_dispatch(StateTable *me, Event const *e);
void StateTable_empty(StateTable *me, Event const *e);

StateTable_ctor是状态表的“构造函数”,仅指向一个基本的初始化动作,不会触发初始转换。

StateTable_empty是一个默认的空动作,用于状态表初始化时,某些需要空单元的地方使用。

另外,这里还要体会函数指针的用法。什么是函数指针,下面再来复习一下。

2.1.2 体会函数指针的用法

函数指针,本质是一个指针,其指向的一个函数,其类型定义为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
返回值类型 (* 函数名) ([形参列表]);

注意和指针函数的区别:

何为指针函数? 指针函数,本质是一个函数,例如 int *pfun(int, int); 其返回值是指针类型,即返回一个指针(或称地址),这个指针指向的数据是什么类型都可以。 一个记忆小技巧:指针函数,可以类比int函数,它们都是函数,只是返回值不一样,一个是返回指针,一个返回int。

首先来看函数指针的定义,以及基础用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//定义一个函数指针pFUN,它指向一个返回类型为void,有一个参数类型为int的函数
void (*pFun)(int);

//定义一个返回类型为void,参数为int的函数。从指针层面上理解该函数,其函数名实际上是一个指针,该指针指向函数在内存中的首地址
void glFun(int a)
{
    printf("%d\n", a);
}

int main()
{
    pFun = glFun; //将函数glFun的地址赋值给变量pFun
    (*pFun)(2);//“*pFun”是取pFun所指向地址的内容,即取出了函数glFun()的内容,然后给定参数为2
    
    return 0;
}

实际使用时,常常通过typedef的方式让函数指针更直观方便的进行使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//定义新的类型PTRFUN, 此类型的实际含义为函数指针,指向的函数的返回值是void,参数是int
typedef void (*PTRFUN)(int); 

//定义一个返回类型为void,参数为int的函数
void glFun(int a)
{ 
    printf("%d\n", a);
} 

int main() 
{ 
    PTRFUN pFun; //使用定义的(函数指针)类型,实例化一个函数指针
    
    pFun = glFun; //把定义的glFun函数,以函数名(本质即指针)的形式为其赋值
    (*pFun)(2); //执行该函数指针指向的内容,即指向指向的函数,并指定参数2
    
    return 0;
}

关于函数指针的实际应用,也可参考我之前的这篇文章:STM32简易多级菜单(数组查表法)

2.1.3 具体实现

看完了通用的状态表事件处理器的接口定义,下面再来看下具体实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//状态表的构造
void StateTable_ctor(StateTable *me,
                     Tran const *table, uint8_t n_states, uint8_t n_signals,
                     Tran initial)
{
    //第一个参数me为StateTable结构,由具体业务的派生状态表的tateTable结构传入
    me->state_table = table;   //状态表, 由具体业务的二维状态表传入
    me->n_states = n_states;   //二维状态表的状态数量
    me->n_signals = n_signals; //二维状态表的信号(事件)数量
    me->initial = initial;     //状态表的初始准换函数
}

//状态表的初始化
void StateTable_init(StateTable *me)
{
    me->state = me->n_states;
    (*me->initial)(me, (Event *)0); //初始转换

    assert(me->state < me->n_states); //确保事件范围的合理
}

//状态表的调度(派送一个事件给状态机处理)
void StateTable_dispatch(StateTable *me, Event const *e)
{
    Tran t;

    assert(e->sig < me->n_signals); //确保信号范围的合理

    //通过当前状态与当前的信号,以及信号的总数,计算得到状态表中要执行的转换函数在状态表(二维的函数指针数组)中的位置
    t = me->state_table[me->state * me->n_signals + e->sig];
    (*t)(me, e); //然后执行转换函数

    assert(me->state < me->n_states); //确保状态范围的合理
}

//状态表的空元素
void StateTable_empty(StateTable *me, Event const *e)
{
    (void)me; //用于消除参数未使用的警告
    (void)e;  
}

这里要体会一下状态表的调度,即派送一个事件给状态机处理的代码逻辑,StateTable_dispatch的两个参数,一个是StateTable结构的二维表,一个是Event结构的信号(事件),注意这个二维状态表,存储的函数指针(各种转换函数),所以是一个二维的函数指针数组,根据信号,如何知道要执行二维数组中的哪个函数呢?还要借助当前状态机所处的状态,即可通过简单的数学运算得出,示意如下图:

2.2 应用逻辑(具体业务代码)

看完了通用的状态表事件处理器,就可以在此基础上,编写具体的状态机业务代码,实现上一篇介绍的炸弹拆除小游戏。

2.2.1 接口定义

还是先看下炸弹拆除小游戏这个具体业务逻辑用到的数据结构与接口定义,主要包括:

  • 炸弹状态机的状态与信号(事件)
  • 从状态表事件处理器的Event结构派生的带有事件参数的TickEvt结构
  • 从状态表事件处理器的StateTable结构派生的具体的炸弹状态机数据结构
  • 状态表中用到的所有的转换函数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 炸弹状态机的所有状态
enum BombStates
{
  SETTING_STATE, // 设置状态
  TIMING_STATE,   // 倒计时状态
  STATE_MAX
};

// 炸弹状态机的所有信号(事件)
enum BombSignals
{
  UP_SIG,   // UP键信号
  DOWN_SIG, // DOWN键信号
  ARM_SIG,  // ARM键信号
  TICK_SIG,  // Tick节拍信号
  SIG_MAX
};

typedef struct TickEvtTag
{
  Event super;       // 派生自Event结构
  uint8_t fine_time; // 精细的1/10秒计数器
} TickEvt;

// 炸弹状态机数据结构
typedef struct Bomb2Tag
{
  StateTable super; // 派生自StateTable结构
  uint8_t timeout; // 爆炸前的秒数
  uint8_t code;    // 当前输入的解除炸弹的密码
  uint8_t defuse;  // 解除炸弹的拆除密码
  uint8_t errcnt;  // 当前拆除失败的次数
} Bomb2;

//炸弹构造
void Bomb2_ctor(Bomb2 *me, uint8_t defuse);
//状态表中需要用到的转换函数(函数指针)
void Bomb2_initial(Bomb2 *me, Event const *e);      //初始转换
void Bomb2_setting_UP(Bomb2 *me, Event const *e);   //转换函数, 设置状态时, 处理UP事件
void Bomb2_setting_DOWN(Bomb2 *me, Event const *e); //转换函数, 设置状态时, 处理DOWN事件
void Bomb2_setting_ARM(Bomb2 *me, Event const *e);  //转换函数, 设置状态时, 处理ARM事件
void Bomb2_timing_UP(Bomb2 *me, Event const *e);    //转换函数, 倒计时状态时, 处理UP事件
void Bomb2_timing_DOWN(Bomb2 *me, Event const *e);  //转换函数, 倒计时状态时, 处理DOWN事件
void Bomb2_timing_ARM(Bomb2 *me, Event const *e);   //转换函数, 倒计时状态时, 处理ARM事件
void Bomb2_timing_TICK(Bomb2 *me, Event const *e);  //转换函数, 倒计时状态时, 处理Tick事件

2.2.2 具体实现

1)炸弹构造与初始化

炸弹构造与初始化的实现如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//炸弹构造
void Bomb2_ctor(Bomb2 *me, uint8_t defuse)
{
  //定义炸弹状态机的状态表(函数指针构成的二维数组)
  static const Tran bomb2_state_table[STATE_MAX][SIG_MAX] = {
    {(Tran)&Bomb2_setting_UP, (Tran)&Bomb2_setting_DOWN, (Tran)&Bomb2_setting_ARM, &StateTable_empty},
    {(Tran)&Bomb2_timing_UP, (Tran)&Bomb2_timing_DOWN, (Tran)&Bomb2_timing_ARM, (Tran)&Bomb2_timing_TICK}
  };

  //使用通用的通用状态表事件处理器构造状态表
  StateTable_ctor(&me->super, &bomb2_state_table[0][0], STATE_MAX, SIG_MAX, (Tran)&Bomb2_initial); 
  me->defuse = defuse; //设置默认的拆除密码
}

//炸弹初始化
void Bomb2_initial(Bomb2 *me, Event const *e)
{
  (void)e; //用于消除参数未使用的警告
  me->timeout = INIT_TIMEOUT;
  me->errcnt = 0;
  TRAN(SETTING_STATE); //默认进行设置状态
}
2)各个转换函数

各个转换函数(函数指针)的具体实现如下,其特征为短函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*.................设置状态下的事件处理函数......................*/
void Bomb2_setting_UP(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  if (me->timeout < 60)
  {
    ++me->timeout; //设置超时时间+1
    bsp_display_set_time(me->timeout); //显示设置的超时时间
  }
}

void Bomb2_setting_DOWN(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  if (me->timeout > 1)
  {
    --me->timeout; //设置超时时间-1
    bsp_display_set_time(me->timeout); //显示设置的超时时间
  }
}

void Bomb2_setting_ARM(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  me->code = 0;
  TRAN(TIMING_STATE); /* transition to "timing" */
}

/*...................倒计时状态下的事件处理函数................*/
void Bomb2_timing_UP(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  me->code <<= 1;
  me->code |= 1; //添加一个1
  bsp_display_user_code(me->code);
}

void Bomb2_timing_DOWN(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  me->code <<= 1; //添加一个0
  bsp_display_user_code(me->code);
}

void Bomb2_timing_ARM(Bomb2 *me, Event const *e)
{
  (void)e; /* avoid compiler warning about unused parameter */
  if (me->code == me->defuse)
  {
    TRAN(SETTING_STATE); //转换到设置状态
    bsp_display_user_success(); //炸弹拆除成功
    Event *e = NULL;
    Bomb2_initial(me, e);
  }
  else
  {
    me->code = 0;
    bsp_display_user_code(me->code);
    bsp_display_user_err(++me->errcnt);
  }
}

void Bomb2_timing_TICK(Bomb2 *me, Event const *e)
{
  if (((TickEvt const *)e)->fine_time == 0)
  {
    --me->timeout;
    bsp_display_remain_time(me->timeout);
    if (me->timeout == 0)
    {
      bsp_display_bomb(); //显示爆炸效果
      Event *e = NULL;
      Bomb2_initial(me, e);
    }
  }
}

2.3 主函数

本篇同样使用Arduino控制器进行测试,对应的主函数代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static Bomb2 l_bomb;

void setup(void)
{
  Serial.begin(115200);
  bsp_display_init();
  bsp_display_hello();
  bsp_key_init();

  Bomb2_ctor(&l_bomb, 0x0D); // 构造, 密码1101
  StateTable_init((StateTable *)&l_bomb); // 初始转化
}

void loop(void)
{
  static TickEvt tick_evt = {TICK_SIG, 0};
  delay(100);

  if (++tick_evt.fine_time == 10)
  {
    tick_evt.fine_time = 0;
  }

  char tmp_buffer[256];
  sprintf(tmp_buffer, "T(%1d)%c", tick_evt.fine_time, (tick_evt.fine_time == 0) ? '\n' : ' ');
  Serial.print(tmp_buffer);

  StateTable_dispatch((StateTable *)&l_bomb, (Event *)&tick_evt);

  BombSignals userSignal = bsp_key_check_signal();
  if (userSignal != SIG_MAX)
  {
    static Event const up_evt = {UP_SIG};
    static Event const down_evt = {DOWN_SIG};
    static Event const arm_evt = {ARM_SIG};
    Event const *e = (Event *)0;

    switch (userSignal)
    {
      case UP_SIG: //UP键事件
      {
        Serial.print("\nUP  : ");
        e = &up_evt;
        break;
      }
      case DOWN_SIG: //DOWN键事件
      {
        Serial.print("\nDOWN: ");
        e = &down_evt;
        break;
      }
      case ARM_SIG: //ARM键事件
      {
        Serial.print("\nARM : ");
        e = &arm_evt;
        break;
      }
      default:break;
    }

    /* keyboard event available? */
    if (e != (Event *)0)
    {
       StateTable_dispatch((StateTable *)&l_bomb, e); /* dispatch the event */
    }
  }
}

3 总结

本编介绍了状态机编程的第2种方法——状态表法,通过一个非常有规律的二维表数据结构,以及函数指针,实现炸弹拆除小游戏中的状态机功能。

本篇代码实现的功能,与上篇的一样,可以再看下实际效果:

本篇,需要重点体会的点包括:

  • 通用的“事件处理器”的结构与功能
  • 函数指针的妙用,一般与数组查表法结合
  • 根据实际的状态转换与处理的业务需求,利用状态表法,实现状态机编程
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农爱学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
状态机编程实例-面向对象的状态设计模式
本篇,继续介绍状态机编程的第三种方法:面向对象的设计模式。此方法从名字上看,用到了面向对象的思想,所以本篇的代码,需要以C++为基础,利用C++中“类”的特性,实现状态机中状态的管理。
xxpcb
2023/08/22
3650
状态机编程实例-面向对象的状态设计模式
嵌入式中状态机的几种骚操作
状态机可以分为状态和事件 ,状态的跃迁都是受事件驱动的,因此可以通过一个二维表格来表示状态的跃迁。
混说Linux
2023/02/24
1.1K0
嵌入式中状态机的几种骚操作
状态机编程实例-嵌套switch-case法
嵌入式软件开发中,状态机编程是一个比较实用的代码实现方式,特别适用于事件驱动的系统。
xxpcb
2023/08/22
4820
状态机编程实例-嵌套switch-case法
从零开始的状态机漫谈(4)——多实例
在前面的讲解中,我们介绍了如何使用状态图的方式来设计有限状态机、明确了状态图设计的“清晰”原则,并结合最简单和常用的switch状态机翻译模式详细说明了状态图的“无脑翻译”方法。
GorgonMeducer 傻孩子
2021/10/12
1.5K0
从零开始的状态机漫谈(2)——switch:你的状态机初恋
在前面的一篇文章《从零开始的状态机漫谈(1)——万物之始的语言》中,我们介绍了状态机在整个计算机科学中宛如“世界基石”般的地位,同时介绍了一种“面向嵌入式环境”“高度简化”了的实用型状态图绘制方法——这里的“简化”是相对UML状态图的“繁杂”而言、且更接近课本上所使用的状态机图例;而这里的“实用”体现在:基于这套方法绘制的状态图是可以“无脑”而“严格”的翻译成C语言代码的。
GorgonMeducer 傻孩子
2021/03/10
2K1
表驱动+状态机法AD传感器驱动检测框架
今天这篇文章不作为气体探测器实战项目的最后一节,因为很多功能还在编写中,前两天在世伟兄的开源群里提到了传感器检测框架,群友反应说:杨工有空你要多搞点这种框架出来分享分享,感觉很有用啊!
杨源鑫
2020/11/02
9250
表驱动+状态机法AD传感器驱动检测框架
自己动手写编译器:自顶向下的自动状态机
本节我们介绍编译原理中一种新的数据结构叫自顶向下的自动状态机。前面我们在做词法解析时接触了大量自动状态机,他们存在一个缺陷那就是无法对要识别的字符串进行计数,因此当我们要判断括号对是否匹配时,使用在词法解析的状态机就处理不了,例如给定字符串”((())()))”,我们判断其中左右括号是否都能匹配上,以前的状态机就无法处理。
望月从良
2024/01/16
3170
自己动手写编译器:自顶向下的自动状态机
如何以面向对象的思想设计有限状态机
有限状态机又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型,用英文缩写也被简称为 FSM。
wenzid
2020/05/03
1.4K0
如何以面向对象的思想设计有限状态机
一个应用在单片机上的极简图形化状态机框架NorthFrame
配合NF_FsmDesigner图形化开发工具,可无负担替代传统switch-case状态机开发
杨源鑫
2021/07/30
1.1K0
一个应用在单片机上的极简图形化状态机框架NorthFrame
使用FreeRTOS要好好理解任务状态机
前面一文利用FreeRTOS点灯,算是将FreeRTOS给跑起来了,要用好RTOS,从黑盒角度去理解一下调度器是怎么工作的是很必要的,当然如果想研究其内部实现原理,可以去读其内部实现代码,但是个人感觉如果是从用的角度,把内核看成黑盒,跳出来梳理一下概念也很有用。
wenzid
2021/08/13
1.3K0
使用FreeRTOS要好好理解任务状态机
一个有限状态机的C++实现
先放一个用C++11实现的FSM的代码: kuafu 咱们先来看一下什么是有限状态机(Finite-state machine, FSM), 先给一个 百度百科的解释 简单说就是作一件事可能会经
扫帚的影子
2018/09/05
3.6K0
一个有限状态机的C++实现
精妙设计:支付系统状态机与核心代码实现
本篇主要讲清楚什么是状态机,简洁的状态机对支付系统的重要性,状态机设计常见误区,以及如何设计出简洁而精妙的状态机,核心的状态机代码实现等。
路人甲Java
2024/02/01
2.8K0
精妙设计:支付系统状态机与核心代码实现
项目终于用上了Spring状态机,非常优雅!
先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。
终码一生
2023/08/22
8300
项目终于用上了Spring状态机,非常优雅!
什么是状态机?用C语言实现进程5状态模型
状态机在实际工作开发中应用非常广泛,在刚进入公司的时候,根据公司产品做流程图的时候,发现自己经常会漏了这样或那样的状态,导致整体流程会有问题,后来知道了状态机这样的东西,发现用这幅图就可以很清晰的表达整个状态的流转。
刘盼
2020/12/11
2.7K0
什么是状态机?用C语言实现进程5状态模型
从零开始的状态机漫谈(3)——状态机设计原则:清晰!清晰!还是清晰!
我们常说状态机是一种思维方式、一种工具,同时它也是一种拥有极高自由度的语言。说到语言,类比我们日常使用的口语,你会发现:有的人表达能力很强——说话条理清晰、逻辑严密、详略得当——能充分表达自己意图的同时还很凝练;相对的,有人颠三倒四、缺乏逻辑性还罗里吧嗦一大堆——在需要认真交换观点(而不是闲聊)的场合,往往沟通双方都很憋屈——大有一副茶壶里煮饺子,有货倒不出的感觉。其实,作为一种翻译思维的语言工具,不同人在使用状态机时也有类似的表达能力的问题。
GorgonMeducer 傻孩子
2021/03/10
11.2K0
Thrift线程和状态机分析
启动Thrift时,可启动两类线程,一是TNonblockingIOThread,另一是Worker:
一见
2018/08/06
1.2K0
Thrift线程和状态机分析
c 语言函数指针之回调函数
我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。
C语言与CPP编程
2020/10/20
1.2K0
状态机的实现探讨
(译)状态机的实现探讨 原文链接地址:http://drdobbs.com/cpp/184401236?pgno=1          实现一个状态机很容易,但是实现一个好的状态机却不简单。一般实现状
知然
2018/03/09
2K0
状态机的实现探讨
【安富莱】【RL-TCPnet网络教程】第8章 RL-TCPnet网络协议栈移植(RTX)
本章教程为大家讲解RL-TCPnet网络协议栈的RTX操作系统移植方式,学习了第6章讲解的底层驱动接口函数之后,移植就比较容易了,主要是添加库文件、配置文件和驱动文件即可。
Simon223
2018/09/04
1.4K0
【安富莱】【RL-TCPnet网络教程】第8章  RL-TCPnet网络协议栈移植(RTX)
【安富莱】【RL-TCPnet网络教程】第10章 RL-TCPnet网络协议栈移植(FreeRTOS)
本章教程为大家讲解RL-TCPnet网络协议栈的FreeRTOS操作系统移植方式,学习了第6章讲解的底层驱动接口函数之后,移植就比较容易了,主要是添加库文件、配置文件和驱动文件即可。另外,RL-TCPnet移植到FreeRTOS要重新配置RL-TCPnet的接口函数,以此来支持RL-TCPnet多任务运行。使用RTX无需重新配置,因为默认情况下就是采用RTX的API函数配置的。
Simon223
2018/09/04
1.8K0
【安富莱】【RL-TCPnet网络教程】第10章 RL-TCPnet网络协议栈移植(FreeRTOS)
推荐阅读
相关推荐
状态机编程实例-面向对象的状态设计模式
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验