> Arduino
查看: 3888|回复: 2

[进阶教程] arduino宏的应用实例6--走火入魔篇--用宏实现状态机框架

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



[size=0.83em]2016-5-18 23:54 上传
下载附件 [size=0.83em](189.29 KB)





这个就是实现状态机框架的宏定义
  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()中注释或开启可以单独观察每个状态。
[size=0.83em]2016-5-19 10:47 上传
下载附件 [size=0.83em](101.95 KB)




取出单个状态来解释一下状态机的工作:
  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//状态结束标识符
复制代码


dsweiliang  版主

发表于 2016-6-11 11:38:33

刚才删掉的帖子回复我什么了,我看不到啊
回复 支持 反对

使用道具 举报

大连林海  初级技神
 楼主|

发表于 2016-6-11 21:27:23

dsweiliang 发表于 2016-6-11 11:38
刚才删掉的帖子回复我什么了,我看不到啊

我说 重复 我就删除吧
回复 支持 反对

使用道具 举报

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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
wifi气象站

硬件清单

btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2019 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail