梯形图显示到编译--软PLC的实现

梯形图显示到编译--软PLC的实现

PLC(Programmable Logic Controller 可编程逻辑控制器)是在工业环境下使用的数字逻辑操作系统。其编程语言有我们最为熟悉的梯形图,本篇文章将从梯形图的原理、编译、运行,实现一个软PLC。

一、基本原理

梯形图可以理解为一个电路图,通过梯形图上的元件的通断来控制整个程序逻辑。元件的状态只有“通”和“断”;元件的关系可以分为“与”、“或”、“非”。是不是似曾相识?这里我们把含有逻辑关系的元件当做一个逻辑块;通过这些逻辑块最终输出到元件——输出块

如下图:

图1 简单梯形图

从母线出发,连接逻辑块到输出块的通路称为输出图。如上图为一个输出图

只有一个逻辑块和输出块的图称为最简图,如下图所示:

图2 最简图

有了上面的基本原理,接下来就可以抽象梯形图,从而实现梯形图的显示和编译。

二、算法原理

2.1 算法思路

通过自上而下,从左到右遍历梯形图,找到每一个块,根据块与块间的逻辑关系——与或非,只要确定了两个块的关系,就可以将这两个块合并变成一个块,以此的处理方式一直重复,直至剩下一个逻辑块和一个输出块,那么算法就结束了。

当然还需要异常处理,这个不在这里讨论。

2.2 算法的伪代码

  1. 初始状态将每个元件当做一个逻辑块或者输出块。
  2. 取出一个输出图。
  3. 合并相邻输出块、输入块
  4. 如果合并后为最简图则返回第2步取下一个输出图,不是则继续3步

伪代码如下:

initGraph();                       //步骤1

while( getNextOutputGraph() )      //步骤2
{
	while( !isTheSimpleGraph() )   //步骤4
	{
		shiftGraph();              //步骤3
	}
    outputPLCInstructions();       //输出转换的指令
}

2.3 算法示意

  1. 首先取出一个输出图,刚好图一就是一个完整的输出图,标记X000、M001、M000为3个逻辑块,Y001、Y002为2个逻辑块
  2. 判断是否为最简图,有5个块,所以不是最简图
  3. 合并X000、M000为一个逻辑块,标记为Block0,同时记录OR关系
  4. 判断是否为最简图,有4个块,所以不是最简图
  5. 合并Block0、M001为一个逻辑块,标记为Block1,同时记录ANDP关系
  6. 判断是否为最简图,有3个块,所以不是最简图
  7. 合并Y001、Y002为一个输出块,标记为Block2,同时记录输出关系(合并只能合并同种属性的块,所以Block1块没有逻辑块可以合并了)
  8. 判断是否为最简图,一个逻辑看,一个输出块,所以化简结束
  9. 输出每个块:

Block0 = LD X000; OR M000

Block1 = Block0 ANDP M001

            = LD X000; OR M000; ANDP M001

Block2 = OUT Y001; OUT Y002

Block1 => Block2  =  LD X000; OR M000; ANDP M001; OUT Y001; OUT Y002

注: =>表示输出到的意思,处理只要把左右两边的文本连接就可以        

图1的梯形图处理结果如下,输出PLC指令:

2.4 扩展思考

该算法的思路只针对简单的情况,实际还有嵌套的情况,比如在输出块里面又有一个逻辑块连接输出块。算法需要做一下处理,按照上面的算法先处理里面的逻辑块连接输出块即可。

三、运行

将上面编译出来的PLC指令,输入到PLCCore,使用解析运行将模拟PLC运行。图中灰色表示元件导通,双击元件可以改变元件状态,如果双击M001导通,则Y001和Y002导通一次。

下面是已经支持的指令表,PLCCore是在三菱PLC下参考制作,可以直接模拟三菱PLC的程序

