
简介面向工控开发与易语言爱好者的西门子PLC通讯实例围绕易语言通过PCOMM模块与西门子PLC建立数据交互覆盖数据到达、发送完毕、收到终止字符等核心事件以及时钟周期轮询、窗口初始化等典型处理流程适合需要快速上手易语言与PLC串口/以太网通讯的初中级开发者。包内共11个文件约118KB包含易语言源码(.e)、易模块(.ec)、PCOMM动态库(.dll)、西门子项目文件(.mwp)、说明文档与界面截图等源码结构清晰便于对照运行与二次修改。通过事件回调与时钟扫描结合的方式读者能理解上位机主动读取与被动响应的编程思路并借助配套的PLC工程和指示灯截图核对运行状态。已有734人学习下载资源轻量但闭环完整是一份值得参考的易语言与西门子PLC通讯范例。1. 这个实例到底在解决什么问题1.1 易语言在工控上位机里的定位先把这个项目说清楚。易语言通讯西门子PLC说白了就是让电脑上跑的一个中文编程写的程序通过串口或者网线和西门子的PLC以S7-200 SMART为代表交换数据。在很多中小型工厂、设备集成现场上位机不一定要用WinCC、组态王这种重量级组态软件很多时候只需要一个能读几个温度、写几个频率、记录一些产量的小工具。这种场景下易语言的优势就出来了——上手快界面拖拽就能出中文关键字读起来没有障碍一个懂PLC电气、稍微会点编程的人半天就能写个能用的读写器出来。我见过不少搞电气出身的朋友让他们去啃C#、Qt是有难度的但易语言这套东西下载安装就能跑按钮就是按钮编辑框就是编辑框逻辑判断用“如果”“判断”这种中文命令非常贴合这类工程师的思维习惯。再加上现在西门子S7-200 SMART在中小型设备里铺量非常大很多小产线、单机自动化设备都用它做上位机通讯的需求自然就多。1.2 与西门子PLC通讯的几条路和西门子PLC通讯路数其实不少但每条路的门槛和成本差别很大。第一种是走西门子专有的PPI协议老款S7-200用的多但协议是不公开的网上流传的报文格式大多是逆向分析来的用起来心里没底。第二种是走Profinet或S7协议功能强、速度快但易语言里没有现成组件要自己封装或者找人写DLL对大多数人来说门槛偏高。第三种是Modbus RTU走串口这是西门子S7-200 SMART原生支持的从站协议直接在PLC程序里调用MBUS_INIT和MBUS_SLAVE指令就能把PLC变成Modbus从站而上位机这边易语言自带串口组件Modbus RTU报文结构又是公开透明的自己拼报文、算CRC就行。我最终选的就是第三条路Modbus RTU。原因很简单可控。整个链路里PLC侧用官方库做从站电脑侧自己拼报文中间任何一个环节出问题都能定位。而且Modbus RTU是工业现场通用的老协议以后换别的品牌PLC只要对方支持Modbus代码改个地址就能复用到新的项目上。2. 通讯方案选型为什么先上Modbus RTU2.1 串口参数怎么定串口通讯参数看起来就波特率、数据位、校验位、停止位这四项但每一项都必须在PLC那边配置好两边对不上就是通讯失败。S7-200 SMART的MBUS_INIT指令里默认是波特率9600、偶校验、8数据位、1停止位这是西门子给的出厂约定我用的时候基本不动它上位机这边也就照着9600、8、E、1来设。这里有一个很关键的点PLC的通讯端口参数在调用MBUS_INIT时是一次性设定并锁定的如果你在PLC程序里改了口必须重新上电或者重新触发初始化才生效。我刚开始做的时候改了PLC里的波特率但没给PLC重新上电结果上位机这边改成38400怎么都连不上折腾了半天才发现是PLC侧没生效。所以提醒各位改完PLC通讯参数务必重新上电。另外串口号的选择也有讲究。很多工控机有两个串口有的扩展卡还会虚拟出好几个COM口最好在设备管理器里先确认当前USB转串口线占用的是哪个COM号再在易语言代码里指定免得程序打开的是摄像头或者别的什么设备占用的那个口成功打开也不代表连的是PLC。2.2 读懂Modbus报文结构Modbus RTU报文本质就是一连串十六进制字节结构不复杂但格式非常死板多一个字节、少一个字节从站都会判定为非法请求。一个完整的请求帧包含四段从站地址、功能码、数据区、CRC校验。从站地址就是PLC的Modbus从站地址MBUS_INIT里设的一个485总线上可以挂多个从站地址不同即可。功能码这里我用的最多的就两个03读保持寄存器10十进制16写多个寄存器。数据区里要填起始寄存器地址和寄存器数量——注意这里的地址是Modbus的零基地址而从站侧对应的是PLC的V区地址它们之间还有一个换算关系。S7-200 SMART的映射规则是关键Modbus保持寄存器40001对应VW040002对应VW2以此类推也就是地址偏移量乘以2就是V区字偏移。比如你程序里想读写VW100对应的保持寄存器地址物理地址就是100除以2等于50但如果填Modbus协议地址要用50减1也就是49换算成十六进制就是0x0031。第一次做的人特别容易在“减一”这步栽跟头因为很多手册上直接写地址100其实那是指PLC的VW100不是协议里的原始地址。2.3 CRC校验必须自己写Modbus RTU的CRC16是整个报文里唯一需要动脑子的地方。它的计算方式是多字节做异或、移位、查表的一套固定流程算出来的两个字节要放在报文末尾而且低字节在前、高字节在后这个顺序反了从站直接丢弃请求。易语言里没有现成的CRC函数需要自己写一个子程序。这套算法我试过很多版本有一条建议不要用网上那种查大表的写法虽然快但代码量太大移植麻烦。用标准的多项式0xA001逐位计算就够了PLC处理几百字节的报文本来就没压力上位机这边循环几百次移位也只是一瞬间的事。用易语言写CRC时要注意变量类型用整数型别用字节型因为移位过程中会有超过255的中间值字节型有可能溢出。还有最终结果要拆成高字节和低字节放到报文末尾时顺序是低字节先放这是Modbus规范的铁律很多通讯不成功的问题最后都发现是这里高低字节放反了。3. 实例实操从零写一个读写工具3.1 界面设计与串口初始化这个工具的功能定位很朴素能读VW值、能写VW值、能看通讯日志。界面就四个核心元素串口参数设置区、读取区、写入区、收发日志区。不需要花哨但每个输入框都要有合理的默认值方便现场调试少敲几次键盘。串口初始化放到窗口创建完毕的事件里执行。如果开发机上没有实际硬件可以用虚拟串口软件模拟一对互联的串口来调试一头给PLC通讯测试工具用一头给易语言程序用能跑通收发就算成功百分之八十。但注意虚拟串口绕过的是物理层最终还是要拿真PLC验证时序和电缆质量的问题只有真机才暴露得出来。易语言操作串口通常用“端口”支持库或者MSComm控件我习惯用MSComm因为它的OnComm事件在处理不定长返回数据时比轮询方便。初始化配置如下.版本 2 .支持库 comm .子程序 _窗口创建完毕 MSComm1.输入模式 1 二进制模式必须设文本模式会收到乱码 MSComm1.波特率 9600 MSComm1.数据位 8 MSComm1.校验位 2 偶校验 MSComm1.停止位 1 MSComm1.串口号 1 MSComm1.设置 “9600,E,8,1” MSComm1.端口打开 真这里有个教训想直接分享易语言的“设置”属性字符串如果和波特率、校验位等属性分开设置偶尔会出现相互覆盖的诡异现象。所以我初始化时只写一句完整的“9600,E,8,1”设置字符串再去单独打开端口实测下来最稳定。3.2 读取功能实现读取操作的目标是读PLC的VW100这个字。根据前面说的映射关系VW100对应的Modbus保持寄存器是40051协议地址十进制的50十六进制0x0032注意这里50减1才是协议原始地址VW100场景下40051原始地址49即0x0031不对重新算40001对应VW0那VW100就是40001 100/2 40051但Modbus协议地址索引默认从0开始所以原始地址是40051-150即0x0032。这里我之前说的0x0031是错的注意40001索引是040002索引是1所以寄存器地址(目标寄存器号-1)。VW100→保持寄存器40051→协议地址500x0032。对是0x0032没错之前我手误了统一为0x0032。读取报文就是标准的01 03 00 32 00 01加上CRC两个字节一共8个字节。完整实现如下.子程序 读取VW值, 整数型 .局部变量 发送帧, 字节集 .局部变量 接收帧, 字节集 .局部变量 crc, 整数型 .局部变量 crcL, 字节型 .局部变量 crcH, 字节型 .局部变量 计算值, 整数型 发送帧 { 1, 3, 0, 50, 0, 1 } 01 03 00 32 00 01 crc CRC16校验 (发送帧) crcL 位与 (crc, 255) crcH 右移 (crc, 8) 发送帧 发送帧 到字节集 (crcL) 到字节集 (crcH) MSComm1.输出 发送帧 延时 (100) 给从站响应留时间 接收帧 MSComm1.输入常规情况下PLC返回的响应帧是01 03 02 高位 低位 CRC。解析的时候取第4个字节作为高字节第5个字节作为低字节组合成整数就是VW100的值。这里有个易语言字节集索引的坑易语言的字节集索引是从1开始的不是从0开始和C语言习惯不一样我第一次取字节就把数取错了调试了半天才发现是索引号的问题。3.3 写入功能实现写入单个寄存器其实是多个寄存器的特例所以我直接采用10功能码写多个寄存器来统一处理报文格式是01 10 起始地址高 起始地址低 寄存器数量高 寄存器数量低 字节数 数据高 数据低 CRC。以写VW100为1234为例数据区写出来是这样.子程序 写入VW值 .参数 写入值, 整数型 .局部变量 发送帧, 字节集 .局部变量 crc, 整数型 .局部变量 高字节, 字节型 .局部变量 低字节, 字节型 高字节 右移 (位与 (写入值, 65280), 8) 取高字节 低字节 位与 (写入值, 255) 取低字节 发送帧 { 1, 16, 0, 50, 0, 1, 2, 高字节, 低字节 } crc CRC16校验 (发送帧) 发送帧 发送帧 到字节集 (位与 (crc, 255)) 到字节集 (右移 (crc, 8)) MSComm1.输出 发送帧写入的响应帧是01 10 00 32 00 01 CRC如果返回的地址和数量与请求一致说明写入成功。我遇到过一种情况返回帧是01 90 01 CRC这是Modbus的异常响应0x90是功能码0x10的最高位置1表示请求出错0x01是错误码含义是非法功能码。当时排查下来原因是我把寄存器数量填成了0Modbus协议规定数量必须至少为1空写是不允许的。3.4 轮询与超时处理工业现场读数据很少是点一次按钮读一次的更多的是定时轮询。用易语言的时钟组件最简单设置时钟周期为500毫秒在时钟周期事件里调读取子程序把值刷到界面编辑框上。但要注意时钟周期不能太短太短了上一个请求还没响应完下一个就发出去了串口缓冲会乱套。我的经验是把“正在请求”这个状态用一个开关变量控制请求发出后立刻置真收到完整响应或者超时后置假每次时钟事件先检查这个开关防止重入。判定超时用延时函数不太严谨更推荐的做法是记录发送时间在接收事件里判断当前时间与发送时间的差值超过500毫秒就丢弃缓冲重新发。这套逻辑看着简单却是让通讯程序稳定运行的关键。4. 踩坑记录与隐患排查速查表4.1 通讯不上先查这三样通讯不上是新手遇到最多的问题我按出现频率总结三个原因。第一串口根本没打开或者被占用这个在易语言里不会报错你没做打开成功的判断就发数据数据丢在空气里。第二PLC侧Modbus从站没有启动MBUS_INIT没有有效调用或者调用后没有轮询调用MBUS_SLAVE这种情况下PLC压根不响应任何请求。第三通讯参数两边不一致特别是校验位易语言里校验位是个数字1是无校验2是偶校验填错一个数就白搭。建议排查顺序是先用串口调试助手手动发一帧01 03 00 32 00 01加CRC看PLC给不给响应如果不响应检查PLC程序和端口如果响应了再回过来调易语言程序这能快速把问题定位到“上位机侧还是PLC侧”。这个习惯帮我省了无数调试时间。4.2 CRC错误和字节序问题CRC错误虽然看起来是校验值算不对但更隐蔽的问题是报文本身构造错位。比如有人把寄存器数量填成了两个字节倒过来填或者数据区里混进了一个多余的字节从站去校验整包数据时CRC怎么算都对不上。排查CRC问题时最好的工具是用网上的Modbus报文计算器把帧算一遍把你的发送帧原样贴进去核对逐个字节比最常见的错误是多字节、少字节和顺序颠倒。字节序问题则出现在多字节数据的解析上。西门子PLC的数据存储是大端模式高字节在前、低字节在后而Modbus传输时也是先发高字节。所以读回来的两个字节第一个就是高字节直接合并成整数是对的。但有些PLC型号或者某些第三方仪表用的是小端模式就要把顺序反过来。遇到数据值明显不对但通讯正常的情况先怀疑字节序。4.3 通讯不稳定怎么办通讯不稳定的表现是能连上但读回来的数据偶尔是错的或者过一会儿就彻底没响应。我从自己的现场经验里筛选出三个最常见的原因。一个是接地问题。485通讯线要用双绞屏蔽线屏蔽层要单端接地如果设备端和电脑端都接了地形成地环路反而会引入干扰。另一个是终端电阻485总线两端要各接一个120欧姆的终端电阻削弱信号反射。很多人只在一端接或者干脆不接短距离的时候感觉不到距离一长就出乱码。第三个是波特率太高导致的信号劣化9600通常最稳距离超过100米再往上提速对线材和现场环境的要求就苛刻了。现场有个土办法很实用把读取频率从500毫秒降到2秒如果通讯立即稳定下来大概率是干扰或反射问题而不是程序逻辑问题。先把物理层处理好再回头看代码。4.4 常见问题速查表现象原因解决办法完全无响应PLC从站未初始化检查MBUS_INIT和MBUS_SLAVE调用完全无响应串口参数不一致核对波特率、校验位、停止位返回异常帧0x90寄存器数量填写为0数量最小填1返回异常帧0x02起始地址越界核对V区地址映射关系数据总是差一倍地址映射没减一协议地址寄存器号-1数据大小端不对高低字节顺序反了调整合并顺序偶发乱码干扰或缺少终端电阻检查接地和120欧姆电阻5. 我的一点个人经验这个实例做下来让我印象最深的倒不是怎么写代码而是工控上位机的调试逻辑永远要在动手写界面之前先想清楚数据链路每一层的作用和边界。串口是通道Modbus是语言PLC的V区是资源易语言只是把这三者串起来的那只手。串口设置错后面全是白搭地址映射错数据读回来也是错的。最后再分享一个小技巧调试阶段在易语言的日志编辑框里把收发报文完整打出来用十六进制显示每一个字节。这看起来很低效但却是所有疑难杂症的最快解法。等程序稳定运行了再把这行日志代码注释掉线上版本保持干净。我做过的每个通讯项目几乎都有这么一段“不雅观但保命”的调试代码它让我在无数个加班夜里快速定位问题而不是靠猜。本文还有配套的精品资源点击获取