请教:顺序事件型含等待的代码各位是怎样处理的?
场景:空闲的时候,等待一个触发信号A,然后执行事件1,然后等待时间T后,执行事件2,然后等待时间T2或者触发A2,执行事件3……像这样的等待比较多,用switch好像特别啰嗦,各位有没有更好的方法更优雅的形式? "更好的方法更优雅的形式"就是让别人去写,不自己写{:titter:} 状态机 我看stm32 usb库 枚举相关代码,也是老老实实用switch状态机来写的。
我感觉,除非你学习傻孩子老师那种把宏用到极致,否则,这只是一段代码而已(又不是女人),结果才是最重要的... 若顺序不乱,甚至可以写成阻塞式。
若有乱序要求,本质是自己写多线程,没啥好的办法 看看大佬们是怎么做的进来学习学习{:smile:}
在一个项目中我是这么傻干的.
把这个当做是一个事件驱动机制, 触发条件 和时间到都产生一个消息.
把每个执行事件做成一个函数,这些函数用一个函数指针管理.
产生触发信号A -> 发消息(事件1函数地址)到消息队列 -> 轮询任务从消息对列获取消息
->用函数指针调用事件对应的函数.
事件1函数
{
uint32_tmsg;
执行动作。。。
msg = (uint32_t)等待时间T函数名;
SendMsg(msg);
}
触发信号A产生
{
uint32_tmsg;
msg = (uint32_t)事件1函数名;
SendMsg(msg);
}
轮询从消息队列取消息
{
获取消息不为空且为函数地址消息
{
lpFun= (void(*)())msg;
(*lpFun)();
}
} GZZXB 发表于 2023-3-17 20:06
看看大佬们是怎么做的进来学习学习
在一个项目中我是这么傻干的.
把这个当做是一个事件驱动机 ...
(引用自6楼)
在nordic 的sdk 里也是这种方法,但还是觉得比较麻烦。想用类似表格的那种,把所有的事件和触发条件都列出来,但还没想好怎么做…… 擦鞋匠 发表于 2023-3-17 18:02
我看stm32 usb库 枚举相关代码,也是老老实实用switch状态机来写的。
我感觉,除非你学习傻孩子老师那种把 ...
(引用自4楼)
其实我的目的也是比较简单,就是想搞一个比较适合自己的有效的做法,最好是形成一定的格式,这样才能快速的开发,而且不容易出错。如果每一次都是重新造轮子,真的很麻烦 t3486784401 发表于 2023-3-17 18:21
若顺序不乱,甚至可以写成阻塞式。
若有乱序要求,本质是自己写多线程,没啥好的办法 ...
(引用自5楼)
如果每一个状态只有一个触发条件还好,但若有多几个,或者合并式的,就很麻烦,很难形成统一的风格,至少我目前没有什么好办法 tim 发表于 2023-3-17 17:39
"更好的方法更优雅的形式"就是让别人去写,不自己写
(引用自2楼)
你说的对!哈哈? 不想搞状态机就上RTOS,多线程及线程间通信帮你解决顺序事件中阻塞的烦恼 RTOS不香吗,不然绕不开状态机,要不就是状态机的变体,只是何处事件触发和状态跳转 状态机基本能解决绝大多数问题,如果解决不了,就再加一个状态机。 jssd 发表于 2023-3-17 21:05
在nordic 的sdk 里也是这种方法,但还是觉得比较麻烦。想用类似表格的那种,把所有的事件和触发条件都列 ...
(引用自7楼)
参考MFC消息映射机制,用一个.h文件里实现消息映射。用宏把这些触发条件和执行事件预定义好.
#define触发条件_MSG 执行事件函数名
...
这样只要维护好这个映射表,.c中的执行代码基本不用修改,修改每个条件时只修改消息映射会不会更好点呢?
是不是接近你说的类似表格? 状态机了解一下 GZZXB 发表于 2023-3-18 11:02
参考MFC消息映射机制,用一个.h文件里实现消息映射。用宏把这些触发条件和执行事件预定义好.
#de ...
(引用自14楼)
MFC背后帮你写好switch了,而且后台代码更不易读… 消息队列 stateMachine状态机框架
传送门: https://github.com/misje/stateMachine说明文档: http://misje.github.io/stateMachine/index.html https://blog.csdn.net/qq_37662088/article/details/122441289
https://blog.csdn.net/qq_36969264/article/details/122365696
结合了这两位大侠的代码搞了一个比较合适自己的状态机
#ifndef __fsm_H
#define __fsm_H
#include <stdbool.h> //true false
#include <stdint.h> //uint8_t
#include <stdio.h> //printf
typedef struct action_map
{
uint8_t state;
void (*EnterAct)(void); //进入状态之前的执行函数,执行一次
void (*RunningAct)(void); //状态执行函数,一直执行
void (*ExitAct)(void); //退出状态之时的执行函数,执行一次
}action_map_t; /* 动作action表描述 */
typedef struct event_map
{
uint8_t stCurState; //当前状态
uint8_t stNextState; //下一个状态
bool (*trans_fun)(void); //转移条件函数
}event_map_t; /* 事件event表描述 */
typedef struct fsm
{
uint8_t state; //当前状态
uint8_t actSum; //动作总数
uint8_t eventSum; //事件总数
int8_t runActionID; //执行函数的id
action_map_t *pActionMap; //动作执行
event_map_t *pEventMap; //状态转移
struct fsm *next; //链表
}fsm_t; /* 状态机控制结构 */
void FSM_Init(fsm_t* pFsm,event_map_t* pEventMap,uint8_t eveMapSum,action_map_t* pActionMap,uint8_t actMapSum,uint8_t curState);
#endif /* __fsm_H */
#include "fsm.h"
#include "system.h"
static fsm_t* head_handle = NULL;
int8_t FSM_EnterAct(fsm_t* pFsm)
{
int8_t i;
//printf("FSM_EnterAct:pFsm->state=%d\n",pFsm->state);
if(pFsm->pActionMap){
for(i=0; i<pFsm->actSum; i++){
if(pFsm->state == pFsm->pActionMap.state){
if(pFsm->pActionMap.EnterAct){
pFsm->pActionMap.EnterAct(); //执行进入函数
}
return i;
}
}
}
return -1;
}
int8_t FSM_ExitAct(fsm_t* pFsm)
{
int8_t i;
//printf("FSM_ExitAct:pFsm->state=%d\n",pFsm->state);
if(pFsm->pActionMap){
for(i=0; i<pFsm->actSum; i++){
if(pFsm->state == pFsm->pActionMap.state){
if(pFsm->pActionMap.ExitAct){
pFsm->pActionMap.ExitAct(); //执行退出函数
}
return i;
}
}
}
return -1;
}
void FSM_Run(fsm_t* pFsm)//传入当前状态,因为需要对其进行修改,所以传变量指针
{
uint8_t i;
for(i=0;i<pFsm->eventSum;i++){
if ((pFsm->pEventMap.stCurState == pFsm->state) && (pFsm->pEventMap.trans_fun() == true))//当前状态相等并且转移条件为真
{
//这里可以添加自己需要执行的代码
FSM_ExitAct(pFsm);
pFsm->state = pFsm->pEventMap.stNextState;//转移为下一个状态
pFsm->runActionID = FSM_EnterAct(pFsm);
break;
}
}
if(i==pFsm->eventSum){
if((pFsm->runActionID>0)&&(pFsm->pActionMap.RunningAct)){
pFsm->pActionMap.RunningAct(); //执行退出函数
}
}
}
void FSM_Process(void)
{
fsm_t* target;
for(target=head_handle;target;target=target->next){
FSM_Run(target);
}
}
void FSM_Init(fsm_t* pFsm,event_map_t* pEventMap,uint8_t eveMapSum,action_map_t* pActionMap,uint8_t actMapSum,uint8_t curState)
{
//以链表形式
fsm_t* target = head_handle;
while(target){
if(target==pFsm) return;
target = target->next;
}
pFsm->next = head_handle;
head_handle = pFsm;
pFsm->state = curState;
pFsm->actSum = actMapSum;
pFsm->eventSum = eveMapSum;
pFsm->pEventMap = pEventMap;
pFsm->pActionMap = pActionMap;
pFsm->runActionID = FSM_EnterAct(pFsm);
Process_Start(0,"FSM_Process",FSM_Process);
printf("pFsm->actSum=%d,pFsm->eventSum=%d\n",pFsm->actSum,pFsm->eventSum);
}
/********************END OF FILE************/
使用
#include "fsm.h"
typedef enum {
state_1 = 0,
state_2,
state_3,
state_4
}State;
int x;
int y = 5;
bool trans_1to2(void)
{
return (x == y);
}
bool trans_1to3(void)
{
return (x > y);
}
bool trans_2to3(void)
{
return (x > y);
}
bool trans_3to4(void)
{
return (x == y);
}
bool trans_4to1(void)
{
return (x < y);
}
void state1_entry(void)
{
printf("state1_entry\n");
}
void state1_do(void)
{
//printf("state1_do\n");
}
void state1_exit(void)
{
printf("state1_exit\n");
}
void state2_entry(void)
{
printf("state2_entry\n");
}
void state2_do(void)
{
//printf("state2_do\n");
}
void state2_exit(void)
{
printf("state2_exit\n");
}
void state3_entry(void)
{
printf("state3_entry\n");
}
void state3_do(void)
{
printf("state3_do\n");
}
void state3_exit(void)
{
printf("state3_exit\n");
}
void state4_entry(void)
{
printf("state4_entry\n");
}
void state4_do(void)
{
//printf("state4_do\n");
}
void state4_exit(void)
{
printf("state4_exit\n");
}
event_map_t eventMap[] = {
{state_1,state_2,trans_1to2},
{state_2,state_3,trans_2to3},
{state_3,state_4,trans_3to4},
{state_4,state_1,trans_4to1},
{state_1,state_3,trans_1to3}
};
action_map_t actionMap[] =
{
{state_1, 0, state1_do, state1_exit},
{state_2, state2_entry, state2_do, 0},
{state_3, state3_entry, 0, state3_exit},
{state_4, state4_entry, state4_do, state4_exit},
};
fsm_t fsm1;
void USR_Init(void)
{
FSM_Init(&fsm1,eventMap,sizeof(eventMap)/sizeof(event_map_t),actionMap,sizeof(actionMap)/sizeof(action_map_t),state_1);
}
jssd 发表于 2023-3-26 14:47
https://blog.csdn.net/qq_37662088/article/details/122441289
https://blog.csdn.net/qq_36969264/articl ...
(引用自19楼)
请教下楼主触发条件(定时到或收到串口命令等等)和FSM你打算怎么耦合?消息机制吗?
如果是消息机制,准备以什么方式派发独立消息(某个FSM响应)全局消息(所有FSM都响应)?
如果不是,那么trans_fun里想用什么方式和触发条件耦合?
GZZXB 发表于 2023-3-27 09:50
请教下楼主触发条件(定时到或收到串口命令等等)和FSM你打算怎么耦合?消息机制吗?
如果是消息机制, ...
(引用自20楼)
event_map_t eventMap[] = {
{state_1,state_2,trans_1to2},
{state_2,state_3,trans_2to3},
{state_3,state_4,trans_3to4},
{state_4,state_1,trans_4to1},
{state_1,state_3,trans_1to3}
};
事件触发,也就是定义的eventMap的事件触发函数为真就会触发当前的状态转移。至于耦合,这里需要转换一下,把所需要的条件直接写到事件触发函数里判断。
但实际使用起来,原理没问题,只是感觉不比用switch case简单,反而有点啰嗦的感觉。。。主要是要列一大堆的函数。。。 jssd 发表于 2023-3-27 11:11
event_map_t eventMap[] = {
{state_1,state_2,trans_1to2},
{state_2,state_3,trans_2to3 ...
(引用自21楼)
把所需要的条件直接写到事件触发函数里判断。
你指的是怎么个直接法?每个触发函数都里这样写吗 \
获取按键值 if(按键条件满足) 切换到next_state
获取串口命令if(串口命令满足) 切换到next_state
...
这样不太符合接口规范,不太好做到弱耦合吧?假定这个工程有串口事件(也就是你说的条件)移植到另一个工程时(没有串口模块),每个判断函数的代码都需要修改.
那个do函数 一直在主程序执行..
那就是反复调用.. 会不会比较麻烦呢? GZZXB 发表于 2023-3-27 11:48
把所需要的条件直接写到事件触发函数里判断。
你指的是怎么个直接法?每个触发函数都里这样写吗 \
...
(引用自22楼)
bool trans_1to2(void)
{
if(keydown) return true;
if(uartCom) return true;
...
return false;
}
像这样,但这样也有一个问题,比如按键,如果使用类似multibutton这样的回调的,就很难用比如:
按键回调
key_callback(){ keydown = 1; }
触发函数
bool trans_1to2(void)
{
if(keydown){
keydown = 0;
return true;
}
return false;
}
这样需要一个fifo记录key的触发,当然这个例子fifo深度只有1,但如果多个触发函数都用到呢?这就麻烦了
所以把这个触发函数改成事件代号会好一些,比如:
enum{
event_1,
event_2,
...
};
当有消息回调时,进行切换状态
key_callback(){ fsm_trans(curState,event_1)}
这样其实还有一个问题,就是要是event_1是key1down&&key2down共同的结果呢?感觉又回到的上面的问题
sweet_136 发表于 2023-3-27 12:01
那个do函数 一直在主程序执行..
那就是反复调用.. 会不会比较麻烦呢?
(引用自23楼)
我也觉得貌似多余了。。。
还是有待改进,刚好现在在搞AT命令的8266,觉得这个AT流程搞完,比较适用的状态机应该也可以提取出来了 我自己的感觉是:
在MCU上面开发,如果逻辑复杂到这样,与其搞一个复杂的状态机框架,不如直接上rtos。可以避免很多问题,代码可读性也会好很多。
除非应用场景特殊,无法上rtos才有必要引入这种状态机框架。 推荐上rtos,我在最近的项目使用freertos,效果确实挺好 这种用 rust 的 async/awit 实现起来就很简单,底层原理就是状态机 + 异步运行时
页:
[1]