资讯动态

STM32+CODESYS实战:从零打造百元级工业PLC

发布时间:2026/9/23 8:38:10 来源:尧图企业网站定制
1. 项目源起一个35元主控换三五千元PLC的疯狂念头先交代一下背景。做非标自动化这行的人都清楚近几年项目里PLC的成本压力越来越大。三菱FX5U、西门子S7-1200正常渠道拿货都是两三千往上走碰上带模拟量、带以太网口的型号价格再翻一番也不稀奇。但在单机设备、教学实验台、小型产线改造这些场景里客户预算本来就卡得紧算下来整个电控柜里PLC占了很大一块成本。我在给本地一个小厂做产线改造时对方控制柜里用的是一台二手三菱FX3U带一个国产触摸屏要求工期短、预算压得很狠。当时我脑子里冒出来一个念头能不能用STM32F4这类工业级单片机把CODESYS V3.5的运行时Runtime跑起来做一台低成本PLC而且要保留IEC 61131-3的编程体验让电气工程师拿梯形图就能上手而不是逼大家去写裸机C代码。先说结论这个事不但能做成做出来后稳定性也超出预期。CODESYS的运行时本来就支持多种处理器架构官方有树莓派版本、软PLC版本也支持通过SDK移植到自有硬件上。社区里针对STM32F4的Runtime移植已经比较成熟网上能找到编译好的固件包配合CODESYS V3.5的IDE端做应用层编程完全是可用状态。我把从选型、画板、烧录、写程序到现场跑Modbus通信的完整过程梳理了一遍工程里能公开的源码、配置文件、接线参考都整理在文末。如果你也在琢磨低成本PLC方案或者想把手里的STM32板子变成带工业编程环境的控制器这篇应该能帮你省掉不少弯路。2. 方案可行性的底层逻辑CODESYS不是“一个软件”而是“三件套”很多人的第一反应是CODESYS不就是个编程软件吗怎么能跑进单片机里这里必须先拆解清楚CODESYS的架构。2.1 IDE、Runtime与设备描述的分工关系CODESYS严格来说是“IDE Runtime 设备描述”三件套。IDE跑在Windows上负责写梯形图、ST、FBD编译后下载到目标设备Runtime是跑在目标控制器里的执行引擎它解释执行IDE生成的中间代码同时负责IO刷新、任务调度、通信协议栈。设备描述文件Device Description则告诉IDE“这个控制器有哪些IO、哪些参数可以配置”。STM32F4能跑CODESYS本质上是有人把Runtime层移植到了STM32F4这个硬件平台上。移植的核心工作包括这几点定时器抽象接口Runtime需要1ms级别的系统节拍来做任务调度这个节拍不能靠HAL_Delay()这种阻塞延时必须用一个硬件定时器产生周期中断。以太网驱动CODESYS的Modbus TCP、EtherCAT主站等通信功能依赖LwIP协议栈所以板子上必须有一颗PHY芯片比如LAN8720A。串口驱动Modbus RTU从站/主站走的是UART。很多人一开始只配了以太网结果发现Modbus RTU通信不上其实就是串口驱动没有对接好。IO映射层把PLC变量绑定到GPIO、ADC、PWM等外设寄存器。这个映射做得好不好直接影响程序运行的实时性。2.2 什么工况下适合用“STM32F4 CODESYS”这个方案最适合的场景是逻辑控制为主、点数不多、通信对象以Modbus体系为主、对成本极度敏感的项目。比如小型分拣台、传送带启停控制、恒压供水里的泵阀逻辑、教学实训装置。如果项目需要几十路模拟量闭环或者要用EtherCAT总线伺服做多轴插补联动那STM32F4这点资源就吃力了老老实实上中大型PLC更稳妥。另外要明确一点这不是要替代西门子、三菱在重工业场合的地位而是给轻量级应用多一个选择。定位想清楚后面做技术选型就不会纠结。3. 硬件底座怎么搭从开发板验证到自绘控制板3.1 第一版验证开发板 洞洞板我最早用正点原子探索者STM32F4开发板做验证因为板载了W25Q64 SPI Flash、LAN8720以太网PHY和USB转串口外设非常齐全跑CODESYS Runtime所需的存储和网络条件都满足了。加上一套洞洞板焊接的继电器输出和光耦输入电路两周左右就跑通了第一个“PLC点亮LED”的Demo。这里有个关键点CODESYS Runtime固件编译出来后一般有几百KB到1MB左右而STM32F4内置的Flash通常只有512KB到1MB所以很多移植版本会把Runtime放在外部SPI Flash里启动时再加载到RAM执行。这意味着板子上的SPI Flash不是可选项是必选项。验证阶段我把工程里的硬件配置记录下来主控STM32F407ZET6主频168MHz内部Flash 512KBRAM 192KB外部存储W25Q648MB SPI Flash以太网LAN8720ARMII接口串口USART1/2/3预留电源5V输入板上AMS1117-3.3稳压3.2 输入电路的RC滤波与光耦选型开发板跑通只是第一步真正要做成能上设备的控制板输入输出电路才是决定可靠性的大头。输入部分我采用NPN型接法即PLC的输入端是漏型输入公共端接24V正极传感器输出低电平有效。输入通道电路结构是这样的24V信号先经过1kΩ限流电阻进入光耦的输入端光耦输出端接3.3V上拉后进入STM32的GPIO。光耦我选了TLP521-4或者PC817C这里有个容易被忽略的参数——电流传输比CTR。PC817C的CTR范围是200%~400%在1mA级别的驱动电流下也能可靠导通但如果选了CTR只有50%的A档信号源驱动力弱时就会偶尔丢脉冲。这个成本差只有几分钱千万别省。RC滤波我放在光耦的输入侧典型值是1kΩ电阻串联0.1μF电容并联形成的截止频率大约在1.6kHz左右。对于普通按钮和接近开关几十到几百赫兹的开关频率这个滤波足够干净同时也不会把快速计数脉冲滤掉——前提是你别用同一个通道既做普通输入又做高速计数。3.3 输出驱动继电器还是晶体管输出部分有两种路线继电器输出和晶体管输出。继电器输出适合驱动接触器线圈、电磁阀这类交流负载带载能力强触点隔离好缺点是有机械寿命动作频繁时容易坏。我第一版用了松乐SRD-05VDC-SL-C5V线圈触点容量10A 250VAC价格一块多一只。注意驱动继电器的三极管必须加续流二极管我用的是SS14贴片肖特基直接并在继电器线圈两端方向反了会烧三极管。晶体管输出漏型适合驱动步进驱动器脉冲口、伺服使能信号这类直流负载响应速度快没有机械磨损。我用的N沟道MOS管是AO3400SOT-23封装最大4A搭配1kΩ栅极电阻。实际做的板子上两种输出都留了8路继电器输出管交流负载4路晶体管输出管脉冲信号。板子面积大概10cm×8cm四层板关键的模拟地、数字地、功率地在电源入口处单点汇接。3.4 电源设计与抗干扰处理现场设备最怕的不是程序写错而是电源被干扰导致死机。我这块板子电源部分做了三层处理第一层是输入端的TVS管。24V电源进来后先经过一个SMBJ24A TVS管吸收浪涌再串一个自恢复保险丝PPTC1.5A防止现场接线错误把板子烧了。第二层是DC-DC隔离。24V经过B0505S-1WR2隔离模块转成5V隔离模块的输出再供给继电器和逻辑部分实现电源侧的电气隔离。第三层才是AMS1117-3.3给MCU供电输入输出都并了10μF钽电容和0.1μF陶瓷电容。这套电源方案做下来现场经历了变频器启停、接触器吸合释放的强干扰没有出现过一次程序跑飞。对比之下我见过很多人直接用USB供电跑开发板一到现场就各种莫名重启问题十有八九出在电源上。4. 从烧录到点亮第一个LEDRuntime固件与IDE对接4.1 烧录Runtime固件到STM32Runtime固件本质上是一个特定的二进制文件需要通过ST-Link或者串口ISP烧进STM32的Flash。烧录方式和普通STM32程序完全一样差别在于运行后你不会看到任何串口打印信息——因为所有资源都被Runtime占用了没有留给你的裸机代码。烧录步骤我整理了一下用STM32CubeProgrammer连接ST-Link确认能识别到芯片。擦除整个芯片Flash必须整片擦除否则残留的向量表会导致Runtime启动后跑飞。把目标.bin文件烧写到0x08000000地址。烧完后复位板载LED如果是Runtime自带的Bootloader会进入等待状态可以看到板子固定频率闪烁。不同移植版本的启动指示灯含义不一样有的版本是常亮表示运行正常有的版本是每500ms翻转变一次具体看Release Notes。如果你用的是STM32F407烧录后通过网线直连电脑PC端应该能枚举出一个以太网设备。4.2 安装CODESYS V3.5并添加目标设备描述PC端需要安装CODESYS V3.5 SP17以上版本我用的SP18安装包从官方渠道下载即可。装完之后默认的设备库里面是没有STM32F4这个目标设备的需要手动添加设备描述文件。在CODESYS IDE中操作路径是工具 → 设备仓库 → 安装选择下载好的CODESYS Control for STM32F4.xml设备描述文件。安装成功后新建工程时在设备列表里就能看到“CODESYS Control for STM32F4”这个节点。4.3 网络通信配置与IP修改RTOS Runtime跑起来后设备会默认开启DHCP客户端如果局域网里没有DHCP服务器它会回退到一个默认IP通常是192.168.1.10。而CODESYS IDE所在的电脑需要和它在同一网段才能扫到。最快的做法是把电脑网卡设成静态IP 192.168.1.2子网掩码255.255.255.0然后打开IDE的“扫描网络”一般几秒钟内就能看到目标PLC。这里要给新手提个醒如果你在生产现场需要修改PLC的IP地址不要试图通过串口终端去改。CODESYS Runtime的IP修改入口在IDE里设备 → PLC → 通信设置 → 网络接口可以直接给目标设备绑定静态IP。改完之后设备会自动重启这个功能在第一次远程调试时非常救命因为它省去了给现场PLC插显示器的麻烦。4.4 第一个工程点灯实验在IDE的PLC_PRG里写一段最简单的程序IF xStart THEN xOutput : TRUE; ELSE xOutput : FALSE; END_IF然后在设备树里找到IO映射把xOutput映射到板子上的一个LED引脚把xStart映射到一个按钮输入引脚。编译、登录、运行按下按钮LED亮起——整个过程走通之后你的STM32F4才算真正变成了“PLC”。这里的核心感受是你不再需要关心寄存器配置、位操作这些底层细节编程方式就是标准的IEC 61131-3梯形图该怎么做就怎么做电气工程师完全可以无缝切换。5. 从IO映射到生产级逻辑梯形图与ST混用的实战工程5.1 GPIO映射的几个隐藏坑IO映射看似简单实际操作中有几个容易出问题的地方。第一是引脚复用冲突。有些STM32F4的GPIO复用功能是重叠的比如PA9和PA10既可以是USART1的TX/RX也可以是其他外设的引脚。Runtime固件默认把USART1分配给了Modbus RTU从站结果你想拿PA9当普通输出用电平就怎么都拉不起来。解决办法是在固件编译阶段改引脚定义或者干脆避开这些引脚选用定时器通道、ADC、DAC以外的纯GPIO。第二是输入极性。很多Runtime版本默认输入是高电平有效但现场接的传感器可能是PNP输出高电平有效也可能是NPN输出低电平有效。如果在IDE里不设置输入极性反转就会出现传感器遮挡时PLC读到1、不遮挡时读到0的颠倒情况。在IO映射的配置里通常有Invert选项勾选后极性就反过来了。第三是模拟量输入的缩放。STM32F4的ADC是12位满量程4095对应3.3V。如果你接的是0~10V的外部传感器电平转换电路已经把10V分压到3.3V了那么在CODESYS里读到的整数值和实际工程值的换算关系就是工程值 ADC原始值 / 4095 × 量程。这个换算逻辑最好单独做一个功能块不要在梯形图里用一堆除法指令去算可读性太差。5.2 梯形图搭框架、ST写算法实际工程我推荐混合编程逻辑主流程用梯形图数学运算和复杂算法用ST。这样电气工程师看得懂主逻辑软件工程师也能把算法封装好。举个恒压供水例子。梯形图里做泵启停、液位开关、手自动切换这些顺控逻辑而在一个名为PID_Control的ST功能块里写PID运算PROGRAM PID_Control VAR e : REAL; ePrev : REAL; integral : REAL : 0.0; output : REAL; END_VAR e : SetPoint - ProcessValue; integral : integral e * SampleTime; output : Kp * e Ki * integral Kd * (e - ePrev) / SampleTime; ePrev : e; IF output 50.0 THEN output : 50.0; ELSIF output 0.0 THEN output : 0.0; END_IF这里的Kp、Ki、Kd、SampleTime都设为VAR_INPUT变量这样在梯形图里也能直接调用这个功能块并在每次扫描周期里传入参数。5.3 任务周期与看门狗CODESYS里可以创建多个任务每个任务有独立的周期。我一般把IO刷新的任务周期设成10ms把Modbus通信的任务周期设成50ms把梯形图主逻辑设成20ms。任务周期的设定直接影响PLC的响应速度太慢会感觉“肉”太快则会挤占CPU资源导致通信卡顿。关于看门狗CODESYS的设备配置里有一个Watchdog选项建议打开并设成1秒左右。Runtime会在主循环里喂狗如果某个任务卡死超过这个时间设备会自动重启。我在现场遇到过极端情况Modbus总线上一台变频器掉线后通信任务一直在重试导致其他低优先级任务无法执行——打开看门狗后起码能自动恢复不会一直“死”到人过去断电。6. 用Modbus RTU带32台变频器的实测记录6.1 为什么这里选Modbus RTU而不是Modbus TCP现场那批变频器是国产的支持Modbus RTU但没有以太网口。如果全部改用带Modbus TCP的变频器成本每台多出大概200块32台就是6400块——这个差价足以推翻整个“低成本PLC”的初衷。所以最终方案定为PLC作为Modbus主站通过RS485总线挂接32台变频器通信参数9600bps、8位数据、无校验、1位停止位。RS485总线的接线必须注意两个点一是双绞线屏蔽层单端接地在PLC侧接地变频器侧不接地防止地环路电流二是总线两端各接一个120Ω终端电阻PLC侧电阻在板子上已经焊好最远端那台变频器需要在其450Ω端子上并接120Ω电阻。很多通信不稳定的问题追到根上就是少了这个终端电阻。6.2 轮询调度逻辑不能“串行阻塞”如果按常规思路用一个大FOR循环去轮询32台变频器每台等100ms一轮下来3.2秒最远端那台的响应就太滞后了。我给轮询调度设计成了基于任务周期的状态机状态机的核心思路是每个通信周期只发一帧报文收到回复或超时后再发下一帧。代码骨架如下CASE eState OF 0: // 发送请求帧启动超时计时 SendRequest(uiSlaveID : 1, uiRegAddr : 0x1000, uiValue : 50); tTimeoutTimer : TIME(); eState : 1; 1: // 等待回复或超时 IF bResponseReceived THEN HandleResponse(uiSlaveID : 1); uiSlaveID : 2; // 指向下一台 eState : 0; ELSIF TIME() - tTimeoutTimer T#200MS THEN LogError(uiSlaveID : 1, eErr : ERR_TIMEOUT); uiSlaveID : 2; eState : 0; END_IF; END_CASE这样32台变频器轮询一轮的耗时取决于每台回复的快慢实测在9600bps下平均每台35~45ms左右一轮总耗时约1.2到1.5秒远快于串行阻塞方式。6.3 实测数据响应时间与总线稳定性连续跑了48小时记录数据如下指标实测值单台变频器平均响应时间38ms单台最大响应时间95ms发生在总线干扰瞬间32台设备一轮完整轮询约1.4秒48小时通信失败次数4次均为瞬时干扰重试后恢复CPU占用率最高37%需要说明的是9600bps的波特率决定了单帧报文传输时间本身就接近10ms所以响应时间的下限是受波特率限制的和你单片机的性能关系不大。如果想让响应更快可以把波特率提到19200甚至38400但要确认现场的屏蔽布线和终端电阻到位不然反而会因误码率上升而得不偿失。6.4 通信掉线后的重试与报警策略Modbus通信的可靠性再高现场总有意外。我的策略是“三级容错”第一级单帧超时后重发两次每次间隔200ms。第二级若连续3轮轮询大约5秒都没有收到某台从站的回复则将该从站标记为离线在触摸屏上弹报警但不阻塞其他从站的通信。第三级当从站恢复通信后自动清除离线标记并把恢复时间记入日志。这个思路比“一掉线就停机”要友好得多——变频器通信瞬断在工厂里是比较常见的事能在不影响生产的情况下自动恢复才是现场真正需要的。7. 把这些坑踩一遍常见问题排查记录7.1 输入抖动误触发RC参数是“经验值”不是“死值”第一版板子拿到现场后有个光电传感器频繁误触发。示波器一看信号波形在跳变沿附近有大概20ms的毛刺这是设备启停时电机电缆和信号线平行走线导致的感应干扰。我原先设的RC滤波截止频率是1.6kHz理论上能滤掉高频毛刺但现场干扰的频段比预期低很多。后来我把输入滤波电容从0.1μF加大到1μFRC截止频率降到约160Hz毛刺大部分被吸收掉了。代价是输入响应变慢原本1ms能采到的信号变成了6ms左右但对于接近开关和光电传感器6ms的延迟完全在可接受范围内。所以RC参数一定要结合现场的干扰特性来定上来就照抄别人参数表是不可行的。7.2 以太网通信突然断开当DHCP遇到现场乱接的网线有一次在现场PLC程序跑了一个月突然某天出现“无法在线连接”的情况。到现场一查PLC运行其实是正常的因为输出点还在按逻辑动作只是IDE连不上。排查过程是这样的先用U盘在PLC旁边接笔记本直连看能不能Ping通。结果能Ping通那说明网络层没问题。再去IDE扫描网络扫不到设备。后来发现是车间里有人把PLC插的那个交换机换成了家用路由器路由器默认开了DHCPPLC开机后从路由器拿到了新IP而原来IDE里记录的IP已经不对了。解决办法是进路由器管理后台看当前IP或者在PLC的通信设置里绑定静态IP从根源上杜绝这类问题。7.3 固件升级失败的恢复方法CODESYS Runtime固件并不是一劳永逸的我试过从SP15版本升级到SP18版本烧录后发现设备扫描不出来。原因是新的Runtime版本对Flash布局做了调整必须同步更新设备描述文件和Bootloader。恢复方法有个小技巧不要急着用ST-Link把整个Flash擦掉重烧先试一下“Boot模式”。有些STM32板子在BOOT0拉高后上电会进入系统存储器自带的Bootloader此时可以通过串口ISP重新烧录固件。如果Bootloader本身也被冲掉了那就只能老老实实上ST-Link用STM32CubeProgrammer连接后选“Erase All”再把旧版本固件烧回去。结论是升级Runtime前先备份好当前能用的固件和对应的设备描述文件降级永远比升级容易。7.4 程序下载后无输出的低级错误最后说一个很蠢但很常见的错误。我写完梯形图后下载到PLC点击运行结果所有输出点都没反应。排查了半天最后发现是IO映射里没把物理输出引脚勾选“Enable”——CODESYS里GPIO映射默认是禁用状态必须手动在映射表里勾上“Enable”才能真正输出。这个问题至少浪费了我半天时间写在这给后来人避坑。8. 成本核算与工程文件说明8.1 一套完整控制板的成本清单做了这么多工作成本到底多少我按小批量采购5套的价格列了个表物料型号/规格单价元STM32F407ZET6LQFP-14435W25Q64 SPI FlashSOP-83.5LAN8720AQFN-248AMS1117-3.3SOT-2230.8TLP521-4光耦DIP-162.5SRD-05VDC继电器10A1.5AO3400-MOSSOT-230.5B0505S隔离模块1W4.5PCB四层板小批量10cm×8cm5片分摊20阻容、接插件等杂项10合计约86.3元这个成本还不算外壳、导轨卡扣这些结构件和人工焊接费用。如果只算BOM物料加SMT贴片单台控制板成本在100元左右。配上触摸屏国产威纶通或昆仑通态七八百整体成本也只是商用PLC方案的零头。8.2 我能公开的工程内容整理了一下可以放出来的东西硬件原理图与PCB文件Altium Designer格式和Gerber文件各一份按上面的结构画的。Runtime固件烧录脚本基于STM32CubeProgrammer命令行工具的批处理脚本一键烧录。CODESYS设备描述文件.xml格式安装到IDE即可识别STM32F4目标。示例工程一个恒压供水的演示工程源码包含梯形图主逻辑、PID功能块、Modbus RTU主站通信程序。所有这些在文末我留了百度网盘链接和GitHub仓库地址需要自取。8.3 一个中立的技术选型建议写这么多不是劝大家都把PLC替换成STM32。我的态度很明确能用商用PLC的场景不要为了省钱而换方案商用PLC在可靠性、售后服务、认证资质上都有不可替代的优势。这个低成本方案的真正价值在于当你做原型验证、教学演示、小批量设备、或者预算实在砍无可砍的时候它提供了一个“能打”的备选。用100元成本换回一个带完整IEC 61131-3编程环境的控制器这笔账怎么算都不亏。根据我的经验这个板子在实验室跑了半年多、现场跑了一个多月累计运行时间超过3000小时没有出现过死机或程序跑飞。但每个人的使用习惯和现场环境不同如果你要把它投入工业现场量产建议先做充分的EMC测试和老化测试别拿一批次品到客户现场找骂。真把路走通了后续还可以加CANopen、加EtherCAT从站、加更多的模拟量通道方案的可扩展性还是很值得期待的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价