搜索
bottom↓
回复: 27

请教:顺序事件型含等待的代码各位是怎样处理的?

[复制链接]

出0入55汤圆

发表于 2023-3-17 17:11:18 来自手机 | 显示全部楼层 |阅读模式
场景:空闲的时候,等待一个触发信号A,然后执行事件1,然后等待时间T后,执行事件2,然后等待时间T2或者触发A2,执行事件3……
像这样的等待比较多,用switch好像特别啰嗦,各位有没有更好的方法更优雅的形式?

出0入93汤圆

发表于 2023-3-17 17:39:49 | 显示全部楼层
"更好的方法更优雅的形式"就是让别人去写,不自己写

出0入4汤圆

发表于 2023-3-17 17:57:37 来自手机 | 显示全部楼层
状态机   

出0入0汤圆

发表于 2023-3-17 18:02:30 | 显示全部楼层
我看stm32 usb库 枚举相关代码,也是老老实实用switch状态机来写的。
我感觉,除非你学习傻孩子老师那种把宏用到极致,否则,这只是一段代码而已(又不是女人),结果才是最重要的...

出200入2554汤圆

发表于 2023-3-17 18:21:39 来自手机 | 显示全部楼层
若顺序不乱,甚至可以写成阻塞式。

若有乱序要求,本质是自己写多线程,没啥好的办法

出0入36汤圆

发表于 2023-3-17 20:06:16 | 显示全部楼层
看看大佬们是怎么做的进来学习学习
在一个项目中我是这么傻干的.   
把这个当做是一个事件驱动机制, 触发条件 和时间到都产生一个消息.
把每个执行事件做成一个函数,这些函数用一个函数指针管理.   

产生触发信号A -> 发消息(事件1函数地址)到消息队列 -> 轮询任务从消息对列获取消息
->用函数指针调用事件对应的函数.   

事件1函数
{
    uint32_t  msg;
    执行动作。。。
   msg = (uint32_t)等待时间T函数名;
   SendMsg(msg);
}
触发信号A产生
{
    uint32_t  msg;
   msg = (uint32_t)事件1函数名;
   SendMsg(msg);
}

轮询从消息队列取消息
{
       获取消息不为空且为函数地址消息
      {
            lpFun= (void(*)())msg;
            (*lpFun)();

            
      }
}

出0入55汤圆

 楼主| 发表于 2023-3-17 21:05:47 来自手机 | 显示全部楼层
GZZXB 发表于 2023-3-17 20:06
看看大佬们是怎么做的进来学习学习
在一个项目中我是这么傻干的.   
把这个当做是一个事件驱动机 ...

(引用自6楼)

在nordic 的sdk 里也是这种方法,但还是觉得比较麻烦。想用类似表格的那种,把所有的事件和触发条件都列出来,但还没想好怎么做……

出0入55汤圆

 楼主| 发表于 2023-3-17 21:11:44 来自手机 | 显示全部楼层
擦鞋匠 发表于 2023-3-17 18:02
我看stm32 usb库 枚举相关代码,也是老老实实用switch状态机来写的。
我感觉,除非你学习傻孩子老师那种把 ...

(引用自4楼)

其实我的目的也是比较简单,就是想搞一个比较适合自己的有效的做法,最好是形成一定的格式,这样才能快速的开发,而且不容易出错。如果每一次都是重新造轮子,真的很麻烦

出0入55汤圆

 楼主| 发表于 2023-3-17 21:14:17 来自手机 | 显示全部楼层
t3486784401 发表于 2023-3-17 18:21
若顺序不乱,甚至可以写成阻塞式。

若有乱序要求,本质是自己写多线程,没啥好的办法 ...

(引用自5楼)

如果每一个状态只有一个触发条件还好,但若有多几个,或者合并式的,就很麻烦,很难形成统一的风格,至少我目前没有什么好办法

出0入55汤圆

 楼主| 发表于 2023-3-17 21:15:39 来自手机 | 显示全部楼层
tim 发表于 2023-3-17 17:39
"更好的方法更优雅的形式"就是让别人去写,不自己写
(引用自2楼)

你说的对!哈哈?

出0入85汤圆

发表于 2023-3-17 21:41:27 | 显示全部楼层
不想搞状态机就上RTOS,多线程及线程间通信帮你解决顺序事件中阻塞的烦恼

