大连林海 发表于 2016-6-11 09:00:26

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

如何用宏实现一个状态机框架?
我在这里参考了西门子s7-200 plc的stl编程样式。
首先来看看工控中状态转移图的实现方式:
http://image.geek-workshop.com/forum/201605/18/235409cnkmfkdvi2cwllkl.png
http://image.geek-workshop.com/forum/201605/18/235410iknm67k7bssnh6ks.png
http://image.geek-workshop.com/forum/201605/18/235410qm8ii5rp8pmzgvep.png
http://image.geek-workshop.com/forum/201605/18/235411ysiyr6b31zz71fbr.png 2016-5-18 23:54 上传
下载附件 (189.29 KB)




这个就是实现状态机框架的宏定义
//定义状态机切换的四种状态
enum sta {idel, waitLive, live, waitIdel};
//状态的总数设定,根据需要修改状态总数
#define smMum 100
//全局状态变量
sta s;
//状态开始符,用的时候后面不要加";"
#define LSCR(i) {\
    int index=i;\
    bool mc=s==live ;\
    if(s!=idel){\
      if(s==waitLive){s=live;}\
      if(s==waitIdel){s=idel;}
//状态转移
#define SCRT(t) ({s=waitIdel,s=waitLive;})
//状态结束符
#define SCRE }}
//设置状态
#define setSm(i) s=waitLive
//重置状态
#define rstSm(i) s=waitIdel
//读取状态
#define readSm(i) ({s==live;})
这个是延时继电器的宏定义,可以参考上个帖子。//宏名:delayRelay。返回值:bool 到达时间。输入值:enable使能,del延时时间。作用:延时继电器,当enable使能后,延时del毫秒后接通。
//static unsigned long tim = ({Serial.println("start"), millis();}); //这句用于测试是否能在static 变量设置的时候加入只执行一次的初始化代码块。
#define delayRelay(enable,del) ({\
    static unsigned long tim = millis();\
    if (!enable) {\
      tim = millis();\
    }\
    millis() - tim >= del;\
})

这个是边沿检测的宏定义,可以参考上上个帖子//逻辑量的四种状态:低,上升,高,低
enum edgeSta {low, trg, high, pop};
//宏名:edge。 返回值:逻辑量状态。输入参数:bool值 用途:检测逻辑量的边沿。
#define edge(ReadData) ({\
    static bool Trg = false;\
    static bool Cont = false;\
    static bool Pop = false;\
    Trg = ReadData & (ReadData ^ Cont); \
    Pop = Cont != ReadData & !Trg; \
    Cont = ReadData; \
    enum edgeSta sta = low;\
    if (Cont) {\
      sta = high;\
    } else {\
      sta = low;\
    }\
    if (Trg) {\
      sta = trg;\
    }\
    if (Pop) {\
      sta = pop;\
    }\
    sta;\
})
#define raiseEdge(ReadData) ({edge(ReadData)==trg;})

这个是主程序//自定义状态机的状态
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.....
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
setSm(l0_st0);
//setSm(l1_st0);
//setSm(l2_st0);
pinMode(8, INPUT_PULLUP);   //pin8接按键
}
void loop() {
// put your main code here, to run repeatedly:

//loop0是一个简单的无限循环,状态0,1,0,1。。。。。
LSCR(l0_st0)
if (raiseEdge(mc))
{
    Serial.println("Sta0");
}
if (delayRelay(mc, 1000))
{
    SCRT(l0_st1);
}
SCRE

LSCR(l0_st1)
if (raiseEdge(mc))
{
    Serial.println("Sta1");
}
if (delayRelay(mc, 1000))
{
    SCRT(l0_st0);
}
SCRE

//loop1是一个并行分支结构
LSCR(l1_st0)
if (mc) {
    SCRT(l1_st1);
    SCRT(l1_st2);
}
SCRE

LSCR(l1_st1)
if (delayRelay(mc, 1000))
{
    SCRT(l1_st3);
}
SCRE

LSCR(l1_st2)
if (delayRelay(mc, 3000))
{
    SCRT(l1_st4);
}
SCRE

LSCR(l1_st3)
if (raiseEdge(mc))
{
    Serial.println("st3");
}
SCRE

LSCR(l1_st4)
if (readSm(l1_st3) && readSm(l1_st4))
{
    SCRT(l1_st5);
    rstSm(l1_st3);
    rstSm(l1_st4);
    Serial.println("st3,4");
}
SCRE

LSCR(l1_st5)
if (delayRelay(mc, 1000))
{
    SCRT(l1_st0);
    Serial.println("reStart");
}
SCRE

//loop2是一个选择分支结构,当8端口的按键不按下时进入分支1,当8端口的按键按下时进入分支2。
LSCR(l2_st0)
if (!digitalRead(8))
{
    SCRT(l2_st2);
}
else
{
    SCRT(l2_st1);
}
SCRE

LSCR(l2_st1)
if (raiseEdge(mc))
{
    Serial.println("key up,way0");
}
if (delayRelay(mc, 1000))
{
    SCRT(l2_st3);
}
SCRE

LSCR(l2_st2)
if (raiseEdge(mc))
{
    Serial.println("key down,way1");
}
if (delayRelay(mc, 1000))
{
    SCRT(l2_st3);
}
SCRE

LSCR(l2_st3)
if (delayRelay(mc, 1000))
{
    SCRT(l2_st0);
    Serial.println("reStart");
}
SCRE

}
这个是loop()中的三个状态循环结构,delayRelay是无阻塞的,因此多个状态机构成的循环可以并行运行,在setup()中注释或开启可以单独观察每个状态。
http://image.geek-workshop.com/forum/201605/19/104740xktn3pd440e550bp.jpg 2016-5-19 10:47 上传
下载附件 (101.95 KB)



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


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

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

大连林海 发表于 2016-6-11 21:27:23

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

我说 重复 我就删除吧
页: [1]
查看完整版本: arduino宏的应用实例6--走火入魔篇--用宏实现状态机框架