浏览代码

P3a 硬件层上板验证:修复 PWM 输出两个硬件缺陷,优化段间衔接,扩展三段自测

- PlsrHwStartPwmTimer 补配 CCMR1.OC1M=PWM模式1:裸定时器复位为冻结模式,
  通道输出恒定电平(上板实测 PF6 无方波,仅一段 672ms 恒高)
- ARR/CCR 使能预装载(ARPE/OC1PE)+ 启动时 EGR.UG 加载影子寄存器:
  运行中调频不再因 CNT 超调提前回绕,加速段多余脉冲消除(7517→7500)
- 方向延时仅在方向变化时生效(首次/换向/换方向点),
  段间同向衔接由 14.9ms 缩短至 ~2ms
- 上电自测扩展为 3 段(2000Hz/1000 脉冲、5000Hz/6000、1000Hz/500),
  上电延时 1s 后启动,顺带验证段间跳转
- 新增波形分析脚本(破译 16bit/CH0/100MS/s 导出格式 + 时频曲线拟合),
  实测三段总脉冲 7500,与理论完全一致

已知遗留(不影响脉冲计数与位置精度,留待 P4 上板调试):
- 段尾 profile DONE 后冻结频率收尾(段3 尾部 150Hz 平台 38 脉冲)
- 段首第一周期被压缩(首脉冲占空比异常)
deepseek
ywh 1 个月前
父节点
当前提交
b2e407d91c
共有 10 个文件被更改,包括 1123 次插入35 次删除
  1. +2
    -2
      Core/Src/main.c
  2. +826
    -0
      Document/PLSR_document/xd_1_4_extract.txt
  3. 二进制
      Document/PLSR_document/截图/Snipaste_2026-08-07_16-01-10.png
  4. 二进制
      Document/PLSR_document/波形/2026-08-07_16-43-23.bin
  5. 二进制
      Document/PLSR_document/波形/2026-08-07_16-43-23_timefreq.png
  6. +162
    -0
      Document/PLSR_document/波形/plot_waveform.py
  7. +2
    -0
      PLSR/Inc/plsr_hal_f407.h
  8. +101
    -29
      PLSR/Src/plsr_hal_f407.c
  9. +11
    -4
      PLSR/Src/plsr_self_test.c
  10. +19
    -0
      PLSR/Test/test_plsr_hal.c

+ 2
- 2
Core/Src/main.c 查看文件

@@ -200,9 +200,9 @@ int main(void)
Error_Handler();
}

/* 上电自测:Q0 发一段测试脉冲(验证后删除)。 */
/* 上电自测:延时 1s 待电源/外设稳定后,Q0 发一段测试脉冲(验证后删除)。 */
HAL_Delay(1000U);
(void)PlsrSelfTestQueue();

OSStart();
/* USER CODE END 2 */



+ 826
- 0
Document/PLSR_document/xd_1_4_extract.txt 查看文件

@@ -0,0 +1,826 @@
===== PDF page 162 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
152
1-4.输出端子接线及注意事项
Y0
COM0
Y1
COM1
Y2
COM2
下面是 T 型输出端子与步进电机驱动器的接线示意图:
PLC侧 步进电机驱动侧
Y0
PU
PU
Y1
【注】:如果步进电机脉冲和方向端子是 DC5V驱动,请在脉冲输出端子和方向输出端子后接2.2K电阻。
下面是 T 型输出端子与信捷伺服电机驱动器的接线示意图:
【注】 :请将 P+5V和 D+5V悬空。
下面是差分型输出端子与伺服驱动器的接线示意图:
详细的硬件接线图请参考《XD/XL系列可编程控制器用户手册【硬件篇】 》 。
COM0
Y0
+24V
P-
R=3.3K
COM1
Y1 D-
R=3.3K
P+5V
D+5V
屏蔽层0V
D+24V
P+24V
Y0+
双绞线
Y0-
COM
Y1+
Y1-
A+
A-
FG
B+
B-
Y0
Y1
PLC侧-差分输出端 负载端
X0+
X0-
COM
X1+
X1-
双绞线
A+
A-
FG
B+
B-
编码器端
A
B
PLC侧-差分输入端
X0+
X0-
X1+
X1-
PLC侧-差分输入端
+
-5VDC
NPN型
PNP型
X0+
X0-
X1+
X1-
PLC侧-差分输入端
NPN型
PNP型
3KΩ/0.5W
3KΩ/0.5W
NPN传感器
PNP传感器
+
-24VDC
NPN传感器
PNP传感器
输出端口 Y0:脉冲输出口 1(单相)
输出端口 Y1:脉冲输出口 2(单相)
===== PDF page 163 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
153
1-4-1.连接设备的构成
1)XD系列T型输出PLC
晶体管输出
※:可控制 2~10 轴伺服电机或者步进电机。
2)XL系列T型输出PLC
晶体管输出
※:可控制 2~10 轴伺服电机或者步进电机。
伺服电机或者
步进电机
伺服电机或者
步进电机

===== PDF page 164 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
154
1-4-2.脉冲输出性能规格
脉冲输出性能规格如下表所示:
机型 T、RT
型 T4 型 T6型 T10 型 D4T4型 D2T2型 XDH XLH
控制轴数 独立
2 轴
独立
4轴
独立
6 轴
独立
10轴
独立
8 轴
独立
4 轴
独立
4 轴
独立
4 轴
插补功能 不支持
XDM/XDME
/XLME 支持
不支持
XDM/XDME
/XLME 支持
不支持 支持
输出方式 OC OC、差分 OC
输出形式 脉冲+方向
最大频率 100KHz OC:100KHz
差分:920KHz 100KHz
加减速处理 直线加减速+S型曲线加减速+正弦曲线加减速
控制单位 脉冲、1mm、0.1mm、0.01mm、1um
定位范围 -2147483648~2147483647(脉冲)
编程语言 梯形图
手脉连接 不支持
XDM/XDME
/XLME 支持
不支持
XDM/XDME
/XLME 支持
不支持 支持
【注】 :
(1)所有 XD/XL系列 PLC的脉冲输出必须为晶体管或差分输出类型,否则无法发送脉冲!
(2)PLC 可以输出 100KHz~200KHz的高速脉冲(XD3-24T4/32T4 的 Y2、Y3 为 10KHz),但不能保证
所有伺服都正常运行,请在输出端和 24V电源之间接入约 500Ω电阻,电阻功率需>2W。
1-4-3.定位控制接线以及布线注意事项
>>>>> 设计注意事项 <<<<<
危险!
请在可编程控制器的外部设置安全回路,以便在出现外部电源异常、可编程控制器故障等情况
时,也能确保整个系统在安全状态下运行。误动作、误输出有可能会导致事故发生。
1. 请务必在可编程控制器的外部设置紧急停止回路、保护回路、防止正反转等相反动作同时进行的互
锁回路、定位上下限等防止机械破损的互锁回路等。
2. 当可编程控制器 CPU 通过看门狗定时器出错等的自诊断功能检测出异常时,所有的输出变为OFF。
此外,当发生了可编程控制器 CPU 不能检测出的输入输出控制部分等的异常时,输出控制有时候会
失效。
此时,请设计外部回路以及结构,以确保机械在安全状态下运行。
3. 由于输出单元的继电器、晶体管、晶闸管等的故障,有时候会导致输出一直接通,或是一直断开。
为了确保机械在安全状态下运行,请为可能导致重大事故的输出信号设计外部回路以及结构。
注意!
1. 控制线请勿与主回路或动力线等捆在一起接线,或是靠近接线。