出0入42汤圆

发表于 2023-3-18 07:00:52 来自手机 | 显示全部楼层
RTOS不香吗,不然绕不开状态机,要不就是状态机的变体,只是何处事件触发和状态跳转

出0入0汤圆

发表于 2023-3-18 09:32:31 | 显示全部楼层
状态机基本能解决绝大多数问题,如果解决不了,就再加一个状态机。

出0入36汤圆

发表于 2023-3-18 11:02:21 | 显示全部楼层
jssd 发表于 2023-3-17 21:05
在nordic 的sdk 里也是这种方法,但还是觉得比较麻烦。想用类似表格的那种,把所有的事件和触发条件都列 ...
(引用自7楼)

    参考MFC消息映射机制,用一个.h文件里实现消息映射。用宏把这些触发条件和执行事件预定义好.
    #define  触发条件_MSG    执行事件函数名
    ...
   
    这样只要维护好这个映射表,  .c中的执行代码基本不用修改,  修改每个条件时只修改消息映射会不会更好点呢?
    是不是接近你说的类似表格?

出0入0汤圆

发表于 2023-3-20 16:13:20 | 显示全部楼层
状态机了解一下

出200入2554汤圆

发表于 2023-3-20 16:28:00 来自手机 | 显示全部楼层
GZZXB 发表于 2023-3-18 11:02
参考MFC消息映射机制,用一个.h文件里实现消息映射。用宏把这些触发条件和执行事件预定义好.
    #de ...

(引用自14楼)

MFC背后帮你写好switch了,而且后台代码更不易读…

出10入12汤圆

发表于 2023-3-20 18:58:39 | 显示全部楼层
消息队列   

出0入0汤圆

发表于 2023-3-20 23:27:13 来自手机 | 显示全部楼层
stateMachine状态机框架
传送门: https://github.com/misje/stateMachine说明文档: http://misje.github.io/stateMachine/index.html

出0入55汤圆

 楼主| 发表于 2023-3-26 14:47:22 | 显示全部楼层
https://blog.csdn.net/qq_37662088/article/details/122441289
https://blog.csdn.net/qq_36969264/article/details/122365696
结合了这两位大侠的代码搞了一个比较合适自己的状态机
  1. #ifndef __fsm_H
  2. #define __fsm_H

  3. #include <stdbool.h> //true false
  4. #include <stdint.h>        //uint8_t
  5. #include <stdio.h>        //printf

  6. typedef struct action_map
  7. {
  8.         uint8_t state;
  9.         void (*EnterAct)(void);                //进入状态之前的执行函数,执行一次
  10.         void (*RunningAct)(void);        //状态执行函数,一直执行
  11.         void (*ExitAct)(void);                //退出状态之时的执行函数,执行一次
  12. }action_map_t; /* 动作action表描述 */

  13. typedef struct event_map
  14. {
  15.         uint8_t stCurState;                        //当前状态
  16.         uint8_t stNextState;                //下一个状态
  17.         bool (*trans_fun)(void);        //转移条件函数
  18. }event_map_t; /* 事件event表描述 */

  19. typedef struct fsm
  20. {
  21.         uint8_t state;                                //当前状态
  22.         uint8_t actSum;                                //动作总数
  23.         uint8_t eventSum;                        //事件总数
  24.         int8_t runActionID;                //执行函数的id
  25.         action_map_t *pActionMap;        //动作执行
  26.         event_map_t *pEventMap;                //状态转移
  27.         struct fsm *next;                        //链表
  28. }fsm_t; /* 状态机控制结构 */

  29. void FSM_Init(fsm_t* pFsm,event_map_t* pEventMap,uint8_t eveMapSum,action_map_t* pActionMap,uint8_t actMapSum,uint8_t curState);

  30. #endif /* __fsm_H */
