当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]速博官方地址机的实现无非就是3个要素:速博官方地址、事件、响应。转换成具体的行为就3句话。发生了什么事?现在系统处在什么速博官方地址?在这样的速博官方地址下发生了这样的事,系统要干什么?用C语言实现速博官方地址机主要有3种方法:switch—case法、亚星娱乐登录、亚星娱乐官网app下载中心。switch—case法速博官方地址用switch...

速博官方地址机的实现无非就是 3 个要素:速博官方地址、事件、响应。转换成具体的行为就 3 句话。

  • 发生了什么事?
  • 现在系统处在什么速博官方地址?
  • 在这样的速博官方地址下发生了这样的事,系统要干什么?
用 C 语言实现速博官方地址机主要有 3 种方法:亚星娱乐登录老虎机亚星娱乐登录亚星娱乐官网app下载中心

亚星娱乐登录老虎机

速博官方地址用 switch—case 组织起来, 将事件也用switch—case 组织起来, 然后让其中一个 switch—case 整体插入到另一个 switch—case 的每一个 case 项中  。

「程序清单 List4  :」

switch(StateVal)
{
case S0:
switch(EvntID)
  {
case E1:
    action_S0_E1();
    StateVal =new state value;
break;
case E2:
    action_S0_E2();
    StateVal =new state value;
break;
   ......
case Em:
    action_S0_Em();
    StateVal =new state value;
break;
default:
break;
  }
break;
case S1:
  ......
break;
    ......
case Sn:
  ......
break;
default:
break;
}
上面的伪代码示例只是通用的情况,实际应用远没有这么复杂。虽然一个系统中事件可能有很多种,但在实际应用中,许多事件可能对某个速博官方地址是没有意义的。

例如在程序清单 List4中,如果 E2、······ Em 对处在 S0 速博官方地址下的系统没有意义,那么在 S0 的 case 下有关事件E2、······ Em 的代码根本没有必要写,速博官方地址 S0 只需要考虑事件 E1 的处理就行了。

既然是两个 switch—case 之间的嵌套, 那么就有一个谁嵌套谁的问题, 所以说 switch—case法有两种写法:速博官方地址嵌套事件事件嵌套速博官方地址。这两种写法都可以, 各有利弊, 至于到底选用哪种方式就留给设计人员根据具体情况自行决断吧。

关于 亚星娱乐登录老虎机还有最后一点要说明, 因为 switch—case 的原理是从上到下挨个比较,越靠后,查找耗费的时间就越长,所以要注意速博官方地址和事件在各自的 switch 语句中的安排顺序,不推荐程序清单 List4 那样按顺序号排布的方式。出现频率高或者实时性要求高的速博官方地址和事件的位置应该尽量靠前

亚星娱乐登录

如果说** 亚星娱乐登录老虎机是线性的**,那么亚星娱乐登录则是平面的。亚星娱乐登录的实质就是将速博官方地址和事件之间的关系固化到一张二维表格里, 把事件当做纵轴,把速博官方地址当做横轴,交点[Sn , Em]则是系统在 Sn 速博官方地址下对事件 Em 的响应  。

如图 4, 我把表格中的 Node_SnEm 叫做速博官方地址机节点, 速博官方地址机节点 Node_SnEm 是系统在 Sn速博官方地址下对事件 Em 的响应。这里所说的响应包含两个方面:输出动作和速博官方地址迁移。速博官方地址机节点一般是一个类似程序清单 List5 中的结构体变量 。

structfsm_node
{

void (*fpAction)(void* pEvnt);
    INT8U u8NxtStat;
};
程序清单 List5 中的这个结构体有两个成员:fpAction 和u8NxtStatfpAction 是一个函数指针, 指向一个形式为void func(void * pEvnt)的函数,func 这个函数是对速博官方地址转移中动作序列的标准化封装。

也就是说, 速博官方地址机在速博官方地址迁移的时候, 不管输出多少个动作、操作多少个变量、调用多少个函数,这些行为统统放到函数func 中去做。

把动作封装好了之后,再把封装函数func 的地址交给函数指针fpAction,这样,想要输出动作,只需要调用函数指针fpAction 就行了。

