1. 项目概述与背景
在工业自动化领域,PLC(可编程逻辑控制器)与单片机的协同工作是一种常见架构。PLC负责逻辑控制和系统级调度,而单片机则执行具体的设备驱动和信号采集任务。这种架构结合了PLC的高可靠性和单片机的灵活性,广泛应用于生产线控制、设备监控等场景。
传统开发过程中,工程师需要同时连接真实的PLC和单片机硬件进行调试,这不仅增加了设备成本,也延长了开发周期。通过虚拟串口和仿真技术,我们可以在没有物理硬件的情况下,完整模拟PLC与单片机的通信过程。这种方法特别适合:
- 前期功能验证
- 通信协议开发
- 教学演示环境搭建
2. 环境搭建与工具准备
2.1 虚拟串口创建工具
VSPD(Virtual Serial Port Driver)是业界常用的虚拟串口创建工具,最新版本支持Windows 10/11系统。安装时需注意:
- 以管理员身份运行安装程序
- 在防火墙设置中允许程序通信
- 安装完成后建议重启系统
创建虚拟串口对的典型步骤:
- 打开VSPD主界面
- 在"Manage ports"选项卡点击"Add pair"
- 选择未被占用的端口号(如COM3和COM4)
- 确认创建后,在设备管理器中应能看到新增的端口
注意:某些杀毒软件可能会误报VSPD为风险程序,需要提前添加白名单。建议使用官方正版软件以避免驱动兼容性问题。
2.2 PLC仿真环境配置
使用西门子TIA Portal(博图)V17进行PLC仿真时,关键的串口参数配置要点:
xml复制<Communication>
<Port>COM3</Port>
<BaudRate>9600</BaudRate>
<DataBits>8</DataBits>
<StopBits>1</StopBits>
<Parity>None</Parity>
<FlowControl>None</FlowControl>
</Communication>
硬件配置时需要特别注意:
- 在设备视图中添加RS232通信模块
- 设置正确的硬件标识符(HW ID)
- 配置接口参数与虚拟串口保持一致
- 下载硬件配置到PLCSIM仿真器
3. PLC仿真程序设计
3.1 通信指令设计规范
本项目中采用的指令格式为三字节ASCII码:
- 首字母表示指令类型(F=正向运行,R=反向运行,S=停止)
- 后两位数字表示参数值(00-99)
典型指令示例:
F50:电机正向50%转速运行R30:电机反向30%转速运行S00:立即停止电机
在SCL语言中的发送程序实现:
scl复制// 电机控制函数块
FUNCTION_BLOCK MotorControl
VAR
Speed : INT;
Direction : BOOL;
END_VAR
IF Direction THEN
TCON_Send(Connection := COM_Port,
Data := CONCAT('F', INT_TO_STRING(Speed)));
ELSE
TCON_Send(Connection := COM_Port,
Data := CONCAT('R', INT_TO_STRING(Speed)));
END_IF
3.2 定时发送机制实现
为保证指令的周期性发送,需要使用PLC的定时器组织块:
lad复制// 梯形图实现
NETWORK 1
TON(IN := TRUE, PT := T#1S);
MOVE_BLK(IN := TON.Q, COUNT := 1, DEST => Send_Trigger);
NETWORK 2
CALL MotorControl(Speed := Actual_Speed,
Direction := Motor_Direction,
COM_Port := COM3);
4. 串口数据转发实现
4.1 串口助手配置要点
推荐使用AccessPort或Tera Term作为串口转发工具,关键配置参数:
| 参数项 | 值 | 说明 |
|---|---|---|
| 波特率 | 9600 | 必须与PLC设置一致 |
| 数据位 | 8 | 标准配置 |
| 停止位 | 1 | 常见设置 |
| 校验方式 | None | 无校验 |
| 流控制 | None | 禁用 |
| 接收超时 | 100ms | 避免缓冲区积压 |
4.2 数据转发逻辑实现
串口转发的基本工作流程:
- COM3接收来自PLC的原始指令
- 对数据进行有效性校验(长度、格式)
- 通过COM4转发给目标单片机
- 记录通信日志供调试分析
Python实现的简易转发脚本示例:
python复制import serial
from datetime import datetime
plc_port = serial.Serial('COM3', 9600, timeout=0.1)
mcu_port = serial.Serial('COM4', 9600, timeout=0.1)
while True:
data = plc_port.read(3) # 读取3字节指令
if len(data) == 3 and data[0] in (b'F', b'R', b'S'):
mcu_port.write(data)
print(f"[{datetime.now()}] Forwarded: {data.decode()}")
5. 单片机端实现
5.1 通信协议解析
单片机端需要实现的指令解析逻辑:
c复制#define CMD_START 'F'
#define CMD_REVERSE 'R'
#define CMD_STOP 'S'
void parse_command(char* cmd) {
if(strlen(cmd) != 3) return;
char direction = cmd[0];
int speed = atoi(&cmd[1]);
switch(direction) {
case CMD_START:
motor_run(FORWARD, speed);
break;
case CMD_REVERSE:
motor_run(BACKWARD, speed);
break;
case CMD_STOP:
motor_stop();
break;
}
}
5.2 电机驱动控制
基于PWM的电机速度控制实现:
c复制void motor_run(MotorDirection dir, uint8_t speed) {
// 限制速度范围
speed = speed > 99 ? 99 : speed;
// 设置方向引脚
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin,
dir == FORWARD ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 更新PWM占空比
uint16_t pulse = (TIM_PERIOD * speed) / 100;
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse);
}
6. 调试与问题排查
6.1 常见通信问题
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 接收数据不完整 | 波特率不匹配 | 检查两端波特率设置 |
| 数据乱码 | 校验位/停止位配置错误 | 确认所有串口参数一致 |
| 通信完全不通 | 虚拟串口未正确创建 | 重新创建串口对并测试连通性 |
| 指令执行延迟 | 缓冲区设置过小 | 增大串口接收缓冲区 |
6.2 性能优化建议
-
增加指令校验:在指令末尾添加校验和字节,提高通信可靠性
c复制// 示例:F50 的校验和为 'F'+'5'+'0'=0xCA void add_checksum(char* cmd) { uint8_t sum = cmd[0] + cmd[1] + cmd[2]; sprintf(cmd+3, "%02X", sum); } -
实现双向通信:让单片机返回状态确认信息,形成闭环控制
-
引入超时机制:超过指定时间未收到新指令时自动停止电机
7. 系统扩展思路
-
多电机控制:扩展指令格式支持多设备寻址
- 格式:
@设备ID 指令(如@1F50表示1号设备正向50%运行)
- 格式:
-
状态监控:通过Modbus RTU协议上传运行参数
-
安全保护:实现过流、过热等异常状态的自动处理
-
可视化界面:使用组态软件开发上位机监控系统
在实际项目中,我发现这种仿真方法可以节省约40%的硬件调试时间。特别是在开发初期,能够快速验证通信协议和基本控制逻辑的正确性。一个实用的技巧是:在PLC程序中添加模拟量波动算法,可以更真实地测试单片机端的控制稳定性。