青穗三三 发表于 2026-3-2 17:25:55

用 51 做了个 PLC:X、Y、高速计数输入、高速输出、Modbus等直接用,超低学习成本

大家好,我是一名嵌入式开发者。一直觉得工业 PLC 价格高、定制难,于是决定用 51 单片机从零做一个小型 PLC。现在已经实现了DI、DO、AI、AO、高速计数输入、Modbus RTU 通信、辅助继电器、通用数据寄存器、16位通用加计数器、32位通用加减计数器、1ms定时器、10ms定时器、100ms定时器、1s定时器、1min定时器,这个帖子将逐步介绍,项目在proteus上的STC15W4K32S4上实现,若有需要可提供实板原理图,进行定制专用的MCU-PLC。


2026.3.14
视频教程已上传至B站

青穗三三 发表于 2026-3-2 17:50:05

本项目是一个已建立好的KEIL工程,下载后直接打开即可。

直接在app_plc文件夹编写自己的代码就好了,
图片中的程序代码是在Y103输出500Hz,占空比50%的PWM波形
高速计数输入计250时,即250/500为0.5s,
在仿真中,将Y103连接到了X0,Y0接了一个LED灯,可以看到LED闪烁。

青穗三三 发表于 2026-3-2 20:03:35

这里放上proteus的仿真(需要8.15版本)、工程(内含设计说明书)。

青穗三三 发表于 2026-3-3 08:11:54

通用输入/通用输出操作。
X为输入,Y为输出。
读取输入,直接写名称即可。
IF (X0)
{
    ……
}
将输出置为0的三种写法:
RST(Y0);
Y0=0;
MOV(0,Y0);
将输出置为1的三种写法:
SET(Y0);
Y0=1;
MOV(1,Y0);
将输入短路至输出的两种写法:
Y0=X0;
MOV(X0,Y0);

青穗三三 发表于 2026-3-3 08:23:55

定时器。
所有的定时器,都是倒计时定时器,将从设置的值开始倒计时,若计数值达到0,运行状态T_STA_RUN,将转变为计时到达状态T_STA_OK。定时器有4种状态、5种使用方法。
4种状态分别是:停止(T_STA_STOP)、运行(T_STA_RUN)、暂停(T_STA_PAUSE)、计时到达(T_STA_OK)。
5种使用方法分别是TON、TPAUSE、TRST、TGetSta、TGetVal,用以下例子加以说明。
//若T0定时10ms时间到,则输出Y0。
IF (TON(T0,10)==T_STA_OK)
{
    SET(Y0);
} ELSE {
    RST(Y0);
}
//时间未到时,若X0输入,则暂停定时器,若X1输入,则重置定时器
IF (TGetSta(T0)!=T_STA_OK)
{
    IF (X0) TPAUSE(T0);
    IF (X1) TRST(T0);
}
//将计数值导出到D0
D0 = TGetVal(T0);
注意,定时器定时值的单位为对应时基:如1ms定时器T0,TON(T0,10)表示定时10ms;100ms定时器T100,TON(T100,5)表示定时500ms,以此类推。

青穗三三 发表于 2026-3-3 08:36:07

计数器
计数器有3种状态,分别是停止(C_STA_STOP)、运行(C_STA_RUN)、计数到达(C_STA_OK)。
16位通用加计数器有2种方法。
第1种方法,加计数:CTU(要使用的计数器,信号源,计数目标值)。
例如使用计数器C0对X0的上升沿,进行加计数,到达10次时,Y0输出1s后重新开始计数。
IF (CTU(C0,X0,10)==C_STA_OK)//若计数器C0,捕捉到X0上升沿10次
{
    TON(T50,100);//启动1s定时器
    SET(Y0);//输出Y0
} ELSE {
    RST(Y0);//清除Y0
}
IF (TGetSta(T50)==T_STA_OK)//若1s时间到
{
    RST(Y0);//清除Y0
    TRST(T50);//重置定时器
    C16RST(C0);//重置计数器
}
第2种方法,减计数:CTD(要使用的计数器,信号源,计数目标值)。
例如使用计数器C1对X1的下降沿,进行减计数,到达10次时,Y1输出2s后重新开始计数。
IF (CTD(C1,NOT(X1),10)==C_STA_OK)//若计数器C1,捕捉到X0下降沿10次
{
    TON(T50,200);//启动2s定时器
    SET(Y1);//输出Y1
} ELSE {
    RST(Y1);//清除Y1
}
IF (TGetSta(T50)==T_STA_OK)//若计时时间到
{
    RST(Y1);//清除Y1
    TRST(T50);//重置定时器
    C16RST(C1);//重置计数器
}
32位通用计数器有1种方法。CTUD(计数器,加计数信号源,减计数信号源,计数目标值)。
例如使用计数器C200,X0作为加计数信号源,X1作为减计数信号源,当数值到达200时,Y0输出2s,然后重新计数。
由于32位通用计数器在计数完成后,仍会受到加减操作的影响,导致状态变更,因此引入辅助继电器,避免Y0的输出波动。
IF (M0==0)//若M0为0
{
    IF (CTUD(C200,X0,X1,200)==C_STA_OK)//若计数器C200,在X0和X1的计数信号下,达到200次
    {
      TON(T50,200);//启动2s定时器
      SET(Y0);//输出Y0
      SET(M0);//置位M0
    } ELSE {
      RST(Y0);//清除Y0
    }
}
IF (TGetSta(T50)==T_STA_OK)//若定时时间到
{
    RST(Y0);//清除Y0
    C32RST(C200);//重置计数器
    RST(M0);//清除M0
    TRST(T50);//重置定时器
}

YangHY 发表于 2026-3-3 11:42:55

佩服版主!

我先慢慢看懂,打算好好跟您学习这个技能!

wangxiangtan 发表于 2026-3-3 13:44:15

不错,加上EtherCAT、Profinet、ModbusTCP吧

jwd 发表于 2026-3-3 15:55:59

请问能控制几轴?

青穗三三 发表于 2026-3-3 16:39:20

jwd 发表于 2026-3-3 15:55
请问能控制几轴?

可以做4轴控制,目前这个基础免费版项目是建立在proteus仿真上面的。要落地电路板,需要有客户定制
页: [1] 2 3 4
查看完整版本: 用 51 做了个 PLC:X、Y、高速计数输入、高速输出、Modbus等直接用,超低学习成本