再看看上面的func 函数,会发现函数有一个形参pEvnt,这是一个类型为void * 的指针, 在程序实际运行时指向一个能存储事件的变量,通过这个指针我们就能获知关于事件的全部信息,这个形参是很有必要的。

事件一般包括两个属性:事件的类型和事件的内容。

例如一次按键事件,我们不仅要知道这是一个按键事件,还要知道按下的到底是哪个键。

事件的类型和速博官方地址机当前的速博官方地址可以让我们在图 4 的表格中迅速定位,确定该调用哪个动作封装函数, 但是动作封装函数要正确响应事件还需要知道事件的内容是什么, 这也就是形参pEvnt 的意义。

由于事件的多样性,存储事件内容的数据格式不一定一样,所以就把 pEvnt 定义成了void * 型,以增加灵活性。

有关 fpAction 的最后一个问题:如果事件Em 对速博官方地址Sn 没有意义,那么速博官方地址机节点Node_SnEm 中的 fpAction 该怎么办?

我的答案是:那就让它指向一个空函数呗!前面不是说过么,什么也不干也叫响应。

u8NxtStat 存储的是速博官方地址机的一个速博官方地址值。我们知道, 速博官方地址机响应事件要输出动作, 也就是调用函数指针 fpAction 所指向的那个封装函数, 函数调用完毕后程序返回主调函数, 速博官方地址机对事件的响应就算结束了, 下一步就要考虑速博官方地址迁移的问题了。

可能要保持本速博官方地址不变, 也可能要迁移到一个新的速博官方地址,该如何抉择呢?u8NxtStat 存储的速博官方地址就是速博官方地址机想要的答案!

图 4 的这张表格反映在 C 语言代码里就是一个二维数组,第 1 维就是速博官方地址机的速博官方地址,第 2维就是统一分类的事件,而数组的元素则是程序清单 List5 中的结构体常量。

如果程序中使用亚星娱乐登录,还需要注意一些特别的事项。要将速博官方地址当做表格的横轴,那么就要求速博官方地址值集合必须满足以下条件

  • (1) 该集合是一个递增的等差整数数列
  • (2) 该数列初值为 0
  • (3) 该数列等差值为 1
“事件” 作为纵轴,其特点和要求与用来做横轴的“速博官方地址” 完全一致。在 C 语言提供的数据类型中, 没有比枚举更符合以上要求的可选项了, 极力推荐将速博官方地址集合和事件类型集合做成枚举常量。亚星娱乐登录的优点:调用接口统一 ,定位快速。

亚星娱乐登录屏蔽了不同速博官方地址下处理各个事件的差异性,因此可以将处理过程中的共性部分提炼出来,做成标准统一的框架式代码,形成统一的调用接口。

根据程序清单 List5 中的速博官方地址机节点结构体,做成的框架代码如程序清单 List6 所示。

亚星娱乐登录查找目标实际上就是一次二维数组的寻址操作,所以它的平均效率要远高于亚星娱乐登录老虎机。

「程序清单 List6  :」

extern structfsm_nodeg_arFsmDrvTbl[][];
INT8U u8CurStat =0;
INT8U u8EvntTyp =0;
void* pEvnt =NULL;
structfsm_nodestNodeTmp = { NULL,0};
u8CurStat = get_cur_state();
u8EvntTyp = get_cur_evnt_typ();
pEvnt = (void*)get_cur_evnt_ptr();
stNodeTmp = g_arFsmDrvTbl[u8CurStat ][u8EvntTyp ];
stNodeTmp.fpAction(pEvnt );
set_cur_state(stNodeTmp.u8NxtStat);
.....
亚星娱乐登录好则好矣,但用它写出来的程序还有点儿小问题,我们先来看看按照亚星娱乐登录写出来的程序有什么特点 。

前面说过,亚星娱乐登录可以把速博官方地址机调度的部分做成标准统一的框架代码,这个框架适用性极强, 不管用速博官方地址机来实现什么样的应用, 框架代码都不需要做改动, 我们只需要根据实际应用场合规划好速博官方地址转换图,然后将图中的各个要素(速博官方地址、事件、动作、迁移,有关“条件”要素一会儿再说)用代码实现就行了,我把这部分代码称作应用代码。