static InsItem g_insItems[] = {

	{{dinsDEBUG},	sinsNOP-1,	"DEBUG",	1,0, ARG_COIL_G },

	{{dinsLD},		sinsLD,		"LD",		1,0, ARG_COIL_BIT},	
	{{dinsLDI},		sinsLDI,	"LDI",		1,0, ARG_COIL_BIT},
	{{dinsLDP},		sinsLDP,	"LDP",		1,0, ARG_COIL_BIT},
	{{dinsLDF},		sinsLDF,	"LDF",		1,0, ARG_COIL_BIT},
	{{dinsAND},		sinsAND,	"AND",		1,0, ARG_COIL_BIT},
	{{dinsANI},		sinsANI,	"ANI",		1,0, ARG_COIL_BIT},
	{{dinsANDP},	sinsANDP,	"ANDP",		1,0, ARG_COIL_BIT},
	{{dinsANDF},	sinsANDF,	"ANDF",		1,0, ARG_COIL_BIT},
	{{dinsOR},		sinsOR,		"OR",		1,0, ARG_COIL_BIT},
	{{dinsORI},		sinsORI,	"ORI",		1,0, ARG_COIL_BIT},
	{{dinsORP},		sinsORP,	"ORP",		1,0, ARG_COIL_BIT},
	{{dinsORF},		sinsORF,	"ORF",		1,0, ARG_COIL_BIT},
	{{dinsANB},		sinsANB,	"ANB",		0,0, 0},
	{{dinsORB},		sinsORB,	"ORB",		0,0, 0},
	{{dinsMPS},		sinsMPS,	"MPS",		0,0, 0},
	{{dinsMRD},		sinsMRD,	"MRD",		0,0, 0},
	{{dinsMPP},		sinsMPP,	"MPP",		0,0, 0},
	{{dinsINV},		sinsINV,	"INV",		0,0, 0},
	{{dinsMEP},		sinsMEP,	"MEP",		0,0, 0},
	{{dinsMEF},		sinsMEF,	"MEF",		0,0, 0},

	{{dinsOUT},		sinsOUT,	"OUT",		1,0, ARG_COIL_X | ARG_COIL_Y | ARG_COIL_M },
	{{dinsOUTT},	sinsOUTT,	"OUT",		2,0, ARG_COIL_T,ARG_COIL_DATA},
	{{dinsOUTC},	sinsOUTC,	"OUT",		2,0, ARG_COIL_C,ARG_COIL_DATA},
	{{dinsOUT},		sinsOUTMS,	"OUT",		1,0, ARG_COIL_M },
	{{dinsOUTS},	sinsOUTMS,	"OUT",		1,0, ARG_COIL_S },
	{{dinsSET},		sinsSET,	"SET",		1,0, ARG_COIL_X | ARG_COIL_Y | ARG_COIL_M },
	{{dinsSETS},	sinsSETMS,	"SET",		1,0, ARG_COIL_S },
	{{dinsSET},		sinsSETMS,	"SET",		1,0, ARG_COIL_M },
	{{dinsRST},		sinsRST,	"RST",		1,0, ARG_COIL_X | ARG_COIL_Y | ARG_COIL_M },
	{{dinsRSTTC},	sinsRSTMSTC,"RST",		1,0, ARG_COIL_T | ARG_COIL_C },
	{{dinsRSTS},	sinsRSTMSTC,"RST",		1,0, ARG_COIL_S },
	{{dinsRST},		sinsRSTMSTC,"RST",		1,0, ARG_COIL_M },
	{{dinsPLS},		sinsPLS,	"PLS",		1,0, ARG_COIL_X | ARG_COIL_Y | ARG_COIL_M | ARG_COIL_S },
	{{dinsPLF},		sinsPLF,	"PLF",		1,0, ARG_COIL_X | ARG_COIL_Y | ARG_COIL_M | ARG_COIL_S },

	{{dinsSTL},		sinsSTL,	"STL",		1,0, ARG_COIL_S},
	{{dinsRET},		sinsRET,	"RET",		0,0, 0},

	{{dinsMC},		sinsMC,		"MC",		1,0, ARG_COIL_N },
	{{dinsMCR},		sinsMCR,	"MCR",		1,0, ARG_COIL_N },

	{{dinsNOP},		sinsNOP,	"NOP",		0,0, 0},
	{{dinsEND},		sinsEND,	"END",		0,0, 0},
	
	{{dinsADD},		sinsADD,	"ADD",		3,0, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V },
	{{dinsSUB},		sinsSUB,	"SUB",		3,0, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V },
	{{dinsMUL},		sinsMUL,	"MUL",		3,0, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V },
	{{dinsDIV},		sinsDIV,	"DIV",		3,0, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V },
	{{dinsINC},		sinsINC,	"INC",		1,0, ARG_COIL_D | ARG_COIL_V },
	{{dinsDEC},		sinsDEC,	"DEC",		1,0, ARG_COIL_D | ARG_COIL_V },

	{{dinsMOV},		sinsMOV,	"MOV",		2,0, ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V },
	{{dinsCMP},		sinsCMP,	"CMP",		3,0, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_M},

	{{dinsCJ},		sinsCJ,		"CJ",		1,0, ARG_COIL_P},
	{{dinsCJP},		sinsCJP,	"CJP",		1,0, ARG_COIL_P},

	{{dinsSRET},	sinsSRET,	"SRET",		0,0, 0},
	{{dinsFEND},	sinsFEND,	"FEND",		0,0, 0},
	{{dinsCALL},	sinsCALL,	"CALL",		1,0, ARG_COIL_P},
	{{dinsP},		sinsP,		"P",		1,0, ARG_COIL_P},

	{{dinsDADD},	sinsDADD,	"DADD",		3,1, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V},
	{{dinsDSUB},	sinsDSUB,	"DSUB",		3,1, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V},
	{{dinsDMUL},	sinsDMUL,	"DMUL",		3,1, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V},
	{{dinsDDIV},	sinsDDIV,	"DDIV",		3,1, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V},
	{{dinsDINC},	sinsDINC,	"DINC",		1,1, ARG_COIL_D | ARG_COIL_V},
	{{dinsDDEC},	sinsDDEC,	"DDEC",		1,1, ARG_COIL_D | ARG_COIL_V},

	{{dinsDMOV},	sinsDMOV,	"DMOV",		2,1, ARG_COIL_DATA,ARG_COIL_D | ARG_COIL_V},
	{{dinsDCMP},	sinsDCMP,	"DCMP",		3,1, ARG_COIL_DATA,ARG_COIL_DATA,ARG_COIL_M},

	{{dinsLD_E},	sinsLD_E,	"LD=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_E},	sinsAND_E,	"AND=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_E},	sinsOR_E,	"OR=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLD_NE},	sinsLD_NE,	"LD<>",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_NE},	sinsAND_NE,	"AND<>",	2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_NE},	sinsOR_NE,	"OR<>",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLD_B},	sinsLD_B,	"LD>",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_B},	sinsAND_B,	"AND>",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_B},	sinsOR_B,	"OR>",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLD_EB},	sinsLD_EB,	"LD>=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_EB},	sinsAND_EB,	"AND>=",	2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_EB},	sinsOR_EB,	"OR>=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLD_S},	sinsLD_S,	"LD<",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_S},	sinsAND_S,	"AND<",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_S},	sinsOR_S,	"OR<",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLD_ES},	sinsLD_ES,	"LD<=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsAND_ES},	sinsAND_ES,	"AND<=",	2,0, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsOR_ES},	sinsOR_ES,	"OR<=",		2,0, ARG_COIL_DATA,ARG_COIL_DATA},


	{{dinsLDD_E},	sinsLDD_E,	"LDD=",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_E},	sinsANDD_E,	"ANDD=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_E},	sinsORD_E,	"ORD=",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLDD_NE},	sinsLDD_NE,	"LDD<>",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_NE},	sinsANDD_NE,"ANDD<>",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_NE},	sinsORD_NE,	"ORD<>",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLDD_B},	sinsLDD_B,	"LDD>",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_B},	sinsANDD_B,	"ANDD>",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_B},	sinsORD_B,	"ORD>",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLDD_EB},	sinsLDD_EB,	"LDD>=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_EB},	sinsANDD_EB,"ANDD>=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_EB},	sinsORD_EB,	"ORD>=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLDD_S},	sinsLDD_S,	"LDD<",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_S},	sinsANDD_S,	"ANDD<",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_S},	sinsORD_S,	"ORD<",		2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsLDD_ES},	sinsLDD_ES,	"LDD<=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsANDD_ES},	sinsANDD_ES,"ANDD<=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},
	{{dinsORD_ES},	sinsORD_ES,	"ORD<=",	2,1, ARG_COIL_DATA,ARG_COIL_DATA},

	{{dinsDDRVA},	sinsDDRVA,	"DDRVA",	4,1, ARG_COIL_DATA, ARG_COIL_DATA, ARG_COIL_Y, ARG_COIL_Y },
	{{dinsDDRVI},	sinsDDRVI,	"DDRVI",	4,1, ARG_COIL_DATA, ARG_COIL_DATA, ARG_COIL_Y, ARG_COIL_Y },
	{{dinsDRVA},	sinsDRVA,	"DRVA",		4,0, ARG_COIL_DATA, ARG_COIL_DATA, ARG_COIL_Y, ARG_COIL_Y },
	{{dinsDRVI},	sinsDRVI,	"DRVI",		4,0, ARG_COIL_DATA, ARG_COIL_DATA, ARG_COIL_Y, ARG_COIL_Y },
};

由于项目很久没有维护,PLCCore使用c语言编写,已经成功跑在Stm32上,windows模拟器也是同个PLCCore。但界面使用MFC,后面整理完会将项目开源。敬请期待。

四、写在最后

由于这个是半成品,也没有空维护,目前已经带领团队完成了IEC的编译型的PLC编程软件和runtime,引入了语法树和梯形图的逻辑树来翻译梯形图。本文写到的算法也算是入门,让更多感兴趣的朋友来了解PLC梯形图的翻译方法,还有感兴趣的可以留言或者私信,可以打包送,最近才有空上上CSDN,写写文字。

  • 27
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值