复制代码
  1. #include "fsm.h"
  2. #include "system.h"

  3. static fsm_t* head_handle = NULL;


  4. int8_t FSM_EnterAct(fsm_t* pFsm)
  5. {
  6.         int8_t i;
  7.         //printf("FSM_EnterAct:pFsm->state=%d\n",pFsm->state);
  8.         if(pFsm->pActionMap){
  9.                 for(i=0; i<pFsm->actSum; i++){
  10.                         if(pFsm->state == pFsm->pActionMap[i].state){
  11.                                 if(pFsm->pActionMap[i].EnterAct){
  12.                                         pFsm->pActionMap[i].EnterAct();        //执行进入函数
  13.                                 }
  14.                                 return i;
  15.                         }
  16.                 }
  17.         }
  18.         return -1;
  19. }

  20. int8_t FSM_ExitAct(fsm_t* pFsm)
  21. {
  22.         int8_t i;
  23.         //printf("FSM_ExitAct:pFsm->state=%d\n",pFsm->state);
  24.         if(pFsm->pActionMap){
  25.                 for(i=0; i<pFsm->actSum; i++){
  26.                         if(pFsm->state == pFsm->pActionMap[i].state){
  27.                                 if(pFsm->pActionMap[i].ExitAct){
  28.                                         pFsm->pActionMap[i].ExitAct();        //执行退出函数
  29.                                 }
  30.                                 return i;
  31.                         }
  32.                 }
  33.         }
  34.         return -1;
  35. }

  36. void FSM_Run(fsm_t* pFsm)//传入当前状态,因为需要对其进行修改,所以传变量指针
  37. {
  38.         uint8_t i;
  39.     for(i=0;i<pFsm->eventSum;i++){
  40.         if ((pFsm->pEventMap[i].stCurState == pFsm->state) && (pFsm->pEventMap[i].trans_fun() == true))//当前状态相等并且转移条件为真
  41.         {
  42.             //这里可以添加自己需要执行的代码
  43.                         FSM_ExitAct(pFsm);
  44.             pFsm->state = pFsm->pEventMap[i].stNextState;//转移为下一个状态
  45.                         pFsm->runActionID = FSM_EnterAct(pFsm);
  46.                         break;
  47.         }
  48.     }
  49.         if(i==pFsm->eventSum){
  50.                 if((pFsm->runActionID>0)&&(pFsm->pActionMap[pFsm->runActionID].RunningAct)){
  51.                         pFsm->pActionMap[pFsm->runActionID].RunningAct();        //执行退出函数
  52.                 }
  53.         }
  54. }

  55. void FSM_Process(void)
  56. {
  57.         fsm_t* target;
  58.         for(target=head_handle;target;target=target->next){
  59.                 FSM_Run(target);
  60.         }
  61. }



  62. void FSM_Init(fsm_t* pFsm,event_map_t* pEventMap,uint8_t eveMapSum,action_map_t* pActionMap,uint8_t actMapSum,uint8_t curState)
  63. {
  64.         //以链表形式
  65.         fsm_t* target = head_handle;
  66.         while(target){
  67.                 if(target==pFsm) return;
  68.                 target = target->next;
  69.         }
  70.         pFsm->next = head_handle;
  71.         head_handle = pFsm;
  72.        
  73.         pFsm->state = curState;
  74.         pFsm->actSum = actMapSum;
  75.         pFsm->eventSum = eveMapSum;
  76.         pFsm->pEventMap = pEventMap;
  77.         pFsm->pActionMap = pActionMap;
  78.        
  79.         pFsm->runActionID = FSM_EnterAct(pFsm);
  80.        
  81.         Process_Start(0,"FSM_Process",FSM_Process);
  82.        
  83.         printf("pFsm->actSum=%d,pFsm->eventSum=%d\n",pFsm->actSum,pFsm->eventSum);
  84. }


  85. /********************END OF FILE************/
复制代码