===== PDF page 165 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
155
原则上请离开100mm以上或者远离主回路。否则会因噪音引起误动作。
2. 使用时,请确保内置编程接口、电源连接器、输入输出连接器不受外力。
否则会导致断线以及故障。
>>>>> 接线注意事项 <<<<<
危险!
1. 进行安装、接线等作业时, 请务必在外部将所有电源均断开后方可进行操作。
否则有触电、产品损坏的危险。
2. 在安装、接线等作业后执行上电运行时,请务必在产品上安装附带的接线端子盖板。
否则有触电的危险性。
注意!
1. AC 电源的配线请与基本单元手册记载的专用端子连接。
如果将AC电源连接到直流的输出输入端子及DC电源端子,可编程控制器将被烧毁。
2. DC 电源的配线请与基本单元手册记载的专用端子连接。
如果将AC电源连接到直流的输出输入端子及DC电源端子,可编程控制器将被烧毁。
3. 请不要在外部对空端子进行配线。
有可能会损坏产品。
4. XD/XL 系列基本单元的接地端子请使用2mm
2
以上的电线进行D种接地(接地电阻:100Ω以下)。
但是,请勿与强电流共同接地(参照XD/XL系列可编程控制器用户手册【硬件篇】)。
5. 在进行螺栓孔加工及配线作业时, 请不要将切屑及电线屑落入可编程控制器的通风孔内。
否则有可能导致火灾、故障及误动作。
6. 使用时,请确保输入输出连接器不受外力。
否则会导致断线以及故障。
7. 输入输出电缆请牢固地安装在所规定的连接器上。
接触不良会导致误动作。
8. 对 XD/XL 系列基本单元以及XD/XL 系列扩展设备的端子排型产品进行接线时,请遵照以下的注意事
项操作。
否则有可能导致触电、故障、短路、断线、误动作、损坏产品。
--请依据手册中记载的尺寸对电线的末端进行处理。
--紧固扭矩请依照手册中记载的扭矩。
>>>>> 启动维护保养时的注意事项 <<<<<
危险!
1. 在通电时请勿触碰到端子。
否则有触电的危险性,并且有可能引起误动作。
2. 进行清扫以及拧紧接线端子时,请务必在断开所有外部电源后方可操作。
如果在通电的状态下进行操作,则有触电的危险。
3. 要在运行过程中更改程序、执行强制输出、RUN、STOP等操作前,请务必先熟读手册,在充分确认安
全的情况下方可进行操作。
操作错误有可能导致机械破损及事故发生。

===== PDF page 166 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
156
注意!
1. 请勿擅自拆解、改动产品。
否则有可能引起故障、误动作、火灾。
2. 对扩展电缆等连接电缆进行拆装时请在断开电源之后再进行操作。
否则有可能引起故障、误动作。
3. 在对以下的设备进行拆装时请务必将电源切断。
否则有可能引起故障、误动作。
--外围设备、扩展功能板、特殊适配器、
--输入输出扩展模块、网络模块等。
1-4-4.伺服放大器(驱动单元)侧的设定
1)可编程控制器侧的脉冲输出形式
XD/XL系列 PLC的脉冲输出类型全部为集电极开路信号(脉冲+方向) ,如下图所示:
脉冲输出端口Y0
任意的输出Y□
(指定旋转方向)
脉冲输出端口Y1
任意的输出Y□
(指定旋转方向)
H
L
H
L
H
L
H
L
正转 反转
ON
ON
OFF
OFF
脉冲输出端口Y2
任意的输出Y□
(指定旋转方向)
脉冲输出端口Y11
任意的输出Y□
(指定旋转方向)
H
L
H
L
H
L
H
L
正转 反转
ON
ON
OFF
OFF
正转 反转
【注】 :ON、OFF表示可编程控制器的输出状态;H、L表示波形的 HIGH、LOW。
2)伺服放大器(驱动单元)的指令脉冲输入形式的设定
如下表所示,请使伺服放大器(驱动单元)参数中的脉冲串输入形式与可编程控制器的脉冲输出形式相
符合。
伺服放大器
(驱动单元)
基本单元的脉冲输出形式 集电极转差分板DC-Diff
晶体管输出(漏型输出) 差动驱动
脉冲+方向 正转脉冲、反转脉冲
指令脉冲输入形式 脉冲+符号 正转脉冲、反转脉冲
指令脉冲逻辑 负逻辑 负逻辑
【注】 :XD/XL系列 PLC本体脉冲输出形式为集电极开路信号输出(脉冲+方向) ,可以通过集电极转差分
扩展板 OC-Diff将集电极开路信号输出(脉冲+方向)转化为差动信号输出。
集电极开路信号(脉冲+方向)通过 OC-Diff转化为差分信号接线图(以 DS2-21P5-A为例):

