找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 800|回复: 16
打印 上一主题 下一主题
收起左侧

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

  [复制链接]
跳转到指定楼层
楼主
ID:353115 发表于 2026-3-3 08:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
大家好,我是一名嵌入式开发者。一直觉得工业 PLC 价格高、定制难,于是决定用 51 单片机从零做一个小型 PLC。现在已经实现了DI、DO、AI、AO、高速计数输入、Modbus RTU 通信、辅助继电器、通用数据寄存器、16位通用加计数器、32位通用加减计数器、1ms定时器、10ms定时器、100ms定时器、1s定时器、1min定时器,这个帖子将逐步介绍,项目在proteus上的STC15W4K32S4上实现,若有需要可提供实板原理图,进行定制专用的MCU-PLC。

51hei1.jpg (240.86 KB, 下载次数: 0)

51hei1.jpg

51hei2.jpg (183.39 KB, 下载次数: 0)

51hei2.jpg

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏6 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:353115 发表于 2026-3-3 17:01 | 只看该作者
本项目是一个已建立好的KEIL工程,下载后直接打开即可。

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


回复

使用道具 举报

板凳
ID:353115 发表于 2026-3-3 17:33 | 只看该作者
这里放上proteus的仿真(需要8.15版本)、工程(内含设计说明书)。

PLC单片机proteus仿真(需8.15版本).zip

21.79 KB, 下载次数: 0, 下载积分: 黑币 -5

v1.0.0.260228.zip

562.76 KB, 下载次数: 0, 下载积分: 黑币 -5

回复

使用道具 举报

地板
ID:353115 发表于 2026-3-4 08:14 | 只看该作者
本帖最后由 qsssuv 于 2026-3-11 08:20 编辑

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

回复

使用道具 举报

5#
ID:283207 发表于 2026-3-4 10:11 | 只看该作者
感谢楼主,下载学习一下。51做PLC仅供学习原理应该是没什么问题的
回复

使用道具 举报

6#
ID:647576 发表于 2026-3-4 13:40 | 只看该作者
提议降分, 代码不全, 功能关键代码缺失
回复

使用道具 举报

7#
ID:353115 发表于 2026-3-5 13:46 | 只看该作者
本帖最后由 qsssuv 于 2026-3-11 08:20 编辑

【模拟量输入】
直接读取值即可:
D0=AI0;
回复

使用道具 举报

8#
ID:258566 发表于 2026-3-5 17:31 | 只看该作者
plc相关提供一个外部头文件,而不是在几个头文件中
mcu plc。lib中有没有使用时间限制。
回复

使用道具 举报

9#
ID:353115 发表于 2026-3-5 20:03 | 只看该作者
cccc888 发表于 2026-3-5 17:31
plc相关提供一个外部头文件,而不是在几个头文件中
mcu plc。lib中有没有使用时间限制。

这个LIB没有使用限制,但是只在仿真中测试通过
回复

使用道具 举报

10#
ID:353115 发表于 2026-3-6 08:00 | 只看该作者
【定时器】
所有的定时器,都是倒计时定时器,将从设置的值开始倒计时,若计数值达到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,以此类推。
回复

使用道具 举报

11#
ID:353115 发表于 2026-3-6 16:56 | 只看该作者
【计数器】
计数器有3种状态,分别是停止(C_STA_STOP)、运行(C_STA_RUN)、计数到达(C_STA_OK)。
16位通用加计数器有2种方法。
第1种方法,加计数:CTU(要使用的计数器,信号源,计数目标值)。
例如使用计数器C0对X0的上升沿,进行加计数,到达10次时,Y0输出1s后重新开始计数。
  1. IF (CTU(C0,X0,10)==C_STA_OK)  //若计数器C0,捕捉到X0上升沿10次
  2. {
  3.     TON(T50,100);  //启动1s定时器
  4.     SET(Y0);  //输出Y0
  5. } ELSE {
  6.     RST(Y0);  //清除Y0
  7. }
  8. IF (TGetSta(T50)==T_STA_OK)  //若1s时间到
  9. {
  10.     RST(Y0);  //清除Y0
  11.     TRST(T50);  //重置定时器
  12.     C16RST(C0);  //重置计数器
  13. }
复制代码



第2种方法,减计数:CTD(要使用的计数器,信号源,计数目标值)。
例如使用计数器C1对X1的下降沿,进行减计数,到达10次时,Y1输出2s后重新开始计数。
  1. IF (CTD(C1,NOT(X1),10)==C_STA_OK)  //若计数器C1,捕捉到X0下降沿10次
  2. {
  3.     TON(T50,200);  //启动2s定时器
  4.     SET(Y1);  //输出Y1
  5. } ELSE {
  6.     RST(Y1);  //清除Y1
  7. }
  8. IF (TGetSta(T50)==T_STA_OK)  //若计时时间到
  9. {
  10.     RST(Y1);  //清除Y1
  11.     TRST(T50);  //重置定时器
  12.     C16RST(C1);  //重置计数器
  13. }