在应用代码的.c 文件中, 你会看到一个声明为 const 的二维数组, 也就是图 4 所示的速博官方地址驱动表格, 还会看到许多彼此之间毫无关联的函数, 也就是前面提到的动作封装函数。

这样的一份代码, 如果手头上没有一张速博官方地址转换图, 让谁看了也会一头雾水, 这样的格式直接带来了代码可读性差的问题。

如果我们想给速博官方地址机再添加一个速博官方地址,反映到代码上就是给驱动表格再加一列内容,同时也要新添加若干个动作封装函数。

如果驱动表格很大, 做这些工作是很费事儿的, 而且容易出错。如果不小心在数组中填错了位置, 那么程序跑起来就和设计者的意图南辕北辙了,

远没有在switch—case 法中改动来得方便、安全。Extended State Machine 的最大特点就是速博官方地址机响应事件之前先判断条件,根据判定结果选择执行哪些动作,转向哪个速博官方地址。

也就是说,系统在速博官方地址 Sn 下发生了事件 Em 后,转向的速博官方地址不一定是唯一的,这种灵活性是 Extended State Machine 的最有价值的优点。

回过头来看看程序清单 List5 中给出的速博官方地址机节点结构体,如果系统在速博官方地址 Sn 下发生了事件 Em, 速博官方地址机执行完 fpAction 所给出的动作响应之后, 必须转到 u8NxtStat 指定的速博官方地址。

亚星娱乐登录的这个特性直接杜绝了 Extended State Machine 在亚星娱乐登录中应用的可能性, 所以亚星娱乐登录的代码实现中不存在“条件” 这个速博官方地址机要素。ESM,你是如此的优秀,我怎么舍得抛弃你 ?!

再看图 4 所示的亚星娱乐登录示例图,如果我们把表格中的代表事件的纵轴去掉,只留下代表速博官方地址的横轴,将一列合并成一格,前文提到的问题是不是能得到解决呢?不错!这就是失传江湖多年的《葵花宝典》 ——阉割版亚星娱乐登录 !!

阉割版亚星娱乐登录,又名压缩亚星娱乐登录,一维速博官方地址表格与事件 switch—case 的合体。压缩亚星娱乐登录使用了一维数组作为驱动表格,数组的下标即是速博官方地址机的各个速博官方地址。

表格中的元素叫做压缩速博官方地址机节点, 节点的主要内容还是一个指向动作封装函数的函数指针, 只不过这个动作封装函数不是为某个特定事件准备的, 而是对所有的事件都有效的。

节点中不再强制指定速博官方地址机输出动作完毕后所转向的速博官方地址, 而是让动作封装函数返回一个速博官方地址, 并把这个速博官方地址作为速博官方地址机新的速博官方地址。

压缩亚星娱乐登录的这个特点,完美的解决了 Extended State Machine 不能在亚星娱乐登录中使用的问题 。

程序清单 List7 中的示例代码包含了压缩速博官方地址机节点结构体和速博官方地址机调用的框架代码。

「程序清单 List7:」

structfsm_node
{

 INT8U (*fpAction)(void* pEvnt);
 INT8U u8StatChk;
};
......
u8CurStat = get_cur_state();
......
if(stNodeTmp.u8StatChk == u8CurStat )
{
 u8CurStat = stNodeTmp.fpAction(pEvnt );
 set_cur_state(u8CurStat );
}
else
{
 state_crash(u8CurStat );
}
.....
对照程序清单 List5,就会发现程序清单 List7 中 struct fsm_node 结构体的改动之处。首先, fpAction 所指向函数的函数形式变了,动作封装函数 func 的模样成了这样的了:

INT8Ufunc (void * pEvnt) ;
现在的动作封装函数func 是要返回类型为INT8U 的返回值的,这个返回值就是速博官方地址机要转向的速博官方地址, 也就是说, 压缩亚星娱乐登录中的速博官方地址机节点不负责速博官方地址机新速博官方地址的确定, 而把这项任务交给了动作封装函数funcfunc 返回哪个速博官方地址, 速博官方地址机就转向哪个速博官方地址。