===== PDF page 167 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
157
1
5
6
10
11
15
+24V 2P- +5V +24V 1P- +5V
24V+ 24V- PUL2- PUL2+ PUL1- PUL1+
开关电源
集电极转差分OC-Diff
Z0
GN
24V
0V
差分输出
CN1口
DS系列伺服放大器的各系列的参数设定:
系列名称 参数号 设定值
脉冲+方向(负逻辑) 差动信号(负逻辑)
DS2系列 AS — √ —
DS2系列 AS2 — √ —
DS2系列 AS6 P2-00 2 1
DS2系列 BS — √ —
DS2系列 BS6 P2-00 2 1
DS2系列 BSW — √ —
DS2系列 BSW6 P2-00 2 1
DS3系列 PQA P2-00 2 1
DS3E系列 PFA P2-00 2 1
DS3系列 P0-10 2 1
DS3E系列 P0-10 2 1
3)伺服放大器(驱动单元)电子齿轮比的设定(以DS2系列为例)
通过使用伺服电机的电子齿轮,可以设定每个脉冲的移动量。
关于电子齿轮的设定,请参考使用的伺服电机以及伺服放大器的手册,设定与用途相符合的数值。
(1)例 1
将每个脉冲的移动量设定为 10μm时(使用丝杆的机械时)。
机械规格
伺服放大器 DS2系列
伺服电机的额定转速 3000r/min
滚珠丝杆导程螺距(Pb) 10mm
减速机减速比(n) 1:5
伺服电机分辨率(Pt) 10000PLS/REV
伺服电机 减速
机构
工作台
f0:指令脉冲频率
CMX:电子齿轮/分子
CDV:电子齿轮/分母
NR:伺服电机转速r/min
X:每个脉冲的移动量mm
===== PDF page 168 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
158
电子齿轮比计算公式如下:
= X ×CMX
CDV
Pt
n×Pb
=10×10 ×-3 10000
1/5×10
50
1
由上图可知,伺服驱动器电子齿轮比应该设定为 50:1
此时,基本单元最大输出脉冲频率(200,000Hz)时的伺服电机的旋转速度如下计算所示:
×CMX
CDV
NR = 60
Pt
× f0
50
1
× 60
10000
× 200000
= 60000r/min > 3000r/min(额定转速)
注意:请设定可编程控制器侧的最高速度,以使伺服电机的旋转速度控制在额定转速以下。
(2)例 2
将每个脉冲的移动量设定为 0.01°时(旋转台时)。
机械规格
伺服放大器 DS2系列
伺服电机的额定转速 3000r/min
旋转台角度 360°/ REV
减速比(n) 1:5
伺服电机分辨率(Pt) 10000PLS/REV
伺服电机
Pt=10000[PLS/REV]
旋转台
同步皮带:1:5
F0 :指令脉冲频率[Hz](集电极开路方式)
CMX:电子齿轮(指令脉冲倍率分子)
CDV:电子齿轮(指令脉冲倍率分母)
NR :伺服电机旋转速度[r/min]
X :每个脉冲的移动量[°]
电子齿轮比计算公式如下:
= X ×CMX
CDV
Pt
n×360
= 1×10 ×-2 10000
1/5×360
25
18
由上图可知,伺服驱动器电子齿轮比应该设定为 25:18
此时,基本单元最大输出脉冲频率(200,000Hz)时的伺服电机的旋转速度如下计算所示:
CMX
CDV
× 60
Pt
× f0
25
18
× 60
10000
× 100000=
= 833.33r/min < 3000r/min(额定转速)
NR=
因为伺服电机的旋转速度在额定转速以下,所以可编程控制器侧的最高速度不需要有限制。
===== PDF page 169 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
159
4)伺服放大器(驱动单元)准备好信号设定(以DS2系列为例)
DS2系列伺服使能信号有效代表伺服电机通电工作,当伺服使能信号无效时,电机不运行。
系列名 参数号 设定值
DS2系列 P5-10 0010
1-4-5.定位指令脉冲发送完成标志位的使用及注意事项
正在发脉冲标志位SM1000、SM1020、SM1040……等标志位由ON变为OFF时,意味着指令的动作
(脉冲输出动作等)结束了。但是,并不意味着伺服电机的动作也结束(停止)了。为了确切掌握伺服电机
的动作结束情况,请正确使用正在发脉冲标志位。
正在发脉冲标志位如下表所示:
序号 线圈 轴数 说明
1 SM1000 Y0 当脉冲发送时线圈置 ON, 脉冲发送结束后立即置OFF,
利用线圈的下降沿判断脉冲发送是否结束。
t0
频率
SM1000
脉冲段
2 SM1020 Y1
3 SM1040 Y2
4 SM1060 Y3
5 SM1080 Y4
6 SM1100 Y5
7 SM1120 Y6
8 SM1140 Y7
9 SM1160 Y10
10 SM1180 Y11
如果编写多个同一脉冲输出端口的定位指令, 那么指令执行时, 正在发送脉冲标志位SM1000、SM1020、
SM1040……等随着各个指令而ON/0FF变化。因此,如果将多个指令执行正在发送脉冲标志位 SM1000、
SM1020、SM1040……等同时用在同一段程序内,则无法判断是因为执行哪条指令而ON/OFF,同时也不能
正常获取与各个指令相支持的标志位。
错误的写法如下:

===== PDF page 170 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
160
正确的写法如下:
1-4-6.定位指令触发条件注意事项
XD/XL 系列 PLC 定位指令主要有 PLSR(边沿触发) 、PLSF(常开/常闭触发) 、DRVI(边沿触发) 、
DRV A(边沿触发) 、ZRN(边沿触发) ;除了PLSF指令外,其余的脉冲指令全部是边沿触发,同一脉冲输
出端口(例如Y0)在执行一条定位指令的过程中,正在发送脉冲标志位(SM1000)会一直处于导通状态,
此时再触发其它同一脉冲输出端口的脉冲指令,则PLC将不会予以响应,直到正在执行的脉冲输出指令脉
冲发送结束,正在发送脉冲标志位复位以后才会响应。
由于 PLSF脉冲指令的导通条件为常开/常闭,所以在使用 PLSF指令时,当脉冲发送结束不需要执行
时,请立即对 PLSF 指令的导通条件进行复位(不要只将脉冲输出频率设定为 0Hz,而不对脉冲导通条件
进行复位) 。
1-4-7.定位指令与系统参数块相关参数
下表整理了脉冲输出指令与系统参数块有关参数设定:
系统参数块内容 PLSR PLSF DRVI DRVA ZRN
公共参数—脉冲方向逻辑 必须设定 必须设定 必须设定 必须设定 必须设定
公共参数—启用软限位功能 可不设 可不设 可不设 可不设 可不设
公共参数—机械回原点默认方向 × × × × 必须设定
公共参数—脉冲单位 必须设定 必须设定 必须设定 必须设定 必须设定
公共参数—插补坐标模式 × × × × ×
公共参数—脉冲发送模式 必须设定 必须设定 必须设定 必须设定 必须设定
公共参数—脉冲数(1转) 可不设 可不设 可不设 可不设 可不设
公共参数—移动量(1转) 可不设 可不设 可不设 可不设 可不设
公共参数—脉冲方向端子 可不设 可不设 可不设 可不设 必须设定
公共参数—脉冲方向延时时间 可不设 可不设 可不设 可不设 可不设
公共参数—齿轮间隙正向补偿 可不设 可不设 可不设 可不设 可不设
公共参数—齿轮间隙负向补偿 可不设 可不设 可不设 可不设 可不设
公共参数—电气原点位置 × × × × ×

