PLC编程框架实战:状态机与模块化设计,轻松搞定变频器RS485通信
最开始接触 PLC 编程的时候,很多朋友的习惯是:拿到项目直接打开编程软件,一边看着 IO 表一边拖梯形图,常开常闭触点、线圈、定时器随手往上放。程序少的时候还好说,等到设备动作越来越多,程序越来越长,问题就来了——今天改一个输出要翻半天程序,明天加一个传感器又担心影响之前的逻辑。设备一响,脑子一片空白。
这篇文章想和你聊的,是一套适合 PLC 工程的编程框架。它不是某个厂商的专用软件,也不是只能用在某一种 PLC 上的库函数,而是一套从程序结构、状态规划、接口定义到命名注释的完整思路。学会它之后,你会发现一个明显的区别:以前是在“画梯形图”,以后是在“设计程序”。刚入门的工程师可以把它当成一套编程规范;已经写过不少项目的工程师,也可以用它来优化现有程序的维护体验。
1. 为什么你的 PLC 程序总是越改越乱——PLC 编程也要讲框架
1.1 没有框架的 PLC 程序长什么样
先来看一个典型的“自由发挥”型程序特征:
- 启动、停止、报警、手动、自动逻辑全部混在一段程序里;
- 同样一个电机输出,可能在程序里出现四五次线圈输出;
- 定时器、计数器散落在不同程序段,看不出彼此关系;
- 修改一个传感器信号,要影响很多无关逻辑;
- 设备出故障时,只能靠在线监控一段一段地猜。
这样的程序不是不能运行,而是“能跑”和“好维护”是两回事。尤其在设备交付之后,现场调试、客户修改、突然报警,程序的清晰程度直接决定你是在解决问题,还是在制造更大的问题。
1.2 框架的价值:让程序从“能运行”变成“可维护”
打个比方,PLC 程序就像写文章。没有框架的时候,文章是“意识流”,想到哪写到哪,作者自己回头可能都看不懂。有了框架之后,文章有了段落、章节、逻辑主线,哪怕换一个人来读,也能快速定位重点。
具体到 PLC 工程里,框架化之后的效果非常明显:
- 程序按功能模块划分,每个模块职责单一;
- 设备的每一个动作都能对应到明确的状态;
- 手动、自动、报警、复位逻辑相互隔离;
- 现场出现故障时,能通过状态显示快速定位;
- 新增设备或修改动作时,不需要重写整段程序。
所以说,PLC 编程中的“框架”,并不是互联网开发里那种代码框架,而是一套适合 PLC 周期的程序组织方法。它的本质,是对“输入—处理—输出”这一循环的工程化管理。
1.3 谁适合学习这套框架
如果你属于下面几类人,这篇文章会比较适合你:
- 刚接触 PLC 编程,想建立规范的编程习惯;
- 已经能写梯形图,但项目一大就感觉混乱;
- 做非标设备或小型自动化项目,经常要改程序;
- 想从三菱 PLC 扩展到西门子、汇川等品牌,需要一套通用的编程思路。
这套框架的核心不依赖具体品牌,我用三菱 PLC 做演示,但思路可以平移到其他品牌上。
2. PLC 框架的本质:程序组织方式、状态机和接口规范
2.1 PLC 的扫描机制决定了程序要有“节奏”
不管是三菱、西门子还是汇川,PLC 的本质都是周期扫描:读取输入、执行程序、刷新输出,如此循环。这个循环意味着程序里所有逻辑都在一个时间轴上反复执行。
正因为这样,PLC 程序中不适合出现“一次性执行”的思维方式。很多初学者常犯的错误,就是想把自动化程序写成“顺序执行”的脚本语言。实际上,设备的每一个状态都应该在一个扫描周期内被稳定计算,然后再由下一个扫描周期根据新条件进入下一个状态。
理解这一点,是理解状态机框架的前提。
2.2 状态机:设备控制的“总导演”
什么是状态机?简单来说,就是把设备当前所处的状态用一个变量保存,整个程序根据“当前状态 + 转移条件”来决定输出和下一次状态。
举个例子,一个最简单的电机启停控制,用传统自锁方式写梯形图非常容易:启动按钮置位输出,停止按钮复位输出。但如果设备是一个气缸机械手,动作包含下降、夹紧、上升、平移多个步骤,再用自锁方式去写,互锁关系会越来越乱。
这时候状态机的优势就出来了。
| 状态编号 | 状态名称 | 进入条件 | 输出动作 | 转移条件 |
|---|---|---|---|---|
| S0 | 待机 | 上电/复位 | 无输出 | 启动按钮按下 |
| S1 | 取件下降 | S0 + 启动按钮 | 下降阀输出 | 下限位传感器 |
| S2 | 夹紧工件 | S1 + 下限位 | 夹紧阀输出 | 夹紧到位传感器 |
| S3 | 取件上升 | S2 + 夹紧到位 | 上升阀输出 | 上限位传感器 |
| S4 | 搬运到位 | S3 + 上限位 | 平移阀输出 | 平移到位传感器 |
| S5 | 放件下降 | S4 + 平移到位 | 下降阀输出 | 下限位传感器 |
| S6 | 松开工件 | S5 + 下限位 | 放松阀输出 | 放松到位传感器 |
| S7 | 返回原点 | S6 + 放松到位 | 上升+平移回位 | 原点传感器 |
这种做法的好处是:程序里只有一个地方负责“当前状态”的改变,其他所有逻辑都围绕状态变量来展开。查故障时,只需要看当前状态在哪个编号,就知道设备走到了哪一步,下一步为什么没有继续。
2.3 模块化:把大程序拆成小积木
状态机解决的是“动作顺序”问题,模块化解决的是“程序组织”问题。
一套比较通用的 PLC 程序模块划分如下:
| 模块名称 | 负责内容 |
|---|---|
| 系统初始化模块 | 上电初始值、变量复位、通信初始化 |
| 手动操作模块 | 点动、单步动作、调试功能 |
| 自动运行模块 | 状态机、自动流程 |
| 报警处理模块 | 故障检测、报警输出、报警复位 |
| 输出映射模块 | 逻辑输出与物理输出的对应关系 |
| 通信处理模块 | 触摸屏、变频器、上位机数据交换 |
每个模块只负责自己的事情,模块之间通过“中间变量”通信。比如自动运行模块需要控制一个气缸,它不再直接控制物理输出点,而是给一个“自动动作输出变量”赋值,最后由输出映射模块统一把逻辑变量映射到物理输出。这样做有很多好处,其中之一是手动和自动逻辑不会互相干扰。
2.4 接口变量和命名规范:程序的可读性密码
框架的最后一部分,是接口变量和命名规范。三菱 PLC 中,直接使用 X、Y、M、D 编程虽然简单,但可读性很差。程序写多了之后,D100 到底代表什么频率,M50 是哪个报警,不查注释根本不知道。
建议在程序开头集中定义符号名,或者在变量声明区统一管理。命名尽量包含数据类型和功能前缀,比如:
- DI_RUN_BTN:运行按钮输入
- DO_MOTOR_FWD:电机正转输出
- AI_TEMP_1:1号温度模拟量输入
- ST_CUR_STATE:当前状态编号
- AL_OVERCURRENT:过流报警标志
虽然看起来不如直接写 X0、M100 简单,但程序维护时省下的时间,远远超过命名花掉的那几分钟。
3. 框架化实战:三菱 PLC 通过 RS485 读取/写入变频器频率
这一节用一个真实高频需求来实操一下:用三菱 PLC 通过 RS485 通信读取和写入变频器频率。这也是很多朋友搜索的“三菱PLC读取写入变频器频率程序”问题。
3.1 需求分析与方案规划
需求:在触摸屏上设置目标频率,PLC 通过 RS485 下发到变频器;同时读取变频器的当前运行频率,显示在触摸屏上。
按照框架化的思路,第一步不是打开编程软件,而是先列需求清单:
| 功能 | 通信方向 | 数据内容 |
|---|---|---|
| 写入运行频率 | PLC -> 变频器 | 目标频率值 |
| 读取当前频率 | 变频器 -> PLC | 当前频率 |
| 变频器启停控制 | PLC -> 变频器 | 启动/停止命令 |
| 故障状态读取 | 变频器 -> PLC | 故障代码 |
3.2 硬件接线与通信参数确认
以三菱 FX3U/FX5U 系列为例,使用 RS485 通信板或扩展模块连接变频器。接线时通常使用双绞屏蔽线,PLC 侧接 S+ / S-,变频器侧接对应通信端子,屏蔽层单端接地。
通信参数必须 PLC 和变频器保持一致,常见设置为:
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| 波特率 | 9600 bps | 两端必须一致 |
| 数据位 | 8 | 常见设置 |
| 校验方式 | 偶校验 Even | 也可以是无校验 |
| 停止位 | 1 | 常见设置 |
| 站号 | 例如 1 | 变频器通信从站地址 |
需要特别提醒:不同品牌变频器的通信参数名称和默认值不一样,修改前先把变频器手册翻到通信章节,确认寄存器地址和数据格式,不要凭经验套用。
3.3 通信报文与寄存器规划
以常见的三菱 FR-E700 系列变频器为例,采用三菱变频器计算机链接协议时,报文的典型结构是:
站号 指令码 等待时间 数据 和校验 CR/LF例如写入频率指令码常用H6E,读取频率指令码常用H6F,具体地址和格式请以对应型号手册为准。如果变频器使用 MODBUS-RTU 协议,则是“站号 + 功能码 + 寄存器地址 + 数据 + CRC”的结构。无论哪种协议,框架化的处理方式是一样的。
在写通信程序前,建议先做好寄存器地址规划表:
| 数据项目 | 写入地址 | 读取地址 | 数据范围 | 数据类型 |
|---|---|---|---|---|
| 运行频率 | 按手册确认 | 按手册确认 | 0.00~400.00 Hz | 16位/32位 |
| 启动命令 | 按手册确认 | - | 0=停止 1=正转 | 位 |
| 当前频率 | - | 按手册确认 | 0.00~400.00 Hz | 16位/32位 |
记住一个原则:先用表格把通信数据规划清楚,再写代码。这个习惯可以避免很多通信数据错位的问题。
3.4 通信程序模块划分与核心代码
按照框架,通信程序可以拆成以下模块:
- 通信初始化模块:设置通信格式、清空收发缓冲区;
- 发送请求模块:根据操作类型组帧并发送;
- 接收解析模块:判断接收完成、解析数据;
- 数据处理模块:把原始数值换算成工程单位(Hz);
- 超时处理模块:通信失败时报警,避免程序卡死。
以三菱 FX5U 系列 ST 语言为例,频率写入核心示意代码如下,具体指令请按实际机型和手册调整:
// 频率写入模块示意 // 实际使用时,需要根据所选PLC的通信指令调整 IF REQ_WRITE_FREQ THEN // 将目标频率转换为通信原始值 // 例如 50.00Hz -> 5000 RAW_FREQ := INT_TO_UINT(REAL_TO_INT(FREQ_SET * 100.0)); // 组织报文:站号 + 指令码 + 数据 + 校验 // 这里只演示思路,实际组帧依赖具体协议 COMM_SEND_BUFFER[0] := STATION_NO; // 站号 COMM_SEND_BUFFER[1] := WRITE_FREQ_CMD; // 写入频率指令码 // 发送请求 SEND_REQ := TRUE; END_IF;读取当前频率的核心思路如下:
// 读取频率解析示意 IF COMM_RECV_DONE THEN // 假设接收数据中频率值在指定字节 FREQ_RAW := GET_WORD_FROM_BUFFER(COMM_RECV_BUFFER, DATA_INDEX); // 原始值换算为Hz,例如原始值5000 -> 50.00Hz FREQ_CUR := UINT_TO_REAL(FREQ_RAW) / 100.0; // 清零接收完成标志 COMM_RECV_DONE := FALSE; END_IF;需要特别说明的是,上面代码是框架示意,重点在于“先判断通信完成标志,再解析数据,最后换算工程值”。不同 PLC 的通信指令差异很大,直接照搬可能无法运行。实际项目里,应该先看通信手册,确认收发缓冲区如何读取、接收完成标志是什么,然后按这个流程填充实现。
3.5 通信超时与异常保护
框架化编程中,通信逻辑不能裸奔。常见的保护措施包括:
- 发送后启动通信超时定时器,例如 500ms 内未收到响应则报警;
- 连续多次通信失败后,输出“通信异常”报警,并进入安全状态;
- 频率写入必须在变频器运行允许的情况下进行;
- 通信恢复后,自动重新初始化通信参数。
这里要特别强调安全原则:变频器远程控制涉及设备运行安全,改动和测试必须在断电、停机、授权情况下进行。通信控制只能作为逻辑控制的一部分,紧急停止、安全回路必须由硬接线回路独立实现,不能只靠 PLC 程序。
4. 状态机框架在机械手/伺服控制中的应用
4.1 从气缸动作到状态设计
除了通信,状态机框架在机械手、搬运设备、伺服控制中应用更广。网上常见的“PLC控制柜控制气缸机械手”就是典型例子。
以一台小型搬运机械手为例,设备包含:
- 两个升降气缸:取件气缸、放件气缸;
- 一个夹紧气缸;
- 一个直线移动机构(气缸或伺服)。
如果按照传统的“置位/复位”方式,动作一多,互锁关系很容易错。用状态机框架后,整个设备就在前面那张状态表的指导下完成。
4.2 状态机的三要素:状态变量、转移条件、动作输出
用代码来表达状态机,通常围绕三个要素:
- 状态变量:当前设备处于哪个状态;
- 转移条件:满足什么信号才允许跳转到下一个状态;
- 输出动作:在当前状态内,需要控制哪些输出。
以 ST 语言演示,状态机核心结构如下:
// 状态机转移逻辑示意 IF bEnableAuto THEN CASE ST_CUR_STATE OF 0: // 待机 DO_DOWN := FALSE; DO_CLAMP := FALSE; IF bStartBtn THEN ST_CUR_STATE := 1; END_IF; 1: // 取件下降 DO_DOWN := TRUE; IF X_DOWN_SENSOR THEN DO_DOWN := FALSE; ST_CUR_STATE := 2; END_IF; 2: // 夹紧 DO_CLAMP := TRUE; IF X_CLAMP_SENSOR THEN ST_CUR_STATE := 3; END_IF; 3: // 取件上升 DO_UP := TRUE; IF X_UP_SENSOR THEN DO_UP := FALSE; ST_CUR_STATE := 4; END_IF; // 其他状态按相同规则展开 END_CASE; END_IF;这段代码的核心是:状态编号ST_CUR_STATE是唯一决定程序走向的变量,它的修改只发生在 CASE 结构内部。输出动作的赋值也只在对应状态下发生。这样调试时,只需要观察ST_CUR_STATE的当前值,就能判断设备卡在哪一步,以及为什么卡住。
4.3 为状态机增加报警与复位机制
状态机还需要配套“报警停止”和“手动复位”机制。建议做法是:
- 在状态机外层增加一个使能位
bEnableAuto,只有值为 TRUE 时状态机才运行; - 增加一个急停/报警输入,报警时强制把状态变量切回待机状态;
- 增加一个“暂停/继续”变量,用于现场临时处理;
- 手动模式下,不使用状态机逻辑,直接使用手动操作模块的点动输出。
这样做最大的好处是:自动逻辑不会干扰手动调试,手动操作也不会破坏自动状态机的状态。
4.4 周期扫描与状态机运行的匹配
要注意,PLC 是周期扫描运行,状态机的状态变更并不能在一个扫描周期内完成多次跳转。如果条件允许,建议给每个状态设置一个“最小保持时间”,防止传感器抖动导致状态跳变过快。
例如在状态进入时启动定时器,定时 50ms 后才允许检测跳转条件。这个思路类似于软件工程里面的“滤波”,在 PLC 控制中同样重要。
5. 常见问题与排查思路
实际项目中,PLC 编程和调试总会遇到一些问题。下面整理几个出现频率较高的场景,以及对应排查思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| RS485 通信不上 | 通信参数不一致、接线错误、站号冲突 | 先确认接线,再用 PLC 监控对比两端参数,逐一核对站号、波特率、校验方式 |
| 频率写进去了但变频器不运行 | 只写了频率,没有同时下发运行命令;或运行使能未接通 | 检查上位机命令时序,确认“使能+频率+运行命令”组合是否完整 |
| 触摸屏设定频率与实际不符 | 上位机数据类型和 PLC 不一致;高低字顺序相反 | 检查触摸屏数值格式,核对 32 位数据的高低位顺序 |
| 程序输出闪烁 | 输出点在多个模块中被重复赋值 | 全网搜索该输出点的引用位置,统一由输出映射模块控制 |
| 状态机卡在某个状态 | 某个转移条件没有满足;传感器信号丢失 | 用在线监控查看当前状态和输入传感器状态,对照状态表检查 |
| 修改程序后设备动作异常 | 变量地址冲突;版本未管理 | 修改前备份,修改后检查变量交叉引用 |
关于“PLC 的 IP 地址如何设置”这类问题:如果是带以太网口的 PLC,IP 地址通常在 PLC 参数——以太网端口设置里配置,设置 IP 地址、子网掩码、默认网关后下载到 PLC,部分机型需要断电重启才能生效。不同品牌菜单位置不同,建议以对应手册为准。
6. PLC 框架化编程的最佳实践
6.1 程序结构固定下来
不管项目大小,建议程序结构固定为以下几段:
- 系统初始化段:上电复位、初始值赋值;
- IO 映射段:物理输入输出与逻辑变量对应;
- 模式切换段:手动/自动/调试模式切换;
- 手动控制段:各设备点动调试;
- 自动运行段:状态机主体;
- 报警处理段:故障判断、报警输出、复位逻辑;
- 通信处理段:触摸屏、变频器、上位机数据交换。
固定结构的好处是:每个项目都按这个套路组织,换项目时熟悉度很高,别人接手也能快速上手。
6.2 命名规范要贯穿始终
推荐一套简单的命名规则:
- 变量前缀用 DI / DO / AI / AO 表示输入输出;
- 状态变量统一用 ST_ 开头;
- 报警变量统一用 AL_ 开头;
- 定时器统一用 T_ 开头;
- 所有变量必须有注释,注释比例不要省。
虽然这样做前期会慢一点,但调试阶段节省的时间非常可观。
6.3 安全边界必须硬接线优先
这是最容易被忽视的一点。PLC 程序可以做软互锁、报警、状态判断,但设备安全的最后一道防线必须是硬件回路。
- 急停必须能直接切断动力电源,而不是依赖 PLC 程序输出;
- 正反转换向互锁,除了程序互锁,还应在接触器回路做硬件互锁;
- 伺服使能、变频器运行命令,应把硬接线安全继电器串入回路。
系统联调时要反复验证:按下急停后,输出是否立即断开,状态机是否安全退出。这个测试不能跳过。
6.4 修改前备份,修改后及时导出
PLC 程序最容易出的“事故”不是逻辑错了,而是改来改去没有备份,最后想回到上一版却发现回不去了。
建议做法:
- 每次修改前导出一份完整程序,文件命名带日期和时间;
- 程序注释里增加修订记录;
- 现场调试阶段,每隔半天或每次重大改动后备份一次;
- 备份文件存放在项目电脑上,并同步到网盘或服务器。
6.5 先定 IO 表和寄存器表,再写程序
很多新手拿到图纸后直接开始拖指令,这样容易边写边乱。更推荐的做法是:
- 先整理输入输出表;
- 再列内部变量表;
- 再画状态表;
- 最后才开始写程序。
表格就是程序的“图纸”。图纸清楚了,写程序就变成了把表格翻译成代码的过程。
6.6 加一个状态监视页
如果你的设备有人机界面,强烈建议加一页“状态监视”画面,显示:
- 当前自动状态编号和状态文本;
- 各传感器输入状态;
- 手动/自动模式标志;
- 当前报警信息;
- 通信状态。
这样现场调试时,不需要拿电脑连 PLC 就能快速判断设备所处状态,大大提升排查效率。
7. 写在最后
这套框架听起来复杂,真正用起来其实很简单:拿到设备后,先画状态表,再定义变量,最后写程序。动作少了,可能感觉不到差别;设备一复杂,框架的价值就完全体现出来了。
建议你先找一个以前写过的小项目,把里面的动作逻辑重新整理成状态机,看看程序的可读性和调试效率有没有变化。一次、两次之后,框架就会变成你自己的编程习惯。
都看到这里了,如果这篇文章对你有帮助,感谢收藏备用,也欢迎在评论区聊聊你在 PLC 编程中遇到过的“程序越写越乱”的经历。
