c语言正三角形编程plc,02-C语言实现一个简单PLC仿真小程序(下)

本文介绍了如何构建一个简单的PLC(可编程逻辑控制器)模拟器,包括创建初始界面,定义输入输出数组,实现按键监控,以及编写程序解析器来执行LD和OUT指令。模拟器通过键盘输入模拟输入信号,输出信号根据输入信号实时更新。最后,文章展示了如何扩展程序以读取外部程序文件,实现了更复杂的逻辑控制。
摘要由CSDN通过智能技术生成

程序主体

第一步,要在控制台上实现一个初始画面,假定这个仿真PLC具有8个输入点,8个输出点,为X0-X7,Y0-Y7,我希望实现如图1的界面:

17756d1f148e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

图1.PNG

第一行的 * 表示 数字量输入点 的信号,同时也表示该点的指示灯,第二行 * 表示 数字量输出点 的信号。

需要使用到 printf()函数,代码如下:

#include

void initial()

{

printf("########################################\n");

printf("\n");

printf("\n");

printf(" PLC SIMULATOR \n");

printf("\n");

printf("\n");

printf(" * * * * * * * * \n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" # #\n");

printf(" # #\n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" * * * * * * * * \n");

printf("\n");

printf("\n");

printf("****************************************");

}

int main()

{

initial();

//gotoxy(0, 0);

return 0;

}

接下来,定义两个长度为8的数组,以这两个数组作为输入输出的数据存储区,如下:

int input[8];

int ouput[8];

当值为 0 时表示对应的 IO 点的信号为 FALSE,当值为 1 时表示对应的 IO 点的信号为 TRUE;

同时,将 initial() 函数修改为用于打印显示的 show() 函数,并重写一个 initial() 函数来进行数据存储区的初始化,然后调用 show() 函数进行打印。

另外,也要编写一个 gotox() 函数来将光标移动到界面的第一行第一个列,这样在循环打印时就相当于完成了界面的刷新,程序如下:

#include

#include

#include

// IO array

int input[8];

int output[8];

void show()

{

system("cls");

printf("########################################\n");

printf("\n");

printf("\n");

printf(" PLC SIMULATOR \n");

printf("\n");

printf("\n");

printf(" ");

for(int i=0; i<8; i++)

{

if(!input[i])

printf(" ");

else

printf("* ");

}

printf(" \n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" # #\n");

printf(" # #\n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" ");

for(int i=0; i<8; i++)

{

if(!output[i])

printf(" ");

else

printf("* ");

}

printf(" \n");

printf("\n");

printf("\n");

printf("****************************************");

}

void initial()

{

for(int i=0; i<8; i++)

{

input[i]= 0;

output[i]= 0;

}

show();

}

int main()

{

initial();

return 0;

}

运行后的初始化界面如图2:

17756d1f148e?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

图2.PNG

因为程序在控制台上执行,所以如果要做按键监控来监测输入信号比较麻烦,所以这里不做监测,仅简单的等待键盘输入信号即可。

首先实现一个按键的信号,程序如下:

#include // printf()

#include // system()

#include // getch()

#include

// IO array

int input[8];

int output[8];

void show()

{

system("cls");

printf("########################################\n");

printf("\n");

printf("\n");

printf(" PLC SIMULATOR \n");

printf("\n");

printf("\n");

printf(" ");

for(int i=0; i<8; i++)

{

if(input[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" # #\n");

printf(" # #\n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" ");

for(int i=0; i<8; i++)

{

if(output[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf("\n");

printf("\n");

printf("****************************************\n");

}

void initial()

{

for(int i=0; i<8; i++)

{

input[i] = 0;

output[i] = 0;

}

show();

}

int checkInput(int inSignal)

{

inSignal = inSignal - 48;

if(inSignal<0 || inSignal>7)

return 0;

input[inSignal] = !input[inSignal];

return 1;

}

int main()

{

initial();

int inSignal;

int tmp=1;

inSignal = getch();

tmp = checkInput(inSignal);

show();

return 0;

}

以上程序仅实现输入信号的打印,下一步是实现输入信号接通,对应序号的输出信号也接通的效果,类似于三菱PLC执行程序:

LD X0

OUT Y0

LD X1

OUT Y1

···

LD X7

OUT Y7

实现代码如下:

#include // printf()

#include // system()

#include // getch()

#include

// IO array

int input[8];

int output[8];

void show()

{

system("cls");

printf("########################################\n");

printf("\n");

printf("\n");

printf(" PLC SIMULATOR \n");

printf("\n");

printf("\n");

printf(" ");

for(int i=0; i<8; i++)

{

if(input[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" # #\n");

printf(" # #\n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" ");

for(int i=0; i<8; i++)

{

if(output[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf("\n");

printf("\n");

printf("****************************************\n");

}

void initial()

{

for(int i=0; i<8; i++)

{

input[i] = 0;

output[i] = 0;

}

show();

}

int checkInput(int inSignal)

{

inSignal = inSignal - 48;

if(inSignal<0 || inSignal>7)

return 0;

input[inSignal] = !input[inSignal];

return 1;

}

void programRun()

{

for(int i=0; i<8; i++)

{

output[i] = input[i];

}

}

int main()

{

initial();

int inSignal;

int tmp=1;

inSignal = getch();

tmp = checkInput(inSignal);

programRun();

show();

return 0;

}

到此,程序在按下0-7按键之后,对应的输入、输出点会接通,但是还无法循环执行,现在将程序修改为循环执行的程序,按下0-7后对应的点会取反,按下其他按键后程序退出,仅需要修改 main()函数,如下:

int main()

{

initial();

int inSignal;

int tmp=1;

while(tmp)

{

inSignal = getch();

tmp = checkInput(inSignal);

programRun();

show();

}

return 0;

}

解释器

接下来是程序语句解释器的部分。

以指令表(IEC61131-3标准中称为 IL 语言)为基础,定义 FNC值 与 指令助记符 的对应关系,如下表:

· 0 - END

· 1 - LD

· 2 - OUT

目前先实现这两个指令的解释器,来将上一步的程序完善好。

作为测试程序,这里就不采用申请内存空间的做法来定义程序存储区,而是直接用一个长度为 100 的数组替代,如:int proData[100];

按三菱 PLC 的编程习惯,取 X0 的指令为 LD X0,输出 Y0 的指令为 OUT Y0,那么它们的数据应该分别为 1, x 和 2, x。

这里有个问题,如果直接将 X0 和 Y0 都定为 0,这样是不行的,所以要么将指令扩展为 1, 0, 0 和 2, 1, 0 来区分 X0 和 Y0,要么是像 PLC 中的存储一样,将IO点映射到内存中,就像 0000~0007 表示 X0X7,00080015表示 Y0~Y7 这样的定义方式。

这里采用第一种方式,即三个数据,一个表示指令,一个表示数据类型,一个表示数据。

· 0 - 输入位

· 1 - 输出位

这样,我们要写一个输入映射到输出的程序,指令表的程序代码为:

LD X0

OUT Y0

LD X1

OUT Y1

LD X2

OUT Y2

LD X3

OUT Y3

LD X4

OUT Y4

LD X5

OUT Y5

LD X6

OUT Y6

LD X7

OUT Y7

如果写一个编译器,编译出来的程序应该是这样的:

1 0 0

2 1 0

1 0 1

2 1 1

1 0 2

2 1 2

1 0 3

2 1 3

1 0 4

2 1 4

1 0 5

2 1 5

1 0 6

2 1 6

1 0 7

2 1 7

为了方便测试起见,直接定义程序代码,为:

int programZrea[1000] = {

1, 0, 0,

2, 1, 0,

1, 0, 1,

2, 1, 1,

1, 0, 2,

2, 1, 2,

1, 0, 3,

2, 1, 3,

1, 0, 4,

2, 1, 4,

1, 0, 5,

2, 1, 5,

1, 0, 6,

2, 1, 6,

1, 0, 7,

2, 1, 7

};

这个就作为程序存储区,相当于编译好的PLC程序。

然后为了翻译程序语句,实现一个虚拟机功能,按汇编语言的处理方式,需要定义一个寄存器,如下:

int regA;

仿真工作的顺序是,先判断输入区信号,然后扫描PLC程序,这里使用 programRun() 函数来读取PLC程序用于扫描,扫描PLC程序的时候按行执行,即读一行程序,然后调用解析器函数 Language(int command, int type, int data),返回后再读下一行程序,这样循环,直到解析器判断到读取的程序指令是 0,表示 END,结束循环,programRun() 函数返回。

解析器函数 Language(int command, int type, int data) 先判断第一个数据,即指令,根据指令调用对应的处理函数。

指令 0 表示 END,程序结束,直接返回;

指令 1 表示 LD 指令,调用取指令处理函数 LDcommand(int type, int data),该函数返回取得的数值;

指令 2 表示 OUT 指令,调用输出指令处理函数 OUTcommand(int type, int data),如果正常返回 0,如果错误返回 1。

这部分的程序代码如下:

// 取指令处理函数

int LDcommand(int type, int data)

{

int reData;

switch(type)

{

case 0: reData = input[data]; break; // 取输入寄存器的数值

case 1: reData = output[data]; break; // 取输出寄存器的数值

default: reData = 0;

}

return reData;

}

// 输出指令处理函数

int OUTcommand(int type, int data)

{

int reData = 0;

switch(type)

{

case 0: reData = 1; break; // 输入寄存器的数值是不能修改的

case 1: output[data] = regA; break; // 将寄存器A的数值输出到输出寄存器

default: reData = 1;

}

return reData;

}

// 解析器

int Language(int command, int type, int data)

{

int endFlag = 1;

switch(command)

{

case 0: endFlag = 0; break; // END 指令

case 1: regA = LDcommand(type, data); break; // LD 取指令 调用 取指令处理函数

case 2: OUTcommand(type, data); break; // OUT 输出指令 调用 输出指令处理函数

default: endFlag = 0;

}

return endFlag;

}

// 扫描程序

void programRun()

{

int pointToPro = 0;

int proCommand;

int proType;

int proData;

int endFlag = 1;

while(endFlag)

{

// 取一行程序

proCommand = programZrea[pointToPro];

proType = programZrea[pointToPro+1];

proData = programZrea[pointToPro+2];

// 调用解析器

endFlag = Language(proCommand, proType, proData);

// 指针下移

pointToPro = pointToPro + 3;

}

}

最后一步是加入文件读取功能,完成后的整体程序如下:

#include // printf()

#include // system()

#include // getch()

#include

// 输入输出存储区

int input[8];

int output[8];

// 程序存储区

int programZrea[1000] = {

1, 0, 0,

2, 1, 0,

1, 0, 1,

2, 1, 1,

1, 0, 2,

2, 1, 2,

1, 0, 3,

2, 1, 3,

1, 0, 4,

2, 1, 4,

1, 0, 5,

2, 1, 5,

1, 0, 6,

2, 1, 6,

1, 0, 7,

2, 1, 7

};

int regA; // 寄存器A

// 刷新屏幕函数

void show()

{

#ifdef _WIN32

system("cls");

#endif

printf("########################################\n");

printf("\n");

printf("\n");

printf(" PLC SIMULATOR \n");

printf("\n");

printf("\n");

printf(" ");

for(int i=0; i<8; i++)

{

if(input[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" # #\n");

printf(" # #\n");

printf(" # 0 1 2 3 4 5 6 7 # \n");

printf(" ");

for(int i=0; i<8; i++)

{

if(output[i])

printf("* ");

else

printf(" ");

}

printf(" \n");

printf("\n");

printf("\n");

printf("****************************************\n");

}

// 初始化函数

void initial()

{

for(int i=0; i<8; i++)

{

input[i] = 0;

output[i] = 0;

}

// 初始化完成后要刷新一次

show();

}

// 检查输入

int checkInput(int inSignal)

{

inSignal = inSignal - 48;

if(inSignal<0 || inSignal>7)

return 0;

input[inSignal] = !input[inSignal];

return 1;

}

// LD 指令执行函数

int LDcommand(int type, int data)

{

int reData;

switch(type)

{

case 0: reData = input[data]; break;

case 1: reData = output[data]; break;

default: reData = 0;

}

return reData;

}

// OUT 指令执行函数

int OUTcommand(int type, int data)

{

int reData = 0;

switch(type)

{

case 0: reData = 1; break; // input register cannot out

case 1: output[data] = regA; break; // out value to output register

default: reData = 1;

}

return reData;

}

// 解析器

int Language(int command, int type, int data)

{

int endFlag = 1;

switch(command)

{

case 0: endFlag = 0; break;

case 1: regA = LDcommand(type, data); break;

case 2: OUTcommand(type, data); break;

default: endFlag = 0;

}

return endFlag;

}

// 扫描器

void programRun()

{

int pointToPro = 0;

int proCommand;

int proType;

int proData;

int endFlag = 1;

while(endFlag)

{

proCommand = programZrea[pointToPro];

proType = programZrea[pointToPro+1];

proData = programZrea[pointToPro+2];

endFlag = Language(proCommand, proType, proData);

pointToPro = pointToPro + 3;

}

}

// 读取程序文件

int openPro()

{

char path[50];

printf("Please Input Your Program File Path: ");

scanf("%s", &path);

FILE *fp;

if((fp=fopen(path, "r"))== NULL)

return 1;

char ch;

int num;

int proPoint = 0;

while((ch=fgetc(fp))!=EOF)

{

num = ch - 48;

if(num>9 || num<0)

continue;

programZrea[proPoint] = num;

proPoint++;

}

fclose(fp);

return 0;

}

int main()

{

// 打开文件

int reError =openPro();

while(reError)

{

printf("Path ERROR!\n");

printf("Please Input a Program File Path: ");

reError =openPro();

}

// 初始化

initial();

int inSignal;

int tmp=1;

while(tmp)

{

// 获取按键信号

inSignal = getch();

// 检查输入

tmp = checkInput(inSignal);

// 扫描 PLC 程序

programRun();

// 刷新

show();

}

return 0;

}

然后,写一个 txt 文档作为 PLC 程序,内容为:

1, 0, 0,

2, 1, 0,

1, 0, 1,

2, 1, 1,

1, 0, 2,

2, 1, 2,

1, 0, 3,

2, 1, 3,

1, 0, 4,

2, 1, 4,

1, 0, 5,

2, 1, 5,

1, 0, 6,

2, 1, 6,

1, 0, 7,

2, 1, 7,

2, 1, 4

执行程序进行测试,输入 PLC程序 文件路径,通过 0-7 按键来接通、断开输入信号,修改 PLC程序,可以看到 PLC逻辑也产生对应的变化。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值