===== PDF page 171 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
161
系统参数块内容 PLSR PLSF DRVI DRVA ZRN
公共参数—原点开关状态设置 × × × × 必须设定
公共参数—原点信号端子设定 × × × × 必须设定
公共参数—Z 相开关状态设置 × × × × 可不设
公共参数—Z 相端子设定 × × × × 可不设
公共参数—正极限开关状态设置 可不设 可不设 可不设 可不设 可不设
公共参数—正极限端子设定 可不设 可不设 可不设 可不设 可不设
公共参数—负极限开关状态设置 可不设 可不设 可不设 可不设 可不设
公共参数—负极限端子设定 可不设 可不设 可不设 可不设 可不设
公共参数—归零清零 CLR 信号输出端子
设定 × × × × 可不设
公共参数—回归速度 VH × × × × 必须设定
公共参数—爬行速度 VC × × × × 必须设定
公共参数—机械原点位置 × × × × 必须设定
公共参数—Z 相个数 × × × × 可不设
公共参数—CLR信号延时时间 × × × × 可不设
公共参数—软限位正极限值 可不设 可不设 可不设 可不设 ×
公共参数—软限位负极限值 可不设 可不设 可不设 可不设 ×
公共参数—快速定位指令默认参数块 × × 可不设 可不设 ×
公共参数—插补指令默认参数块 × × × × ×
第 1 套参数—脉冲默认速度 必须设定 必须设定 × × 必须设定
第 1 套参数—脉冲默认速度加速时间 必须设定 必须设定 × × 必须设定
第 1 套参数—脉冲默认速度减速时间 必须设定 必须设定 × × 必须设定
第 1 套参数—补间加减速时间 可不设 可不设 可不设 可不设 可不设
第 1 套参数—脉冲加减速模式 必须设定 必须设定 必须设定 必须设定 必须设定
第 1 套参数—最高速度 必须设定 必须设定 必须设定 必须设定 必须设定
第 1 套参数—起始速度 必须设定 必须设定 必须设定 必须设定 必须设定
第 1 套参数—终止速度 必须设定 必须设定 必须设定 必须设定 必须设定
第 1 套参数—脉冲频率刷新时间 可不设 可不设 可不设 可不设 可不设
第 1 套参数—回归速度 VH × × × × 必须设定
第 1 套参数—爬行速度 VC × × × × 必须设定
【注】 :
※1:第 0、2、3、4套参数设定请参照第 1套参数设定。
※2:设定了“第 1套参数—回归速度 VH” 、 “第1套参数—爬行速度 VC”的情况下,公共参数的 VH、
VC可不设定。
1-4-8.伺服电机、步进电机不动作故障排查
伺服电机、步进电机不转时,请确认以下项目:
1)请确认接线。
2)请执行定位指令,确认以下的 LED的状态。
-设定为脉冲输出端软元件的输出信号的LED;
-设定为旋转方向输出的输出信号的 LED。
3)请确认可编程控制器执行定位指令时,各轴累计脉冲寄存器的数值是否在变化。
各脉冲输出累积寄存器如下表所示:
===== PDF page 172 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
162
编号 功能 说明 轴数
HSD0 累计脉冲量低 16位 脉冲个数为单位
Y0 HSD1 累计脉冲量高 16位
HSD2 累计脉冲量低 16位 脉冲当量为单位
HSD3 累计脉冲量高 16位
HSD4 累计脉冲量低 16位 脉冲个数为单位
Y1 HSD5 累计脉冲量高 16位
HSD6 累计脉冲量低 16位 脉冲当量为单位
HSD7 累计脉冲量高 16位
HSD8 累计脉冲量低 16位 脉冲个数为单位
Y2
HSD9 累计脉冲量高 16位
HSD10 累计脉冲量低 16位 脉冲当量为单位
HSD11 累计脉冲量高 16位
HSD12 累计脉冲量低 16位 脉冲个数为单位
Y3 HSD13 累计脉冲量高 16位
HSD14 累计脉冲量低 16位 脉冲当量为单位
HSD15 累计脉冲量高 16位
HSD16 累计脉冲量低 16位 脉冲个数为单位
Y4 HSD17 累计脉冲量高 16位
HSD18 累计脉冲量低 16位 脉冲当量为单位
HSD19 累计脉冲量高 16位
HSD20 累计脉冲量低 16位 脉冲个数为单位
Y5
HSD21 累计脉冲量高 16位
HSD22 累计脉冲量低 16位 脉冲当量为单位
HSD23 累计脉冲量高 16位
HSD24 累计脉冲量低 16位 脉冲个数为单位
Y6 HSD25 累计脉冲量高 16位
HSD26 累计脉冲量低 16位 脉冲当量为单位
HSD27 累计脉冲量高 16位
HSD28 累计脉冲量低 16位 脉冲个数为单位
Y7 HSD29 累计脉冲量高 16位
HSD30 累计脉冲量低 16位 脉冲当量为单位
HSD31 累计脉冲量高 16位
HSD32 累计脉冲量低 16位 脉冲个数为单位
Y10
HSD33 累计脉冲量高 16位
HSD34 累计脉冲量低 16位 脉冲当量为单位
HSD35 累计脉冲量高 16位
HSD36 累计脉冲量低 16位 脉冲个数为单位
Y11 HSD37 累计脉冲量高 16位
HSD38 累计脉冲量低 16位 脉冲当量为单位
HSD39 累计脉冲量高 16位
4)请确认可编程控制器侧和伺服放大器(驱动单元)的脉冲输出形式是否相符。
===== PDF page 173 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
163
5)请确认脉冲输出停止标志位是否动作。
各脉冲输出端软元件的脉冲输出标志位如下表所示:
序号 线圈 轴数 说明
1 SM1001 Y0 当脉冲数值为正值正向发送脉冲时,线圈置 ON;
当脉冲数值为负值反向发送脉冲时,线圈置 OFF。
t0
频率
SM1001
脉冲段
2 SM1021 Y1
3 SM1041 Y2
4 SM1061 Y3
5 SM1081 Y4
6 SM1101 Y5
7 SM1121 Y6
8 SM1141 Y7
9 SM1161 Y10
10 SM1181 Y11
6)请确认极限(正转限位、反转限位)是否动作。
7)请确认定位指令的动作时序。
正在发送脉冲标志位为ON时,使用同一脉冲输出端软元件的定位指令或者脉冲输出指令不能被执
行。
1-4-9.伺服电机、步进电机停止位置不正确故障排查
停止位置不正确时,请确认以下项目:
1)请确认伺服放大器(驱动单元)的电子齿轮的设定是否正确。
2)请确认原点位置是否偏移。
A、设计原点信号时,请考虑有足够为ON的时间能充分减速到爬行速度。
ZRN指令在原点前端开始减速到停止,延时反向加速到爬行速度,在脱离原点时停止, 清除当前值
寄存器。
在原点后端前,没有能够减速到爬行速度时,会导致停止位置偏移。
B、请使爬行速度足够的慢。
原点回归指令的停止是不进行减速停止的, 所以如果爬行速度过快, 会由于惯性导致停止位置偏
移。
C、关于原点信号的软元件。
原点信号端子可以选择 PLC本体上所有的输入点; 但是如果选择的输入点为PLC上的外部中断端子,
则回机械原点的过程中都会按照中断进行处理,从而可以进一步的提高回机械原点的精度(如果使用Z 相
回原点则无影响) ;而选择的输入点为非PLC本体上的外部中断端子,会机械原点的过程中则会受到PLC
扫描周期的影响(如果使用 Z相回原点则无影响) 。
3)进行正反转动作(往返动作)后,停止位置有偏离。
由于移动工作台与滚珠丝杠之间存在接触间隙,工作台由正向移动结束切到反向移动时,从而导致反
向实际移动距离小于设定距离;工作台由反向移动结束切到正向移动时,从而导致正向实际移动距离小于
设定距。
可以通过正向齿轮间隙补偿和反向齿轮间隙补偿进行修正。
===== PDF page 174 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
164
1-5.定位指令案例程序
本节主要通过几个样例程序来介绍 PLSR、PLSF、DRV A、DRVI、ZRN指令的使用方式。
动作 指令 程序举例
顺控梯形图 流程梯形图
多段脉冲定位 PLSR 1-5-4、1-5-6 1-5-5、1-5-7
可变频脉冲输出 PLSF 1-5-2、1-5-4 1-5-3、1-5-5
相对单段定位 DRVI 1-5-2、1-5-6 1-5-3、1-5-7
绝对单段定位 DRV A 1-5-2、1-5-6 1-5-3、1-5-7
机械原点回归 ZRN 1-5-2、1-5-4、1-5-6 1-5-3、1-5-5、1-5-7
1-5-1.I/O点分配
程序举例中使用脉冲输出端 Y0(1 轴)。使用其它脉冲输出端的时候,请修改对应的脉冲轴软元件。
信号名称 输入输出编号 备注
脉冲输出端口 Y0
脉冲方向端口 Y2
CLR清零信号 Y3
伺服准备好 X0
停止控制 X1
脉冲继续 X13
原点回归控制 X4
点动前进控制 X5
点动后退控制 X6
正转定位控制 X7
反转定位控制 X10
原点输入端子 X3 使用外部中断端子
正向限位开关 X11
反向限位开关 X12
1-5-2.正反转顺控样例程序【PLSF、DRVI、DRVA、ZRN】
例1:根据下图运行,采用绝对单段定位方式进行定位。
10000Hz
原点
加减速时间
200ms
(50000,0)
10000Hz加减速时间
200ms
(100,0)
(0,0)
加减速时间
200ms
===== PDF page 175 =====
XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出
165
首先,编写梯形图程序,如下:
X4
X6 X5
M100
X5 X6
M101
X7 X10
原点回
归控制
点动后
退控制
点动前
进控制
点动前进
运行中
点动前
进控制
点动后
退控制
点动后退
运行中
正转定位
控制
反转定位
控制
ZRN K1 Y0
PLSF K10000 K1 Y0
( )
M100
PLSF K-10000 K1 Y0
( )
M101
点动前进运行中
点动后退运行中
DRVA K50000 K10000 K200 Y0 Y2
X10 X7
正转定位
控制
反转定位
控制
DRVA K100 K10000 K200 Y0 Y2
X0 X5 X6
STOP Y0 K0
X1
停止控制
伺服准备
点动前
进控制
点动后
退控制
X13
GOON Y0
脉冲继续
由于样例程序中除 DRVI和 DRV A,其余所有的脉冲指令使用的系统参数块都为第一套参数,所以,
我们点击 PLC编程软件左边的工程栏中的“脉冲配置参数” ,如下图:
//机械回原点指令(选用第一套系统参数块参数)
//点动前进控制, 速度为10000Hz(选用第一套系统参
数块参数)
//点动后退控制, 速度为-10000Hz(选用第一套系统参
数块参数)
//正转定位控制
//反转定位控制
//脉冲停止发送控制(缓停)
//脉冲没有发送完执行脉冲继续将剩余的脉冲发送完
===== PDF page 176 =====
1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇]
166
将会打开如下窗口:
点击打开窗口中的“配置”选项,在下拉菜单中选择“Y0轴” :
在打开的参数配置表格中按如下进行配置(圈起来的参数指需要在默认参数下进行修改的参数) :
系统参数块配置一

