基于状态机的简单控制:
公司是做一些简单的夹具的,大部分功能是几个按键加上几个电磁阀,再加一个显示屏就构成了一个简单的控制系统。
工控行业一般要求是比较稳定的,所以我需要在启动电磁阀之后去判断我的气缸是否到了指定位置,这样我们就需要用到了Sensor,那么问题来了
首先我按下了按键,气缸也动作了,接下来我就要等待气缸到达位置,之前一直用的是while(Sensor),但是这样就出现了一个问题,气缸压着手了,气缸没到位就一直等待,而我需要急停,可是我的这个急停按钮检测放在哪里呢?
1:放在中断中,这个方法无疑是最快的,可以立刻关闭电磁阀,但是,关闭电磁阀之后呢,很明显是退出中断,继续执行之前的程序,这很明显不是我想要的结果,因为既然急停了,就说明我这里有情况了,要么产品有问题,要么外部环境有阻碍,理想状态下应该是复位才对!
2:用一个定时器每间隔多久采集一次,这个问题和外部中断的问题是一样的。
下面就引入我要介绍的这种基于状态机的控制,状态机想必大家都知道了
举个最简单的例子。人有三个状态健康,感冒,康复中。触发的条件有淋雨(t1),吃药(t2),打针(t3),休息(t4)。所以状态机就是健康-(t4)->健康;健康-(t1)->感冒;感冒-(t3)->健康;感冒-(t2)->康复中;康复中-(t4)->健康,等等。就是这样状态在不同的条件下跳转到自己或不同状态的图。
那么我们的触发条件就是Sensor到位,定时器到时间(这里我们有数码管按时刷新),等等
这样的话,我们的主程序只需要去switch状态机,然后根据状态机做动作就行了,这样的好处就是我们不需要去等待气缸到位
case 3:Y2=0;ActionStatus=4;break;
case 4:if(!X7) {ActionStatus=5;};break;
case 6:Y2=1;ActionStatus=7;break;
case 7:if(!X6) ActionStatus=0; break;
有代码可以看出Y2是打开气缸,x7是气缸sensor,这样的话,主程序便有大把时间去干其他事了!
那么,我们的主程序里或许还要去检测其他按键,这样我们的按键处理可以用消息机制来处理
void SysTick_Handler(void)//10ms中断
{
static uint32_t time,temp;
KeyCheck();
if(ActionStatus==2)
{
time++;
if(time>=100)
{
ActionStatus=3;
time=0;
}
}else
if(ActionStatus==5)
{
temp++;
if(temp>=100)
{
temp=0;
KeepPreTimeCount--;
if(KeepPreTimeCount<1)
{
KeepPreTimeCount=KeepPreTime;
ActionStatus=6;
}
OLED_8x16Str(0,2,ToStr(KeepPreTimeCount));
}
}else
if(ActionStatus==0x0a)//stop
{
time=0;
temp=0;
KeepPreTimeCount=KeepPreTime;
OLED_8x16Str(0,2,ToStr(KeepPreTimeCount));
ActionStatus=0xa6;
}
}
switch(Messange)
{
case 3:ActionStatus=0x0a;Messange=0;break;
case 11: KeepPreTime++;FreshTime();Messange=0;break;
case 12: KeepPreTime--;FreshTime();Messange=0;break;
default:break;
}
我们用10ms中断去检测按键,同时也做了消抖处理,不知道大家是否看了出来
Switch message仍然放在主函数中去判断,进行处理,如果有数码管需要进行刷新的话也可以在定时器中发出一个消息,然后在主程序中进行处理,这样免得在中断中耽误太多时间!
回归到我们刚开始提出的中断,通过程序可以看出急停按下后会发出消息,主程序会立刻对消息进行处理,然后可以改变当前状态,这样很容易就改变了程序的运行方向
偶然心血来潮总结了自己的历程,希望对大家有所帮助!