
1. 开工之前先想明白密码锁在PLC里到底怎么“锁”的1.1 为什么是PLC来做这件事又到了案例系列更新的时候这是第4个S7-200 PLC 6位密码锁程序。名字听起来很直白就是给机柜门、操作面板或者实验室设备加一把电子锁密码输入正确才允许操作输错一定次数就报警并锁死一段时间。做这套东西的起因是我给某套小型控制柜做升级时客户提了个需求不想再额外装单片机门禁板希望用现有PLC把权限管理一起做掉。用PLC做密码锁很多人第一反应是“杀鸡用牛刀”。但实际跑过项目就会发现这个选择非常合理。设备柜里本来就有PLC开关电源和继电器回路都是现成的加密码锁只需要增加几个按钮和一只电控锁几乎没有新增的电路设计成本。更重要的是PLC程序可以远程下载和维护密码规则、锁定时间、允许输错次数都能在程序里调整不需要像硬件门禁板那样重新换板子或飞线。另一个原因是可靠性。PLC是循环扫描执行程序的天然没有单片机程序里那些复杂的“事件驱动”问题。按键信号采集、状态判断、输出驱动都在同一个扫描周期里被约束得很好误动作概率很低。对于很多设备厂商来说密码锁的真正目的不是防黑客而是防误操作、防无关人员随意改变设备参数。比如配电柜、车载设备、生产线操作台这些场合更需要的是“谁在什么条件下能操作”的管理逻辑而不只是一把物理钥匙。1.2 6位密码背后的可靠性取舍为什么不用4位密码因为4位密码只有1万种组合对于连续暴力尝试来说太脆弱了。虽然PLC密码锁一般只控制设备操作权限但万一现场有好事者反复试密码4位的穷举时间并不长在安全上明显不够。6位密码有100万种组合配合输错次数限制和锁定计时能把暴力破解的成本推到很高的程度。程序实现上6位只是把比较逻辑从4位扩展出两条指令硬件资源消耗几乎可以忽略所以直接上6位是很划算的。不过也要把丑话说在前面PLC密码锁不是保险箱密码锁它解决的是“应用授权”问题挡不住那种把整个控制柜电都停掉的物理暴力。在设计时程序里必须加入报警输出、锁定计时、错误计数这些机制而不是单纯做密码比较。这也是我在这篇里会重点展开的部分一个能用的密码锁程序真正核心的不是“判断密码对不对”而是“密码错了之后系统怎么反应”。2. 硬件配置与I/O分配一台普通S7-200能扛住这套程序吗2.1 我用的CPU型号和接线思路这个项目我使用的是S7-200 CPU224 DC/DC/DC14个数字量输入和10个数字量输出对于一套6位密码锁绰绰有余。实际上CPU222只有8个输入刻薄一点说也够但CPU224留出了很大余量后面接急停、模式开关、外部状态信号都不用再改PLC本体。输入输出都是24VDC现场按钮供电和PLC输入回路可以直接共用一组直流电源。接线方面有一个细节很关键S7-200的DC数字量输入既可以接成漏型也可以接成源型。实际施工时如果按钮用的是常开触点公共端的极性接反会出现输入点一直亮或者信号完全不进来的现象。我习惯在通电前先查一遍按钮公共端与PLC输入公共端1M、2M的极性再检查一遍常开触点是否接在输入端子与公共端之间。这一条如果忽略了程序写得再漂亮也是白搭。输出端Q0.0不直接带电磁锁而是通过一只中间继电器去控制。电磁锁如果带直流线圈一定要在继电器线圈两端反向并联续流二极管否则分断瞬间的感性浪涌会直接冲击输出晶体管。Q0.1报警输出接声光报警器同样先用继电器隔离。Q0.2是状态指示灯指示“密码锁已锁定”状态也可以省略但现场调试时有个指示灯真的能省很多事。2.2 输入按键和输出执行器的地址规划I/O地址规划是这类程序最容易忽略的地方。做完之后我才发现地址规划得好不好直接影响程序维护时的心情。我使用的分配如下表信号地址说明数字键0~3BCD编码I0.0~I0.3配合键盘模块读取0~9确认键I0.4密码输入完成后的校验按键清除键I0.5输入错误时逐位清除或整体清除管理模式开关I0.6进入修改密码模式复位报警键I0.7报警复位和解除锁定开锁输出Q0.0驱动中间继电器控制电磁锁报警输出Q0.1驱动声光报警器锁定状态指示灯Q0.2提示当前密码锁处于锁定状态数字键部分我直接用了带BCD编码输出的薄膜键盘模块0~9十个按键压缩到4个输入点上。如果没有这类模块用10个按钮接10个输入点也可以只是输入点占用多。程序处理逻辑其实一样区别只在按键识别那段用10个独立按钮的场合每个按钮一个网络写一个“数字值”到缓冲区用BCD键盘的场合多一步把4位二进制码翻译成数字值。为了演示方便下面程序逻辑里的按键值来源我按“每个数字键最终把0~9的数值写入缓冲区”来写具体是10个按钮还是BCD编码不影响后半段的比较和输出。内部位M区和V区我也做了规划。M0.0作为正确标记M0.1作为错误标记M0.2作为锁定标记VW1020用来存放“已输入位数”VB1999作为“当前按键代码”的临时寄存器密码本体放在VB1000~VB1005输入缓冲区放在VB1010~VB1015。这样划分之后后面写子程序时不会出现地址乱飞的情况。3. 程序功能拆解从手指按下按键到锁舌弹出的全链路3.1 六段式操作流程密码锁程序的运行流程从操作员角度看很简单输入6位密码按确认锁开过一会儿自动闭上。但把这段流程拆成程序逻辑实际上是六个状态在循环切换上电复位所有输出复位锁定标记置位等待输入。密码输入操作员逐个按下数字键每次有效按键将数字写入缓冲区同时已输入位数加1。确认校验按下确认键时程序先检查已输入位数是否为6不是则忽略是则逐位比较。验证通过正确标记置位开锁输出闭合启动开锁保持定时器。验证失败错误计数加1缓冲区清零如果错误次数达到上限则报警输出启动同时进入锁定计时。复位状态锁定计时结束后错误计数清零报警复位系统重新回到等待输入状态。这其实是典型的有限状态机思路只是用PLC梯形图实现时不需要显式定义状态机只需要用一组M继电器和定时器把“当前处于哪个阶段”表达出来。我在编程时习惯把每一个阶段对应的输出条件写清楚避免出现“开锁输出和报警输出同时有效”这种尴尬状态。3.2 每个功能模块在程序里长什么样按功能拆程序可以分成四个子程序按键解析、输入缓冲管理、密码校验、输出与定时控制。主程序OB1里只需要按顺序调用它们就行。按键解析子程序处理的是信号抖动和有效沿。机械按钮按下去的时候触点会弹跳几百微秒到几毫秒S7-200的扫描周期通常只有几毫秒如果不对信号做处理一次按键可能被识别成两次甚至三四次。程序里我用了一个10ms的去抖定时器配合上升沿指令只有稳定之后的边沿才被认为是有效按键。这一步在论文里看着不起眼但现场没有它的话密码输入的位数计数会疯狂跳变。输入缓冲管理子程序负责把每次有效按键写入缓冲区。写入前缓冲区的数据从后往前平移新按下的数字放到第一个位置。这样处理的好处是代码整齐不管按了几位缓冲区的前面几位始终是最新输入的数字。配合一个“已输入位数”计数器按下确认键时就可以知道当前到底输入了6位还是只有3位。密码校验子程序只在确认键按下的那个扫描周期执行。先判断VW1020是否等于6不等于就直接清空缓冲区并退出。等于6的时候对6个密码字节和6个输入缓冲字节做逐位比较。S7-200的字节比较指令可以串联在一起所有比较触点为真时最终输出才为真逻辑上很直接。输出与定时控制子程序管理开锁时间、锁定时间、报警复位。开锁时间我用T37定时器设为10秒后自动断电锁。错误锁定用T38这里要注意定时器分辨率。T37的时基是10ms预设值300就代表3秒预设值3000代表30秒。我在调试时吃过亏把3000当成毫秒数结果实际锁定时间是30秒而不是3秒最后对着手册整理了一张定时器分辨率表才理清楚。4. 核心程序段编写密码存储、输入缓冲与逐位比较4.1 密码存储区的规划方式密码本体我放在VB1000到VB1005每个字节存一位数字。程序里默认密码是“123456”即VB10001VB10012依此类推。为什么要用字节而不用整数VW因为逐位比较时字节比较指令最直观密码判断就是六个字节判断的串联一眼就能看出哪一位不匹配。如果用整数存储还要考虑高低字节顺序和进制转换维护成本反而高了。这里有个电源保持的坑必须说。S7-200的V区默认不具备掉电保持功能如果要让现场修改后的密码在断电后不丢必须在系统块里把密码区对应的V区范围设置成保持型存储区或者把密码写入数据块并下载到控制器。我曾经在一个设备上遇到“改了密码断电重启后密码又变回123456”的问题排查到最后才发现是V区保持范围没设置。这个细节不处理密码锁程序就只能当演示程序用没法交付到现场。4.2 按键输入缓存如何逐位落位输入缓冲区VB1010到VB1015也是六个字节。每次有效按键后程序把缓冲区整体向左平移一位也就是VB1011赋给VB1010VB1012赋给VB1011直到VB1015赋给VB1014然后把当前按键代码写入VB1010。这是最典型的“最新输入进队首旧数据依次后退”的做法省掉了判断输入位数的分支逻辑。按键解析里我已经把0~9这十个键的识别结果统一成数字代码放在VB1999。比如按下数字键“5”VB1999就被写入5。编制梯形图时十个按键对应十个并联网络每个网络都是一条上升沿指令加上一条MOVB指令。这个做法看起来有点土但最不容易出错而且调试时可以用状态表同时监控十个按钮的状态和VB1999的数值哪里没通一目了然。VW1020保存已输入位数。每次有效按键后加1最大到6。按下确认键后无论校验成功还是失败VW1020都清零。确认校验里如果发现VW1020不等于6甚至可以直接忽略这次确认操作同时把缓冲区清零这其实也是一种防误触设计。4.3 逐字节比较再统一置位以及开锁输出校验部分的核心逻辑可以这样描述只有当VW1020等于6并且确认键按下时才开始比较。比较网络是六个字节相等触点串联全部为真时置位M0.0正确标记。比较失败时M0.1错误标记被置位同时错误计数器加1。错误计数我放在VB1030里每失败一次加1当VB1030的值等于3时启动锁定计时T38和报警输出Q0.1。这里有一个容易犯的错比较条件和确认键使用DELETE指令很容易把确认键的上升沿误用成电平时导致操作员一直按着确认键的时候逻辑被反复触发。我后来规定比较网络内只用确认键的一个上升沿右边沿作为允许条件执行M0.0置位或者M0.1置位只发生在确认键弹下的那一次扫描。程序下载到现场后这个“按一下只触发一次”的行为是否正常是第一个要验证的点。开锁输出逻辑干净利落M0.0正确标记的上升沿置位Q0.0并启动T37T37到时释放Q0.0。M0.1错误标记的上升沿清除缓冲区并增加错误次数。T38锁定计时到点后同时复位Q0.1报警输出和VB1030错误计数。锁定期间内除了复位报警键和清除键其他数字键都被屏蔽相当于系统进入一个拒绝输入的状态。4.4 程序里用到的关键指令对照为了减少翻手册的时间我把这套程序里最常用到的S7-200指令整理出来方便想要直接抄作业的朋友对照功能指令用途说明上电初始化SM0.1首次扫描时为ON用于复位状态常为真SM0.0可作为某些网络的前置条件上升沿EU按键有效边沿识别字节传送MOVB把数字代码写入缓冲区字节比较相等LDB / 触点比较密码逐位比较整数比较相等LDW / 触点比较判断已输入位数加1INCB错误计数和输入位数累加定时器TON开锁保持和锁定计时置位/复位S / R控制输出和状态位5. 调试中遇到的坑和对应处理计数、锁定与防抖的细节5.1 输入抖动造成的重复计数这套程序刚写完做内部测试时发现一个现象输入一个数字有时缓冲区的位数会变成2甚至3。一开始以为是PLC扫描周期太快导致的逻辑错误后来用万用表和示波器看了一眼按钮触点波形发现是机械抖动捣的鬼。触点在闭合瞬间会出现多次通断每次通断都被PLC当成一次有效信号于是VW1020就往上跳。解决办法是在按键解析子程序里给每个输入加了一个10ms的延时确认。我用的不是复杂滤波算法而是最简单有效的做法先将输入信号通过一个TON定时器延时10ms后再去取上升沿。这样能过滤大部分机械抖动。对锁类应用来说操作员按一个键的时间通常超过100ms10ms的去抖延时不会影响操作手感。5.2 错误次数锁定与解锁机制的时序问题第二类坑出现在错误计数和锁定时间上。最初版本的程序是在比较失败后立即置位锁定标记锁定标记一出来输入全部被屏蔽操作员想用清除键重新输入都不行。这在实际使用中非常不友好一旦按错一个数字整把锁就锁死几分钟。后来我调整了时序比较失败后只清除缓冲区允许操作员重新输入错误次数加到2次仍不锁定只有当错误次数加到3次时才锁定。这个设计既保留了容错空间也保证了安全性。锁定期间复位键也做了特殊处理必须按住3秒以上才解除报警并复位错误计数短按无效。这样防止误触到复位键就把报警轻轻松松消掉。5.3 用Micro/WIN状态表排查V区数据和地址冲突调试过程中最有价值的工具是Micro/WIN的“状态表”功能。把VB1000到VB1015、VW1020、VB1030、M0.0到M0.2全部添加到状态表里PLC运行时就能实时看到这些地址的数值变化。密码输入对不上时我第一时间不是看梯形图而是看状态表里缓冲区每个字节的数值是否和按键一致。快速核对后能发现是按键值错误、缓冲移位错误还是比较逻辑错误定位效率比盯梯形图高得多。地址冲突也是实际项目中遇到过的问题。最开始我把密码区放在了VB100到VB105结果程序里另一段与通信相关的代码也用了V100附近的地段区密码被不定时改写成随机值。后来我改成将不同的V区用途按高位分段VB1000以上全部给密码锁程序使用其它功能模块使用VB500以前的空间并在程序文件开头用一大段注释对V区分配做了记录。这个习惯沿用到了后续每个项目里再也没出现数据互相覆盖的问题。6. 按同一思路还能扩展什么玩法6.1 多级权限与多人开锁这套密码锁的底座其实是一个很通用的授权判断框架稍微改改就能变成多级权限。如果现场不止一个操作员可以分配不同密码组一组密码能开锁但不能进入维护模式另一组密码能开锁还能修改密码第三组只能查看状态。实现方式是把VB1000之后的密码存储区扩展成三组每组6个字节比较时先用一个权限等级标志决定用哪一组密码进行6字节比对。想做成“双人开锁”也可以设定两个密码段第一段和第二段由不同操作员各输入6位程序把两个输入缓冲区分别与两组密码比较后再输出一个与逻辑。这种功能在一些贵重仪器柜、两班倒交接的终端设备上挺实用避免单一操作员拥有完整权限。6.2 界面替换、密码修改通道与上位机联动如果觉得实体按键不够直观可以换成触摸屏。S7-200加一台小型HMI界面里直接做数字键盘和状态显示密码输入从物理按钮变成屏幕触控操作体验会好很多。比较逻辑不用改动只是输入来源从输入映像区I变成HMI写入的V区变量。这种情况下反而要更注意权限隔离HMI密码和PLC密码要分开管理避免任何人拿到触摸屏界面就能直接改V区的密码字节。关于修改密码我强烈建议不要在生产运行时做“任意密码可改密码”的逻辑。较稳妥的做法是进入管理模式需要拨通I0.6并输入一次旧密码然后对新密码进行两次确认输入两次输入一致才写入密码存储区。密码变更之后必须同步考虑掉电保存问题也就是前面提到的保持型范围设置不然这个功能就是摆设。另外如果现场有上位机或者远程监控系统密码锁程序还可以把开锁记录、错误次数、报警发生时间全部写到V区留档再由上位机定时读取。这样就不只是简单开锁而成了一套设备操作审计系统。某个时间段谁开过锁、有没有连续输错密码、报警是否及时复位在监控界面上都能看到。调试这套系统时我先后把通信和密码锁两个子程序分别测稳定再联调整个过程顺利很多。项目管理上的体会是密码锁这种小功能模块单独验证到位后并入大系统比从头到尾一起联调要省力得多。