二进制
Document/PLSR_document/截图/Snipaste_2026-08-07_16-01-10.png 查看文件

之前 之后
宽度: 1266  |  高度: 165  |  大小: 4.0 KiB

二进制
Document/PLSR_document/波形/2026-08-07_16-43-23.bin 查看文件


二进制
Document/PLSR_document/波形/2026-08-07_16-43-23_timefreq.png 查看文件

之前 之后
宽度: 1820  |  高度: 1170  |  大小: 95 KiB

+ 162
- 0
Document/PLSR_document/波形/plot_waveform.py 查看文件

@@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
"""逻辑分析仪波形 .bin → 时频曲线拟合。

文件格式(自动探测):
16 通道逻辑分析仪导出:每采样 2 字节(小端 uint16),bit0..15 = CH0..CH15。
信号通道 = 值 0x0100 的位(bit8),即字节流的奇数索引字节承载 CH8。
采样率默认 100MS/s(可用 --fs 覆盖;也可自动验证 2000/5000Hz 命中)。

用法:
python plot_waveform.py <xxx.bin> [--fs 100e6] [--ch 8] [--out <png>]
"""
import argparse
import sys

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt


def extract_channel(m, ch):
"""从 2 字节/采样(小端 uint16)格式中提取通道 ch 的数字序列。"""
# 目标位在 16bit 值中的字节位置:bit<8 → 低字节(偶索引);>=8 → 高字节(奇索引)
if ch < 8:
lo = m[0::2]
return (lo >> ch) & 1
hi = m[1::2]
return (hi >> (ch - 8)) & 1