新速博官方地址由原来的常量变成了现在的变量,自然要灵活许多。上面说到现在的动作封装函数func 要对当前发生的所有的事件都要负责, 那么func 怎么会知道到底是哪个事件触发了它呢?看一下func 的形参void * pEvnt 。

在程序清单 List5 中我们提到过,这个形参是用来向动作封装函数传递事件内容的,但是从前文的叙述中我们知道, pEvnt 所指向的内存包含了事件的所有信息, 包括事件类型和事件内容 , 所以通过形参 pEvnt , 动作封装函数 func 照样可以知道事件的类型。

程序清单 List7 中struct fsm_node 结构体还有一个成员u8StatChk , 这里面存储的是速博官方地址机 的一个速博官方地址,干什么用的呢?

玩 C 语言数组的人都知道,要严防数组寻址越界。

要知道,压缩亚星娱乐登录的驱动表格是一个以速博官方地址值为下标的一维数组, 数组元素里面最重要的部分就是一个个动作封装函数的地址。

函数地址在单片机看来无非就是一段二进制数据, 和内存中其它的二进制数据没什么两样,不管程序往单片机 PC 寄存器里塞什么值,单片机都没意见。假设程序由于某种意外而改动了存储速博官方地址机当前速博官方地址的变量,使变量值变成了一个非法速博官方地址。

再发生事件时, 程序就会用这个非法的速博官方地址值在驱动表格中寻址, 这时候就会发生内存泄露,程序拿泄露内存中的未知数据当函数地址跳转,不跑飞才怪!

为了防止这种现象的发生, 压缩速博官方地址机节点结构体中又添加了成员u8StatChk 。u8StatChk中存储的是压缩速博官方地址机节点在一维驱动表格的位置, 例如某节点是表格中的第 7 个元素, 那么这个节点的成员u8StatChk 值就是 6。

看一下程序清单 List7 中的框架代码示例, 程序在引用函数指针fpAction 之前, 先检查当前速博官方地址和当前节点成员u8CurStat 的值是否一致,一致则认为速博官方地址合法,事件正常响应,如果不一致,则认为当前速博官方地址非法,转至意外处理,最大限度保证程序运行的安全。

当然,如果泄露内存中的数据恰好和u8CurStat 一致,那么这种方法真的就回天乏力了。

还有一个方法也可以防止速博官方地址机跑飞,如果速博官方地址变量是枚举,那么框架代码就可以获知速博官方地址值的最大值, 在调用动作封装函数之前判断一下当前速博官方地址值是否在合法的范围之内, 同样能保证速博官方地址机的安全运行。

压缩亚星娱乐登录中动作封装函数的定义形式我们已经知道了,函数里面到底是什么样子的呢?程序清单 List8 是一个标准的示例。

「程序清单List8:」


INT8Uaction_S0 (void* pEvnt)
{
 INT8U u8NxtStat =0;
 INT8U u8EvntTyp = get_evnt_typ(pEvnt);
switch(u8EvntTyp )
 {
case E1:
   action_S0_E1();
   u8NxtStat =new state value;
break;
   ......
case Em:
   action_S0_Em();
   u8NxtStat =new state value;
break;
default:
   ;
break;
 }
return u8NxtStat ;
}
从程序清单 List8 可以看出, 动作封装函数其实就是事件switch—case 的具体实现。函数根据形参pEvnt 获知事件类型, 并根据事件类型选择动作响应, 确定速博官方地址机迁移速博官方地址, 最后将新的速博官方地址作为执行结果返回给框架代码。

有了这样的动作封装函数,Extended State Machine 的应用就可以完全不受限制了!到此,有关压缩亚星娱乐登录的介绍就结束了。

个人认为压缩亚星娱乐登录是相当优秀的,它既有亚星娱乐登录的简洁、高效、标准,又有 亚星娱乐登录老虎机的直白、灵活、多变,相互取长补短,相得益彰。

亚星娱乐官网app下载中心

