使用FSM有限状态机模拟点亮LED灯

最近学习了FSM有限状态机,通过网上的优秀案例,我自己实践了一下发现比较高效,从此写项目又多了一套框架选择,下面我来讲解分享一下这个简单的例程。

1、先总结一下它的大致思路:
  • 事件标志(类似于模式1、模式2这样)
  • 动作标志(当前动作、下一个动作)
  • 动作函数(点亮led的操作)

大致的流程框架图结合代码:(代码中有注释,方便理解)

有了这些大致的思路,下面就可以来编写标志位

2、标志位的初始化:
/* 事件标志位 */
// 模式=事件 mode也可以写成event
typedef enum   
{
 Mode1 = 1,
 Mode2,
 Mode3,
 Mode4,
};

/* 动作标志位 */
typedef enum   // 动作
{
 LEW1_ON,
 LEW2_ON,
 LEW3_ON,
 LEW4_ON,
};
3、动作函数的编写
void Led1(void)
{
 printf("led1 on\n");
}
void Led2(void)
{
 printf("led2 on\n");
}
 void Led3(void)
{
 printf("led3 on\n");
}
void Led4(void)
{
 printf("led4 on\n");
}
4、定义一个状态表结构,用来表示一个状态机的状态,综合前三点加上下一个状态
typedef struct ModeStruct_Init_s  // 结构体初始化,综合
{
 unsigned int mode;               //事件
 unsigned int CurState;           //当前状态
 void (*ModeFun)();               //函数指针
 unsigned int NextState;          //下一个状态
}ModeStruct_Init_t;
5、根据上面的结构体,去配置成结构数组,即为状态表
ModeStruct_Init_t StepTable[] = 
{
 /* 事件标志  当前动作  动作函数  下一个动作 */
 {Mode1,  LEW1_ON,   Led1,   LEW2_ON},
 {Mode2,  LEW2_ON,   Led2,   LEW3_ON},
 {Mode3,  LEW3_ON,   Led3,   LEW4_ON},
 {Mode4,  LEW4_ON,   Led4,   LEW1_ON},
};
6、再定义一个状态机总结构,表示一个状态机
typedef struct FSM_s              // 记录当前状态
{
 ModeStruct_Init_t* TempTable;    //指向的状态表
 unsigned int TempState;          //FSM当前所处的状态
}FSM_t;
7、编写初始化状态机的函数:
// 初始化FSM,相当于将传进来的结构体进行配置
void InitModeTable(FSM_t* pModeTable)
{
 // StepTable状态表/ModeStruct_Init_t 16/4=4 获取长度4
 // g_state_max_num 记得定义,我这里在其它地方定义了
 g_state_max_num = sizeof(StepTable) / sizeof(ModeStruct_Init_t); 
 pModeTable->TempState = LEW1_ON;     // 初始状态开启LED1
 pModeTable->TempTable = StepTable;   // 初始化TempTable指向状态表StepTable
}
8、编写事件处理函数:(精髓)
/* 事件处理 */
// 将当前状态结构体传进来,将模式标志位传进来
void FSM_EventHandle(FSM_t* pModeTable, uint16_t Mode)
{
    // 将传进来的结构体状态表 = 赋值给临时结构体状态表
	ModeStruct_Init_t* pActTable;
    // 空函指针
	void (*eventActFun)() = NULL; 
	uint16_t i;
	uint16_t flag = 0; //标识是否满足条件
	uint16_t NextState = 0;
	uint16_t CurState = 0;
	pActTable = pModeTable->TempTable;
	// 将传进来当前状态结构体 当前所处的状态 = 赋值给临时状态
	CurState  = pModeTable->TempState;
	/* 获取当前动作函数 遍历传进来的mode对比状态表中的mode 和 当前状态是否等于传进来的临时状态*/
	for (i = 0; i<g_state_max_num; i++)
	{
		//当且仅当当前状态下来个指定的事件,我才执行它
		if (Mode == pActTable[i].mode && CurState == pActTable[i].CurState)
		{
            // 标志位置1
			flag = 1;
			/* 执行动作函数,就是执行状态表中对应的函数 */
            // 这句也可以,为了展示采用下面一句
            // pActTable[i].ModeFun();               
			eventActFun = pActTable[i].ModeFun;

			/* 执行状态转移,调用FSM_StateTransfer函数,将下一个状态赋值给下一次操作	*/
            // 这句也可以,为了展示采用下面一句
            // pModeTable->TempState = pActTable[i].NextState; 
			FSM_StateTransfer(pModeTable, pActTable[i].NextState); // 此处函数放在下面定义
			
			break;
		}
	}
	if (flag) //如果满足条件了
	{
		/* 动作执行 */
		if (eventActFun)
		{
            // 在这里执行动作表里面的动作函数
			eventActFun();
		}
	}
	else
	{
		// do nothing
	}
}

执行状态转移函数:

void FSM_StateTransfer(FSM_t* pModeTable, uint16_t mode)
{
 pModeTable->TempState = mode;
}
9、有了这些程序结构,就可以进行主函数调用函数
extern void test(int *mode);

void main()
{
  FSM_t ModeTable;
  mode = Mode1;
  InitModeTable(&ModeTable);
  while(1)
  {	
 // Scan_key();  // 按键去触发条件
    test(&mode); // 循环去触发条件
	FSM_EventHandle(&ModeTable,mode);
	sleep(20);   // 延时函数,制造循环间隔,按键触发条件可以不加
  }
}
/* 循环去触发条件 */
void test(int *mode)
{
  if (*mode == 4)
  {
    *mode = 1;
  }
  else
  {
    (*mode)++;
  }
 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值