def rising_edges(sig):
"""上升沿索引(数字序列)。"""
return np.nonzero((sig[1:] == 1) & (sig[:-1] == 0))[0] + 1


def main():
ap = argparse.ArgumentParser()
ap.add_argument("bin_path")
ap.add_argument("--fs", type=float, default=None, help="采样率 Hz")
ap.add_argument("--ch", type=int, default=0, help="信号通道号")
ap.add_argument("--out", default=None, help="输出png路径")
args = ap.parse_args()

m = np.memmap(args.bin_path, dtype=np.uint8, mode="r")
if len(m) % 2 != 0:
sys.exit("odd file size, not 2-byte/sample format?")
nsamp = len(m) // 2
print("samples:", nsamp)

ch = args.ch
sig = extract_channel(m, ch)
ones = int(sig.sum())
print("CH%d: %d ones / %d samples (%.2f%%)"
% (ch, ones, nsamp, 100.0 * ones / nsamp))

nz = np.nonzero(sig)[0]
if len(nz) == 0:
sys.exit("channel has no signal")
lo, hi = int(nz[0] * 0.97), int(nz[-1] * 1.03)
print("signal range: sample %d .. %d" % (lo, hi))

# ---- 采样率:自动验证(检查匀速段 2000/5000Hz 命中) ----
if args.fs is None:
candidates = [100e6, 50e6, 200e6, 25e6]
best = []
for fs in candidates:
down = max(1, int(fs // 5000000))
s = sig[lo:hi:down]
r = rising_edges(s)
if len(r) < 20:
continue
per = np.diff(r) * down / fs
f = 1.0 / np.maximum(per, 1e-9)
f = f[(f > 100) & (f < 20000)]
if len(f) < 10:
continue
h, e = np.histogram(f, bins=400, range=(0, 20000))
e2 = h[(e[:-1] >= 1940) & (e[:-1] <= 2060)].sum()
e5 = h[(e[:-1] >= 4850) & (e[:-1] <= 5150)].sum()
best.append(((e2 + e5) / max(h.sum(), 1), fs))
best.sort(reverse=True)
fs = best[0][1]
print("sample-rate ranking:", ["%g:%.3f" % (f, s) for s, f in best])
print("using fs = %g Hz" % fs)
else:
fs = float(args.fs)

# ---- 上升沿 → 时频 ----
r = rising_edges(sig[lo:hi])
r = r + lo
t_rise = r / fs
per = np.diff(t_rise)
freq = 1.0 / np.maximum(per, 1e-9)
t_mid = (t_rise[:-1] + t_rise[1:]) / 2.0

print("rising edges: %d" % len(r))
print("first pulse @ %.3fs, last @ %.3fs" % (t_rise[0], t_rise[-1]))
print("total pulses: %d" % len(r))
print("average freq: %.1f Hz"
% (len(r) / max(t_rise[-1] - t_rise[0], 1e-9)))

# ---- 三段结构(按 10ms 桶取中位频率) ----
buckets = np.arange(t_rise[0] - 0.05, t_rise[-1] + 0.05, 0.01)
med = []
bt = []
for i in range(len(buckets) - 1):
sel = (t_mid >= buckets[i]) & (t_mid < buckets[i + 1])
if sel.sum() > 0:
med.append(np.median(freq[sel]))
bt.append((buckets[i] + buckets[i + 1]) / 2)
med = np.array(med)
bt = np.array(bt)
active = med > 100
edges = np.diff(active.astype(int))
starts = bt[1:][edges == 1]
stops = bt[1:][edges == -1]
if len(bt) > 0 and active[0]:
starts = np.concatenate(([bt[0]], starts))
if len(bt) > 0 and active[-1]:
stops = np.concatenate((stops, [bt[-1]]))
print("segments:")
for s, e in zip(starts, stops):
sel = (t_rise >= s) & (t_rise <= e)
n = sel.sum()
print(" %.3f ~ %.3fs dur=%.3fs pulses=%d avg=%.0fHz"
% (s, e, e - s, n, n / max(e - s, 1e-9)))
for i in range(len(stops) - 1):
print(" gap seg%d->%d: %.3fms" % (i + 1, i + 2,
(starts[i + 1] - stops[i]) * 1e3))

# ---- 画图 ----
out = args.out or (args.bin_path.rsplit(".", 1)[0] + "_timefreq.png")
fig, axes = plt.subplots(2, 1, figsize=(14, 9), sharex=True,
gridspec_kw={"height_ratios": [1, 3]})

ax = axes[0]
step = max(1, int(fs // 200000))
xs = np.arange(lo, hi, step) / fs
ax.plot(xs, sig[lo:hi:step], lw=0.4, color="steelblue")
ax.set_ylabel("CH%d raw" % ch)
ax.set_yticks([0, 1])
ax.grid(alpha=0.3)

ax = axes[1]
ax.plot(t_mid, freq, lw=0.8, color="crimson")
ax.axhline(2000, color="gray", ls="--", lw=0.8)
ax.axhline(5000, color="gray", ls="--", lw=0.8)
ax.set_ylabel("instantaneous freq (Hz)")
ax.set_xlabel("time (s)")
ax.set_ylim(0, 6000)
ax.grid(alpha=0.3)
ax.legend(["freq", "2000Hz", "5000Hz"], loc="upper right", fontsize=8)

fig.suptitle("CH%d time-frequency: %d pulses, fs=%g Hz, ch=%d"
% (ch, len(r), fs, ch))
fig.tight_layout()
fig.savefig(out, dpi=130)
print("saved:", out)


if __name__ == "__main__":
main()

+ 2
- 0
PLSR/Inc/plsr_hal_f407.h 查看文件

@@ -51,6 +51,8 @@ uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber);
/* 模拟寄存器访问与中断触发(测试用)。 */
uint32_t PlsrHwTestGetArr(uint8_t axis);
uint32_t PlsrHwTestGetCcr(uint8_t axis);
uint32_t PlsrHwTestGetCcmr1(uint8_t axis);
uint32_t PlsrHwTestGetCr1(uint8_t axis);
uint32_t PlsrHwTestGetPsc(uint8_t axis);
uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis);
uint8_t PlsrHwTestGetDirLevel(uint8_t axis);


+ 101
- 29
PLSR/Src/plsr_hal_f407.c 查看文件

@@ -89,6 +89,7 @@ typedef struct
uint32_t psc;
uint32_t arr;
uint32_t ccr1;
uint32_t ccmr1;
uint32_t ccer;
uint8_t dirLevel;
} PLSR_HW_TIMER_REGS;
@@ -170,6 +171,18 @@ static void PlsrHwTimerSetCc1e(uint8_t axis, uint32_t value)
#endif
}