上面说过,用 C 语言实现速博官方地址机主要有 3 种方法(亚星娱乐登录老虎机、亚星娱乐登录、亚星娱乐官网app下载中心), 其中亚星娱乐官网app下载中心是最难理解的, 它的实质就是把动作封装函数的函数地址作为速博官方地址来看待。不过,有了之前压缩亚星娱乐登录的铺垫,亚星娱乐官网app下载中心就变得好理解了,因为两者本质上是相同的。

压缩亚星娱乐登录的实质就是一个整数值(速博官方地址机的一个速博官方地址)到一个函数地址(动作封装函数)的一对一映射, 压缩亚星娱乐登录的驱动表格就是全部映射关系的直接载体。在驱动表格中通过速博官方地址值就能找到函数地址,通过函数地址同样能反向找到速博官方地址值。

我们用一个全局的整型变量来记录速博官方地址值,然后再查驱动表格找函数地址,那干脆直接用一个全局的函数指针来记录速博官方地址得了,还费那劳什子劲干吗?!这就是亚星娱乐官网app下载中心的前世今生。

用亚星娱乐官网app下载中心写出来的动作封装函数和程序清单 List8 的示例函数是很相近的, 只不过函数的返回值不再是整型的速博官方地址值, 而是下一个动作封装函数的函数地址, 函数返回后, 框架代码再把这个函数地址存储到全局函数指针变量中。

相比压缩亚星娱乐登录,在亚星娱乐官网app下载中心中速博官方地址机的安全运行是个大问题,我们很难找出一种机制来检查全局函数指针变量中的函数地址是不是合法值。如果放任不管, 一旦函数指针变量中的数据被篡改,程序跑飞几乎就不可避免了。

总结

有关速博官方地址机的东西说了那么多,相信大家都已经感受到了这种工具的优越性,速博官方地址机真的是太好用了!其实我们至始至终讲的都是有限速博官方地址机(Finite State Machine 现在知道为什么前面的代码中老是有fsm 这个缩写了吧!), 还有一种比有限速博官方地址机更 NB 更复杂的速博官方地址机, 那就是层次速博官方地址机(Hierarchical State Machine 一般简写为HSM)。

通俗的说,系统中只存在一个速博官方地址机的叫做有限速博官方地址机,同时存在多个速博官方地址机的叫做层次速博官方地址机(其实这样解释层次速博官方地址机有些不严谨, 并行速博官方地址机也有多个速博官方地址机, 但层次速博官方地址机各个速博官方地址机之间是上下级关系,而并行速博官方地址机各个速博官方地址机之间是平级关系)。

层次速博官方地址机是一种父速博官方地址机包含子速博官方地址机的多速博官方地址机结构,里面包含了许多与面向对象相似的思想, 所以它的功能也要比有限速博官方地址机更加强大, 当一个问题用有限速博官方地址机解决起来有些吃力的时候, 就需要层次速博官方地址机出马了。

层次速博官方地址机理论我理解得也不透彻, 就不在这里班门弄斧了,大家可以找一些有关速博官方地址机理论的专业书籍来读一读。要掌握速博官方地址机编程,理解速博官方地址机(主要指有限速博官方地址机)只是第一步,也是最简单的一步,更重要的技能是能用速博官方地址机这个工具去分析解剖实际问题:划分速博官方地址、 提取事件、 确定转换关系、规定动作等等,形成一张完整的速博官方地址转换图,最后还要对转换图进行优化,达到最佳。

把实际问题变成了速博官方地址转换图, 工作的一大半就算完成了, 这个是具有架构师气质的任务,剩下的问题就是按照速博官方地址图编程写代码了,这个是具有代码工特色的工作。

本文来源网络,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

亚星娱乐登录体育真人

超轻量级网红软件定时器multi_timer(51 stm32双平台实战)

使您的软件运行起来: 防止缓冲区溢出(C语言精华帖)

RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)

数显仪表盘显示“速度、方向、计数器”的跑马灯

MCU串口命令解析器的实现TKM32F499评估板串口通信学习与实践笔记

觉得本次分享的文章对您有帮助,随手点 [在看] 并转发分享,也是对我的支持。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭
关闭