极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 14390|回复: 5

arduino宏的应用实例6--走火入魔篇--用宏实现状态机框架

[复制链接]
发表于 2016-5-18 23:58:17 | 显示全部楼层 |阅读模式
本帖最后由 wwwymq 于 2016-5-19 18:28 编辑

如何用宏实现一个状态机框架?
我在这里参考了西门子s7-200 plc的stl编程样式。
首先来看看工控中状态转移图的实现方式:





这个就是实现状态机框架的宏定义
  1. //定义状态机切换的四种状态
  2. enum sta {idel, waitLive, live, waitIdel};
  3. //状态的总数设定,根据需要修改状态总数
  4. #define smMum 100
  5. //全局状态变量
  6. sta s[smMum];
  7. //状态开始符,用的时候后面不要加";"
  8. #define LSCR(i) {\
  9.     int index=i;\
  10.     bool mc=s[index]==live ;\
  11.     if(s[index]!=idel){\
  12.       if(s[index]==waitLive){s[index]=live;}\
  13.       if(s[index]==waitIdel){s[index]=idel;}
  14. //状态转移
  15. #define SCRT(t) ({s[index]=waitIdel,s[t]=waitLive;})
  16. //状态结束符
  17. #define SCRE }}
  18. //设置状态
  19. #define setSm(i) s[i]=waitLive
  20. //重置状态
  21. #define rstSm(i) s[i]=waitIdel
  22. //读取状态
  23. #define readSm(i) ({s[i]==live;})
复制代码


这个是延时继电器的宏定义,可以参考上个帖子。
  1. //宏名:delayRelay。返回值:bool 到达时间。输入值:enable使能,del延时时间。作用:延时继电器,当enable使能后,延时del毫秒后接通。
  2. //static unsigned long tim = ({Serial.println("start"), millis();}); //这句用于测试是否能在static 变量设置的时候加入只执行一次的初始化代码块。
  3. #define delayRelay(enable,del) ({\
  4.     static unsigned long tim = millis();\
  5.     if (!enable) {\
  6.       tim = millis();\
  7.     }\
  8.     millis() - tim >= del;\
  9.   })
复制代码


这个是边沿检测的宏定义,可以参考上上个帖子
  1. //逻辑量的四种状态:低,上升,高,低
  2. enum edgeSta {low, trg, high, pop};
  3. //宏名:edge。 返回值:逻辑量状态。输入参数:bool值 用途:检测逻辑量的边沿。
  4. #define edge(ReadData) ({\
  5.     static bool Trg = false;\
  6.     static bool Cont = false;\
  7.     static bool Pop = false;\
  8.     Trg = ReadData & (ReadData ^ Cont); \
  9.     Pop = Cont != ReadData & !Trg; \
  10.     Cont = ReadData; \
  11.     enum edgeSta sta = low;\
  12.     if (Cont) {\
  13.       sta = high;\
  14.     } else {\
  15.       sta = low;\
  16.     }\
  17.     if (Trg) {\
  18.       sta = trg;\
  19.     }\
  20.     if (Pop) {\
  21.       sta = pop;\
  22.     }\
  23.     sta;\
  24.   })
  25. #define raiseEdge(ReadData) ({edge(ReadData)==trg;})
复制代码