/* 通道 1 输出模式 = PWM 模式 1(OC1M=110)+ CCR 预装载(OC1PE)。
* 上电复位后 CCMR1=0(冻结),通道输出恒定电平、无方波,必须显式配置。 */
static void PlsrHwTimerSetPwmMode1(uint8_t axis)
{
#ifdef PLSR_HOST_TEST
PlsrHwTimers[axis].ccmr1 = 0x0068UL;
#else
PlsrHwAxisMap[axis].timer->CCMR1 = (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)
| TIM_CCMR1_OC1PE;
#endif
}

static void PlsrHwTimerSetUie(uint8_t axis, uint32_t value)
{
#ifdef PLSR_HOST_TEST
@@ -195,7 +208,9 @@ static void PlsrHwTimerClearUif(uint8_t axis)
#endif
}

/* ---- DIR 输出 ---- */
/* ---- DIR 输出 ----
* XDM 为晶体管(NPN 漏型)输出:ON(导通)= 引脚低电平。
* 信捷正逻辑:正向发脉冲时方向端子置 ON(低)。 */

static void PlsrHwSetDirLevel(uint8_t axis, uint8_t positive)
{
@@ -213,19 +228,60 @@ static void PlsrHwSetDirLevel(uint8_t axis, uint8_t positive)
{
const PLSR_HW_OUTPUT_PIN *pin =
&PlsrHwOutputPins[state->directionPoint];
GPIO_InitTypeDef gpio;

if (pin->port != NULL)
{
HAL_GPIO_WritePin(pin->port, pin->pin,
(positive != 0U) ? GPIO_PIN_SET : GPIO_PIN_RESET);
/* DIR 点按需配置为推挽输出(上电默认高阻=截止,安全)。 */
gpio.Pin = pin->pin;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(pin->port, &gpio);
/* 漏型输出:ON(导通)= 低电平。 */
HAL_GPIO_WritePin(pin->port,
pin->pin,
(positive != 0U) ? GPIO_PIN_RESET
: GPIO_PIN_SET);
}
}
#endif
}

/* ---- PWM 启停 ---- */
/* ---- PWM 启停 ----
* ARR/CCR 使用预装载(ARPE/OC1PE):运行中调频写入延迟到更新事件生效,
* 避免 ARR 变小瞬间 CNT 超调提前回绕(每段加速会多出 ~ln(f1/f0) 个假脉冲)。
* 首次启动用 EGR.UG 把预装载值加载到影子寄存器,杜绝首个周期用复位值。 */

static void PlsrHwTimerSetArpe(uint8_t axis, uint32_t value)
{
#ifdef PLSR_HOST_TEST
PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0080UL)
| ((value != 0UL) ? 0x0080UL : 0UL);
#else
if (value != 0UL)
{
PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_ARPE;
}
else
{
PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_ARPE;
}
#endif
}

/* 生成更新事件:立即加载 ARR/CCR/PSC 影子寄存器(启动时用)。 */
static void PlsrHwTimerSetUg(uint8_t axis)
{
#ifdef PLSR_HOST_TEST
PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_UPDATE_BIT;
#else
PlsrHwAxisMap[axis].timer->EGR = TIM_EGR_UG;
#endif
}

static void PlsrHwStartPwmTimer(uint8_t axis, uint32_t frequencyHz)
/* 配置 PWM 定时器(预装载写入;启动/调频共用,不触碰使能位)。 */
static void PlsrHwConfigurePwm(uint8_t axis, uint32_t frequencyHz)
{
uint16_t psc;
uint16_t arr;
@@ -240,6 +296,14 @@ static void PlsrHwStartPwmTimer(uint8_t axis, uint32_t frequencyHz)
PlsrHwTimerSetPsc(axis, psc);
PlsrHwTimerSetArr(axis, arr);
PlsrHwTimerSetCcr(axis, (uint32_t)arr / 2UL); /* 50% 占空比 */
PlsrHwTimerSetPwmMode1(axis);
PlsrHwTimerSetArpe(axis, 1UL);
}

/* 首次启动输出:加载影子寄存器后使能更新中断、通道与计数。 */
static void PlsrHwPwmBegin(uint8_t axis)
{
PlsrHwTimerSetUg(axis);
PlsrHwTimerSetUie(axis, 1UL);
PlsrHwTimerSetCc1e(axis, 1UL);
PlsrHwTimerSetCen(axis, 1UL);
@@ -295,9 +359,8 @@ PLSR_RESULT PlsrHwInit(void)
#ifndef PLSR_HOST_TEST
{
GPIO_InitTypeDef gpio;
uint8_t point;

/* 1. 输出点 GPIO 时钟。 */
/* 1. 输出点 GPIO 时钟(DIR 点按需配置时使用)。 */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
@@ -305,22 +368,8 @@ PLSR_RESULT PlsrHwInit(void)
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

/* 2. 全部输出点上电安全电平(推挽输出、低)。 */
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
for (point = 0U; point < PLSR_HW_OUTPUT_POINT_COUNT; point++)
{
if (PlsrHwOutputPins[point].port != NULL)
{
gpio.Pin = PlsrHwOutputPins[point].pin;
HAL_GPIO_WritePin(PlsrHwOutputPins[point].port,
PlsrHwOutputPins[point].pin,
GPIO_PIN_RESET);
HAL_GPIO_Init(PlsrHwOutputPins[point].port,
&gpio);
}
}
/* 2. 上电安全:输出点保持复位默认高阻(漏型输出 = 截止 = OFF)。
* 不驱动任何 Y 点,DIR 点仅在 PlsrHwSetDirLevel 时按需配置。 */

/* 3. 定时器时钟。 */
__HAL_RCC_TIM10_CLK_ENABLE();
@@ -329,8 +378,10 @@ PLSR_RESULT PlsrHwInit(void)
__HAL_RCC_TIM14_CLK_ENABLE();

/* 4. 脉冲点切定时器复用(PF6/7=AF3、PF8/9=AF9)。
* 定时器通道尚未使能(CC1E=0),输出为无效电平(低),无毛刺。 */
* 定时器通道尚未使能(CC1E=0),输出级断开,无毛刺。 */
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Pin = GPIO_PIN_6 | GPIO_PIN_7;
gpio.Alternate = 3U;
HAL_GPIO_Init(GPIOF, &gpio);
@@ -355,6 +406,7 @@ PLSR_RESULT PlsrHwInit(void)
PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params)
{
PLSR_HW_AXIS_STATE *state;
uint8_t directionChanged;

if ((axis >= PLSR_HW_AXIS_COUNT) || (params == NULL))
{
@@ -370,13 +422,20 @@ PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params)
return PLSR_RESULT_BUSY;
}

/* 方向延时只在方向发生变化时生效(首次启动/换向/换方向点):
* 段间同向衔接不再等待 10ms,直接进入 PWM 待启动。 */
directionChanged = (state->directionPoint == PLSR_HW_DIR_POINT_NONE)
|| (state->directionPoint != params->directionPoint)
|| (state->directionPositive != params->directionPositive);

state->targetPulses = params->targetPulses;
state->emittedPulses = 0;
state->currentFrequencyHz = params->frequencyHz;
state->directionPoint = params->directionPoint;
state->directionDelayRemainingMs = params->directionDelayMs;
state->directionDelayRemainingMs =
(directionChanged != 0U) ? params->directionDelayMs : 0U;
PlsrHwSetDirLevel(axis, params->directionPositive);
state->state = (params->directionDelayMs > 0U)
state->state = (state->directionDelayRemainingMs > 0U)
? PLSR_HW_STATE_DIR_SETTLING
: PLSR_HW_STATE_PWM_PENDING;
return PLSR_RESULT_OK;
@@ -396,7 +455,8 @@ PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz)
{
if (frequencyHz > 0UL)
{
PlsrHwStartPwmTimer(axis, frequencyHz);
/* 运行中调频:预装载写入,更新事件时生效,不触碰使能位。 */
PlsrHwConfigurePwm(axis, frequencyHz);
}
else
{
@@ -406,7 +466,8 @@ PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz)
else if ((state->state == PLSR_HW_STATE_PWM_PENDING)
&& (frequencyHz > 0UL))
{
PlsrHwStartPwmTimer(axis, frequencyHz);
PlsrHwConfigurePwm(axis, frequencyHz);
PlsrHwPwmBegin(axis);
state->state = PLSR_HW_STATE_RUNNING;
}
return PLSR_RESULT_OK;
@@ -481,7 +542,8 @@ void PlsrHwTick(uint8_t axis)
case PLSR_HW_STATE_PWM_PENDING:
if (state->currentFrequencyHz > 0UL)
{
PlsrHwStartPwmTimer(axis, state->currentFrequencyHz);
PlsrHwConfigurePwm(axis, state->currentFrequencyHz);
PlsrHwPwmBegin(axis);
state->state = PLSR_HW_STATE_RUNNING;
}
break;
@@ -528,6 +590,16 @@ uint32_t PlsrHwTestGetCcr(uint8_t axis)
return PlsrHwTimers[axis].ccr1;
}

