1. 项目背景与核心痛点
作为一名长期奋战在工业自动化一线的工程师,我深知三菱FX3U PLC在小型运动控制项目中的尴尬处境。这款经典PLC虽然价格亲民、梯形图编程门槛低,但其内置的脉冲输出功能(PLSY/PLSR)在实际三轴控制场景中总是让人抓狂。
原生的PLSY指令只能以固定频率发送脉冲,要实现加减速必须手动编写梯形图逻辑控制定时器。而PLSR指令虽然支持加减速,但方向控制需要操作D8340这类特殊寄存器,三轴同时运行时还要处理寄存器冲突和中断分配问题。更别提FX3U的DRIV功能只能算"半吊子"软轴控制,用起来各种不顺手。
2. 解决方案架构设计
2.1 技术选型与底层改造
我选择了GitHub上star数2.4k的FX3U开源仿真库作为基础,这个库原本运行在STM32F103平台上。核心改造点包括:
-
硬件资源重组:
- 原库仅使用TIM3的CH1/CH2作为单路脉冲输出
- 改造后充分利用STM32F103的定时器资源:
- TIM1、TIM2、TIM3全部启用
- 每路定时器配置为带互补输出的PWM模式(CHx/CHxN)
- 硬件互补输出直接驱动方向信号,省去外部继电器
-
中断优先级优化:
- TIM1(最高优先级)→ Y0/Y1轴
- TIM2(中优先级)→ Y2/Y3轴
- TIM3(最低优先级)→ Y4/Y5轴
- 确保三轴同时运行时不会丢失脉冲
2.2 寄存器映射设计
为保持与FX3U原生编程习惯兼容,设计了特殊的软寄存器映射方案:
c复制// 三轴PLSR寄存器组(每组占用连续D寄存器)
typedef struct {
uint32_t target_pulse; // Dn-Dn+1 目标脉冲数(带符号)
uint16_t base_freq; // Dn+2 起始频率(Hz)
uint16_t max_freq; // Dn+3 最高频率(Hz)
uint16_t acc_time; // Dn+4 加减速时间(ms)
uint8_t ctrl_flags; // Dn+5 控制标志位
// bit0: 启动位
// bit1: 暂停位
// bit2: 完成位
} PLSR_Axis_t;
PLSR_Axis_t axis[3]; // 三轴实例
3. 核心算法实现细节
3.1 智能加减速控制算法
传统梯形加减速存在脉冲数不准的问题,我改进了中断服务程序中的速度规划逻辑:
c复制void TIM1_UP_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE)) {
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
// 剩余脉冲不足时提前降速
if (pulse_left0 <= arr_slope0[arr_cnt0]) {
arr_cnt0--;
if (arr_cnt0 == 0) { // 已降至初始速度
__HAL_TIM_SET_AUTORELOAD(&htim1, arr_slope0[0]);
if (pulse_left0 == 0) __HAL_TIM_DISABLE(&htim1);
} else {
current_arr0 += arr_slope0[arr_cnt0]; // 增大ARR=降低频率
__HAL_TIM_SET_AUTORELOAD(&htim1, current_arr0);
}
}
// 正常加速阶段
else if (current_arr0 > target_arr0) {
arr_cnt0++;
current_arr0 -= arr_slope0[arr_cnt0]; // 减小ARR=提高频率
__HAL_TIM_SET_AUTORELOAD(&htim1, current_arr0);
}
pulse_left0--; // 脉冲计数器递减
}
}
3.2 多轴同步控制策略
为实现真正的三轴并行控制,设计了以下机制:
-
独立的速度曲线预计算:
c复制// 为每轴预计算S型加减速曲线 void precompute_s_curve(uint8_t axis_id) { float t_acc = axis[axis_id].acc_time / 1000.0f; float f_start = axis[axis_id].base_freq; float f_max = axis[axis_id].max_freq; for(int i=0; i<256; i++) { float t = (i/255.0f) * t_acc; // 三次多项式S曲线计算 float freq = f_start + (f_max-f_start)*(3*t*t - 2*t*t*t); arr_slope[axis_id][i] = (uint16_t)(SystemCoreClock / freq); } } -
脉冲补偿机制:
- 每个定时器中断记录实际发出的脉冲数
- 在急停/暂停恢复时自动补偿丢失的脉冲
- 通过32位累加器确保长距离运行不丢步
4. 实用功能增强
4.1 改进的DRIV指令实现
原生FX3U的DRIV功能需要繁琐的梯形图编程,我将其简化为:
c复制// DRIV指令处理逻辑
void process_driv_command(uint8_t axis_id) {
uint32_t cmd_addr = 10100 + axis_id*10; // DRIV寄存器基址
if (d_memory[cmd_addr] & 0x01) { // 绝对坐标模式
int32_t delta = (int32_t)(d_memory[cmd_addr+1] | (d_memory[cmd_addr+2]<<16))
- current_pulse[axis_id];
axis[axis_id].target_pulse = delta;
} else { // 相对坐标模式
axis[axis_id].target_pulse = d_memory[cmd_addr+1] | (d_memory[cmd_addr+2]<<16);
}
// 共享PLSR的频率参数
axis[axis_id].base_freq = d_memory[10002 + axis_id*10];
axis[axis_id].max_freq = d_memory[10003 + axis_id*10];
axis[axis_id].acc_time = d_memory[10004 + axis_id*10];
// 触发运动
axis[axis_id].ctrl_flags |= 0x01;
}
4.2 实用调试接口
为方便现场调试,增加了以下功能:
-
实时状态监控:
- 通过特定D寄存器读取各轴当前脉冲数
- M寄存器映射运行状态标志
-
动态参数调整:
c复制// 运行时修改加减速参数 void update_motion_params(uint8_t axis_id) { uint16_t new_acc = d_memory[10004 + axis_id*10]; if (new_acc != axis[axis_id].acc_time) { axis[axis_id].acc_time = new_acc; precompute_s_curve(axis_id); // 重新计算速度曲线 } }
5. 硬件连接与实战技巧
5.1 推荐接线方案
| STM32引脚 | FX3U对应口 | 伺服驱动器连接 |
|---|---|---|
| PA8 (TIM1_CH1) | Y0 | 脉冲+ |
| PA7 (TIM1_CH1N) | Y1 | 方向+ |
| PA0 (TIM2_CH1) | Y2 | 脉冲+ |
| PA1 (TIM2_CH2) | Y3 | 方向+ |
| PA6 (TIM3_CH1) | Y4 | 脉冲+ |
| PA5 (TIM3_CH2) | Y5 | 方向+ |
重要提示:务必在伺服驱动器侧配置5V上拉电阻,STM32的IO驱动能力有限
5.2 参数调优经验
-
加减速时间设置:
- 步进电机:建议50-200ms
- 伺服电机:可设置为20-100ms
- 通过D10004、D10014、D10024分别设置三轴参数
-
抗干扰措施:
- 脉冲线使用双绞屏蔽线
- 在驱动器脉冲输入端口并联100Ω终端电阻
- 确保所有设备共地良好
6. 性能测试数据
在三轴同步运行测试中,获得了以下实测数据:
| 测试场景 | 脉冲频率 | 脉冲数 | 理论时间(ms) | 实测时间(ms) | 误差 |
|---|---|---|---|---|---|
| 单轴运行 | 10kHz | 20000 | 2000 | 2000 | 0 |
| 三轴同步 | 50kHz | 50000 | 1000 | 1002 | +0.2% |
| 急停恢复 | 100kHz | 100000 | 1000 | 1003 | +0.3% |
7. 常见问题解决方案
7.1 脉冲丢失问题排查
- 现象:实际移动距离与设定值不符
- 排查步骤:
- 检查
current_pulse[]监控值与驱动器接收值 - 确认中断优先级设置正确(TIM1>TIM2>TIM3)
- 测量脉冲信号波形质量(建议用100MHz示波器)
- 检查
7.2 方向控制异常处理
- 现象:电机反向运行
- 解决方案:
c复制// 在定时器初始化时设置默认极性 TIM_OC_InitTypeDef sConfigOC = {0}; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 脉冲正极性 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; // 方向正极性 HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
8. 项目优化方向
-
动态S曲线切换:
- 通过D寄存器位选择线性/指数/S型曲线
- 运行时动态重载预计算表
-
位置比较中断:
c复制// 在指定位置触发输出 void set_compare_point(uint8_t axis_id, uint32_t position) { compare_position[axis_id] = position; compare_enabled[axis_id] = true; } -
网络化扩展:
- 通过Modbus RTU读取运动参数
- 添加EtherCAT从站支持
这个改造项目最让我自豪的是,它既保持了FX3U梯形图的编程便利性,又实现了高端PLC才有的多轴协同控制能力。经过半年多的现场验证,这套系统在小型CNC、包装机等设备上运行稳定,脉冲控制精度完全达到工业级要求。