这个是主程序
  1. //自定义状态机的状态
  2. enum myState {l0_st0, l0_st1, l1_st0, l1_st1, l1_st2, l1_st3, l1_st4, l1_st5, l2_st0, l2_st1, l2_st2, l2_st3};//根据个人的需要尽量取容易理解的名字,当然不设置枚举也是可以的,直接用0,1,2,3.....
  3. void setup() {
  4.   // put your setup code here, to run once:
  5.   Serial.begin(9600);
  6.   setSm(l0_st0);
  7.   //setSm(l1_st0);
  8.   //setSm(l2_st0);
  9.   pinMode(8, INPUT_PULLUP);     //pin8接按键
  10. }
  11. void loop() {
  12.   // put your main code here, to run repeatedly:

  13.   //loop0是一个简单的无限循环,状态0,1,0,1。。。。。
  14.   LSCR(l0_st0)
  15.   if (raiseEdge(mc))
  16.   {
  17.     Serial.println("Sta0");
  18.   }
  19.   if (delayRelay(mc, 1000))
  20.   {
  21.     SCRT(l0_st1);
  22.   }
  23.   SCRE

  24.   LSCR(l0_st1)
  25.   if (raiseEdge(mc))
  26.   {
  27.     Serial.println("Sta1");
  28.   }
  29.   if (delayRelay(mc, 1000))
  30.   {
  31.     SCRT(l0_st0);
  32.   }
  33.   SCRE

  34.   //loop1是一个并行分支结构
  35.   LSCR(l1_st0)
  36.   if (mc) {
  37.     SCRT(l1_st1);
  38.     SCRT(l1_st2);
  39.   }
  40.   SCRE

  41.   LSCR(l1_st1)
  42.   if (delayRelay(mc, 1000))
  43.   {
  44.     SCRT(l1_st3);
  45.   }
  46.   SCRE

  47.   LSCR(l1_st2)
  48.   if (delayRelay(mc, 3000))
  49.   {
  50.     SCRT(l1_st4);
  51.   }
  52.   SCRE

  53.   LSCR(l1_st3)
  54.   if (raiseEdge(mc))
  55.   {
  56.     Serial.println("st3");
  57.   }
  58.   SCRE

  59.   LSCR(l1_st4)
  60.   if (readSm(l1_st3) && readSm(l1_st4))
  61.   {
  62.     SCRT(l1_st5);
  63.     rstSm(l1_st3);
  64.     rstSm(l1_st4);
  65.     Serial.println("st3,4");
  66.   }
  67.   SCRE

  68.   LSCR(l1_st5)
  69.   if (delayRelay(mc, 1000))
  70.   {
  71.     SCRT(l1_st0);
  72.     Serial.println("reStart");
  73.   }
  74.   SCRE

  75.   //loop2是一个选择分支结构,当8端口的按键不按下时进入分支1,当8端口的按键按下时进入分支2。
  76.   LSCR(l2_st0)
  77.   if (!digitalRead(8))
  78.   {
  79.     SCRT(l2_st2);
  80.   }
  81.   else
  82.   {
  83.     SCRT(l2_st1);
  84.   }
  85.   SCRE

  86.   LSCR(l2_st1)
  87.   if (raiseEdge(mc))
  88.   {
  89.     Serial.println("key up,way0");
  90.   }
  91.   if (delayRelay(mc, 1000))
  92.   {
  93.     SCRT(l2_st3);
  94.   }
  95.   SCRE

  96.   LSCR(l2_st2)
  97.   if (raiseEdge(mc))
  98.   {
  99.     Serial.println("key down,way1");
  100.   }
  101.   if (delayRelay(mc, 1000))
  102.   {
  103.     SCRT(l2_st3);
  104.   }
  105.   SCRE

  106.   LSCR(l2_st3)
  107.   if (delayRelay(mc, 1000))
  108.   {
  109.     SCRT(l2_st0);
  110.     Serial.println("reStart");
  111.   }
  112.   SCRE

  113. }
复制代码

这个是loop()中的三个状态循环结构,delayRelay是无阻塞的,因此多个状态机构成的循环可以并行运行,在setup()中注释或开启可以单独观察每个状态。

取出单个状态来解释一下状态机的工作:
  1.   LSCR(l0_st0)//这个是loop0的状态0,状态开始标志。
  2.   if (raiseEdge(mc))//mc类比plc中的母线,代表状态机的执行状态,当状态转移时会延迟一个周期,用于进行定时器等的复位操作,此处检测状态开始时的上升沿,打印状态。
  3.   {
  4.     Serial.println("Sta0");
  5.   }
  6.   if (delayRelay(mc, 1000))//状态机延时1000ms后转跳至loop0的状态1。
  7.   {
  8.     SCRT(l0_st1);//状态转移的同时会自动复位本状态,同样新转移的状态会延迟一个周期用于初始化定时器等操作。
  9.   }
  10.   SCRE//状态结束标识符
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

 楼主| 发表于 2016-5-19 00:25:12 | 显示全部楼层
太晚了,有时间再细说。
回复 支持 反对

使用道具 举报

发表于 2016-5-19 09:30:25 | 显示全部楼层
这个不错哦! 等待楼主细说。
回复 支持 反对

使用道具 举报

发表于 2016-5-19 16:18:11 | 显示全部楼层
学习学习学习学习
回复 支持 反对

使用道具 举报

发表于 2016-5-19 16:25:50 | 显示全部楼层
这东西也太强了吧?做PLC
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-5-21 11:13:29 来自手机 | 显示全部楼层
顶一下,顶一下
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-25 22:15 , Processed in 0.057477 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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