复制代码

32位通用计数器有1种方法。CTUD(计数器,加计数信号源,减计数信号源,计数目标值)。
例如使用计数器C200,X0作为加计数信号源,X1作为减计数信号源,当数值到达200时,Y0输出2s,然后重新计数。
由于32位通用计数器在计数完成后,仍会受到加减操作的影响,导致状态变更,因此引入辅助继电器,避免Y0的输出波动。
  1. IF (M0==0)  //若M0为0
  2. {
  3.     IF (CTUD(C200,X0,X1,200)==C_STA_OK)  //若计数器C200,在X0和X1的计数信号下,达到200次
  4.     {
  5.         TON(T50,200);  //启动2s定时器
  6.         SET(Y0);  //输出Y0
  7.         SET(M0);  //置位M0
  8.     } ELSE {
  9.         RST(Y0);  //清除Y0
  10.     }
  11. }
  12. IF (TGetSta(T50)==T_STA_OK)  //若定时时间到
  13. {
  14.     RST(Y0);  //清除Y0
  15.     C32RST(C200);  //重置计数器
  16.     RST(M0);  //清除M0
  17.     TRST(T50);  //重置定时器
  18. }
复制代码

回复

使用道具 举报

12#
ID:353115 发表于 2026-3-7 11:00 | 只看该作者
【高速输出/PWM】
控制高速输出有4种方法。分别为以下:
以Y100为例。
输出控制:PWM_OUT(Y100,频率,占空比)。频率范围为400Hz~100KHz,占空比范围为0-100,为整数,请勿使用小数。占空比0=无输出,100=持续高电平。
获取输出频率:PWM_GetFre(Y100)。返回设置的频率,若未设置,返回0。
获取输出占空比:PWM_GetDuty(Y100)。返回设置的占空比,若未设置,返回255。
停止输出:PWM_STOP(Y100)。Y100将为0。
注:若不进行高速输出,其默认将为一个通用输出。
回复

使用道具 举报

13#
ID:90212 发表于 2026-3-8 10:30 | 只看该作者
要做PLC起码也得用STC32吧
回复

使用道具 举报

14#
ID:353115 发表于 2026-3-8 16:12 | 只看该作者
gongzhu 发表于 2026-3-8 10:30
要做PLC起码也得用STC32吧

PCB可以用STC32,但是仿真上没有,所以用了STC15,其实任何单片机都可以
回复

使用道具 举报

15#
ID:353115 发表于 2026-3-9 10:31 | 只看该作者
本帖最后由 qsssuv 于 2026-3-11 08:21 编辑

【高速计数输入】
需要用到特殊辅助继电器、特殊数据寄存器,这里使用了一看就明白的变量值。
使能高速计数输入X0,注意使能后,请勿将其用作通用输入接口:
  1. DX0_CNT=0;  //设置起始计数值
  2. MX0_OUT=0;  //清除溢出标志
  3. DX0_MAX=100;  //定义设置上限
  4. SET(MX0_EN);  //启动
复制代码
停用高速计数输入X0,停用后,可用作通用输入接口:
  1. RST(MX0_EN);  //清除使能。除非用户手动清除计数值DX0_CNT,否则启动后继续计数
复制代码
当起始计数值DX0_CNT达到设置上限DX0_MAX,溢出标志MX0_OUT会置1,并且DX0_CNT从0开始。需要用户手动清除溢出标志MX0_OUT。
需要注意,受限于MCU的性能,若值范围为0~255,则计数的设置上限,以及起始计数值,设置时均不可超出此范围。
若需要使用高速计数输入X3,将以上代码中的X0修改为X3使用即可。
回复

使用道具 举报

16#
ID:353115 发表于 2026-3-10 08:20 | 只看该作者
【日期和时间】
dhms是一个集合了设备已运行时间的对象。包含天数、时、分、秒。
dhms.day,天,最大到32767。
dhms.hour,时。
dhms.min,分。
dhms.sec,秒。
用户可将其加载至通用数据寄存器。
回复

使用道具 举报

17#
ID:353115 发表于 2026-3-11 08:21 | 只看该作者
【Modbus-RTU从站协议】
通信配置:4800,N,8,1。访问地址固定为1。
寄存器表就不贴图了,可以下载下来看设计说明中的详细内容。
总之,就是直接连上Modscan工具就可以使用,很方便。

在仿真上,你需要安装虚拟串口驱动,才能使用modscan连接到proteus仿真
在app_plc_reg_update文件中,填写了默认的更新事件内容,用户可自定义其它行为
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表