三菱FX3U三轴控制源代码(本人修改版):新增PLSR定位与DRIV驱动等核心功能详解
三菱fx3u源代码增加plsr,driv等功能,代码是本人改过的,三轴
搞FX3U玩三轴步进/伺服总嫌自带逻辑绕:原生PLSY只管固定频率发、连加减速斜坡要手动写个定时器掐梯形图;PLSR虽然有加减但反转得扒D8340这类内部特殊寄存器或者先断再反M8342方向,三轴同时转还要凑寄存器、分中断,累得掉头发;哦对哦还有DRIV,原生FX3U根本只算“半个软轴控制”的小影子?
没错,最近直接扒了STM32F103移植的那个github上2.4k星的FX3U开源仿真库,自己啃了底层脉冲发生器、软轴寄存器映射的部分,把三轴真正能同时跑的PLSR(带独立加减速斜坡、实时暂停急停脉冲补全那种!)、还有简易但好用的相对/绝对软轴切换DRIV功能焊上去了。今天掏点最核心的、不用死记梯形图原理但能直接看懂底层逻辑的片段。

先看脉冲生成的底层逻辑改动——原版是单PLSY通道挂一个TIM3的CH1/CH2?太浪费,我把TIM1、TIM2、TIM3全部拆成独立的3路带互补方向输出(哦对CH1N/CH2N这些原生硬件PWM互补引脚用上了,不用外部继电器硬接方向了!),每路TIM挂自己的加减斜率数组。
三菱fx3u源代码增加plsr,driv等功能,代码是本人改过的,三轴
比如第一路(对应FX3U的Y0/Y1方向+脉冲)的TIM1中断回调里的加减速判断:
// 全局数组提前算好加减速斜率,FX3U默认的是100ms加减速周期对吧?
// arr_slope0存储的是每个加减速周期ARR(定时值,和脉冲频率成反比)的增量数组
// arr_cnt0是当前走到加减速斜率的第几步
extern uint16_t arr_slope0[256]; // 足够覆盖从1Hz到100kHz(STM32F103最大PWM率)的FX3U常用范围
extern volatile uint16_t arr_cnt0, target_arr0, current_arr0;
extern volatile uint32_t pulse_left0;
void TIM1_UP_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE) != RESET) {
__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
// 先检查脉冲够不够,不够就降速或者停
if (pulse_left0 <= arr_slope0[arr_cnt0]) {
// 这里是提前降速逻辑!原版PLSR有时候脉冲少会冲过头,提前降最稳
arr_cnt0--;
if (arr_cnt0 == 0) { // 到初始速度了
__HAL_TIM_SET_AUTORELOAD(&htim1, arr_slope0[0]);
current_arr0 = arr_slope0[0];
if (pulse_left0 == 0) __HAL_TIM_DISABLE(&htim1); // 发完直接关TIM
} else {
current_arr0 += arr_slope0[arr_cnt0]; // 降速是增大ARR哦别搞反
__HAL_TIM_SET_AUTORELOAD(&htim1, current_arr0);
}
} else if (current_arr0 > target_arr0) {
// 还在加速阶段,继续减ARR
arr_cnt0++;
current_arr0 -= arr_slope0[arr_cnt0];
__HAL_TIM_SET_AUTORELOAD(&htim1, current_arr0);
} else if (current_arr0 < target_arr0) {
// 哦不对目标ARR应该比初始小才对(频率更高),这里防止目标写错
current_arr0 = target_arr0;
__HAL_TIM_SET_AUTORELOAD(&htim1, current_arr0);
}
// 每进一次中断不管加减速减脉冲,这里是补中断计数哦原版也有
pulse_left0--;
}
}
这段加减速提前降速我觉得是最实用的改动——之前用原版梯形图PLSR发10个脉冲,经常冲成11、12个,现在不管脉冲数是2还是20000,只要在arrslope数组范围内,最后都会稳稳停在目标脉冲上,不信你可以把arrslope0[0]设成对应10Hz的ARR,targetarr0对应1kHz,arrslope0里的增量是线性或者S型的(哦我还加了个S型数组切换的宏,不过线性已经足够FX3U的常用应用了)。

