程序主体

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

图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:

图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逻辑也产生对应的变化。

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

  1. python实现udp聊天室_python网络编程基础--socket的简介,以及使用socket来搭建一个简单的udp小程序...

    socket介绍: socket(简称套接字),是进程间通讯的一个工具,他能实现把数据从一方传输到另一方,完成不同电脑上进程之间的通讯,它好比数据的搬运工. socket应用: 不夸张来说,只要跟网络 ...

  2. 《Python核心编程》练习题之2-7:创建一个简单的半双工聊天程序。半双工,就是指建立一个连接且服务开始后,一次只能一端发消息,不能同时对发消息。一个参与者在服务器一侧,另一位在客户端一侧

    按照题目的意思,我写的程序如下: 服务器端程序: import socketHOST = '' # Symbolic name meaning all available interfaces POR ...

  3. 二级c语言上机编程技巧,二级C语言上机编程题技巧总结

    二级C语言上机编程题技巧 一. 方法总结 1. 二级C语言上机编程题在二级上机考试中属于较难题型,因此很多同学都害怕通过不了.综合 往年的考试,结合考试大纲,每年考试的编程题都有一定的规律和方法,只要 ...

  4. 零基础学浙大翁恺C语言(1):Dev C++的安装与第一个小程序

    零基础学C语言(1):Dev C++的安装与第一个小程序 在线C语言开发环境:https://clin.icourse163.org/ 本地安装Dev C++:https://sourceforge. ...

  5. UNIX网络编程笔记(2):一个简单的时间获取程序

    这一讲通过一个简单的时间获取程序简单介绍套接字编程. 1.套接字API 1.1.套接字地址结构 上一讲中介绍了TCP的一些内容,知道了一个套接字对唯一标识了网络中的一个TCP连接,而一个套接字标识了一 ...

  6. 电脑用java怎么编程,使用java编程从0到1实现一个简单计算器

    学习编程语言最重要的就是实践.很多小伙伴在学习完编程语言后,一直停留在基础阶段,只懂一大堆理论知识,而不懂得实践.那么,今天我们一起来动手做一个小计算器,回顾下学习过的知识,同时这也是很多大学计算机专 ...

  7. C语言怎样编程分子变化,C语言经典编程(一)

    <C语言经典编程(一)>由会员分享,可在线阅读,更多相关<C语言经典编程(一)(20页珍藏版)>请在人人文库网上搜索. 1.C语言经典编程(一)[程序1]题目:古典问题:有一对 ...

  8. 汇编和c语言混合编程缺点,汇编C语言混合编程经验总结

    ARM汇编语言和C语言混合编程 ATPCS规灾了一种模块化设计的观念,其基本内容是C模块(函数)和汇编模块(函数)相互的一套规?中还有类似的一套规晕腋芯谙呋惚喙δ芮看用有很多忌讳),厅知道(很寄几它/ ...

  9. 简述arm汇编和c语言混合编程,ARM汇编C语言混合编程

    3.4 ARM汇编&C语言混合编程 3.4.1 C内联ARM汇编 先看一个例子: # cat add.c 1 // add.c for s3c2410c board 2 // arm-linu ...

  10. C语言/C++编程学习:C语言环境设置

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

最新文章

  1. Ferris教程学习笔记:js示例2.13 简易JS年历
  2. 《程序员代码面试指南》第二章 链表问题 构造链表和节点的实体
  3. BlinkOn9 - Viz Update
  4. 用PB从ORACLE导出DBF文件,PB导出规定格式DBF文件
  5. namenode与datanode
  6. Vijos P1696 数与连分数【连分数】
  7. 如何给multicraft装PHP,我的世界Linux搭建Multicraft网页后台教程更新和添加服务端文件...
  8. python用pip安装numpy mac_小白入门Python,mac下如何安装pip、ipython
  9. java string 内存占用_JVM系列之:String,数组和集合类的内存占用大小
  10. 机器学习算法总结--决策树
  11. tga文件怎么打开_教你win10系统怎么打开stp文件
  12. MUI框架:栅格系统 + grid宫格布局 - 案例篇
  13. (内联元素和块级元素)
  14. Linux内核小笔记:spin_lock锁内不能使用sleep休眠
  15. linux之间远程拷贝文件
  16. 怎么用计算机算全勤奖,全勤奖计算与发放细则
  17. 试试用word发博客
  18. 字节跳动面试题后台_JAVA字节跳动面试题分享,一面
  19. 微信小程序服务器.net,微信小程序登陆 .net 后端实现
  20. 浅析帧、帧数、帧率、FPS区别

热门文章

  1. 利用PS 调整 pdf清晰度
  2. 智能家居无线系统为代表的Zigbee和Z-Wave协议的介绍,有线和无线的各自优势?
  3. java office在线预览_Office文件在线预览(JAVA)
  4. centos7安装kylo0.10.1
  5. MATLAB上根号怎么输入,在matlab中怎么输入特殊符号 matlab或运算符怎么打
  6. JMeter详细使用手册
  7. 三调 图斑地类面积_关于三调,国土空间规划中至少需要这些知识
  8. JS实现div随鼠标移动练习
  9. 物联数采网关在电力能效管理系统中的应用
  10. google gflags 库完全使用