使用
  1. #include "fsm.h"

  2. typedef enum {
  3.     state_1 = 0,
  4.     state_2,
  5.     state_3,
  6.     state_4
  7. }State;

  8. int x;
  9. int y = 5;

  10. bool trans_1to2(void)
  11. {
  12.     return (x == y);
  13. }

  14. bool trans_1to3(void)
  15. {
  16.     return (x > y);
  17. }

  18. bool trans_2to3(void)
  19. {
  20.     return (x > y);
  21. }

  22. bool trans_3to4(void)
  23. {
  24.     return (x == y);   
  25. }

  26. bool trans_4to1(void)
  27. {
  28.     return (x < y);
  29. }



  30. void state1_entry(void)
  31. {
  32.         printf("state1_entry\n");
  33. }
  34. void state1_do(void)
  35. {
  36.         //printf("state1_do\n");
  37. }
  38. void state1_exit(void)
  39. {
  40.         printf("state1_exit\n");
  41. }

  42. void state2_entry(void)
  43. {
  44.         printf("state2_entry\n");
  45. }
  46. void state2_do(void)
  47. {
  48.         //printf("state2_do\n");
  49. }
  50. void state2_exit(void)
  51. {
  52.         printf("state2_exit\n");
  53. }

  54. void state3_entry(void)
  55. {
  56.         printf("state3_entry\n");
  57. }
  58. void state3_do(void)
  59. {
  60.         printf("state3_do\n");
  61. }
  62. void state3_exit(void)
  63. {
  64.         printf("state3_exit\n");
  65. }

  66. void state4_entry(void)
  67. {
  68.         printf("state4_entry\n");
  69. }
  70. void state4_do(void)
  71. {
  72.         //printf("state4_do\n");
  73. }
  74. void state4_exit(void)
  75. {
  76.         printf("state4_exit\n");
  77. }

  78. event_map_t eventMap[] = {
  79.        {state_1,state_2,trans_1to2},
  80.        {state_2,state_3,trans_2to3},
  81.        {state_3,state_4,trans_3to4},
  82.        {state_4,state_1,trans_4to1},
  83.        {state_1,state_3,trans_1to3}
  84. };

  85. action_map_t actionMap[] =
  86. {
  87.         {state_1,        0,        state1_do,        state1_exit},
  88.         {state_2,        state2_entry,        state2_do,        0},
  89.         {state_3,        state3_entry,        0,        state3_exit},
  90.         {state_4,        state4_entry,        state4_do,        state4_exit},
  91. };

  92. fsm_t fsm1;


  93. void USR_Init(void)
  94. {
  95.         FSM_Init(&fsm1,eventMap,sizeof(eventMap)/sizeof(event_map_t),actionMap,sizeof(actionMap)/sizeof(action_map_t),state_1);
  96. }
复制代码

出0入36汤圆

发表于 2023-3-27 09:50:13 | 显示全部楼层
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里想用什么方式和触发条件耦合?
   

出0入55汤圆

 楼主| 发表于 2023-3-27 11:11:45 | 显示全部楼层
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简单,反而有点啰嗦的感觉。。。主要是要列一大堆的函数。。。

出0入36汤圆

发表于 2023-3-27 11:48:29 | 显示全部楼层
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
     ...
     
这样不太符合接口规范,不太好做到弱耦合吧?假定这个工程有串口事件(也就是你说的条件)移植到另一个工程时(没有串口模块),每个判断函数的代码都需要修改.

出0入0汤圆

发表于 2023-3-27 12:01:04 | 显示全部楼层
那个do函数 一直在主程序执行..
那就是反复调用.. 会不会比较麻烦呢?

出0入55汤圆

 楼主| 发表于 2023-3-27 12:15:41 | 显示全部楼层
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共同的结果呢?感觉又回到的上面的问题

出0入55汤圆

 楼主| 发表于 2023-3-27 12:19:28 | 显示全部楼层
sweet_136 发表于 2023-3-27 12:01
那个do函数 一直在主程序执行..
那就是反复调用.. 会不会比较麻烦呢?
(引用自23楼)

我也觉得貌似多余了。。。
还是有待改进,刚好现在在搞AT命令的8266,觉得这个AT流程搞完,比较适用的状态机应该也可以提取出来了

出0入0汤圆

发表于 2023-3-27 12:38:50 | 显示全部楼层
我自己的感觉是:
在MCU上面开发,如果逻辑复杂到这样,与其搞一个复杂的状态机框架,不如直接上rtos。可以避免很多问题,代码可读性也会好很多。

除非应用场景特殊,无法上rtos才有必要引入这种状态机框架。

出0入131汤圆

发表于 2023-3-27 12:44:06 | 显示全部楼层
推荐上rtos,我在最近的项目使用freertos,效果确实挺好

出0入148汤圆

发表于 2023-3-27 12:49:57 来自手机 | 显示全部楼层
这种用 rust 的 async/awit 实现起来就很简单,底层原理就是状态机 + 异步运行时
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-29 15:56

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表