接下来是PLSR指令的软接口映射——原版开源库是通过解析梯形图的二进制指令码来调用函数的,所以我们要给新的三轴PLSR(我命名为PLSR3X?不用,直接复用三菱FX5U的软寄存器映射思路,把原来的单PLSR特殊寄存器组拆成3组独立的:
// 软轴1:Y0脉冲 Y1方向 对应FX3U扩展后的D18340-D18350(随便选的D区末尾空寄存器,原版FX3U 64点D区到D8191对吧?不对我记错了,FX3U 64点基本单元D区是D0-D12799,选D10000-D10010、D10020-D10030、D10040-D10050三组,避免和自带的D8340冲突
typedef struct {
uint32_t target_pulse; // Dn-Dn+1 目标脉冲数,正负代表方向
uint16_t base_freq; // Dn+2 基频
uint16_t max_freq; // Dn+3 最高频
uint16_t acc_time; // Dn+4 加减速时间(单位ms)
uint8_t enable_flag; // M对应的Dn+5.0 启动位
uint8_t pause_flag; // Dn+5.1 暂停位
uint8_t done_flag; // Dn+5.2 完成位
// 剩下的Dn+6-Dn+10留作以后加S型切换、急停恢复脉冲数这些用
} PLSR_Axis_t;
PLSR_Axis_t axis[3];
映射的时候就是在开源库的“软寄存器同步”函数里加个循环,把对应的D区数值读进来存到axis结构体里,完成了再把doneflag写回去M区对应的地方——比如轴1的doneflag对应M10002,这样梯形图里写个LD M10002就能触发下一个动作,和原生FX3U的逻辑一模一样!
哦对哦还有简易版的DRIV功能——其实就是把相对/绝对目标脉冲数自动转换的功能加进去,不用梯形图里写LD M8029然后MOV D8340到临时寄存器再改了。DRIV我也选了D10100-D10102三组对应三轴:
// DRIV_Axis_t直接复用PLSR的enable/pause/done,所以不用单独定义
// 软寄存器同步里加:
for (int i=0; i<3; i++) {
if (d_memory[10100 + i*2] & 0x01) { // DRIV的Dn.0是绝对/相对切换:1绝对,0相对
// 绝对位置模式下,要先读当前脉冲数(我在底层加了个volatile uint32_t current_pulse[3],每次TIM中断加/减1)
axis[i].target_pulse = d_memory[10101 + i*2*2] | (d_memory[10102 + i*2*2] << 16);
// 哦对DRIV的基频/最高频/加减速时间复用PLSR的D10002-D10004,这样不用写重复的梯形图
} else {
// 相对位置模式直接读DRIV的目标脉冲
axis[i].target_pulse = d_memory[10101 + i*2*2] | (d_memory[10102 + i*2*2] << 16);
}
// 然后复用PLSR的TIM启动逻辑就行
}
最后补一句硬件接线注意事项——STM32F103的TIM1CH1对应PA8脉冲,TIM1CH1N对应PA7方向;TIM2CH1对应PA0脉冲,TIM2CH2对应PA1方向;TIM3CH1对应PA6脉冲,TIM3CH2对应PA5方向——刚好凑了3路独立的Y口(虽然是STM32的IO口,但开源库本来就是用来模拟FX3U做教学或者小型DIY项目的,够用就行)。
哦还有个小彩蛋——我把TIM中断的优先级调了一下:TIM1最高,TIM2次之,TIM3最低,这样三个轴同时跑的时候不会出现丢脉冲的情况,哪怕是100kHz的脉冲串。不信你们可以同时让三个轴跑20000个脉冲,STM32F103的TIM中断处理完绝对脉冲数一个不差。

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)