uint32_t PlsrHwTestGetCcmr1(uint8_t axis)
{
return PlsrHwTimers[axis].ccmr1;
}

uint32_t PlsrHwTestGetCr1(uint8_t axis)
{
return PlsrHwTimers[axis].cr1;
}

uint32_t PlsrHwTestGetPsc(uint8_t axis)
{
return PlsrHwTimers[axis].psc;


+ 11
- 4
PLSR/Src/plsr_self_test.c 查看文件

@@ -6,12 +6,15 @@

/* 上电自测(验证后可删除):
* - 方向端子设为 Y4(SFD906=4),其余用出厂默认参数(K1)
* - 任务:Q0 发 1 段 2000Hz / 1000 脉冲,H00 完成,梯形曲线
* - 任务:Q0 发 3 段脉冲(H00 完成,顺序衔接),同时验证段间跳转:
* 段1:2000Hz / 1000 脉冲(已单段验证)
* 段2:5000Hz / 6000 脉冲(加速 500ms + 匀速 200ms + 减速 500ms)
* 段3:1000Hz / 500 脉冲
* 数据源为静态数组,仅自测使用(正式 D 设备适配器见 Modbus 阶段)。 */

#define SELF_TEST_WORD_CAPACITY (64U)
#define SELF_TEST_S0_BASE (10U)
#define SELF_TEST_S1_BASE (30U)
#define SELF_TEST_S1_BASE (60U)
#define SELF_TEST_DIR_POINT (4U)

static uint16_t SelfTestWords[3][SELF_TEST_WORD_CAPACITY];
@@ -76,10 +79,14 @@ PLSR_RESULT PlsrSelfTestQueue(void)
/* 方向端子 Y4(PULSE/DIR 模式必需,接线参数)。 */
(void)PlcDeviceWriteSfd(906U, SELF_TEST_DIR_POINT);

/* S0:1 段,2000Hz / 1000 脉冲,H00 完成,顺序跳转。 */
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE, 1U);
/* S0:3 段,H00 完成,顺序跳转。 */
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE, 3U);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 10U, 2000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 12U, 1000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 20U, 5000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 22U, 6000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 30U, 1000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 32U, 500UL);

/* S1:相对模式,起始段 0(=段1)。 */
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S1_BASE, 0U);


+ 19
- 0
PLSR/Test/test_plsr_hal.c 查看文件

@@ -189,6 +189,25 @@ static void TestDirDelaySequence(void)
CHECK(PlsrHwTestGetArr(0U) == arr);
CHECK(PlsrHwTestGetPsc(0U) == psc);
CHECK(PlsrHwTestGetCcr(0U) == arr / 2UL);
/* PWM 模式 1(OC1M=110):复位后 CCMR1=0 冻结,无此配置输出恒定电平。 */
CHECK((PlsrHwTestGetCcmr1(0U) & 0x70UL) == 0x60UL);
/* ARR/CCR 预装载(ARPE=CR1 bit7,OC1PE=CCMR1 bit3):
* 运行中调频不产生提前回绕,否则加速段多出 ~ln(f1/f0) 个假脉冲。 */
CHECK((PlsrHwTestGetCr1(0U) & 0x80UL) == 0x80UL);
CHECK((PlsrHwTestGetCcmr1(0U) & 0x08UL) == 0x08UL);

/* 段间同向衔接:方向不变时跳过方向延时,直接进入 PWM 待启动。 */
CHECK(PlsrHwStopPulse(0U) == PLSR_RESULT_OK);
params.targetPulses = 50;
CHECK(PlsrHwStartPulse(0U, &params) == PLSR_RESULT_OK);
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_PWM_PENDING);
CHECK(PlsrHwTestGetDirLevel(0U) == 1U);

/* 反向时方向延时仍生效。 */
params.directionPositive = 0U;
CHECK(PlsrHwStartPulse(0U, &params) == PLSR_RESULT_OK);
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DIR_SETTLING);
CHECK(PlsrHwTestGetDirLevel(0U) == 0U);
}

static void TestZeroFrequencyWaits(void)


正在加载...
取消
保存