From 2fe534c39f66707274df81f417cb29f1b3469906 Mon Sep 17 00:00:00 2001 From: ywh <2227158009@qq.com> Date: Fri, 7 Aug 2026 09:54:05 +0800 Subject: [PATCH] =?UTF-8?q?PLSR=20=E5=A4=9A=E6=AE=B5=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=99=A8=E8=90=BD=E5=9C=B0=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=AE=8C=E6=88=90=20P0=20=E6=8E=A5=E5=8F=A3=E7=BA=A0=E5=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【P1 多段路径执行器】新增 plsr_path.c/.h,在任务快照与十态状态机之上实现段级执行语义: - 六种等待条件(完成/WAIT时间/WAIT信号/ACT/EXT/EXT或完成),ACT 可截断未完成段,段先完则等 ACT - EXT 按 0→1 新边沿触发,段开始已有效的电平不误触发 - 跳转支持顺序、指定段、自循环,动态跳转(D/HD/FD)每次跳转时重新求值,运行期越界转 ERROR - 绝对模式零位移与相对零脉冲段直接跳转;每轮最多 100 次连续无脉冲跳转后让出 CPU - 状态转换表放宽 RUN/DECEL→ACCEL(段间衔接为正常路径);新增 SEGMENT_COMPLETE 事件与 1ms tick 推进 - STATUS 增加 currentSegment 诊断字段;STOP/PAUSE/急停/限位/故障时路径立即终止 【P0 接口纠偏】 - HSD 改为 16 位 WORD 语义(两个相邻 WORD 组成 32 位值),新增 Dword 读写 API,对外回绕并置溢出诊断 - HSD 检查点记录升级 v2:持久化 position_valid/last_busy,运动中掉电恢复后位置不可信 - SET/CLEAR_POSITION、启动、完成/停止/错误均触发 HSD 检查点(此前生产流程从未保存) - SFD/K0 出厂默认值补齐(FOLLOW=50、最大速度 100000 等),全新 Flash 可直接启动 - directionDelayUs 改名 directionDelayMs;PlsrPostStart 隐藏为仅测试可用 - 新增 test_plsr_path(13 场景 227 项);四套 host 测试共 696 项全绿,IAR 0 错误 0 警告 --- EWARM/Modbus.ewp | 6 + PLSR/Inc/plc_device.h | 19 +- PLSR/Inc/plsr_core.h | 7 +- PLSR/Inc/plsr_job.h | 2 +- PLSR/Inc/plsr_path.h | 71 + PLSR/Inc/plsr_persistence.h | 12 +- PLSR/Inc/plsr_types.h | 5 +- PLSR/Src/plc_device.c | 76 +- PLSR/Src/plsr_core.c | 199 +- PLSR/Src/plsr_job.c | 13 +- PLSR/Src/plsr_path.c | 456 +++++ PLSR/Src/plsr_persistence.c | 90 +- PLSR/Test/run_host_tests.ps1 | 14 + PLSR/Test/test_plc_device.c | 118 +- PLSR/Test/test_plsr_core.c | 48 + PLSR/Test/test_plsr_job.c | 48 + PLSR/Test/test_plsr_path.c | 682 +++++++ tmp/manual_p11_97.txt | 3413 ++++++++++++++++++++++++++++++++++ 18 files changed, 5190 insertions(+), 89 deletions(-) create mode 100644 PLSR/Inc/plsr_path.h create mode 100644 PLSR/Src/plsr_path.c create mode 100644 PLSR/Test/test_plsr_path.c create mode 100644 tmp/manual_p11_97.txt diff --git a/EWARM/Modbus.ewp b/EWARM/Modbus.ewp index e5ecdc1..765f0fc 100644 --- a/EWARM/Modbus.ewp +++ b/EWARM/Modbus.ewp @@ -1284,6 +1284,9 @@ $PROJ_DIR$\..\PLSR\Inc\plsr_job.h + + $PROJ_DIR$\..\PLSR\Inc\plsr_path.h + $PROJ_DIR$\..\PLSR\Src\plsr_persistence.c @@ -1296,6 +1299,9 @@ $PROJ_DIR$\..\PLSR\Src\plsr_job.c + + $PROJ_DIR$\..\PLSR\Src\plsr_path.c + $PROJ_DIR$\..\PLSR\Src\plsr_core.c diff --git a/PLSR/Inc/plc_device.h b/PLSR/Inc/plc_device.h index cb5460c..13a0485 100644 --- a/PLSR/Inc/plc_device.h +++ b/PLSR/Inc/plc_device.h @@ -23,15 +23,20 @@ typedef enum PLC_DEVICE_RESULT PlcDeviceInit(void); -PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value); -PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value); -PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value); -PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value); -PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value); +PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, uint16_t *value); +PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, uint16_t value); +PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, uint16_t value); +PLC_DEVICE_RESULT PlcDeviceReadHsdDword(uint16_t lowAddress, int32_t *value); +PLC_DEVICE_RESULT PlcDevicePublishHsdDword(uint16_t lowAddress, int32_t value); PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void); -PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value); -PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value); +PLC_DEVICE_RESULT PlcDeviceSetHsdCheckpointMeta(uint8_t positionValid, + uint8_t lastBusy); +uint8_t PlcDeviceGetRestoredHsdPositionValid(void); +uint8_t PlcDeviceGetRestoredHsdLastBusy(void); + +PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, uint16_t *value); +PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, uint16_t value); PLC_DEVICE_RESULT PlcDeviceLoadSfd(void); PLC_DEVICE_RESULT PlcDeviceSaveSfd(void); PLC_DEVICE_RESULT PlcDeviceResetSfdDefaults(void); diff --git a/PLSR/Inc/plsr_core.h b/PLSR/Inc/plsr_core.h index 9580ce0..eba1778 100644 --- a/PLSR/Inc/plsr_core.h +++ b/PLSR/Inc/plsr_core.h @@ -13,7 +13,6 @@ PLSR_RESULT PlsrInit(void); void PlsrTask(void *argument); void PlsrProcess(void); -PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); PLSR_RESULT PlsrPostCall(const PLSR_CALL *call); PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask); @@ -21,6 +20,12 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status); PLSR_RESULT PlsrGetLastParseDetail(uint8_t axis, PLSR_PARSE_DETAIL *detail); +#ifdef PLSR_HOST_TEST +/* 仅测试使用的低层启动入口:不解析 S0/S1/S2/D,直接申请资源进入 ACCEL。 + * 生产调用必须使用 PlsrPostCall()。 */ +PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); +#endif + PLSR_RESULT PlsrStateTransition(uint8_t axis, PLSR_STATE target, PLSR_TRANSITION_REASON reason); diff --git a/PLSR/Inc/plsr_job.h b/PLSR/Inc/plsr_job.h index f63cbc9..685f6a1 100644 --- a/PLSR/Inc/plsr_job.h +++ b/PLSR/Inc/plsr_job.h @@ -124,7 +124,7 @@ typedef struct uint16_t accelerationMs; uint16_t decelerationMs; uint16_t gapAccelerationMs; - uint16_t directionDelayUs; + uint16_t directionDelayMs; uint8_t curveMode; uint8_t follow; uint8_t feedforwardPercent; diff --git a/PLSR/Inc/plsr_path.h b/PLSR/Inc/plsr_path.h new file mode 100644 index 0000000..a862343 --- /dev/null +++ b/PLSR/Inc/plsr_path.h @@ -0,0 +1,71 @@ +#ifndef PLSR_PATH_H +#define PLSR_PATH_H + +#include "plsr_job.h" +#include "plsr_types.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PLSR_PATH_ZERO_JUMP_BUDGET (100U) + +typedef enum +{ + PLSR_PATH_SEGMENT_RUNNING = 0, + PLSR_PATH_WAIT_TIME, + PLSR_PATH_WAIT_SIGNAL, + PLSR_PATH_ACT_PENDING, + PLSR_PATH_ENDED +} PLSR_PATH_PHASE; + +typedef enum +{ + PLSR_PATH_ACTION_NONE = 0, + PLSR_PATH_ACTION_NEXT_SEGMENT, + PLSR_PATH_ACTION_ENTER_WAIT, + PLSR_PATH_ACTION_JOB_COMPLETE, + PLSR_PATH_ACTION_YIELD, + PLSR_PATH_ACTION_ERROR +} PLSR_PATH_ACTION; + +typedef struct +{ + PLSR_PATH_PHASE phase; + uint16_t currentSegment; + uint16_t nextSegment; + uint8_t waitCondition; + uint32_t waitRemainingMs; + uint16_t zeroJumpBudget; + uint8_t lastExtLevel; + uint8_t segmentEnded; + uint8_t extActive; + uint8_t actActive; + uint8_t jumpChainPending; + uint8_t jobEnded; + uint8_t waitSignalDevice; + uint32_t waitSignalAddress; +} PLSR_PATH_CONTEXT; + +void PlsrPathBegin(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition); + +PLSR_PATH_ACTION PlsrPathOnSegmentDone(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition); + +PLSR_PATH_ACTION PlsrPathTick(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition); + +uint16_t PlsrPathGetCurrentSegment(const PLSR_PATH_CONTEXT *context); + +void PlsrPathTerminate(PLSR_PATH_CONTEXT *context); + +#ifdef __cplusplus +} +#endif + +#endif /* PLSR_PATH_H */ diff --git a/PLSR/Inc/plsr_persistence.h b/PLSR/Inc/plsr_persistence.h index b579a3e..b849f02 100644 --- a/PLSR/Inc/plsr_persistence.h +++ b/PLSR/Inc/plsr_persistence.h @@ -8,15 +8,21 @@ extern "C" { #endif +/* HSD 检查点元数据位(随 HSD 记录一起掉电保存)。 */ +#define PLSR_HSD_META_POSITION_VALID (1UL << 0U) +#define PLSR_HSD_META_LAST_BUSY (1UL << 1U) +#define PLSR_HSD_META_REASON_MASK (0x0000003CUL) + typedef struct { - int32_t runtime[PLSR_HSD_RUNTIME_COUNT]; - int32_t config[PLSR_HSD_CONFIG_COUNT]; + uint32_t metadata; + uint16_t runtime[PLSR_HSD_RUNTIME_COUNT]; + uint16_t config[PLSR_HSD_CONFIG_COUNT]; } PLSR_HSD_DATA; typedef struct { - int32_t config[PLSR_SFD_CONFIG_COUNT]; + uint16_t config[PLSR_SFD_CONFIG_COUNT]; } PLSR_SFD_DATA; typedef enum diff --git a/PLSR/Inc/plsr_types.h b/PLSR/Inc/plsr_types.h index 0f6a61f..838f038 100644 --- a/PLSR/Inc/plsr_types.h +++ b/PLSR/Inc/plsr_types.h @@ -129,8 +129,9 @@ typedef enum #define PLSR_EVENT_WAIT_BEGIN (1UL << 10U) #define PLSR_EVENT_WAIT_COMPLETE (1UL << 11U) #define PLSR_EVENT_JOB_COMPLETE (1UL << 12U) +#define PLSR_EVENT_SEGMENT_COMPLETE (1UL << 13U) -#define PLSR_EVENT_ALL_MASK (0x00001F3FUL) +#define PLSR_EVENT_ALL_MASK (0x00003F3FUL) #define PLSR_EVENT_CRITICAL_MASK (PLSR_EVENT_SOFTWARE_EMERGENCY \ | PLSR_EVENT_LIMIT_POSITIVE \ | PLSR_EVENT_LIMIT_NEGATIVE \ @@ -177,8 +178,10 @@ typedef struct uint8_t jobValid; uint8_t s2Set; uint8_t speedClamped; + uint8_t positionOverflow; uint16_t segmentCount; uint16_t startSegment; + uint16_t currentSegment; } PLSR_STATUS; #ifdef __cplusplus diff --git a/PLSR/Src/plc_device.c b/PLSR/Src/plc_device.c index 03a449d..5f2c844 100644 --- a/PLSR/Src/plc_device.c +++ b/PLSR/Src/plc_device.c @@ -26,6 +26,8 @@ static uint8_t PlcSfdOperationActive; static uint32_t PlcHsdChangeCounter; static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult; static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult; +static uint8_t PlcRestoredHsdPositionValid; +static uint8_t PlcRestoredHsdLastBusy; static uint32_t PlcDeviceEnterCritical(void) { @@ -90,7 +92,7 @@ static void PlcDeviceEndSfdOperation(void) PlcDeviceExitCritical(interruptState); } -static int32_t *PlcDeviceResolveHsd(uint16_t address) +static uint16_t *PlcDeviceResolveHsd(uint16_t address) { if (address < PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT) { @@ -171,6 +173,8 @@ PLC_DEVICE_RESULT PlcDeviceInit(void) (void)memset(PlcSmFlags, 0, sizeof(PlcSmFlags)); PlcSfdOperationActive = 0U; PlcHsdChangeCounter = 0UL; + PlcRestoredHsdPositionValid = 0U; + PlcRestoredHsdLastBusy = 0U; PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData); PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED) @@ -181,6 +185,12 @@ PLC_DEVICE_RESULT PlcDeviceInit(void) { return PLC_DEVICE_PERSISTENCE_ERROR; } + PlcRestoredHsdPositionValid = + ((PlcHsdData.metadata & PLSR_HSD_META_POSITION_VALID) != 0UL) + ? 1U + : 0U; + PlcRestoredHsdLastBusy = + ((PlcHsdData.metadata & PLSR_HSD_META_LAST_BUSY) != 0UL) ? 1U : 0U; PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; @@ -193,9 +203,9 @@ PLC_DEVICE_RESULT PlcDeviceInit(void) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value) +PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, uint16_t *value) { - int32_t *source; + uint16_t *source; if (value == NULL) { @@ -211,7 +221,7 @@ PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value) +PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, uint16_t value) { if ((address < PLSR_HSD_CONFIG_START) || (address >= PLSR_HSD_CONFIG_START + PLSR_HSD_CONFIG_COUNT)) @@ -228,7 +238,7 @@ PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value) +PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, uint16_t value) { if (address >= PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT) { @@ -241,9 +251,10 @@ PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value) +/* 两个相邻 WORD 组成一个 32 位值:低地址=低16位,高地址=高16位(信捷兼容布局)。 */ +PLC_DEVICE_RESULT PlcDeviceReadHsdDword(uint16_t lowAddress, int32_t *value) { - uint64_t combined; + uint32_t combined; uint32_t interruptState; uint16_t offset; @@ -260,16 +271,16 @@ PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value) offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); interruptState = PlcDeviceEnterCritical(); - combined = (uint64_t)(uint32_t)PlcHsdData.runtime[offset]; - combined |= ((uint64_t)(uint32_t)PlcHsdData.runtime[offset + 1U]) << 32U; + combined = (uint32_t)PlcHsdData.runtime[offset]; + combined |= ((uint32_t)PlcHsdData.runtime[offset + 1U]) << 16U; PlcDeviceExitCritical(interruptState); - *value = (int64_t)combined; + *value = (int32_t)combined; return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value) +PLC_DEVICE_RESULT PlcDevicePublishHsdDword(uint16_t lowAddress, int32_t value) { - uint64_t rawValue; + uint32_t rawValue; uint32_t interruptState; uint16_t offset; @@ -280,17 +291,48 @@ PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value) return PLC_DEVICE_INVALID_ADDRESS; } - rawValue = (uint64_t)value; + rawValue = (uint32_t)value; offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); interruptState = PlcDeviceEnterCritical(); - PlcHsdData.runtime[offset] = (int32_t)(uint32_t)rawValue; - PlcHsdData.runtime[offset + 1U] = (int32_t)(uint32_t)(rawValue >> 32U); + PlcHsdData.runtime[offset] = (uint16_t)(rawValue & 0xFFFFUL); + PlcHsdData.runtime[offset + 1U] = (uint16_t)(rawValue >> 16U); + PlcHsdChangeCounter++; + PlcHsdDirty = 1U; + PlcDeviceExitCritical(interruptState); + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceSetHsdCheckpointMeta(uint8_t positionValid, + uint8_t lastBusy) +{ + uint32_t interruptState = PlcDeviceEnterCritical(); + + PlcHsdData.metadata &= ~(PLSR_HSD_META_POSITION_VALID + | PLSR_HSD_META_LAST_BUSY); + if (positionValid != 0U) + { + PlcHsdData.metadata |= PLSR_HSD_META_POSITION_VALID; + } + if (lastBusy != 0U) + { + PlcHsdData.metadata |= PLSR_HSD_META_LAST_BUSY; + } PlcHsdChangeCounter++; PlcHsdDirty = 1U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } +uint8_t PlcDeviceGetRestoredHsdPositionValid(void) +{ + return PlcRestoredHsdPositionValid; +} + +uint8_t PlcDeviceGetRestoredHsdLastBusy(void) +{ + return PlcRestoredHsdLastBusy; +} + PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) { PLSR_HSD_DATA snapshot; @@ -323,7 +365,7 @@ PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value) +PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, uint16_t *value) { uint32_t interruptState; @@ -348,7 +390,7 @@ PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value) return PLC_DEVICE_OK; } -PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value) +PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, uint16_t value) { uint32_t interruptState; diff --git a/PLSR/Src/plsr_core.c b/PLSR/Src/plsr_core.c index 10885eb..e86d080 100644 --- a/PLSR/Src/plsr_core.c +++ b/PLSR/Src/plsr_core.c @@ -1,6 +1,7 @@ #include "plsr_core.h" #include "plc_device.h" #include "plsr_address_map.h" +#include "plsr_path.h" #include "plsr_resource.h" #include @@ -25,12 +26,14 @@ typedef struct uint32_t illegalTransitionCount; int64_t logicalPosition; int64_t totalPulses; + PLSR_PATH_CONTEXT path; uint8_t hasLastCommand; uint8_t done; uint8_t directionPositive; uint8_t immediateStopPending; uint8_t positionValid; uint8_t jobValid; + uint8_t positionOverflow; } PLSR_AXIS; typedef struct @@ -113,6 +116,23 @@ static void PlsrSetStopReason(PLSR_AXIS *axis, } } +/* 将 64 位逻辑位置发布为 HSD 的 32 位兼容值: + * 超出 INT32 范围时回绕(与信捷 32 位寄存器一致)并置溢出诊断标志。 */ +static void PlsrPublishPosition(uint8_t axis, PLSR_AXIS *axisObject) +{ + int32_t compatValue; + + if ((axisObject->logicalPosition > INT32_MAX) + || (axisObject->logicalPosition < INT32_MIN)) + { + axisObject->positionOverflow = 1U; + } + compatValue = (int32_t)(uint32_t)axisObject->logicalPosition; + (void)PlcDevicePublishHsdDword( + (uint16_t)((uint16_t)axis * PLSR_HSD_RUNTIME_AXIS_COUNT), + compatValue); +} + static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, PLSR_STATE target) { @@ -141,7 +161,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, case PLSR_STATE_ACCEL: case PLSR_STATE_RUN: - return ((target == PLSR_STATE_RUN) + return ((target == PLSR_STATE_ACCEL) + || (target == PLSR_STATE_RUN) || (target == PLSR_STATE_DECEL) || (target == PLSR_STATE_WAIT) || (target == PLSR_STATE_COMPLETED) @@ -150,7 +171,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, : 0U; case PLSR_STATE_DECEL: - return ((target == PLSR_STATE_RUN) + return ((target == PLSR_STATE_ACCEL) + || (target == PLSR_STATE_RUN) || (target == PLSR_STATE_WAIT) || (target == PLSR_STATE_PAUSED) || (target == PLSR_STATE_COMPLETED) @@ -223,7 +245,11 @@ PLSR_RESULT PlsrStateTransition(uint8_t axis, { axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; axisObject->immediateStopPending = 0U; + PlsrPathTerminate(&axisObject->path); PlsrResourceRelease(&axisObject->lease); + /* 运动已结束:记录 lastBusy=0,保证掉电后恢复时位置仍可信。 */ + (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 0U); + (void)PlcDeviceCheckpointHsd(); } PlsrPublishAxis(axis); return PLSR_RESULT_OK; @@ -360,6 +386,7 @@ static uint8_t PlsrPopHighestPriorityCommand(PLSR_COMMAND_SLOT *slot) return 1U; } +#ifdef PLSR_HOST_TEST PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) { PLSR_COMMAND command; @@ -383,6 +410,7 @@ PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) command.argument = 0; return PlsrQueueCommand(&command, request, NULL); } +#endif /* PLSR_HOST_TEST */ PLSR_RESULT PlsrPostCall(const PLSR_CALL *call) { @@ -515,6 +543,12 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject, { PlsrResourceRelease(&axisObject->lease); } + else + { + /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ + (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); + (void)PlcDeviceCheckpointHsd(); + } return result; } @@ -568,6 +602,19 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; axisObject->immediateStopPending = 0U; axisObject->done = 0U; + PlsrPathBegin(&axisObject->path, + &axisObject->job, + axisObject->logicalPosition); + if (axisObject->path.jobEnded != 0U) + { + /* 起点就是零脉冲且跳转链已结束(任务无实际脉冲)。 */ + axisObject->stopReason = PLSR_STOP_REASON_NORMAL_COMPLETE; + (void)PlsrStateTransition(call->dAxis, + PLSR_STATE_COMPLETED, + PLSR_TRANSITION_JOB_COMPLETE); + axisObject->jobValid = 0U; + return PLSR_RESULT_OK; + } result = PlsrStateTransition(call->dAxis, PLSR_STATE_ACCEL, PLSR_TRANSITION_START); @@ -576,6 +623,12 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, axisObject->jobValid = 0U; PlsrResourceRelease(&axisObject->lease); } + else + { + /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ + (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); + (void)PlcDeviceCheckpointHsd(); + } return result; } @@ -600,6 +653,7 @@ static PLSR_RESULT PlsrStopImmediate(uint8_t axis) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_IMMEDIATE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; + PlsrPathTerminate(&axisObject->path); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -633,6 +687,7 @@ static PLSR_RESULT PlsrStopDecel(uint8_t axis) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_DECEL); axisObject->pendingTerminal = PLSR_STATE_STOPPED; + PlsrPathTerminate(&axisObject->path); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -664,6 +719,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis) if (axisObject->state == PLSR_STATE_WAIT) { PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); + PlsrPathTerminate(&axisObject->path); return PlsrStateTransition(axis, PLSR_STATE_PAUSED, PLSR_TRANSITION_DECEL_COMPLETE); @@ -677,6 +733,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); axisObject->pendingTerminal = PLSR_STATE_PAUSED; + PlsrPathTerminate(&axisObject->path); if (axisObject->state == PLSR_STATE_DECEL) { return PLSR_RESULT_OK; @@ -743,10 +800,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) { axisObject->logicalPosition = slot->command.argument; axisObject->positionValid = 1U; - (void)PlcDevicePublishHsdPair( - (uint16_t)(slot->command.axis - * PLSR_HSD_RUNTIME_AXIS_COUNT), - axisObject->logicalPosition); + axisObject->positionOverflow = 0U; + PlsrPublishPosition(slot->command.axis, axisObject); + (void)PlcDeviceSetHsdCheckpointMeta(1U, 0U); + (void)PlcDeviceCheckpointHsd(); result = PLSR_RESULT_OK; } break; @@ -760,10 +817,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) { axisObject->logicalPosition = 0; axisObject->positionValid = 1U; - (void)PlcDevicePublishHsdPair( - (uint16_t)(slot->command.axis - * PLSR_HSD_RUNTIME_AXIS_COUNT), - 0); + axisObject->positionOverflow = 0U; + PlsrPublishPosition(slot->command.axis, axisObject); + (void)PlcDeviceSetHsdCheckpointMeta(1U, 0U); + (void)PlcDeviceCheckpointHsd(); result = PLSR_RESULT_OK; } break; @@ -854,6 +911,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_SOFTWARE_EMERGENCY); axisObject->done = 0U; + PlsrPathTerminate(&axisObject->path); (void)PlsrStateTransition(axis, PLSR_STATE_STOPPED, PLSR_TRANSITION_STOP); @@ -870,6 +928,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_LIMIT_POSITIVE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; + PlsrPathTerminate(&axisObject->path); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -896,6 +955,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_LIMIT_NEGATIVE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; + PlsrPathTerminate(&axisObject->path); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -920,12 +980,75 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) : PLSR_ERROR_COUNTER_FAULT; PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); axisObject->done = 0U; + PlsrPathTerminate(&axisObject->path); (void)PlsrStateTransition(axis, PLSR_STATE_ERROR, PLSR_TRANSITION_FAULT); } } +/* 应用路径执行器的动作:段间推进、进入等待、结束、让出、错误。 */ +static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action) +{ + PLSR_AXIS *axisObject = &PlsrAxes[axis]; + + switch (action) + { + case PLSR_PATH_ACTION_NEXT_SEGMENT: + if (axisObject->state == PLSR_STATE_WAIT) + { + (void)PlsrStateTransition(axis, + PLSR_STATE_ACCEL, + PLSR_TRANSITION_WAIT_COMPLETE); + } + else if ((axisObject->state == PLSR_STATE_ACCEL) + || (axisObject->state == PLSR_STATE_RUN) + || (axisObject->state == PLSR_STATE_DECEL)) + { + (void)PlsrStateTransition(axis, + PLSR_STATE_ACCEL, + PLSR_TRANSITION_START); + } + break; + + case PLSR_PATH_ACTION_ENTER_WAIT: + if ((axisObject->state == PLSR_STATE_ACCEL) + || (axisObject->state == PLSR_STATE_RUN)) + { + (void)PlsrStateTransition(axis, + PLSR_STATE_WAIT, + PLSR_TRANSITION_WAIT_BEGIN); + } + break; + + case PLSR_PATH_ACTION_JOB_COMPLETE: + if (PlsrStateIsBusy(axisObject->state) != 0U) + { + axisObject->stopReason = PLSR_STOP_REASON_NORMAL_COMPLETE; + (void)PlsrStateTransition(axis, + PLSR_STATE_COMPLETED, + PLSR_TRANSITION_JOB_COMPLETE); + } + break; + + case PLSR_PATH_ACTION_YIELD: + /* 预算耗尽:本轮不再推进,下个 tick 由 PlsrPathTick 恢复。 */ + break; + + case PLSR_PATH_ACTION_ERROR: + axisObject->error = PLSR_ERROR_INTERNAL; + PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); + axisObject->done = 0U; + (void)PlsrStateTransition(axis, + PLSR_STATE_ERROR, + PLSR_TRANSITION_FAULT); + break; + + default: + break; + } +} + static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) { PLSR_AXIS *axisObject = &PlsrAxes[axis]; @@ -988,6 +1111,19 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) PLSR_TRANSITION_WAIT_COMPLETE); } + if (((events & PLSR_EVENT_SEGMENT_COMPLETE) != 0UL) + && (axisObject->state == PLSR_STATE_RUN) + && (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED) + && (axisObject->immediateStopPending == 0U)) + { + PLSR_PATH_ACTION action = PlsrPathOnSegmentDone( + &axisObject->path, + &axisObject->job, + axisObject->logicalPosition); + + PlsrApplyPathAction(axis, action); + } + if (((events & PLSR_EVENT_JOB_COMPLETE) != 0UL) && (PlsrStateIsBusy(axisObject->state) != 0U) && (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED) @@ -1002,7 +1138,9 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) PLSR_RESULT PlsrInit(void) { - int64_t restoredPosition; + int32_t restoredPosition; + uint8_t restoredPositionValid; + uint8_t restoredLastBusy; uint8_t axis; (void)memset(PlsrAxes, 0, sizeof(PlsrAxes)); @@ -1011,18 +1149,23 @@ PLSR_RESULT PlsrInit(void) PlsrResourceInit(); PlsrInitialized = 1U; + restoredPositionValid = PlcDeviceGetRestoredHsdPositionValid(); + restoredLastBusy = PlcDeviceGetRestoredHsdLastBusy(); for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED; PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED; PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE; - if ((PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK) - && (PlcDeviceReadHsdPair( - (uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT), - &restoredPosition) - == PLC_DEVICE_OK)) + if (PlcDeviceReadHsdDword( + (uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT), + &restoredPosition) == PLC_DEVICE_OK) { PlsrAxes[axis].logicalPosition = restoredPosition; + } + /* 只有上次正常停机且保存了位置有效标志,才允许绝对定位; + * 运动中掉电(lastBusy=1)时位置不可信。 */ + if ((restoredPositionValid != 0U) && (restoredLastBusy == 0U)) + { PlsrAxes[axis].positionValid = 1U; } if (PlsrStateTransition(axis, @@ -1074,6 +1217,25 @@ void PlsrProcess(void) PlsrProcessNormalEvents(axis, events); } } + + /* 1ms tick:路径执行器推进(WAIT/ACT 计时、信号/EXT 轮询、跳转链)。 */ + for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) + { + PLSR_AXIS *axisObject = &PlsrAxes[axis]; + PLSR_PATH_ACTION action; + + if (PlsrStateIsBusy(axisObject->state) == 0U) + { + continue; + } + action = PlsrPathTick(&axisObject->path, + &axisObject->job, + axisObject->logicalPosition); + if (action != PLSR_PATH_ACTION_NONE) + { + PlsrApplyPathAction(axis, action); + } + } } void PlsrTask(void *argument) @@ -1127,6 +1289,7 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status) : PLSR_DIRECTION_POINT_NONE; status->positionValid = axisObject->positionValid; status->jobValid = axisObject->jobValid; + status->positionOverflow = axisObject->positionOverflow; status->s2Set = (axisObject->jobValid != 0U) ? axisObject->job.s2Set : 0U; status->speedClamped = (axisObject->jobValid != 0U) ? axisObject->job.speedClamped @@ -1137,6 +1300,10 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status) status->startSegment = (axisObject->jobValid != 0U) ? axisObject->job.startSegment : 0U; + status->currentSegment = (axisObject->jobValid != 0U) + ? PlsrPathGetCurrentSegment( + &axisObject->path) + : 0U; PlsrCoreExitCritical(interruptState); return PLSR_RESULT_OK; } diff --git a/PLSR/Src/plsr_job.c b/PLSR/Src/plsr_job.c index 138260f..bb97b22 100644 --- a/PLSR/Src/plsr_job.c +++ b/PLSR/Src/plsr_job.c @@ -188,23 +188,20 @@ static PLSR_RESULT PlsrReadFixedWord(uint8_t useHsd, uint16_t *value, PLSR_PARSE_DETAIL *detail) { - int32_t rawValue = 0; PLC_DEVICE_RESULT deviceResult; - deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, &rawValue) - : PlcDeviceReadSfd(address, &rawValue); - if ((deviceResult != PLC_DEVICE_OK) || (rawValue < 0) - || (rawValue > (int32_t)UINT16_MAX)) + deviceResult = (useHsd != 0U) ? PlcDeviceReadHsd(address, value) + : PlcDeviceReadSfd(address, value); + if (deviceResult != PLC_DEVICE_OK) { PlsrSetDetail(detail, PLSR_RESULT_INVALID_S2, PLSR_PARSE_BLOCK_S2, address, - rawValue, + 0, 0U); return PLSR_RESULT_INVALID_S2; } - *value = (uint16_t)rawValue; return PLSR_RESULT_OK; } @@ -523,7 +520,7 @@ static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call, snapshot->directionPoint = (uint8_t)word; result = PlsrReadFixedWord(0U, (uint16_t)(commonBase + 7U), - &snapshot->s2.directionDelayUs, + &snapshot->s2.directionDelayMs, detail); if (result != PLSR_RESULT_OK) return result; diff --git a/PLSR/Src/plsr_path.c b/PLSR/Src/plsr_path.c new file mode 100644 index 0000000..39d832f --- /dev/null +++ b/PLSR/Src/plsr_path.c @@ -0,0 +1,456 @@ +#include "plsr_path.h" +#include + +/* 等待寄存器类型编码(PLSR_VALUE_SOURCE)与软元件类型(PLSR_DEVICE_TYPE) + * 相差 1:D=1→D、HD=2→HD、FD=3→FD、X=4→X、M=5→M、HM=6→HM。 */ +static uint8_t PlsrPathDeviceFromSource(uint8_t sourceCode) +{ + return (uint8_t)(sourceCode - 1U); +} + +static uint8_t PlsrPathReadDword(const PLSR_DATA_SOURCE *source, + PLSR_DEVICE_TYPE device, + uint32_t address, + int32_t *value) +{ + uint16_t lowWord; + uint16_t highWord; + + if ((source->readWord == NULL) + || (source->readWord(source->context, + device, + address, + &lowWord) == 0U) + || (source->readWord(source->context, + device, + address + 1U, + &highWord) == 0U)) + { + return 0U; + } + *value = (int32_t)(((uint32_t)highWord << 16U) | (uint32_t)lowWord); + return 1U; +} + +static uint8_t PlsrPathReadBit(const PLSR_DATA_SOURCE *source, + PLSR_DEVICE_TYPE device, + uint32_t address, + uint8_t *value) +{ + if ((source->readBit == NULL) + || (source->readBit(source->context, device, address, value) == 0U)) + { + return 0U; + } + return 1U; +} + +/* 求值当前段的跳转目标。 + * 返回:0=顺序下一段,1~segmentCount=指定段。 + * 动态来源在跳转发生时重新读取;非法值置 error。 */ +static int32_t PlsrPathResolveJumpTarget(const PLSR_JOB_SNAPSHOT *job, + uint16_t segment, + uint8_t *error) +{ + const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot = + &job->segments[segment - 1U]; + int32_t target; + + *error = 0U; + if (segmentSnapshot->jumpSource == PLSR_VALUE_CONSTANT) + { + target = segmentSnapshot->jumpValueOrAddress; + } + else + { + if (PlsrPathReadDword(&job->source, + (PLSR_DEVICE_TYPE)PlsrPathDeviceFromSource( + segmentSnapshot->jumpSource), + (uint32_t)segmentSnapshot->jumpValueOrAddress, + &target) == 0U) + { + *error = 1U; + return 0; + } + } + if ((target < 0) || (target > (int32_t)job->segmentCount)) + { + *error = 1U; + return 0; + } + return target; +} + +/* 段是否零脉冲:相对模式脉冲数为 0,或绝对模式目标等于进入段时的位置。 */ +static uint8_t PlsrPathSegmentIsZeroPulse(const PLSR_JOB_SNAPSHOT *job, + uint16_t segment, + int64_t logicalPosition) +{ + const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot = + &job->segments[segment - 1U]; + + if ((segmentSnapshot->flags & PLSR_SEGMENT_FLAG_ZERO_PULSE) != 0U) + { + return 1U; + } + if ((job->positioningMode == 1U) + && (segmentSnapshot->pulseOrTarget == (int32_t)logicalPosition)) + { + return 1U; + } + return 0U; +} + +/* 进入新段时的初始化:求值 ACT 时间、EXT/等待信号地址与当前电平。 */ +static void PlsrPathEnterSegment(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job) +{ + const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot = + &job->segments[context->currentSegment - 1U]; + int32_t actValue; + + context->segmentEnded = 0U; + context->extActive = 0U; + context->actActive = 0U; + context->waitCondition = (uint8_t)segmentSnapshot->waitCondition; + + switch (context->waitCondition) + { + case PLSR_WAIT_ACT_TIME: + context->actActive = 1U; + if (segmentSnapshot->waitSource == PLSR_VALUE_CONSTANT) + { + actValue = segmentSnapshot->waitValueOrAddress; + } + else + { + actValue = 0; + (void)PlsrPathReadDword(&job->source, + (PLSR_DEVICE_TYPE) + PlsrPathDeviceFromSource( + segmentSnapshot->waitSource), + (uint32_t) + segmentSnapshot->waitValueOrAddress, + &actValue); + } + context->waitRemainingMs = + (actValue > 0) ? (uint32_t)actValue : 0U; + break; + + case PLSR_WAIT_SIGNAL: + case PLSR_WAIT_EXT: + case PLSR_WAIT_EXT_OR_COMPLETE: + context->waitSignalDevice = + PlsrPathDeviceFromSource(segmentSnapshot->waitSource); + context->waitSignalAddress = + (uint32_t)segmentSnapshot->waitValueOrAddress; + if (context->waitCondition != PLSR_WAIT_SIGNAL) + { + /* EXT:记录段开始时的电平,已有的有效电平不算新边沿。 */ + uint8_t level = 0U; + + (void)PlsrPathReadBit(&job->source, + (PLSR_DEVICE_TYPE) + context->waitSignalDevice, + context->waitSignalAddress, + &level); + context->lastExtLevel = level; + context->extActive = 1U; + } + break; + + default: + break; + } +} + +/* EXT 边沿检测:电平 0→1 才算新边沿。 */ +static uint8_t PlsrPathPollExt(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job) +{ + uint8_t level = 0U; + uint8_t triggered = 0U; + + if (PlsrPathReadBit(&job->source, + (PLSR_DEVICE_TYPE)context->waitSignalDevice, + context->waitSignalAddress, + &level) != 0U) + { + if ((level != 0U) && (context->lastExtLevel == 0U)) + { + triggered = 1U; + } + context->lastExtLevel = level; + } + return triggered; +} + +/* 推进跳转链:零脉冲段连续跳转(预算内),直到有脉冲段、结束或预算耗尽。 */ +static PLSR_PATH_ACTION PlsrPathAdvance(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition) +{ + uint8_t error; + int32_t target; + uint16_t next; + + while (context->zeroJumpBudget > 0U) + { + target = PlsrPathResolveJumpTarget(job, + context->currentSegment, + &error); + if (error != 0U) + { + context->jobEnded = 1U; + context->phase = PLSR_PATH_ENDED; + return PLSR_PATH_ACTION_ERROR; + } + if (target == 0) + { + if (context->currentSegment >= job->segmentCount) + { + context->jobEnded = 1U; + context->phase = PLSR_PATH_ENDED; + return PLSR_PATH_ACTION_JOB_COMPLETE; + } + next = (uint16_t)(context->currentSegment + 1U); + } + else + { + next = (uint16_t)target; + } + + if (PlsrPathSegmentIsZeroPulse(job, next, logicalPosition) != 0U) + { + context->zeroJumpBudget--; + context->currentSegment = next; + context->nextSegment = next; + PlsrPathEnterSegment(context, job); + continue; + } + + context->currentSegment = next; + context->nextSegment = next; + context->phase = PLSR_PATH_SEGMENT_RUNNING; + PlsrPathEnterSegment(context, job); + return PLSR_PATH_ACTION_NEXT_SEGMENT; + } + + context->jumpChainPending = 1U; + return PLSR_PATH_ACTION_YIELD; +} + +void PlsrPathBegin(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition) +{ + (void)memset(context, 0, sizeof(*context)); + context->currentSegment = job->startSegment; + context->nextSegment = context->currentSegment; + context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET; + context->phase = PLSR_PATH_SEGMENT_RUNNING; + PlsrPathEnterSegment(context, job); + if (PlsrPathSegmentIsZeroPulse(job, + context->currentSegment, + logicalPosition) != 0U) + { + /* 起点段就是零脉冲:直接走跳转链。 */ + (void)PlsrPathAdvance(context, job, logicalPosition); + } +} + +PLSR_PATH_ACTION PlsrPathOnSegmentDone(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition) +{ + const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot; + int32_t waitValue; + uint8_t level; + + if ((context->jobEnded != 0U) || (context->phase == PLSR_PATH_ENDED)) + { + return PLSR_PATH_ACTION_NONE; + } + context->segmentEnded = 1U; + context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET; + context->jumpChainPending = 0U; + segmentSnapshot = &job->segments[context->currentSegment - 1U]; + + switch (context->waitCondition) + { + case PLSR_WAIT_PULSE_COMPLETE: + case PLSR_WAIT_EXT_OR_COMPLETE: + return PlsrPathAdvance(context, job, logicalPosition); + + case PLSR_WAIT_TIME: + /* 等待时间在段完成时求值一次。 */ + if (segmentSnapshot->waitSource == PLSR_VALUE_CONSTANT) + { + waitValue = segmentSnapshot->waitValueOrAddress; + } + else + { + waitValue = 0; + (void)PlsrPathReadDword( + &job->source, + (PLSR_DEVICE_TYPE)PlsrPathDeviceFromSource( + segmentSnapshot->waitSource), + (uint32_t)segmentSnapshot->waitValueOrAddress, + &waitValue); + } + context->waitRemainingMs = + (waitValue > 0) ? (uint32_t)waitValue : 0U; + if (context->waitRemainingMs == 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + context->phase = PLSR_PATH_WAIT_TIME; + return PLSR_PATH_ACTION_ENTER_WAIT; + + case PLSR_WAIT_SIGNAL: + /* 信号地址已在进入段时求值;先立即检查一次,已有效则直接跳。 */ + if (PlsrPathReadBit(&job->source, + (PLSR_DEVICE_TYPE)context->waitSignalDevice, + context->waitSignalAddress, + &level) != 0U) + { + if (level != 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + } + context->phase = PLSR_PATH_WAIT_SIGNAL; + return PLSR_PATH_ACTION_ENTER_WAIT; + + case PLSR_WAIT_ACT_TIME: + if (context->waitRemainingMs == 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + /* 段已完成但 ACT 未到:停在 ACT_PENDING(core 处于 WAIT)。 */ + context->phase = PLSR_PATH_ACT_PENDING; + return PLSR_PATH_ACTION_ENTER_WAIT; + + case PLSR_WAIT_EXT: + /* 段已完成但 EXT 未触发:继续等新边沿。 */ + context->phase = PLSR_PATH_WAIT_SIGNAL; + return PLSR_PATH_ACTION_ENTER_WAIT; + + default: + return PlsrPathAdvance(context, job, logicalPosition); + } +} + +PLSR_PATH_ACTION PlsrPathTick(PLSR_PATH_CONTEXT *context, + const PLSR_JOB_SNAPSHOT *job, + int64_t logicalPosition) +{ + uint8_t level; + + if ((context->jobEnded != 0U) || (context->phase == PLSR_PATH_ENDED)) + { + return PLSR_PATH_ACTION_NONE; + } + + if (context->jumpChainPending != 0U) + { + /* 上轮预算耗尽让出:本轮重置预算继续跳转链。 */ + context->zeroJumpBudget = PLSR_PATH_ZERO_JUMP_BUDGET; + context->jumpChainPending = 0U; + return PlsrPathAdvance(context, job, logicalPosition); + } + + switch (context->phase) + { + case PLSR_PATH_SEGMENT_RUNNING: + if ((context->actActive != 0U) + && (context->waitRemainingMs > 0U)) + { + context->waitRemainingMs--; + if (context->waitRemainingMs == 0U) + { + /* ACT 到时:截断当前段,立即跳转。 */ + return PlsrPathAdvance(context, job, logicalPosition); + } + } + if ((context->extActive != 0U) + && (PlsrPathPollExt(context, job) != 0U)) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + break; + + case PLSR_PATH_WAIT_TIME: + if (context->waitRemainingMs > 0U) + { + context->waitRemainingMs--; + } + if (context->waitRemainingMs == 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + break; + + case PLSR_PATH_WAIT_SIGNAL: + if (context->waitCondition == PLSR_WAIT_SIGNAL) + { + /* H02:电平有效即触发。 */ + if (PlsrPathReadBit(&job->source, + (PLSR_DEVICE_TYPE) + context->waitSignalDevice, + context->waitSignalAddress, + &level) != 0U) + { + if (level != 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + } + } + else + { + /* H04 段完成后的 EXT 等待:新边沿触发。 */ + if (PlsrPathPollExt(context, job) != 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + } + break; + + case PLSR_PATH_ACT_PENDING: + if (context->waitRemainingMs > 0U) + { + context->waitRemainingMs--; + } + if (context->waitRemainingMs == 0U) + { + return PlsrPathAdvance(context, job, logicalPosition); + } + break; + + default: + break; + } + return PLSR_PATH_ACTION_NONE; +} + +uint16_t PlsrPathGetCurrentSegment(const PLSR_PATH_CONTEXT *context) +{ + if (context == NULL) + { + return 0U; + } + return context->currentSegment; +} + +void PlsrPathTerminate(PLSR_PATH_CONTEXT *context) +{ + if (context == NULL) + { + return; + } + context->phase = PLSR_PATH_ENDED; + context->jobEnded = 1U; + context->jumpChainPending = 0U; + context->zeroJumpBudget = 0U; +} diff --git a/PLSR/Src/plsr_persistence.c b/PLSR/Src/plsr_persistence.c index 9e45993..42ead20 100644 --- a/PLSR/Src/plsr_persistence.c +++ b/PLSR/Src/plsr_persistence.c @@ -3,7 +3,7 @@ #include #define PLSR_HSD_BACKUP_MAGIC (0x504C4853UL) -#define PLSR_HSD_BACKUP_VERSION (1U) +#define PLSR_HSD_BACKUP_VERSION (2U) #define PLSR_BACKUP_SLOT_A_OFFSET (0x0100UL) #define PLSR_BACKUP_SLOT_STRIDE (0x0200UL) #define PLSR_SFD_FLASH_MAGIC (0x504C5346UL) @@ -16,6 +16,12 @@ #define PLSR_SFD_PARAMETER_SET_OFFSET (50U) #define PLSR_SFD_PARAMETER_SET_STRIDE (20U) #define PLSR_SFD_DEFAULT_MAX_SPEED (100000UL) +#define PLSR_SFD_DEFAULT_SPEED (1000UL) +#define PLSR_SFD_DEFAULT_ACCEL_MS (100U) +#define PLSR_SFD_DEFAULT_DECEL_MS (100U) +#define PLSR_SFD_DEFAULT_FOLLOW (50U) +#define PLSR_HSD_PARAMETER_SET_STRIDE (20U) +#define PLSR_HSD_DEFAULT_BASE (460U) typedef struct { @@ -167,6 +173,32 @@ static void PlsrPersistenceCopySfdFromVolatile( } } +static void PlsrPersistenceApplyParameterSetDefaults(uint16_t *config, + uint16_t base) +{ + /* 与信捷出厂默认一致:默认速度1000、加减速100ms、最大速度100000、 + * 起始/终止速度0、FOLLOW 50、前馈0、1ms刷新。 */ + config[base + 0U] = (uint16_t)(PLSR_SFD_DEFAULT_SPEED & 0xFFFFUL); + config[base + 1U] = (uint16_t)(PLSR_SFD_DEFAULT_SPEED >> 16U); + config[base + 2U] = (uint16_t)PLSR_SFD_DEFAULT_ACCEL_MS; + config[base + 3U] = (uint16_t)PLSR_SFD_DEFAULT_DECEL_MS; + config[base + 4U] = 0U; + config[base + 5U] = 0U; + config[base + 6U] = (uint16_t)(PLSR_SFD_DEFAULT_MAX_SPEED & 0xFFFFUL); + config[base + 7U] = (uint16_t)(PLSR_SFD_DEFAULT_MAX_SPEED >> 16U); + config[base + 8U] = 0U; + config[base + 9U] = 0U; + config[base + 10U] = 0U; + config[base + 11U] = 0U; + config[base + 12U] = (uint16_t)PLSR_SFD_DEFAULT_FOLLOW; + config[base + 13U] = 0U; + config[base + 14U] = 0U; + config[base + 16U] = 0U; + config[base + 17U] = 0U; + config[base + 18U] = 0U; + config[base + 19U] = 0U; +} + static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) { uint16_t axisOffset; @@ -179,10 +211,35 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) { axisOffset = (uint16_t)((uint16_t)axis * PLSR_SFD_AXIS_STRIDE); - /* Explicit Xinje defaults from the SFD parameter table. */ - data->config[axisOffset + 7U] = 10; - data->config[axisOffset + 27U] = 20; - data->config[axisOffset + 43U] = 0x0201; + /* 公共参数(与信捷出厂默认一致)。 */ + data->config[axisOffset + 0U] = 0; /* SFD900 方向逻辑/单位等 */ + data->config[axisOffset + 1U] = 0; /* SFD901 完成模式 */ + data->config[axisOffset + 2U] = 1; /* SFD902 脉冲数/1转=1 */ + data->config[axisOffset + 3U] = 0; + data->config[axisOffset + 4U] = 1; /* SFD904 移动量/1转=1 */ + data->config[axisOffset + 5U] = 0; + data->config[axisOffset + 6U] = 0xFF; /* SFD906 方向端子:无 */ + data->config[axisOffset + 7U] = 10; /* SFD907 方向延时10ms */ + data->config[axisOffset + 8U] = 0; /* SFD908 正向齿隙 */ + data->config[axisOffset + 9U] = 0; /* SFD909 负向齿隙 */ + data->config[axisOffset + 12U] = 0; /* SFD912 端子开关状态 */ + data->config[axisOffset + 13U] = 0xFF; /* SFD913 原点端子:无 */ + data->config[axisOffset + 14U] = 0xFF; /* SFD914 Z相端子:无 */ + data->config[axisOffset + 15U] = 0xFFFF; /* SFD915 极限端子:无 */ + data->config[axisOffset + 17U] = 0xFF; /* SFD917 CLR端子:无 */ + data->config[axisOffset + 18U] = 0; /* SFD918 VH */ + data->config[axisOffset + 19U] = 0; + data->config[axisOffset + 22U] = 0; /* SFD922 VC */ + data->config[axisOffset + 23U] = 0; + data->config[axisOffset + 24U] = 0; /* SFD924 机械原点位置 */ + data->config[axisOffset + 25U] = 0; + data->config[axisOffset + 26U] = 0; /* SFD926 Z相个数 */ + data->config[axisOffset + 27U] = 20; /* SFD927 CLR延时20ms */ + data->config[axisOffset + 30U] = 0; /* SFD930 软限位正 */ + data->config[axisOffset + 31U] = 0; + data->config[axisOffset + 32U] = 0; /* SFD932 软限位负 */ + data->config[axisOffset + 33U] = 0; + data->config[axisOffset + 43U] = 0x0201; /* SFD943 默认参数块 */ for (parameterSet = 0U; parameterSet < PLSR_SFD_PARAMETER_SET_COUNT; @@ -191,14 +248,26 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) parameterOffset = (uint16_t)( axisOffset + PLSR_SFD_PARAMETER_SET_OFFSET + (uint16_t)parameterSet * PLSR_SFD_PARAMETER_SET_STRIDE); - data->config[parameterOffset + 6U] = - (int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED & 0xFFFFUL); - data->config[parameterOffset + 7U] = - (int32_t)(PLSR_SFD_DEFAULT_MAX_SPEED >> 16U); + PlsrPersistenceApplyParameterSetDefaults(data->config, + parameterOffset); } } } +static void PlsrPersistenceApplyHsdDefaults(PLSR_HSD_DATA *data) +{ + uint8_t axis; + + (void)memset(data, 0, sizeof(*data)); + for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) + { + /* config 数组索引 = HSD地址 - PLSR_HSD_CONFIG_START。 */ + PlsrPersistenceApplyParameterSetDefaults( + data->config, + (uint16_t)((uint16_t)axis * PLSR_HSD_PARAMETER_SET_STRIDE)); + } +} + static PLSR_PERSISTENCE_RESULT PlsrPersistenceBeginSfdOperation(void) { #ifdef PLSR_HOST_TEST @@ -324,6 +393,7 @@ static void PlsrPersistenceCopyHsdFromVolatile( { uint16_t index; + destination->metadata = source->metadata; for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++) { destination->runtime[index] = source->runtime[index]; @@ -385,7 +455,7 @@ PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data) if ((validA == 0U) && (validB == 0U)) { - (void)memset(data, 0, sizeof(*data)); + PlsrPersistenceApplyHsdDefaults(data); return PLSR_PERSISTENCE_DEFAULTED; } diff --git a/PLSR/Test/run_host_tests.ps1 b/PLSR/Test/run_host_tests.ps1 index f57beda..b550a9d 100644 --- a/PLSR/Test/run_host_tests.ps1 +++ b/PLSR/Test/run_host_tests.ps1 @@ -29,6 +29,7 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_persistence.c" "$workspacePath\PLSR\Src\plsr_resource.c" "$workspacePath\PLSR\Src\plsr_job.c" + "$workspacePath\PLSR\Src\plsr_path.c" "$workspacePath\PLSR\Src\plsr_core.c" "$workspacePath\PLSR\Test\test_plsr_core.c" ) @@ -40,9 +41,22 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_persistence.c" "$workspacePath\PLSR\Src\plsr_resource.c" "$workspacePath\PLSR\Src\plsr_job.c" + "$workspacePath\PLSR\Src\plsr_path.c" "$workspacePath\PLSR\Src\plsr_core.c" "$workspacePath\PLSR\Test\test_plsr_job.c" ) + }, + @{ + Name = 'test_plsr_path' + Sources = @( + "$workspacePath\PLSR\Src\plc_device.c" + "$workspacePath\PLSR\Src\plsr_persistence.c" + "$workspacePath\PLSR\Src\plsr_resource.c" + "$workspacePath\PLSR\Src\plsr_job.c" + "$workspacePath\PLSR\Src\plsr_path.c" + "$workspacePath\PLSR\Src\plsr_core.c" + "$workspacePath\PLSR\Test\test_plsr_path.c" + ) } ) diff --git a/PLSR/Test/test_plc_device.c b/PLSR/Test/test_plc_device.c index 7ee7adc..83c443c 100644 --- a/PLSR/Test/test_plc_device.c +++ b/PLSR/Test/test_plc_device.c @@ -40,10 +40,10 @@ static void TestAddressMap(void) static void TestHsdAndPersistence(void) { + uint16_t value16; int32_t value32; - int64_t value64; - const int64_t firstPair = INT64_C(0x1234567887654321); - const int64_t secondPair = -INT64_C(0x102030405060708); + const int32_t firstDword = INT32_C(0x12345678); + const int32_t secondDword = -INT32_C(0x1020304); PlsrPersistenceTestResetStorage(); TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); @@ -51,38 +51,95 @@ static void TestHsdAndPersistence(void) TEST_CHECK(PlcDeviceIsHsdDirty() != 0U); TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 11) == PLC_DEVICE_OK); - TEST_CHECK(PlcDeviceWriteHsdConfig(539U, -22) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteHsdConfig(539U, 0xB4E2U) == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceWriteHsdConfig(0U, 1) == PLC_DEVICE_READ_ONLY); TEST_CHECK(PlcDeviceWriteHsdConfig(459U, 1) == PLC_DEVICE_INVALID_ADDRESS); - TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK); - TEST_CHECK(value32 == 11); - TEST_CHECK(PlcDeviceReadHsd(539U, &value32) == PLC_DEVICE_OK); - TEST_CHECK(value32 == -22); - TEST_CHECK(PlcDeviceReadHsd(540U, &value32) == PLC_DEVICE_INVALID_ADDRESS); - - TEST_CHECK(PlcDevicePublishHsdPair(0U, firstPair) == PLC_DEVICE_OK); - TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK); - TEST_CHECK(value64 == firstPair); - TEST_CHECK(PlcDeviceReadHsdPair(1U, &value64) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceReadHsd(460U, &value16) == PLC_DEVICE_OK); + TEST_CHECK(value16 == 11); + TEST_CHECK(PlcDeviceReadHsd(539U, &value16) == PLC_DEVICE_OK); + TEST_CHECK(value16 == 0xB4E2U); + TEST_CHECK(PlcDeviceReadHsd(540U, &value16) == PLC_DEVICE_INVALID_ADDRESS); + + /* 32 位兼容布局:低地址=低16位,高地址=高16位。 */ + TEST_CHECK(PlcDevicePublishHsdDword(0U, firstDword) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadHsd(0U, &value16) == PLC_DEVICE_OK); + TEST_CHECK(value16 == 0x5678U); + TEST_CHECK(PlcDeviceReadHsd(1U, &value16) == PLC_DEVICE_OK); + TEST_CHECK(value16 == 0x1234U); + TEST_CHECK(PlcDeviceReadHsdDword(0U, &value32) == PLC_DEVICE_OK); + TEST_CHECK(value32 == firstDword); + TEST_CHECK(PlcDeviceReadHsdDword(1U, &value32) + == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceReadHsdDword(15U, &value32) + == PLC_DEVICE_INVALID_ADDRESS); TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceIsHsdDirty() == 0U); TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 22) == PLC_DEVICE_OK); - TEST_CHECK(PlcDevicePublishHsdPair(0U, secondPair) == PLC_DEVICE_OK); + TEST_CHECK(PlcDevicePublishHsdDword(0U, secondDword) == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); PlsrPersistenceTestCorruptNewestHsd(); TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK); - TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK); - TEST_CHECK(value32 == 11); - TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK); - TEST_CHECK(value64 == firstPair); + TEST_CHECK(PlcDeviceReadHsd(460U, &value16) == PLC_DEVICE_OK); + TEST_CHECK(value16 == 11); + TEST_CHECK(PlcDeviceReadHsdDword(0U, &value32) == PLC_DEVICE_OK); + TEST_CHECK(value32 == firstDword); +} + +static void TestHsdDefaults(void) +{ + uint16_t value; + + PlsrPersistenceTestResetStorage(); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_DEFAULTED); + + /* K0(HSD460~539)出厂默认即可直接启动。 */ + TEST_CHECK(PlcDeviceReadHsd(460U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 1000); /* 默认速度低16位 */ + TEST_CHECK(PlcDeviceReadHsd(461U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 0); /* 默认速度高16位 */ + TEST_CHECK(PlcDeviceReadHsd(462U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 100); /* 加速时间 ms */ + TEST_CHECK(PlcDeviceReadHsd(463U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 100); /* 减速时间 ms */ + TEST_CHECK(PlcDeviceReadHsd(466U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 0x86A0U); /* 最大速度低16位 100000 */ + TEST_CHECK(PlcDeviceReadHsd(467U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 1); /* 最大速度高16位 */ + TEST_CHECK(PlcDeviceReadHsd(472U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 50); /* FOLLOW 1~100 */ + TEST_CHECK(PlcDeviceReadHsd(539U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 0); +} + +static void TestHsdMetaPersistence(void) +{ + PlsrPersistenceTestResetStorage(); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 0U); + TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 0U); + + /* 正常停机保存:positionValid=1, lastBusy=0。 */ + TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 0U) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 1U); + TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 0U); + + /* 运动中掉电保存:lastBusy=1,恢复后位置不可信。 */ + TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 1U) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetRestoredHsdPositionValid() == 1U); + TEST_CHECK(PlcDeviceGetRestoredHsdLastBusy() == 1U); } static void TestSfd(void) { - int32_t value; + uint16_t value; PlsrPersistenceTestResetStorage(); TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); @@ -99,6 +156,15 @@ static void TestSfd(void) TEST_CHECK(value == 0x86A0); TEST_CHECK(PlcDeviceReadSfd(957U, &value) == PLC_DEVICE_OK); TEST_CHECK(value == 1); + /* 第1套参数出厂默认可直接启动。 */ + TEST_CHECK(PlcDeviceReadSfd(950U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 1000); + TEST_CHECK(PlcDeviceReadSfd(951U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 0); + TEST_CHECK(PlcDeviceReadSfd(952U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 100); + TEST_CHECK(PlcDeviceReadSfd(962U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 50); TEST_CHECK(PlcDeviceReadSfd(1297U, &value) == PLC_DEVICE_OK); TEST_CHECK(value == 10); TEST_CHECK(PlsrPersistenceLoadSfd(NULL) @@ -107,11 +173,11 @@ static void TestSfd(void) == PLSR_PERSISTENCE_INVALID_ARGUMENT); TEST_CHECK(PlcDeviceWriteSfd(900U, 123) == PLC_DEVICE_OK); - TEST_CHECK(PlcDeviceWriteSfd(1419U, -456) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteSfd(1419U, 0xB456U) == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); TEST_CHECK(value == 123); TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); - TEST_CHECK(value == -456); + TEST_CHECK(value == 0xB456U); TEST_CHECK(PlcDeviceReadSfd(899U, &value) == PLC_DEVICE_INVALID_ADDRESS); TEST_CHECK(PlcDeviceReadSfd(1420U, &value) == PLC_DEVICE_INVALID_ADDRESS); TEST_CHECK(PlcDeviceIsSfdDirty() != 0U); @@ -123,17 +189,17 @@ static void TestSfd(void) TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); TEST_CHECK(value == 123); TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); - TEST_CHECK(value == -456); + TEST_CHECK(value == 0xB456U); TEST_CHECK(PlcDeviceWriteSfd(900U, 789) == PLC_DEVICE_OK); - TEST_CHECK(PlcDeviceWriteSfd(1419U, -987) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteSfd(1419U, 0x9876U) == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceSaveSfd() == PLC_DEVICE_OK); PlsrPersistenceTestCorruptNewestSfd(); TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); TEST_CHECK(value == 123); TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); - TEST_CHECK(value == -456); + TEST_CHECK(value == 0xB456U); TEST_CHECK(PlcDeviceWriteSfd(900U, 1001) == PLC_DEVICE_OK); PlsrPersistenceTestSetSfdFault(PLSR_TEST_SFD_FAULT_ERASE); @@ -215,6 +281,8 @@ int main(void) { TestAddressMap(); TestHsdAndPersistence(); + TestHsdDefaults(); + TestHsdMetaPersistence(); TestSfd(); TestSmAndSd(); diff --git a/PLSR/Test/test_plsr_core.c b/PLSR/Test/test_plsr_core.c index dc7f8a5..a2084d0 100644 --- a/PLSR/Test/test_plsr_core.c +++ b/PLSR/Test/test_plsr_core.c @@ -344,12 +344,60 @@ static void TestQueueAndInputValidation(void) TEST_CHECK(TestGetStatus(0U).lastCommandResult == PLSR_RESULT_OK); } +static void TestPositionCheckpointing(void) +{ + PLSR_STATUS status; + int32_t hsdPosition; + + /* 1. SET_POSITION 后立即写入 HSD 检查点。 */ + TestReset(); + TEST_CHECK(TestPostCommand(1U, 0U, PLSR_CMD_SET_POSITION, 500) + == PLSR_RESULT_QUEUED); + PlsrProcess(); + status = TestGetStatus(0U); + TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK); + TEST_CHECK(status.positionValid != 0U); + TEST_CHECK(PlcDeviceIsHsdDirty() == 0U); + TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK); + TEST_CHECK(hsdPosition == 500); + + /* 2. 模拟重启:位置与 positionValid 应恢复到上次正常停机的检查点。 */ + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlsrInit() == PLSR_RESULT_OK); + status = TestGetStatus(0U); + TEST_CHECK(status.positionValid != 0U); + TEST_CHECK(status.logicalPosition == 500); + + /* 3. 运动中掉电(lastBusy=1):恢复后位置不可信。 */ + TEST_CHECK(PlcDeviceSetHsdCheckpointMeta(1U, 1U) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlsrInit() == PLSR_RESULT_OK); + status = TestGetStatus(0U); + TEST_CHECK(status.positionValid == 0U); + TEST_CHECK(status.logicalPosition == 500); + + /* 4. 64位位置超出 INT32 范围:对外回绕并置溢出诊断。 */ + TEST_CHECK(TestPostCommand(2U, + 0U, + PLSR_CMD_SET_POSITION, + INT64_C(0x100000000)) + == PLSR_RESULT_QUEUED); + PlsrProcess(); + status = TestGetStatus(0U); + TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK); + TEST_CHECK(status.positionOverflow != 0U); + TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK); + TEST_CHECK(hsdPosition == 0); +} + int main(void) { TestResourceManager(); TestStateMachineAndCommands(); TestLimitWaitAndPairs(); TestQueueAndInputValidation(); + TestPositionCheckpointing(); (void)printf("PASS: %u PLSR core checks\n", TestCount); return EXIT_SUCCESS; diff --git a/PLSR/Test/test_plsr_job.c b/PLSR/Test/test_plsr_job.c index ef0c5f7..545f80b 100644 --- a/PLSR/Test/test_plsr_job.c +++ b/PLSR/Test/test_plsr_job.c @@ -387,6 +387,53 @@ static void TestCoreSubmission(void) CHECK(detail.result == PLSR_RESULT_OK); } +static void TestDefaultSfdStartable(void) +{ + TEST_MEMORY memory; + PLSR_CALL call; + PLSR_STATUS status; + + /* 全新 Flash:默认 S2 参数(FOLLOW=50、最大速度=100000 等)应可直接启动。 */ + PlsrPersistenceTestResetStorage(); + CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + CHECK(PlcDeviceGetLastSfdLoadResult() == PLSR_PERSISTENCE_DEFAULTED); + + TestBuildValidBlocks(&memory); + call = TestMakeCall(&memory); + call.sequence = 600UL; + + /* 方向端子是接线参数,未配置(0xFF)时 PULSE/DIR 应报资源错误。 */ + CHECK(PlsrInit() == PLSR_RESULT_OK); + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + PlsrProcess(); + CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK); + CHECK(status.lastCommandResult == PLSR_RESULT_INVALID_RESOURCE); + CHECK(status.state == PLSR_STATE_IDLE); + + /* 用户配置方向端子后,其余出厂默认参数应能直接启动。 */ + CHECK(PlcDeviceWriteSfd(906U, 4) == PLC_DEVICE_OK); + call.sequence = 601UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + PlsrProcess(); + CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK); + CHECK(status.lastCommandResult == PLSR_RESULT_OK); + CHECK(status.state == PLSR_STATE_ACCEL); + CHECK(status.jobValid == 1U); + CHECK(status.s2Set == 1U); + CHECK(status.speedClamped == 1U); + CHECK(status.directionPoint == 4U); + + /* 回到 IDLE,避免资源泄漏影响后续。 */ + CHECK(PlsrPostCommand(&(PLSR_COMMAND){602U, 0U, PLSR_CMD_STOP_IMMEDIATE, 0}) + == PLSR_RESULT_QUEUED); + PlsrProcess(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE) + == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK); + CHECK(status.state == PLSR_STATE_STOPPED); +} + int main(void) { PlsrPersistenceTestResetStorage(); @@ -396,6 +443,7 @@ int main(void) TestValidationFailures(); TestDynamicReferences(); TestOutputAndDivider(); + TestDefaultSfdStartable(); TestCoreSubmission(); if (TestFailures != 0) diff --git a/PLSR/Test/test_plsr_path.c b/PLSR/Test/test_plsr_path.c new file mode 100644 index 0000000..7b6d1b9 --- /dev/null +++ b/PLSR/Test/test_plsr_path.c @@ -0,0 +1,682 @@ +#include "plc_device.h" +#include "plsr_core.h" +#include "plsr_job.h" +#include "plsr_path.h" +#include "plsr_persistence.h" +#include +#include + +#define TEST_WORD_CAPACITY (3000U) +#define TEST_BIT_CAPACITY (128U) + +#define TEST_S0_BASE (100U) +#define TEST_S1_BASE (200U) +#define TEST_SEGMENT_STRIDE (10U) + +/* 等待条件编码(信捷):高8位条件、低8位来源。 */ +#define TEST_WAIT_CODE(condition, source) \ + ((uint16_t)(((uint16_t)(condition) << 8U) | (uint16_t)(source))) + +typedef struct +{ + uint16_t words[3][TEST_WORD_CAPACITY]; + uint8_t bits[3][TEST_BIT_CAPACITY]; +} TEST_MEMORY; + +static int TestFailures; +static int TestChecks; + +#define CHECK(condition) \ + do \ + { \ + TestChecks++; \ + if (!(condition)) \ + { \ + TestFailures++; \ + (void)printf("FAIL line %d: %s\n", __LINE__, #condition); \ + } \ + } while (0) + +static uint8_t TestValidateWords(void *context, + PLSR_DEVICE_TYPE device, + uint32_t firstAddress, + uint32_t wordCount) +{ + (void)context; + if ((device > PLSR_DEVICE_FD) || (wordCount == 0UL)) + { + return 0U; + } + return (((uint64_t)firstAddress + wordCount) <= TEST_WORD_CAPACITY) + ? 1U + : 0U; +} + +static uint8_t TestReadWord(void *context, + PLSR_DEVICE_TYPE device, + uint32_t address, + uint16_t *value) +{ + TEST_MEMORY *memory = (TEST_MEMORY *)context; + + if ((memory == NULL) || (value == NULL) || (device > PLSR_DEVICE_FD) + || (address >= TEST_WORD_CAPACITY)) + { + return 0U; + } + *value = memory->words[device][address]; + return 1U; +} + +static uint8_t TestReadBit(void *context, + PLSR_DEVICE_TYPE device, + uint32_t address, + uint8_t *value) +{ + TEST_MEMORY *memory = (TEST_MEMORY *)context; + uint8_t index; + + if ((memory == NULL) || (value == NULL) || (device < PLSR_DEVICE_X) + || (device > PLSR_DEVICE_HM) || (address >= TEST_BIT_CAPACITY)) + { + return 0U; + } + index = (uint8_t)(device - PLSR_DEVICE_X); + *value = memory->bits[index][address]; + return 1U; +} + +static void TestWriteDword(TEST_MEMORY *memory, + PLSR_DEVICE_TYPE device, + uint32_t address, + int32_t value) +{ + uint32_t raw = (uint32_t)value; + + memory->words[device][address] = (uint16_t)(raw & 0xFFFFUL); + memory->words[device][address + 1UL] = (uint16_t)(raw >> 16U); +} + +/* 写入一个段的数据块(S0 布局,每段 10 字)。 */ +static void TestSetSegment(TEST_MEMORY *memory, + uint16_t number, + uint32_t frequency, + int32_t pulses, + uint16_t waitCode, + int32_t waitValue, + uint16_t jumpSource, + int32_t jumpValue) +{ + uint32_t base = TEST_S0_BASE + (uint32_t)number * TEST_SEGMENT_STRIDE; + + TestWriteDword(memory, PLSR_DEVICE_D, base, (int32_t)frequency); + TestWriteDword(memory, PLSR_DEVICE_D, base + 2UL, pulses); + memory->words[PLSR_DEVICE_D][base + 4UL] = waitCode; + TestWriteDword(memory, PLSR_DEVICE_D, base + 5UL, waitValue); + memory->words[PLSR_DEVICE_D][base + 7UL] = (uint16_t)jumpSource; + TestWriteDword(memory, PLSR_DEVICE_D, base + 8UL, jumpValue); +} + +static void TestResetEnvironment(void) +{ + PlsrPersistenceTestResetStorage(); + CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + /* 默认 SFD + 配置方向端子(接线参数,测试用 Y4)。 */ + CHECK(PlcDeviceWriteSfd(906U, 4) == PLC_DEVICE_OK); + CHECK(PlsrInit() == PLSR_RESULT_OK); +} + +static PLSR_CALL TestMakeCall(TEST_MEMORY *memory) +{ + PLSR_CALL call; + + (void)memset(&call, 0, sizeof(call)); + call.sequence = 10UL; + call.source.context = memory; + call.source.validateWords = TestValidateWords; + call.source.readWord = TestReadWord; + call.source.readBit = TestReadBit; + call.s0.device = PLSR_DEVICE_D; + call.s0.address = TEST_S0_BASE; + call.s1.device = PLSR_DEVICE_D; + call.s1.address = TEST_S1_BASE; + call.s2.type = PLSR_OPERAND_CONSTANT; + call.s2.constant = 1; + call.dAxis = 0U; + call.outputModeOverride = PLSR_OUTPUT_MODE_FROM_SFD; + return call; +} + +static PLSR_STATUS TestGetStatus(void) +{ + PLSR_STATUS status; + + (void)memset(&status, 0, sizeof(status)); + CHECK(PlsrGetStatus(0U, &status) == PLSR_RESULT_OK); + return status; +} + +/* 当前段加速完成进入 RUN。 */ +static void TestAccelToRun(void) +{ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_ACCEL_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_RUN); +} + +/* 启动一个 N 段任务(全部 H00 完成条件、顺序跳转)。 */ +static void TestStartSequentialJob(TEST_MEMORY *memory, + uint16_t segmentCount, + uint32_t sequence) +{ + uint16_t segment; + + (void)memset(memory, 0, sizeof(*memory)); + TestWriteDword(memory, PLSR_DEVICE_D, TEST_S0_BASE, segmentCount); + for (segment = 1U; segment <= segmentCount; segment++) + { + TestSetSegment(memory, + segment, + 1000U + (uint32_t)segment * 1000U, + (int32_t)(segment * 100), + TEST_WAIT_CODE(0U, 0U), + 0, + 0U, + 0); + } + { + PLSR_CALL call = TestMakeCall(memory); + + call.sequence = sequence; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + CHECK(TestGetStatus().lastCommandResult == PLSR_RESULT_OK); +} + +static void TestSequentialSegments(void) +{ + TEST_MEMORY memory; + + TestResetEnvironment(); + TestStartSequentialJob(&memory, 3U, 10UL); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 1U); + + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); + + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 3U); + + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED); + CHECK(TestGetStatus().done != 0U); +} + +static void TestJumpToSpecifiedSegment(void) +{ + TEST_MEMORY memory; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3); + TestSetSegment(&memory, 1U, 1000U, 100, TEST_WAIT_CODE(0U, 0U), 0, 0U, 3); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + TestSetSegment(&memory, 3U, 3000U, 300, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 20UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + CHECK(TestGetStatus().currentSegment == 1U); + + /* 段1 完成 → 跳到段3(跳过段2)。 */ + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 3U); + + /* 段3 完成 → 顺序下一段超出 → 任务结束。 */ + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED); +} + +static void TestWaitTime(void) +{ + TEST_MEMORY memory; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(1U, 0U), 200, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 30UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段1 完成 → 进入 WAIT(等 200ms)。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + + /* 事件处理同轮已递减 1ms:198 个 tick 后仍在等待。 */ + for (ticks = 0; ticks < 198; ticks++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + CHECK(TestGetStatus().currentSegment == 1U); + + /* 第 200 个 tick:计时到,跳到段2。 */ + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestWaitSignal(void) +{ + TEST_MEMORY memory; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(2U, 5U), 10, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 40UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段1 完成 → 等 M10(当前 OFF)。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + + /* 信号仍 OFF:不跳转。 */ + PlsrProcess(); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + + /* 信号置 ON:tick 轮询到后跳段。 */ + memory.bits[1U][10U] = 1U; /* M10 */ + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestActTruncatesSegment(void) +{ + TEST_MEMORY memory; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(3U, 0U), 200, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 50UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 不注入 SEGMENT_COMPLETE:ACT 200ms 到时截断当前段。 */ + for (ticks = 0; ticks < 200; ticks++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestActPendingAfterSegmentDone(void) +{ + TEST_MEMORY memory; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(3U, 0U), 200, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 60UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段脉冲先完成(ACT 还剩 199ms)→ 停在 ACT_PENDING(WAIT 状态)。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + + /* ACT 到时 → 跳段2。 */ + for (ticks = 0; ticks < 199; ticks++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestExtEdgeTruncates(void) +{ + TEST_MEMORY memory; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(4U, 4U), 0, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + /* X0 在任务开始前就有效:进入段时记录为已有电平,不算新边沿。 */ + memory.bits[0U][0U] = 1U; + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 70UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* X0 持续 ON:没有新边沿,不应触发。 */ + for (ticks = 0; ticks < 5; ticks++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_RUN); + CHECK(TestGetStatus().currentSegment == 1U); + + /* 先拉低再拉高:新上升沿,截断当前段。 */ + memory.bits[0U][0U] = 0U; + PlsrProcess(); + memory.bits[0U][0U] = 1U; + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestExtOrComplete(void) +{ + TEST_MEMORY memory; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(5U, 4U), 0, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 80UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段脉冲先完成 → 正常跳段(EXT 或完成)。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestDynamicJump(void) +{ + TEST_MEMORY memory; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 3); + TestSetSegment(&memory, 2U, 2000U, 200, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + /* 段3 跳转也来自 D300(初始 3 = 自跳转,运行期改非法值验证越界)。 */ + TestSetSegment(&memory, 3U, 3000U, 300, + TEST_WAIT_CODE(0U, 0U), 0, 1U, 300); + TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 3); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 90UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段1 完成:跳到段3。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().currentSegment == 3U); + + /* 段3 完成前把 D300 改成非法值 → 运行期错误 → ERROR。 */ + TestAccelToRun(); + TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 99); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ERROR); +} + +static void TestAbsoluteZeroDisplacement(void) +{ + TEST_MEMORY memory; + PLSR_COMMAND command; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + /* S1+0 = 1:绝对模式。 */ + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S1_BASE, 1); + /* 段1 目标 0(=当前位置 0,零位移);段2 目标 5000。 */ + TestSetSegment(&memory, 1U, 1000U, 0, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + TestSetSegment(&memory, 2U, 2000U, 5000, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + + /* 绝对定位需要 positionValid:先 SET_POSITION(0)。 */ + command.sequence = 91UL; + command.axis = 0U; + command.opcode = PLSR_CMD_SET_POSITION; + command.argument = 0; + CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED); + PlsrProcess(); + + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 92UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + /* 起点段1 零位移:直接跳过,从段2 开始发脉冲。 */ + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); +} + +static void TestZeroJumpBudgetYields(void) +{ + TEST_MEMORY memory; + int rounds; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 3); + /* 段1:零脉冲,跳到段2;段2:零脉冲,动态跳转(初始自跳转);段3:正常段。 */ + TestSetSegment(&memory, 1U, 1000U, 0, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 2); + TestSetSegment(&memory, 2U, 1000U, 0, + TEST_WAIT_CODE(0U, 0U), 0, 1U, 300); + TestSetSegment(&memory, 3U, 1000U, 100, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 2); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 100UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + + /* 每轮 100 次预算:零脉冲自循环链停在段2 并让出,任务不结束。 */ + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); + CHECK(TestGetStatus().done == 0U); + for (rounds = 0; rounds < 3; rounds++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 2U); + CHECK(TestGetStatus().done == 0U); + + /* 运行中改动态跳转目标 → 链走向段3(有脉冲)→ 正常执行并完成。 */ + TestWriteDword(&memory, PLSR_DEVICE_D, 300U, 3); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 3U); + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED); +} + +static void TestSelfLoopInterruptedByStop(void) +{ + TEST_MEMORY memory; + PLSR_COMMAND command; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1); + TestSetSegment(&memory, 1U, 1000U, 100, + TEST_WAIT_CODE(0U, 0U), 0, 0U, 1); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 110UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + + /* 段1 完成 → 自跳转回段1。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_ACCEL); + CHECK(TestGetStatus().currentSegment == 1U); + + /* STOP 可打断自循环。 */ + command.sequence = 111UL; + command.axis = 0U; + command.opcode = PLSR_CMD_STOP_IMMEDIATE; + command.argument = 0; + CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED); + PlsrProcess(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE) + == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_STOPPED); + CHECK(TestGetStatus().done == 0U); +} + +static void TestLastSegmentWaitTime(void) +{ + TEST_MEMORY memory; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100, TEST_WAIT_CODE(0U, 0U), 0, 0U, 0); + /* 最后一段用 WAIT 时间(信捷警告场景:验证不会卡死)。 */ + TestSetSegment(&memory, 2U, 2000U, 200, + TEST_WAIT_CODE(1U, 0U), 100, 0U, 0); + { + PLSR_CALL call = TestMakeCall(&memory); + + call.sequence = 120UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + } + PlsrProcess(); + TestAccelToRun(); + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + TestAccelToRun(); + + /* 段2(最后一段)完成 → 等待 100ms。 */ + CHECK(PlsrPostEvent(0U, PLSR_EVENT_SEGMENT_COMPLETE) == PLSR_RESULT_OK); + PlsrProcess(); + CHECK(TestGetStatus().state == PLSR_STATE_WAIT); + + /* 计时到 → 任务正常结束。 */ + for (ticks = 0; ticks < 100; ticks++) + { + PlsrProcess(); + } + CHECK(TestGetStatus().state == PLSR_STATE_COMPLETED); + CHECK(TestGetStatus().done != 0U); +} + +int main(void) +{ + TestSequentialSegments(); + TestJumpToSpecifiedSegment(); + TestWaitTime(); + TestWaitSignal(); + TestActTruncatesSegment(); + TestActPendingAfterSegmentDone(); + TestExtEdgeTruncates(); + TestExtOrComplete(); + TestDynamicJump(); + TestAbsoluteZeroDisplacement(); + TestZeroJumpBudgetYields(); + TestSelfLoopInterruptedByStop(); + TestLastSegmentWaitTime(); + + if (TestFailures != 0) + { + (void)printf("FAIL: %d of %d PLSR path checks failed\n", + TestFailures, + TestChecks); + return 1; + } + (void)printf("PASS: %d PLSR path checks\n", TestChecks); + return 0; +} diff --git a/tmp/manual_p11_97.txt b/tmp/manual_p11_97.txt new file mode 100644 index 0000000..3f08aa2 --- /dev/null +++ b/tmp/manual_p11_97.txt @@ -0,0 +1,3413 @@ +===== 手册第11页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 1 + +1. 脉冲输出 +本章主要介绍 XD/XL 系列可编程控制器的脉冲定位功能,内容包括脉冲输出指令用法及编程应用、 +输出端子的接线、注意事项以及相关线圈和寄存器等。 + + + +1. 脉冲输出 .................................................................................. 1 +1-1.功能概述 .............................................................................. 2 +1-2. 脉冲输出的种类与指令应用 ............................................................... 5 +1-2-1.脉冲参数介绍及配置方法 ............................................................ 5 +1-2-2.多段脉冲输出 [PLSR] .............................................................. 49 +1-2-3.可变频率脉冲输出[PLSF] ........................................................... 88 +1-2-4.相对单段定位[DRVI] .............................................................. 101 +1-2-5.绝对单段定位[DRVA] .............................................................. 107 +1-2-6.机械归零[ZRN] ................................................................... 113 +1-2-7.脉冲停止[STOP] .................................................................. 141 +1-2-8.脉冲继续[GOON] .................................................................. 142 +1-3.脉冲参数配置向导 .................................................................... 144 +1-3-1.脉冲参数配置向导打开方式 ........................................................ 144 +1-3-2.脉冲参数配置向导使用说明 ........................................................ 145 +1-4.输出端子接线及注意事项 .............................................................. 152 +1-4-1.连接设备的构成 .................................................................. 153 +1-4-2.脉冲输出性能规格 ................................................................ 154 +1-4-3.定位控制接线以及布线注意事项 .................................................... 154 +1-4-4.伺服放大器(驱动单元)侧的设定 .................................................. 156 +1-4-5.定位指令脉冲发送完成标志位的使用及注意事项 ...................................... 159 +1-4-6.定位指令触发条件注意事项 ........................................................ 160 +1-4-7.定位指令与系统参数块相关参数 .................................................... 160 +1-4-8.伺服电机、步进电机不动作故障排查 ................................................ 161 +1-4-9.伺服电机、步进电机停止位置不正确故障排查 ........................................ 163 +1-5.定位指令案例程序 .................................................................... 164 +1-5-1.I/O点分配 ....................................................................... 164 +1-5-2.正反转顺控样例程序【PLSF、DRVI、DRVA、ZRN】 ..................................... 164 +1-5-3.正反转流程样例程序【PLSF、DRVI、DRVA、ZRN】 ..................................... 172 +1-5-4.正反转多段顺控样例程序【PLSF、PLSR、ZRN】 ....................................... 182 +1-5-5.正反转多段流程样例程序【PLSF、PLSR、ZRN】 ....................................... 193 +1-5-6.正反转多段顺控样例程序【DRVI、DRVA、PLSR、ZRN】.................................. 207 +1-5-7.正反转多段流程样例程序【DRVI、DRVA、PLSR、ZRN】.................................. 218 +1-6.脉冲输出相关线圈与寄存器 ............................................................ 232 +1-6-1. 脉冲输出相关标志位 .............................................................. 232 +1-6-2. 脉冲输出相关特殊寄存器 .......................................................... 233 +1-6-3. 高速脉冲特殊数据寄存器HSD(掉电记忆) ........................................... 236 +===== 手册第12页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +2 + + +脉冲输出相关指令一览 +指令助记符 功能 回路表示及可用软元件 章节 +脉冲输出 +PLSR 多段脉冲输出 +PLSR S0 S1 S2 D 1-2-2 +PLSF 可变频率脉冲输出 +PLSF S0 S1 D 1-2-3 +DRVI 相对单段定位 +DRVI S0 S1 S2 D1 D2 1-2-4 +DRV A 绝对单段定位 +DRVA S0 S1 S2 D1 D2 1-2-5 +ZRN 机械归零 +ZRN S0 D 1-2-6 +STOP 脉冲停止 +STOP S0 S1 1-2-7 +GOON 脉冲继续发送 +GOON Yn 1-2-8 + +1-1.功能概述 +XD 系列(XD1及部分机型除外) 、XL 系列(XL1及部分机型除外)PLC一般具有 2 路脉冲输出;特 +殊 PLC具有 4~10路脉冲输出。通过使用不同的指令编程方式,可以进行无加速/减速的单向脉冲输出,也 +可以进行带加速/减速的单向脉冲输出,还可以进行多段、正反向输出等等,输出频率最高可达 100KHz +(XD3-24T4/32T4的 Y2、Y3为 10KHz)。 +注意:在 XD/XL系列 PLC里,因前置条件为边沿触发,故不存在脉冲双线圈问题。 +脉冲输出端口配置如下表: +PLC 型号 脉冲 +路数 脉冲输出端口 最高输出 +频率 +输出 +方式 输出形式 +XD2-16/24/32/42/48/60T/RT 2 轴 Y0、Y1 全部 +0~100KHz +OC 脉冲+方向 +XD3-16/24/32/42/48/60T/RT 2 轴 Y0、Y1 +XD3-24T4/32T4 4 轴 Y0、Y1 +Y2、Y3 10KHz +XD5-16/24/32/42/48/60/80T/RT 2 轴 Y0、Y1 全部 +0~100KHz XD5-24/32/48/60 T4 4 轴 Y0、Y1、Y2、Y3 +XD5-48/60 T6 6 轴 Y0、Y1、Y2、Y3、Y4、Y5 +XD5-24D2T2 4 轴 +Y0、Y2 0~920KHz 差分 脉冲+方向 +Y0/Y1、Y2/Y3 AB相 +Y4、Y6 0~100KHz OC 脉冲+方向 +XD5-48D4T4 8 轴 +Y0、Y2、Y4、Y6 0~920KHz 差分 脉冲+方向 +Y0/Y1、Y2/Y3、Y4/Y5、Y6/Y7 AB相 +Y10、Y12、Y14、Y16 全部 +0~100KHz OC 脉冲+方向 XD5-60T10 10 Y0、Y1、Y2、Y3、Y4、Y5、 +===== 手册第13页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 3 + +PLC 型号 脉冲 +路数 脉冲输出端口 最高输出 +频率 +输出 +方式 输出形式 +轴 Y6、Y7、Y10、Y11 +XDM-24/32/60 T4 +XDM-60T4L 4 轴 Y0、Y1、Y2、Y3 +XDM-60T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XDC-24/32/48/60T 2 轴 Y0、Y1 +XD3E-24/30/48/60T 2 轴 Y0、Y1 +XD3E-30/48/60NPT4 4 轴 Y0、Y1、Y2、Y3 +XD5E-24/30/48/60T 2 轴 Y0、Y1 +XD5E-30/60T4 4 轴 Y0、Y1、Y2、Y3 +全部 +0~100KHz OC 脉冲+方向 +XD5E-60T6 6 轴 Y0、Y1、Y2、Y3、Y4、Y5 +XD5E-60T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XDME-30T4 4 轴 Y0、Y1、Y2、Y3 +XDME-60T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XDME-60T4 4 轴 Y0、Y1、Y2、Y3 +XDH-24A8(L)/30A16(L) +XDH-60T4/60A32/60A64 4 轴 Y0、Y1、Y2、Y3 +XL3-16/32 T 2 轴 Y0、Y1 +XL3-16T4 4 轴 Y0、Y1、Y2、Y3 +XL5-16/32 T 2 轴 Y0、Y1 +XL5-16T4/32T4 4 轴 Y0、Y1、Y2、Y3 +XL5-64T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XL3E-16/32T 2 轴 Y0、Y1 +XL5N-32T 2 轴 Y0、Y1 +XL5E-16/32 T 2 轴 Y0、Y1 +XL5E-32T4 4 轴 Y0、Y1、Y2、Y3 +XL5E-64T6 6 轴 Y0、Y1、Y2、Y3、Y4、Y5 +XL5E-64T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XLME-16T4/32T4 4 轴 Y0、Y1、Y2、Y3 +XLME-64T10 10 +轴 +Y0、Y1、Y2、Y3、Y4、Y5、 +Y6、Y7、Y10、Y11 +XLH-24A16(L)/30A32 4 轴 Y0、Y1、Y2、Y3 +XL5H-24A8L 2 轴 Y0、Y1 +【注】 : +※1:PLC可输出 100KHz~200KHz的脉冲(XD3-24T4/32T4的 Y2、Y3 为 10KHz),但无法保证所有伺服 +都正常运行,请在输出端和 24V电源之间接入约 500Ω的电阻,电阻功率需>2W; +※2:使用定位指令时,脉冲方向端子可以在除脉冲输出端子以外的所有输出端子中自由定义,建议使用 +PLC本体上的晶体管输出点。 +※3:脉冲输出端口晶体管响应时间在 0.5us以下,其余输出晶体管响应时间在 0.2ms以下; +===== 手册第14页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +4 + +※4:当脉冲输出端子不作为脉冲输出使用时,则可作为脉冲方向端子使用; +※5:差分脉冲输出时,如果是脉冲+方向模式,方向端子可选用晶体管或差分输出端子(差分输出端子+、 +-都要接) ;如果是AB相模式,端子必须成对使用,如 Y0、Y1,具体分配见上表。 +※6:脉冲需要配置脉冲输出端子和脉冲方向端子, 建议将所有脉冲输出端子相对应的脉冲方向端子区分开, +否则会导致脉冲无法换向等问题。 +负载电流 +针对基本单元(晶体管输出型)的脉冲输出端口使用与定位相关的指令时,请将集电极开路晶体管输 +出的负载电流调节在10~100mA(DC5~24V)。 +Y0 +COM0 +Y1 +COM1 +Y2 +COM2 + +【注】 : +※1:为了使用脉冲输出,必须要使用带有晶体管输出的PLC。如 XD3-16T-E或 XD3-60T-E等。 +※2:脉冲输出端口请参见上表,方向端子可以从剩余输出端子中任意选择对应输出端子。 +※3:脉冲方向端子在脉冲发送完后会保持原脉冲的方向端子状态,即当原脉冲正向发送,脉冲方向端子为 +ON状态,当脉冲发送完毕后,方向端子仍然会保持ON的状态;如果下次执行的脉冲指令带有方向端子,脉冲 +指令会在发送脉冲之前自动对脉冲方向端子进行控制;如果下次执行的脉冲指令不带有方向端子,用户可以在 +程序中通过程序手动控制脉冲方向端子的状态。 +※4:脉冲输出端子在脉冲发送过程中 LED 指示灯显示微亮,是由于脉冲输出端子输出脉冲时是占空比为 +50%的方波,一个脉冲周期一半时间ON一半时间 OFF,所以脉冲输出的LED指示灯也是一半时间亮一半时间 +不亮,从而显示出微亮。 +※5:脉冲输出端子Yn在发送脉冲的过程中,软元件Yn在上位机软件上监控会置 ON,当脉冲发送完成时 +置 OFF。 +※6:如果脉冲输出端子没有用作脉冲输出, 此Y点可以作为普通 Y端子使用, 也可以作为方向端子使用。 + + +步进电机 +驱动器 +===== 手册第15页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 5 + +1-2. 脉冲输出的种类与指令应用 +1-2-1.脉冲参数介绍及配置方法 +XD/XL 系列 PLC 在使用脉冲输出功能时,需要分别配置脉冲数据、用户参数块、系统参数块。由于 +参数较多,故本节将重点介绍这些参数的含义和配置方法。在后续具体指令中不再重复介绍这些参数的含 +义。 +现以多段脉冲输出 PLSR指令来说明各参数的基本功能。 +PLSR的基本指令形式如下所示: +PLSR HD0 HD100 K1 +RST M0 +M0 +SM1000 +S1· S2· D· +Y0 +S0· + +PLSR指令在软件中的配置,可通过单击“ ”图标,也可以右键点击梯形图中的PLSR指令,配置 +界面如下图所示: + +主要配置项的功能如下表: +配置项 功能 + 指定脉冲数据参数块地址,占用【S0】~【S0+N*10+8】 (双字,N为脉冲 +段数) ,用于存放脉冲总段数、每段脉冲的脉冲频率和脉冲个数、等待条 +件及寄存器类型和编号、跳转寄存器类型及编号等 + 指定用户参数地址块,占用【S1】~【S1+2】 (双字) ,用于存放模式(相 +对、绝对) 、起始执行段数 + 表示选用哪一套系统参数,每个脉冲输出端子最多可设置 4 套参数,默 +认为 K1(即第 1 套参数) + 相对、绝对可选,默认为相对模式 + 指定 PLSR从哪一段脉冲开始执行,默认为 0 时,表示从第 1 段开始 + +用于设定系统参数, 系统参数存放在特殊Flash寄存器 SFD900~SFD2193 +中,总共可设定 10 个脉冲输出端子的各 4套系统参数 + +===== 手册第16页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +6 + +1-2-1-1. 脉冲数据参数块(S0) +脉冲数据参数在 S0为首的地址块中设定,具体参数介绍如下表: +数据起始地址(S0)说明: +地址 内容 备注 +S0+0(双字) 脉冲总段数(1~100) +S0+2(8 个字) 保留(8 word) +S0+10(双字) 第 1段脉冲频率 +第 1段 +S0+12(双字) 第 1段脉冲个数 +S0+14 +高 8位: 【等待条件】 (指定何时发送下一段脉冲) +H00:脉冲发送完成( “H”表示 16进制数) +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +低 8位: 【等待条件寄存器类型】 (与【等待条件】配合使用) +H00:常数 +H01:D +H02:HD +H03:FD +H04:X +H05:M +H06:HM +S0+15(双字) 【常数值/寄存器编号(等待条件) 】 ,与【等待条件】 、 【等待条件寄存器类 +型】配合使用 +S0+17 +低 8位: 【跳转寄存器类型】 (指定要发送的下一段脉冲段号) +H00:常数 +H01:D +H02:HD +H03:FD +S0+18(双字) 【常数值/寄存器编号(跳转寄存器) 】 ,与【跳转寄存器类型】配合使用 +…… …… …… +S0+N*10+0(双字) 第 N 段脉冲频率 +第 N +段 +S0+N*10+2(双字) 第 N 段脉冲个数 +S0+N*10+4 等待条件,等待条件寄存器类型 +S0+N*10+5(双字) 常数值或者寄存器编号(等待条件) +S0+N*10+7 跳转寄存器的类型 +S0+N*10+8(双字) 常数值或者寄存器编号(跳转寄存器) +【注】 : +※1:脉冲频率是正数(≥0) ,数值变大为加速,数值变小为减速,与脉冲方向无关系; +※2:脉冲个数可设正数或者负数,当设定为负数时表示反向发脉冲。 +※3:EXT与外部中断不可同时使用,否则会导致外部中断只进入一次。 +※4:S0+14高八位和低八位是由分开的含义, 高八位和低八位配合使用, 表格中的H00表示十六进制的 0, +例如:设置等待条件为 wait时间,等待寄存器类型选择HD类型,那么 S0+14的值应该为 H0102。等待条件设 +===== 手册第17页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 7 + +为 X位状态类型,对应地址为八进制。 +※5:目前 PLSR的最后一段不建议设置成脉冲发送完成以外的其他设置,否则会导致SM1000一直处于等 +待状态,导致 SM1000不会复位。 +(A)等待条件(【S0+14】高8位): +用于指定何时进入下一段脉冲输出。 +⚫ 脉冲发送完成(H00) +执行完本段的设定脉冲个数后,立即跳转到后面指定的脉冲段。 +例1:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第1段2000个脉冲数,第1段的 +脉冲数发送完毕后立即跳转到第2段脉冲段;当第2段脉冲以2000Hz的速度发送完4000个脉冲数后再立即 +跳转到第3段脉冲段,直至第3段脉冲数6000个发送结束。 +面板配置如下: + +多段顺控脉冲输出配置表 +2000 6000 12000 脉冲数 +频率 +1000 +2000 +3000 +第1段 第2段 第3段 + +多段顺控脉冲发送波形图 +例2:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第一段2000个脉冲数,第一段 +的脉冲数发送完毕后立即跳转到第三段脉冲段;当第三段脉冲以3000Hz的速度发送完6000个脉冲数后再 +立即跳转到第二段脉冲段,直至第二段脉冲数4000个发送结束,再跳转至第三段以此循环。 + + +===== 手册第18页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +8 + +面板配置如下: + +多段跳转脉冲输出配置表 +2000 8000 12000 脉冲数 +频率 +1000 +2000 +3000 +第2段和第3段将会 +无限循环发送脉冲 +第1段 第3段 第2段 第3段 + +多段跳转脉冲发送波形图 +【注】 : +※1:加减速时间可在【参数】表中配置, 【系统参数块】中将会做详细说明。 +※2: 【跳转至】选项中为“K0”时,则默认跳转至下一段;如果设置为非0数值,则跳转至指定的脉冲 +段,如设为K3,则跳转到第3段脉冲。 +※3:当配置多段脉冲指令时,使用了【跳转至】功能,一定要避免进入不停循环发脉冲的情况! + +⚫ Wait 时间(H01) +当前段脉冲输出完成后开始计时,当计时时间到后,立即跳转到指定的脉冲段;计时时间可以是常 +数,也可以通过寄存器D、HD以及FD来指定,单位:ms。 +例如:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第一段2000个脉冲数,第一段 + +===== 手册第19页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 9 + +的脉冲数发送完毕后延时200ms跳转到第二段脉冲段;当第二段脉冲以2000Hz的速度发送完4000个脉冲数 +后,延时寄存器D100所存储的数值时间(例如D100=100,即延时100ms)再跳转到第三段脉冲段,直至 +第三段脉冲数6000个发送结束。 +面板配置如下: + +多段脉冲输出配置表 +延时200ms +时间 t +频率 +1000 +2000 +3000 +第1段 +2000 +第2段 +4000 +第3段 +6000 +延时D100ms +0 + +脉冲发送波形图 +【注】 : +※1:加减速时间可在参数表中配置,系统参数块中将会做详细说明。 +※2:延时时间范围:1~2,147,483,647ms,设定为0时按照1ms处理。该时间会占用双字寄存器。 + +⚫ Wait 信号(H02) +当前段脉冲发送完成后,开始等待【等待条件】中的位信号,当位信号动作(ON状态,或由 +OFF->ON时) ,则立即跳转到指定的脉冲段;位信号可以是X、M以及HM等位状态类型。 +例如:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第1段2000个脉冲数,第1段的 +脉冲数发送完毕后,等待线圈M10由OFF置ON后立即跳转到第2段脉冲段;当第2段脉冲以2000Hz的速度 +发送完4000个脉冲数后,等待输入点X2由OFF置ON后再立即跳转到第3段脉冲段,直至第3段脉冲数6000 +个发送结束。 + +===== 手册第20页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +10 + +面板配置如下: + +多段脉冲输出配置表 +时间 t +频率 +1000 +2000 +3000 +第1段 +2000 +第2段 +4000 +第3段 +6000 +0 +M10 +X2 + +脉冲发送波形图 +【注】 : +※1:加减速时间可在参数表中配置,系统参数块中将会做详细说明。 +※2:如果当前段的脉冲数还没有发送结束,而Wait信号已经导通,则当前段脉冲发送结束后立即跳转到 +下一段脉冲,波形图如下(M10信号位提前由OFF置ON): + +===== 手册第21页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 11 + +时间 t +频率 +1000 +2000 +3000 +第1段 +2000 +第2段 +4000 +第3段 +6000 +0 +M10 +X2 + +脉冲发送波形图 +※3:如果当前段的脉冲数发送结束后,信号位没有置ON,则会一直等到信号位置ON才会发送下一段脉 +冲,否则将不执行下一段脉冲。 + +⚫ ACT 时间(H03) +当前脉冲段执行由ACT指定时间的脉冲输出后,不管当前段脉冲是否发送完,立即跳转到指定的下 +一段脉冲;ACT时间可以是常数,也可以通过寄存器D、HD以及FD来指定,单位:ms。 +例如:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第1段脉冲数,第1段的脉冲从 +发送计时时间达到1200ms后,无论脉冲是否发送结束,立即跳转到第2段脉冲段;当第2段脉冲以2000Hz +的速度发送即使时间达到寄存器D100(如果D100=1000)存储数值时间后,无论脉冲是否发送结束,再 +立即跳转到第3段脉冲段,直至第3段脉冲数6000个发送结束。 +面板配置如下: + +多段脉冲输出配置表 + +===== 手册第22页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +12 + +频率 +1000 +2000 +3000 +1200 1000 +0 +时间 t +ms + +脉冲发送波形图 +【注】 : +※1:加减速时间可在参数表中配置,系统参数块中将会做详细说明。 +※2:当设定的ACT时间较短时,刚好处于脉冲段的加速段,则直接从ACT时间到时的当前位置直接加速 +开始第2段脉冲,同理直接从ACT时间到时的当前位置直接加速开始第3段脉冲,如下图所示: +频率 +1000 +2000 +3000 +t1 t20 时间 t +ms + +脉冲发送波形图 +※3:当设定的ACT时间较长时,刚好处于脉冲段的减速段,则直接从ACT时间到时的当前位置直接加速 +开始第2段脉冲,同理直接从ACT时间到时的当前位置直接加速开始第3段脉冲,如下图所示: +频率 +1000 +2000 +3000 +t1 t2 +0 +时间 t +ms + +脉冲发送波形图 +===== 手册第23页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 13 + +※4:当设定的ACT时间很长时,脉冲段脉冲已发送结束ACT时间仍未结束,则需要等到ACT时间到才开 +始加速发送第二段脉冲,如下图所示: +频率 +1000 +2000 +3000 +t1 t2 +0 时间 t +ms + +脉冲发送波形图 + +⚫ EXT 信号(H04) +当前脉冲段脉冲输出中(脉冲个数尚未发送完) ,如果外部位信号触发动作(OFF->ON) ,则立即跳 +转到指定下一段脉冲;如果当前脉冲段脉冲发送后,外部位信号触发还没有动作,则继续等待该位信 +号;位信号为输入点X(如果使用的是外部中断端子则响应性更高) 。 +注意:EXT信号选择外部中断端子和非外部中断端子,跳转是不一样的,选择外部中断端子不会受 +IO映射的影响,以外部端子上的(OFF-ON跳转) ;使用非外部中断端子,会受IO映射影响,按照软件中 +软元件的(OFF-ON跳转) 。 +例如:当边沿触发相对模式脉冲指令PLSR时,先以1000Hz的速度发送第1段脉冲数,第1段脉冲发送 +过程中外部信号给至PLC输入点X0,立即跳转到第2段脉冲段;当第2段脉冲以2000Hz的速度发送的过程 +中,外部信号给至PLC输入点X1,立即跳转到第3段脉冲段;当第3段脉冲以3000Hz的速度发送的过程 +中,外部信号给至PLC输入点X2,立即以缓停模式停止脉冲发送。 +面板配置如下: + +多段脉冲输出配置表 + +===== 手册第24页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +14 + +频率 +1000 +2000 +3000 +时间 t +ms +X0 +X1 +X2 +0 + +脉冲发送波形图 +【注】 : +※1:加减速时间可在参数表中配置,系统参数块中将会做详细说明。 +※2:当外部EXT信号触发很早时,刚好处于脉冲段的加速段,则直接从EXT信号触发的当前位置直接加 +速开始第2段脉冲,同理直接从EXT信号触发的当前位置直接加速开始第3段脉冲,如下图所示: +频率 +1000 +2000 +3000 +时间 t +ms +X0 +X1 +X2 +0 + +脉冲发送波形图 + + + + + + + +===== 手册第25页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 15 + +※3:当外部EXT信号触发很晚时,直到当前脉冲段脉冲发送结束后才来,则需要等待EXT信号触发后加 +速开始下一段脉冲,如下图所示: +频率 +1000 +2000 +3000 +时间 t +ms +X0 +X1 +0 + +脉冲发送波形图 +※4:当外部EXT信号触发较晚时,刚好处于脉冲段的减速段,则直接从EXT信号触发的当前位置直接加 +速开始第2段脉冲,同理直接从EXT信号触发的当前位置直接加速开始第3段脉冲,如下图所示: +频率 +1000 +2000 +3000 +时间 t +ms +X0 +X1 +0 + +脉冲发送波形图 +※5:EXT与外部中断不可同时使用,否则会导致外部中断只进入一次。 + +⚫ EXT 信号或者脉冲发送完成(H05) +位信号到来(OFF->ON) ,或者脉冲发送完成,都跳转到指定段。 +当前脉冲段开始发送脉冲后,如果在当前段脉冲数发送结束前,外部位信号触发动作(OFF->ON), +则立即跳转到指定下一指定段脉冲,否则等到当前段的脉冲发送结束时,也会立即跳转到指定下一指定 +段脉冲(即表示配置好的脉冲段将会按照脉冲发送完成的模式发脉冲,但是当前段脉冲在发送的过程中 +有EXT外部信号触发,则会立即放弃本段剩余脉冲,立即跳转到下一指定脉冲段开始发脉冲) 。 + + + + +===== 手册第26页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +16 + +例如如下配置: + +多段脉冲输出配置表 +EXT外部信号X0在第1段(频率1000Hz,2000个脉冲数)发送脉冲的过程中有效;EXT外部信号X1 +在第2段(频率2000Hz,4000个脉冲数)发送脉冲的过程中有效;EXT外部信号X2在第3段(频率 +3000Hz,6000个脉冲数)发送脉冲的过程中有效。 +(B)等待寄存器类型及编号: +⚫ 常数(H00) +则S0+N*10+5(双字)寄存器里面的值为常数,范围:K0~K2147483647;例如K2、K6、K3000等。 +⚫ D(H01) +则S0+N*10+5(双字)寄存器里面的值为D(普通数据寄存器)的编号;例如D0、D200等。 +⚫ HD(H02) +则S0+N*10+5(双字)寄存器里面的值为HD(断电记忆数据寄存器)的编号;例如HD0、HD200 +等。 +⚫ FD(H03) +则S0+N*10+5(双字)寄存器里面的值为FD(FLASH擦写数据寄存器)的编号;例如FD0、FD200 +等。 +⚫ X(H04) +则S0+N*10+5(双字)寄存器里面的值为X(输入信号)的编号,如果该位信号对应为外部中断端 +子,则由外部中断信号触发(响应时间更快) ;例如X0、X6等。 +⚫ M(H05) +则S0+N*10+5(双字)寄存器里面的值为M(普通内部线圈)的编号;例如M0、M200等。 +⚫ HM(H06) +则S0+N*10+5(双字)寄存器里面的值为HM(断电记忆内部线圈)的编号;例如HM0、HM200等。 +(C)跳转寄存器类型及编号: +⚫ 常数(H00) +则S0+N*10+8(双字)寄存器里面的值为常数; 范围:K0~K100;例如K2、K6等。 +⚫ D(H01) +则S0+N*10+8(双字)寄存器里面的值为D(普通数据寄存器)的编号;例如D0、D200等。 + +===== 手册第27页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 17 + +⚫ HD(H02) +则S0+N*10+5(双字)寄存器里面的值为HD(断电记忆数据寄存器)的编号;例如HD0、HD200 +等。 +⚫ FD(H03) +则S0+N*10+5(双字)寄存器里面的值为FD(FLASH擦写数据寄存器)的编号;例如FD0、FD200 +等。 +【注】 : +※1:无论是常数还是寄存器,数值范围为K0~K100。 +※2:此参数表示当前脉冲段脉冲结束后,将要跳转到指定的下一个脉冲段;例如:当前数值为K6(常数 +或者寄存器)时,脉冲发送结束后立即跳转到第6段脉冲。 +※3:如果跳转常数或者寄存器值为0,表示默认跳转到配置表顺序的下一段脉冲;如果是最后一段脉冲的 +跳转常数或寄存器为0,则表示结束脉冲输出。 +※4:如果跳转常数或者寄存器值为自己本身的数值,则将会无限循环发送当前段的脉冲。 +1-2-1-2. 脉冲用户参数块(S1) + 脉冲用户参数块在 S1为首的地址块中设定,参数介绍如下表: +脉冲用户参数块起始地址(S1): +地址 内容 +S1+0(双字) 脉冲相对/绝对模式(0:相对;1:绝对)*1 +S1+2(双字) 脉冲起始执行段数(1~100)*2 +(A)相对/绝对模式 +S1+0(双字)用来设定脉冲配置的参数为相对模式还是绝对模式,默认为相对模式。如下图: + +举例:现需要连续发送3段脉冲,第1段脉冲频率为1000Hz,脉冲数为2000个;第2段脉冲频率为 +2000Hz,脉冲数为4000个;第3段脉冲频率为3000Hz,脉冲数为6000个;脉冲配置表相对、绝对模式分别 +如下图: + +相对模式配置表 + +===== 手册第28页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +18 + + +绝对模式配置表 +(B)起始执行段数 +脉冲起始执行段数,用于指令脉冲从哪一段开始执行(即脉冲指令执行时从脉冲起始执行段数所设 +定的段数开始执行,而并非一定从第1段开始执行) 。 +【注】 :设置为0或1时,都是从第1段开始执行。如下图: + +例如:现在脉冲配置表中配置3段脉冲,第1段脉冲频率为1000Hz,脉冲数为2000个;第2段脉冲频率 +为2000Hz,脉冲数为4000个;第3段脉冲频率为3000Hz,脉冲数为6000个;起始执行段数设定为2;如下 +图所示: + +多段脉冲输出配置表 +当 PLSR指令开始执行时,将会按照 2000Hz的频率先发送第 2段 4000个脉冲数,结束后立即发送 +频率 3000Hz脉冲数 6000的第 3段脉冲段。 +1-2-1-3. 系统参数块(S2) +用户通过该参数决定使用当前路脉冲的哪一组参数。脉冲指令在执行过程中可以从4 组参数中选择 +适合的脉冲参数进行脉冲发送。 +每个脉冲输出端子,都有一块对应的系统参数地址,可设定4组不同的参数块,用户需要在脉冲指 +令的 S2中(可以是常数,也可以是寄存器 D、HD、FD等)指定使用 4 组参数块中的哪一组。 +如下图中所示,系统参数块为 K2,输出端子为 Y0,则表示选用 Y0 轴的第 2 套系统参数: + +===== 手册第29页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 19 + + +点击“参数”按钮,进入系统参数配置,如下图所示: + +点“配置” ,可对Y0~Y11这 10 路脉冲输出轴进行参数配置,面板中间为系统参数配置区,双击各 +参数对应的“设定值” ,可进行选择或新的设定,如下图所示: + +部分指令没有面板配置方式,但需要用到系统参数,此时可以在XDPPro 软件左侧“工程栏”中找 +到“脉冲参数” ,如下图所示: + + +===== 手册第30页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +20 + +单击“脉冲参数” ,即可进入系统参数配置面板,如下图所示: + +注意:对于同一个脉冲输出轴,其系统参数是共用的,例如,对 Y0使用不同的脉冲指令,如果指 +定的系统参数块均为 K1,那么所有对 Y0进行脉冲输出的指令中的系统参数都是一样的,均为 Y0轴的 +第 1套系统参数。 +下面表格中为第一路脉冲(即 Y0)的 5套参数,每套参数中分别可以设不同的脉冲默认速度、脉冲 +默认速度加速时间、脉冲默认速度减速时间、补间加减速时间、最高速度限制、起始速度以及终止速度 +等(各参数将在下文中进行详细说明) 。 +以第一路脉冲(即Y0)的系统参数块进行说明,其它端子的脉冲输出系统参数块见本手册附录3部分 +的《特殊FLASH寄存器一览》 。 +编号 功能 说明 +Y0(公共参数) +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑,1:负逻辑;默认为 0 +Bit 2:启用软限位功能 +0:不启用,1:启用;默认为 0 +Bit 3:机械回原点方向 +0:负向,1:正向;默认为 0 +Bit 4:电机运行模式(闭环脉冲) +0:位置模式,1:脉冲模式;默认为 0 +Bit 10~8:脉冲单位 +Bit8:0:脉冲个数,1:当量 +000:脉冲个数,001:微米,011:忽米, +101:丝米,111:毫米;默认为 000 +Bit13:脉冲类型 + 0:单向脉冲,1:AB相脉冲;默认为 0 +Bit15:插补坐标模式 +0:十字坐标;默认为 0 +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 +SFD902 脉冲数/1转 低 16 位 +SFD903 高 16 位 + +===== 手册第31页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 21 + +编号 功能 说明 +SFD904 移动量/1转 低 16 位 +SFD905 高 16 位 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 +SFD907 方向延时时间 默认是 10,单位:ms +SFD908 齿轮间隙正向补偿 +SFD909 齿轮间隙负向补偿 +SFD910 电气原点位置 低 16 位 +SFD911 高 16 位 +SFD912 信号端子开关状态设置 +Bit0:原点信号开关状态设置 +Bit1:Z 相开关状态设置 +Bit2:正极限开关状态设置 +Bit3:负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻辑);默认为 0 +SFD913 原点信号端子设定 Bit0~Bit7:指定 X 端子的编号,0xFF为无端子 +SFD914 Z 相端子设定 Bit0~Bit7:指定 X 端子的编号,0xFF为无端子 +SFD915 极限端子设定 +Bit7~Bit0:指定正极限的 X 端子编号,0xFF为无端子 +Bit15~Bit8:指定负极限的 X 端子编号,0xFF 为无端 +子 +SFD917 归零清除 CLR信号输出端子设定 Bit0~Bit7:指定 Y 端子的编号,0xFF为无端子 +SFD918 回归速度 VH 低 16 位 +SFD919 高 16 位 +SFD922 爬行速度 VC 低 16 位 +SFD923 高 16 位 +SFD924 机械原点位置 低 16 位 +SFD925 高 16 位 +SFD926 Z 相个数 +SFD927 CLR信号延时时间 默认是 20,单位:ms +SFD930 软限位正极限值 低 16 位 +SFD931 高 16 位 +SFD932 软限位负极限值 低 16 位 +SFD933 高 16 位 +SFD934 编码器脉冲数/1转(闭环脉冲) 低 16 位 +SFD935 高 16 位 +SFD936 编码器移动量/1转(闭环脉冲) 低 16 位 +SFD937 高 16 位 +SFD938 定位完成宽度(闭环脉冲) +SFD939 偏差位置限值(闭环脉冲) +SFD940 电机额定转速(闭环脉冲) +SFD941 额定转速对应频率(闭环脉冲) 单位:100Hz +SFD942 定位完成时间限值(闭环脉冲) 单位:ms +SFD943 运动控制默认参数块 +Bit0~bit7:快速定位指令默认参数块 + 0~4,默认 1 +Bit8~bit15:插补指令默认参数块 + 0~4,默认 2 +===== 手册第32页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +22 + +编号 功能 说明 +…… +Y0(第 0套参数) +HSD460 脉冲默认速度低 16位 速度为 0 的时候才用默认速度发送脉冲 HSD461 脉冲默认速度高 16位 +HSD462 脉冲默认速度加速时间 +HSD463 脉冲默认速度减速时间 +HSD464 补间加减速时间 +HSD465 脉冲加减速模式 +Bit1~Bit0:加减速段加减速方式 +00:直线加减速,01:S曲线加减速 +10:正弦曲线加减速,11:保留 +Bit15~Bit2:保留 +HSD466 最高速度 低 16 位 +HSD467 高 16 位 +HSD468 起始速度 低 16 位 +HSD469 高 16 位 +HSD470 终止速度 低 16 位 +HSD471 高 16 位 +HSD472 Follow性能参数 1~100,100 表示时间常数为 1 个 Tick,1 表示时间常 +数为 100 个 Tick +HSD473 Follow前馈补偿参数 0~100,百分比 +HSD474 脉冲频率刷新时间 1ms、0.1ms刷新 +HSD476 ZRN回归速度 VH 低 16 位 +HSD477 高 16 位 +HSD478 ZRN爬行速度 VC 低 16 位 +HSD479 高 16 位 +…… +Y0(第 1套参数) +SFD950 脉冲默认速度低 16位 速度为 0 的时候才用默认速度发送脉冲 SFD951 脉冲默认速度高 16位 +SFD952 脉冲默认速度加速时间 +SFD953 脉冲默认速度减速时间 +SFD954 补间加减速时间 +SFD955 脉冲加减速模式 +Bit1~Bit0:加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +Bit15~Bit2:保留 +SFD956 最高速度 低 16 位 +SFD957 高 16 位 +SFD958 起始速度 低 16 位 +SFD959 高 16 位 +SFD960 终止速度 低 16 位 +SFD961 高 16 位 +===== 手册第33页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 23 + +编号 功能 说明 +SFD962 Follow性能参数 1~100,100 表示时间常数为 1 个 Tick,1 表示时间常 +数为 100 个 Tick +SFD963 Follow前馈补偿参数 0~100,百分比 +SFD964 脉冲频率刷新时间 1ms、0.1ms刷新 +SFD966 ZRN回归速度 VH 低 16 位 +SFD967 高 16 位 +SFD968 ZRN爬行速度 VC 低 16 位 +SFD969 高 16 位 +…… +Y0(第 2套参数) +SFD970 脉冲默认速度低 16位 速度为 0 的时候才用默认速度发送脉冲 SFD971 脉冲默认速度高 16位 +SFD972 脉冲默认速度加速时间 +SFD973 脉冲默认速度减速时间 +SFD974 补间加减速时间 +SFD975 脉冲加减速模式 同 SFD955 +SFD976 最高速度 低 16 位 +SFD977 高 16 位 +SFD978 起始速度 低 16 位 +SFD979 高 16 位 +SFD980 终止速度 低 16 位 +SFD981 高 16 位 +SFD982 Follow性能参数 1~100,100 表示时间常数为 1 个 Tick,1 表示时间常 +数为 100 个 Tick +SFD983 Follow前馈补偿参数 0~100,百分比 +SFD984 脉冲频率刷新时间 1ms、0.1ms刷新 +SFD986 ZRN回归速度 VH 低 16 位 +SFD987 高 16 位 +SFD988 ZRN爬行速度 VC 低 16 位 +SFD989 高 16 位 +…… +Y0(第 3套参数) +SFD990 脉冲默认速度低 16位 速度为 0 的时候才用默认速度发送脉冲 SFD991 脉冲默认速度高 16位 +SFD992 脉冲默认速度加速时间 +SFD993 脉冲默认速度减速时间 +SFD994 补间加减速时间 +SFD995 脉冲加减速模式 同 SFD955 +SFD996 最高速度 低 16 位 +SFD997 高 16 位 +SFD998 起始速度 低 16 位 +SFD999 高 16 位 +SFD1000 终止速度 低 16 位 +SFD1001 高 16 位 +===== 手册第34页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +24 + +编号 功能 说明 +SFD1002 Follow性能参数 1~100,100 表示时间常数为 1 个 Tick,1 表示时间常 +数为 100 个 Tick +SFD1003 Follow前馈补偿参数 0~100,百分比 +SFD1004 脉冲频率刷新时间 1ms、0.1ms刷新 +SFD1006 ZRN回归速度 VH 低 16 位 +SFD1007 高 16 位 +SFD1008 ZRN爬行速度 VC 低 16 位 +SFD1009 高 16 位 +…… +Y0(第 4套参数) +SFD1010 脉冲默认速度低 16位 速度为 0 的时候才用默认速度发送脉冲 SFD1011 脉冲默认速度高 16位 +SFD1012 脉冲默认速度加速时间 +SFD1013 脉冲默认速度减速时间 +SFD1014 补间加减速时间 +SFD1015 脉冲加减速模式 同 SFD955 +SFD1016 最高速度 低 16 位 +SFD1017 高 16 位 +SFD1018 起始速度低 16位 低 16 位 +SFD1019 高 16 位 +SFD1020 终止速度低 16位 低 16 位 +SFD1021 高 16 位 +SFD1022 Follow性能参数 1~100,100 表示时间常数为 1 个 Tick,1 表示时间常 +数为 100 个 Tick +SFD1023 Follow前馈补偿参数 0~100,百分比 +SFD1024 脉冲频率刷新时间 1ms、0.1ms刷新 +SFD1026 ZRN回归速度 VH 低 16 位 +SFD1027 高 16 位 +SFD1028 ZRN爬行速度 VC 低 16 位 +SFD1029 高 16 位 +…… +一、公共参数 +(一)脉冲参数设定——脉冲方向逻辑 +脉冲方向逻辑分为正逻辑(默认设置)和负逻辑。 +【正逻辑】 :表示每段脉冲的脉冲数设定值为正值时,正向发脉冲(例如Y0轴的累计脉冲量寄存器 +HSD0数值增大) ,脉冲方向端子置ON;脉冲数设定值为负值时,反向发脉冲(例如Y0轴的累计脉冲量寄 +存器HSD0数值减小) ,脉冲方向端子置OFF。 +【负逻辑】 :表示每段脉冲的脉冲数设定值为正值时,正向发脉冲(例如Y0轴的累计脉冲量寄存器 +HSD0数值增大) ,脉冲方向端子置OFF;脉冲数设定值为负值时,反向发脉冲(例如Y0轴的累计脉冲量 +寄存器HSD0数值减小) ,脉冲方向端子置ON。 +当脉冲发送时,方向端子置ON,脉冲发送结束后方向端子不会自动复位,仍会处于ON状态,下次发 +脉冲时,方向端子会根据设定自动改变方向端子的状态。 + +===== 手册第35页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 25 + +【注】 : +※1:此参数一般默认为正逻辑不需要修改,本手册中所有脉冲定位样例程序全部按照正逻辑编写。 +※2:适用于PLSR、PLSF、DRVI、DRV A、ZRN指令。 +(二)脉冲公共参数——启用软限位功能/软限位正极限值/软限位负极限值 +【启用软限位功能】指是否需要启用软限位功能保护;软限位功能是指为了保护工作台移动超出行 +程范围,而在行程正负两端添加坐标轴保护功能;回机械原点时用于自动搜索原点信号以及保护,进而 +引入的通过脉冲轴当前累积脉冲寄存器数值判断,起到正负硬限位所能起到的保护作用。 +注意:正负软限位和正负硬限位可以同时使用。 +参数配置表如下: + +【软限位正极限值】 :是指执行PLSR、PLSF、DRV A、DRVI、插补脉冲指令时,为了防止工作台移 +动超出可移动行程范围,而在行程正端添加脉冲轴当前累积脉冲寄存器数值判断,进而起到保护的功 +能。参数配置表如下: + +在使用PLSR、PLSF、DRV A、DRVI、插补脉冲指令时,当正向脉冲发送过程中达到软件正极限值,脉 +冲将立即以缓停模式停止脉冲发送;当前累积脉冲寄存器数值超出软限位正极限值时,正向脉冲将会一直 +处于被禁止状态,但是可以触发反向脉冲使工作台返回。 + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +当前累计脉冲寄存器数值超 +过软限位正极限值 +前进 +后退 +× +√ + +【注】 : +※1:输入值请勿超出正向行程的最大数值。 +※2:适用于PLSR、PLSF、DRV A、DRVI、插补指令。 + + + + + +===== 手册第36页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +26 + +【软限位负极限值】 :是指在执行PLSR、PLSF、DRV A、DRVI、插补脉冲指令时,为了防止工作台 +移动超出可移动行程范围,而在行程负端添加脉冲轴当前累积脉冲寄存器数值判断,进而起到做保护的 +功能。参数配置表如下: + +在使用PLSR、PLSF、DRV A、DRVI、插补脉冲指令时,当反向脉冲发送过程中达到软件负极限值,脉 +冲将立即以缓停模式停止脉冲发送;当前累积脉冲寄存器数值超出软限位负极限值时,反向脉冲将会一直 +处于被禁止状态,但是可以触发正向脉冲使工作台返回。 + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +前限位开关 +当前累计脉冲寄存器数值 +低于软限位负极限值 +前进 +后退 +√ +× + +【注】 : +※1:输入值请勿低于反向行程的最小数值。 +※2:适用于PLSR、PLSF、DRV A、DRVI、插补指令。 +(三)脉冲公共参数——机械回原点默认方向 +【机械回原点默认方向】是指当开始执行机械回原点ZRN指令时,工作台默认开始移动的方向。参 +数配置表如下: + +负向:当设置的为负向时,开始执行机械回原点ZRN指令,工作台开始往反向运行,如下图所示: + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +前限位开关 +后 +限 +位 +开 +关 + + + + + + +===== 手册第37页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 27 + +正向:当设置的为正向时,开始执行机械回原点ZRN指令,工作台开始往正向运行,如下图所示: + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +前限位开关 +后 +限 +位 +开 +关 + +(四)脉冲参数设定——脉冲单位、脉冲数(1转)、移动量(1转) +脉冲单位分为脉冲个数(默认设置)和当量(1um、0.01mm、0.1mm、1mm可选) 。设置如下图: + +【脉冲个数】 :表示在脉冲配置表中配置的脉冲频率和脉冲个数都是按照脉冲个数进行计算的,例如 +下图: + +配置表中一共配置了三段脉冲(设置为脉冲个数) ,第1段脉冲指的就是按照每秒1000个脉冲速度发 +送2000个脉冲,第2段脉冲指的就是按照每秒2000个脉冲速度发送4000个脉冲,第3段脉冲指的就是按照 +每秒3000个脉冲速度发送6000个脉冲。 +【当量】 :1um、0.01mm、0.1mm、1mm可选,表示在脉冲配置表中配置的脉冲频率和当量都是按照 +长度单位进行计算的;但是,在了解当量之前,我们需要首先了解下公共参数——脉冲数(1转)和公共 +参数——移动量(1转) 。 +【公共参数——脉冲数(1转) 】 :是指传动机构转动一圈需要的脉冲数。由于这里面还涉及到减速机 +构,所以电机转动一圈传动机构并不一定转动一圈。 +:一台伺服电机通过减速机构带动丝杆传动,伺服驱动器使用的是本公司的DS2-20P7-AS,伺服电机 +使用的是本公司的MS-80ST-M02430□□-20P7(编码器2500线),伺服驱动器电子齿轮比设定默认值为1: +1,减速机构的减速比为1:5,滚珠丝杆的螺距为5mm。 + 伺服电机 减速 +机构 +负载轴 丝杠螺距:5mm + +则滚珠丝杠转动一圈需要的脉冲数为: +50000 = 2500 * 4 * 5 +1 + + +===== 手册第38页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +28 + +【公共参数——移动量(1转) 】:是指传动机构转动一圈带动物体向前的移动量。例如上例中所使用 +的滚珠丝杠指的就是滚珠丝杠的螺距5mm;要是使用的是同步带,移动量指的是同步带传动轴的周长长 +度等等。 +我们通过以上对公共参数——脉冲数(1转)和公共参数——移动量(1转)的了解,来看下当量 +(1um、0.01mm、0.1mm、1mm可选)如何设定。我们仍通过上面例子中的机械结构,发送三段脉冲, +脉冲配置表配置如下: + +配置表中一共配置了三段脉冲(设置为当量) ,第1段脉冲指的就是按照每秒10mm移动速度移动 +20mm,第2段脉冲指的就是按照每秒15mm移动速度移动30mm,第3段脉冲指的就是按照每秒20mm移动 +速度移动40mm。公共参数配置表如下: + +如果我们将当量折算成对应的脉冲输出频率和脉冲个数,对应如下表: +序号 选择类型 频率/速度 脉冲个数/长度 +1 当量 10mm/s 20mm +脉冲个数 100000pulse/s 200000 pulse +2 当量 15mm/s 30mm +脉冲个数 150000pulse/s 300000 pulse +3 当量 20mm/s 40mm +脉冲个数 200000pulse/s 400000 pulse +从表格中我们不难看出当量与脉冲个数的区别,在设计的应用中可以根据需要选用对应的模式。 +【注】 : +※1:当设置的是脉冲个数,Y0轴脉冲累计寄存器HSD0(双字)显示累计的脉冲个数;当设置的是当 +量,Y0轴脉冲累计寄存器HSD0(双字)显示累计的脉冲个数,累计寄存器HSD2(双字)显示累计当量长度 +数值。 +※2:当设置为当量时,其它所有的相关参数都会按当量值运行;而当量的长度单位完全由脉冲单位选择 +的单位决定(选择1um时,所有单位按um转换;选择0.01mm、0.1mm、1mm时所有单位分别按0.01mm、 +0.1mm、1mm转换) ;公共参数——移动量(1转)设定值的单位需要等同脉冲单位选择的长度单位(例如,脉 +冲单位选择0.1mm,移动量(1转)的设定值为6,单位是0.1mm,说明1转移动量为6*0.1mm=0.6mm,则其它 +所有涉及到长度单位以及速度单位的全部为0.1mm或者0.1mm/s等) 。 +※3:当设置为当量时,请注意转换后的脉冲输出频率不能超过100KHz。 +※4:适用于PLSR、PLSF、DRVI、DRV A、ZRN指令。 + + + +===== 手册第39页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 29 + +(五)脉冲公共参数——脉冲类型 +此参数主要用于差分输出型PLC的脉冲输出方式选择,有单向脉冲和AB相脉冲两种模式。 + +(六)脉冲公共参数——插补坐标模式 +此参数功能暂时无法使用,无需修改。 +(七)脉冲公共参数——脉冲发送模式 +脉冲发送模式分为完成方式和后续方式两种。 +【完成方式】 :是指当配置多段脉冲时,当上一段脉冲个数按照上一段脉冲输出频率发送完后才开始 +加减速至当前段脉冲频率。 +第1段 第2段 第3段 第4段 + +多段脉冲波形图按照上图所示进行分段,在按本段速度,发送本段个数脉冲完成处分段,除最后一段 +脉冲外,每个脉冲段都是由上升或下降部分、平稳部分构成;最后一段脉冲由上升(或下降)部分、平稳 +部分、上升(或下降)部分构成。 +例如:现需要连续发送四段脉冲,第 1 段的脉冲频率为 1000Hz,脉冲个数为 2000,第 2 段的脉冲频 +率为 2000Hz,脉冲个数为 4000,第 3 段的脉冲频率为 3000Hz,脉冲个数为 6000,第 4 段的脉冲频率为 +4000Hz,脉冲个数为 8000,按照完成方式发送脉冲波形如下图所示: +2000 6000 12000 +脉冲数 +频率 +1000 +2000 +3000 +第1段 第2段 第3段 +4000 +20000 +第4段 + + + +===== 手册第40页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +30 + +【后续方式】 :是指当配置多段脉冲时,当上一段脉冲个数按照上一段脉冲输出频率发送完时,已经加 +减速至当前段脉冲频率。 +第1段 第2段 第3段 第4段 + +多段脉冲波形图按照上图所示进行分段,发送本段个数脉冲完成时,已切换到后续段速度,在此处分 +段除第一段脉冲外,每个脉冲段都是由平稳部分、上升或下降部分构成;第一段脉冲有上升或下降部分、 +平稳部分、上升或下降部分构成。 +例如:现需要连续发送四段脉冲,第一段的脉冲频率为 1000Hz,脉冲个数为 2000,第二段的脉冲频 +率为 2000Hz,脉冲个数为 4000,第三段的脉冲频率为 3000Hz,脉冲个数为 6000,第四段的脉冲频率为 +4000Hz,脉冲个数为 8000,按照后续方式发送脉冲波形如下图所示: +2000 6000 12000 +脉冲数 +频率 +1000 +2000 +3000 +第1段 第2段 第3段 +4000 +20000 +第4段 + +【注】 :以上两种脉冲发送模式适用于 PLSR指令。 +(八)脉冲公共参数——脉冲方向端子 +PLSR指令的方向端子需要在系统参数配置表中进行配置,参数配置表如下: + +方向端子可以选择除脉冲端子以外的所有输出端子,各型号PLC脉冲输出端子详见本手册1-1节内 +容。 +脉冲输出端子的光耦为高速光耦(响应时间5us以下) ,而其它端子光耦为普通光耦(响应时间0.2ms +以下) 。 +当Y0作为脉冲输出端,而其它的脉冲输出端子无需用来发送脉冲时,也可以将其设置为Y0的脉冲方 +向端子;反之,当其它脉冲端子作为脉冲输出端,而Y0无需用来发送脉冲时,也可以将Y0端子设置为脉 +冲方向端子。 + + +===== 手册第41页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 31 + +【注】 : +※1:请勿选择超出PLC本体上的实际输出端子。 +※2:适用于PLSR、PLSF、ZRN指令。 +(九)脉冲公共参数——脉冲方向延时时间(ms) +【脉冲方向延时时间(ms)】:是指当正向发送脉冲时,会先立即将方向端子置位,然后延时设定的 +时间后,脉冲输出端子才开始发送脉冲;或者当反向发送脉冲时,会先立即将方向端子复位,然后延时 +设定的时间后,脉冲输出端子才开始发送脉冲;这里的延时时间就是脉冲方向延时时间(ms)。参数配置 +表如下: + + +脉冲方向延时时间 +脉冲方向延时时间 +脉冲输出 +脉冲方向 + +脉冲启动、正向脉冲切换至反向脉冲 +脉冲方向延时时间 脉冲方向延时时间 +脉冲输出 +脉冲方向 + +反向脉冲切换至正向脉冲 +由于脉冲输出端子的光耦为高速光耦(响应时间5us以下) ,而其它端子光耦为普通光耦(响应时间 +0.2ms以下) (例如XD3-32T-E等)或者为继电器输出(约10ms) (例如XD3-24R-E等) ,方向端子输出往往 +会滞后于脉冲端子输出;所以我们需要先触发方向端子,延时一段时间后开始发送脉冲,这样就能够避 +免由于方向端子切换的滞后而导致输出脉冲出错(正向脉冲被切换至反向脉冲里或者反向脉冲被切换至 +正向脉冲里) 。 +默认的脉冲方向延时时间为10ms,我们也可以根据具体的方向端子的输出类型结合扫描周期(Y0或 +者Y1作为方向滞后最小(响应时间5us以下) 、其次是普通晶体管(响应时间0.2ms以下) 、最后是继电器 +(约10ms) )适当的进行调整。 +【注】 :适用于PLSR、PLSF、DRVI、DRV A、ZRN指令。 +(十)脉冲公共参数——齿轮间隙正向补偿 +【齿轮间隙正向补偿】 :是指工作台由反向移动结束切到正向移动时,由于移动工作台与滚珠丝杠之 +间存在接触间隙,从而导致正向实际移动距离小于设定距离;为了消除实际移动距离与设定移动距离之 +间的误差,从而引入了【齿轮间隙正向补偿】功能。如下示意图所示: + 伺服电机 减速 +机构 +滚珠丝杆间隙 BA + + +===== 手册第42页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +32 + +机械结构示意图 + +机械间隙结构示意图 +工作台从右边往左边移动,当工作台左边缘移动到位置A时停止,开始由A位置反向向右移动;但是 +由于滚珠丝杠间隙的问题,导致向右移动时,前面发送的一小段脉冲工作台没有向右移动,从而导致工 +作台向右移动的实际距离变小;如果不存在滚珠丝杠间隙,发送的一小段脉冲已由A位置移动到B位置。 +为了能够将这个误差消除,我们需要在向右发脉冲之前先发送一小段脉冲(数值客户需根据现场实际自 +己设定) ,再开始正式发送向右的脉冲段时能够使工作台立即向右移动。 +向右移动脉冲段 +t0 +f +齿轮正向间隙补偿 +频率 + +【注】 : +※1:只有当前脉冲段与上一脉冲段脉冲方向相反时才会执行齿轮间隙正向补偿。 +※2:目前齿轮间隙正向补偿与向右(正向)移动脉冲段只能分两段脉冲发送,不可以两端脉冲合为一段 +发送。 +※3:齿轮间隙正向补偿的脉冲不会累计到相应段的脉冲累计寄存器(例如Y0脉冲输出端口的HSD0)里 +面。 +※4:只适用于DRVI、DRV A、PLSR指令。 +※5:设定的齿轮间隙正向补偿量单位由公共参数设定的脉冲单位决定。 +(十一)脉冲公共参数——齿轮间隙负向补偿 +【齿轮间隙负向补偿】 :是指由于移动工作台与滚珠丝杠之间存在接触间隙,工作台由正向移动结束 +切到反向移动时,从而导致反向实际移动距离小于设定距离;为了消除实际移动距离与设定移动距离之 +间的误差,从而引入了【齿轮间隙负向补偿】功能。如下示意图所示: + 伺服电机 减速 +机构 +滚珠丝杆间隙 B A + +机械结构示意图 +旋转台 +进给丝杆 +回差 +(空隙、松动间隙) +===== 手册第43页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 33 + + +机械间隙结构示意图 +工作台从左边往右边移动,当工作台右边缘移动到位置A时停止,开始由A位置反向向左移动;但是 +由于滚珠丝杠间隙的问题,导致向左移动时,前面发送的一小段脉冲工作台没有向左移动,从而导致工 +作台向左移动的实际距离变小;如果不存在滚珠丝杠间隙,发送的一小段脉冲已由A位置移动到B位置。 +为了能够将这个误差消除,我们需要在向左发脉冲之前先发送一小段脉冲(数值客户需根据现场实际自 +己设定) ,再开始正式发送向左的脉冲段时能够使工作台立即向左移动。 +向左移动脉冲段 +t0 +f +齿轮负向间隙补偿 +频率 + +【注】 : +※1:只有当前脉冲段与上一脉冲段脉冲方向相反时才会执行齿轮间隙负向补偿。 +※2:目前齿轮间隙负向补偿与向左(反向)移动脉冲段只能分两段脉冲发送,不可以两端脉冲合为一段 +发送。 +※3:齿轮间隙负向补偿的脉冲不会累计到相应段的脉冲累计寄存器(例如Y0脉冲输出端口的HSD0)里 +面。 +※4:只适用于DRVI、DRV A、PLSR指令。 +※5:设定的齿轮间隙负向补偿量单位由公共参数设定的脉冲单位决定。 +(十二)脉冲公共参数——电气原点位置 +此参数功能暂时无法使用,无需修改。 +(十三)脉冲公共参数——信号端子开关状态设置 +【信号端子开关状态设置】:指一些信号采集端子状态的设置,可以根据客户现场的需要,将对应的 +信号端子设置为常开或者常闭端子。需要设置的信号端子主要包括原点开关状态设置、Z相开关状态设 +置、正极限开关状态设置以及负极限开关状态设置。 +信号端子开关状态设置参数配置表如下: + +旋转台 +进给丝杆 +回差 +(空隙、松动间隙) +===== 手册第44页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +34 + +【常开】 :是指机械原点开关在回机械原点时一直处于常开(OFF)状态,只有当回机械原点时才会 +触碰机械原点开关使之置ON。 +【常闭】 :是指机械原点开关在回机械原点时一直处于常闭(ON)状态,只有当回机械原点时才会 +触碰机械原点开关使之置OFF。 +(十四)脉冲公共参数——原点信号端子设定 +【原点信号端子设定】 :是指回机械原点时机械原点开关所接PLC的输入点,参数配置表如下: + +【注】 : +※1:输入点的选择请勿超出PLC本体实际输入点。 +※2:只适用于机械回原点指令ZRN。 +※3:原点信号端子可以选择PLC本体上所有的输入点;但是如果选择的输入点为PLC上的外部中断端子, +则回机械原点的过程中都会按照中断进行处理,从而可以进一步的提高回机械原点的精度(如果使用Z相回原 +点则无影响) ;而选择的输入点为非PLC本体上的外部中断端子,会机械原点的过程中则会受到PLC扫描周期的 +影响(如果使用Z相回原点则无影响) 。 +※4:详细外部中断端子请参考本手册附录4。 +(十五)脉冲公共参数——Z相端子设定 +【Z相端子设定】 :是指回机械原点时,以加速时间之斜率做低速反向慢行,直至达到原点爬行速 +度,当离开原点信号的瞬间,开始对Z相输入信号进行计数,这个Z相计数输入端子可在此进行设置。参 +数配置表如下: + +【注】 : +※1:只适用于机械回原点指令ZRN。 +※2:Z相端子设定可以选择PLC本体上的外部中断输入点;由于伺服驱动器输出的Z相信号脉宽较窄,普 +通的输入点带有10ms的滤波时间,所以建议通过带有高速光耦输入部分外部中断的方式来捕捉Z相信号。选择 +带有高速计数功能的外部中断输入点时,Z相速度不得高于接收频率最大值;选择普通外部中断输入点时,速 +度不得高于10KHz;如果选择其它输入点,有可能无法捕捉伺服驱动器输出的Z相信号,导致回机械原点故 +障。 +※3:Z相端子设定可选择的外部中断输入点配置表如下表: +PLC型号 Z相端子设定 +XD2-16/24/32/48/60 X2、X3、X4、X5、X6、X7 + +===== 手册第45页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 35 + +PLC型号 Z相端子设定 +XD3-16/24/24T4/32/32T4/42/48/60 X2、X3、X4、X5、X6、X7 +XD5-16 X2、X3、X4、X5、X6、X7 +XD5-24/32/24T4/32T4/48T4/60T4 +XD5-24D2T2/48D4T4 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XD5-42/48/60/48T6/60T6/60T10/80 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XDM-24T4/32T4 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XDM-60T4/60T4L/60T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XDC-24/32/48/60 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XD3E-24/30/48/60 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XD5E-24/30/48/60/30T4/60T4/60T6/60T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XDME-30T4/60T4/60T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XDH-24A8/24A8L/30A16/30A16L +XDH-60T4/60A32/60A64 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL3-16 X2、X3、X4、X5、X6、X7 +XL3-32 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL5-16 X2、X3、X4、X5、X6、X7 +XL5-32/32T4/64T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL3E-16 X2、X3、X4、X5、X6、X7 +XL3E-32 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL5N-32 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL5E-16 X2、X3、X4、X5、X6、X7 +XL5E-32T/32T4 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL5E-64T6/64T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XLME-32T4/64T10 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XLH-24A16/24A16L/30A32 X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +XL5H-24A8L X2、X3、X4、X5、X6、X7、X10、X11、X12、X13 +(十六)脉冲公共参数——正极限端子设定 +【正极限端子设定】 :是指回机械原点(ZRN指令)时,为了防止工作台移动超出可移动行程范围, +而在行程两端添加做保护的端子(一般为行程开关) ,详细使用方式可以参考机械回原点指令ZRN指令。 +参数配置表如下: + +【正极限端子设定】除了在执行机械回原点ZRN指令时有效,在使用PLSR、PLSF等脉冲指令时,当 +正向脉冲发送过程中碰到前限位,脉冲将立即以缓停模式停止脉冲发送(脉冲停止后请保证前限位开关仍 +然处于触发状态) ;前限位开关在处于触发状态下,正向脉冲将会一直处于被禁止状态,但是可以触发反向 +脉冲使工作台返回。 + +===== 手册第46页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +36 + + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +前限位开关 +后 +限 +位 +开 +关 +前进 +后退 +× +√ + +【注】 : +※1:输入点选择请勿超出PLC本体实际输入点。 +※2:请保证前限位挡块足够长,以保证工作台触发前限位开关时缓停停止后,前限位仍处于触发状态; +否则再次触发正向脉冲时,将会导致工作台撞机事故! +※3:适用于DRVI、DRV A、PLSR、PLSF、ZRN指令。 +(十七)脉冲公共参数——负极限端子设定 +【负极限端子设定】 :是指回机械原点(ZRN指令)时,为了保护工作台移动超出可移动行程范围, +而在行程两端添加做保护的端子(一般为行程开关) ,详细使用方式可以参考机械回原点指令ZRN指令。 +参数配置表如下: + +【负极限端子设定】除了在机械回原点ZRN指令时有效,在使用PLSR、PLSF等脉冲指令时,当反向 +脉冲发送过程中碰到后限位,脉冲将立即以缓停模式停止脉冲发送(脉冲停止后,请保证后限位开关仍然 +处于触发状态) ;后限位开关在处于触发状态下,反向脉冲将会一直处于被禁止状态,但是可以触发正向脉 +冲使工作台返回。 + 伺服电机 减速 +机构 +机 +械 +原 +点 +工作台 +前限位开关 +后 +限 +位 +开 +关 +前进 +后退 × +√ + +【注】 : +※1:输入点选择请勿超出PLC本体实际输入点。 +※2:请保证后限位挡块足够长,以保证工作台触发后限位开关时缓停停止后,后限位仍处于触发状态;否 +则再次触发反向脉冲时,将会导致工作台撞机事故! +※3:适用于DRVI、DRV A、PLSR、PLSF、ZRN指令。 +(十八)脉冲公共参数——归零清除CLR信号输出端子设定/CLR信号延时时间 +【归零清除CLR信号输出端子设定】 :是指当回机械原点结束后,立即输出一个输出信号,可以将此 +信号给一些其它控制设备,以实现相互间的快速信息传送的目的。例如,当回机械原点结束后,立即输 +出CLR信号给伺服驱动器,以实现立即输出清除信号来清除伺服马达的误差计数器(Error Counter) ,最 + +===== 手册第47页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 37 + +后将机械原点位置值拷贝至目前位置,归零动作即算完成。参数配置表如下: + +【CLR信号延时时间】 :是指回机械原点结束后输出的CLR信号的脉宽时间,单位为ms,范围 +0~32767(默认20ms) 。参数配置表如下: + +回机械原点脉冲输出 +CLR信号输出 +CLR信号脉宽 +回机械原点结束 + +CLR信号示意图 +【注】 : +※1:只适用于机械回原点指令ZRN。 +※2:CLR信号输出端子请使用PLC本体上的输出端子。 +※3:CLR信号延时时间请勿设置过小,否则有可能由于CLR输出信号脉宽太窄导致伺服驱动器无法正常 +接收。 +(十九)脉冲公共参数——回归速度VH(建议使用参数块) +【回归速度VH】:是指开始执行回机械原点ZRN时,工作台立即往机械原点位置加速至回归速度VH +快速移动,以达到能够减少回机械原点时间的目的。参数配置表如下: + + +【注】 : +※1:只适用于机械回原点指令ZRN。 +※2:启动ZRN指令时,VH加速按照设定的加速斜率进行加速;触碰到者原点信号时减速按照设定的减速 +斜率进行减速。 + +===== 手册第48页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +38 + +※3:回归速度VH优先选用参数块中的设置,当用户配置了参数块中的回归速度VH,则使用参数块中数 +值;当参数块中未配置,则使用公共参数中的回归速度VH,因此建议使用参数块设置。 +(二十)脉冲公共参数——爬行速度VC(建议使用参数块) +【爬行速度】 :是指当遇到原点信号时,开始速度减速为零,延时后再反向加速至爬行速度,直至工 +作台脱离原点信号立即停止爬行速度。由于工作台脱离原点信号后停止的位置就是机械原点位置,所以 +为了提高机械原点的精度,一般爬行速度都较小。参数配置表如下: + +【注】 : +※1:只适用于机械回原点指令ZRN。 +※2:爬行速度加速按照设定的加速斜率进行加速;脱离原点信号时急停或者开始记伺服驱动器Z相脉冲个 +数。 +※3:请勿将爬行速度设置的速度大于或者等于回原点速度VH,遵循“爬行速度<回原点速度VH”的原 +则。 +※4:爬行速度VC优先选用参数块中的设置,当用户配置了参数块中的爬行速度VC,则使用参数块中数 +值;当参数块中未配置,则使用公共参数中的爬行速度VC。 +(二十一)脉冲公共参数——机械原点位置 +【机械原点位置】 :是指当回机械原点结束后工作台的当前位置的数值。以Y0轴为例,回完机械原点 +后,设定当前位置HSD0(双字)或者HSD2(双字)数值。 +一般来说机械原点位置的当前值设为0,但是用户也可以根据自己的需要设定其它数值。当回机械原 +点完成后,对应脉冲输出端的累积脉冲寄存器的数值会自动更新成所设定的数值。参数配置表如下: + +【注】 : +※1:只适用于机械回原点指令ZRN。 +※2:如果Y0轴公共参数—脉冲单位选择的为脉冲个数,当返回机械原点结束时,会立即将机械原点设定 +值写入当前位置HSD0(双字) ;如果Y0轴公共参数—脉冲单位选择的为当量(1mm、0.1mm、0.01mm、 +1um) ,当返回机械原点结束时,会立即将机械原点设定值写入当前位置HSD2(双字) 。 +(二十二)脉冲公共参数——Z相个数 +【Z相个数】 :是指当遇到原点信号时,开始速度减速为零,延时后再反向加速至爬行速度,直至工 +作台脱离原点信号后,可以对伺服电机的Z相脉冲进行计数,当计数到设定的Z相脉冲值时立即停止爬行 +速度,结束回机械原点。参数配置表如下: + +【注】 : +※1:只适用于机械回原点指令ZRN。 + +===== 手册第49页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 39 + +※2:当Z相个数设定值设置为0时,表示不使用Z相脉冲捕捉功能,以爬行速度脱离原点的瞬间立即停 +止,结束回原点。 +※3:请尽量避免工作台以爬行速度脱离原点信号与Z相信号间隔时间过短,导致原点停止位置发生错误。 +※4:重新安装伺服电机后Z相信号可能会发生变化,请重新作调整。 +※5:如果是步进电机,可以通过外部接近开关信号充当Z相信号。 +(二十三)脉冲公共参数——快速定位指令默认参数块 +DRV、DRVI、DRV A使用的参数块号,默认使用第1套。 + +【注】 :本参数仅对V3.4.6b及以上固件版本普通PLC或V3.5.3b及以上固件版本以太网型PLC有效。 +(二十四)脉冲公共参数——插补指令默认参数块 +LIN、CW、CCW、ARC等插补指令使用的参数块号,默认使用第2套。 + +【注】 :本参数仅对V3.4.6b及以上固件版本普通PLC或V3.5.3b及以上固件版本以太网型PLC有效。 +二、第1套参数(第0、2、3、4套参数参见第1套参数) +【注】 : +※1:第0套参数仅V3.4.6b及以上固件版本普通PLC或V3.5.3b及以上固件版本以太网型PLC支持。 +※2:在用户需要频繁改动默认速度、加减速时间等参数时,建议用户使用第0套参数。 +(一)第 1套参数——脉冲默认速度/ 脉冲默认速度加速时间(ms)/脉冲默认速度减速时间(ms) +此处的三个参数主要用来定义脉冲加速与减速斜率,加减速斜率计算公式为: +加速斜率 =(脉冲默认速度-0)/ 脉冲默认速度加速时间; +减速斜率 =(脉冲默认速度-0)/ 脉冲默认速度减速时间。 +【脉冲默认速度】单位仍由【脉冲单位】是脉冲个数还是当量(1mm、0.1mm、0.01mm、1um)决 +定(即当脉冲单位选择的是脉冲个数时,此设定参数单位为Hz;当脉冲单位选择的是当量时,此设定参 +数单位为长度) 。参数配置表如下: + + +===== 手册第50页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +40 + +例1:当脉冲单位设置为脉冲个数,脉冲默认速度设置值为1000(Hz),脉冲默认速度加速时间 +(ms)设置值为100(ms),脉冲默认速度减速时间(ms)设置值为200(ms) ,脉冲起始速度、终止速度 +设置值为0(Hz) ;则表示当脉冲指令处于加速阶段时,脉冲频率加速每增加1000Hz所用的时间为 +100ms,而当处于减速阶段时,脉冲频率减速每减小1000Hz所用的时间为200ms。 +如果速度由0Hz加速至5000Hz,则加速时间一共为5000(Hz)/1000(Hz)*100ms=500ms;同样速度 +由5000Hz减速为0Hz,则减速时间一共为5000(Hz)/1000(Hz)*200ms=1000ms。 +起始速度0Hz +加速时间100ms 减速时间200ms +终止速度0Hz +加速斜率K1= +1000Hz +100ms = 10000Hz/s +减速斜率K2= +-1000Hz +200ms = -5000Hz/s +1000Hz +t0 + +例2:当脉冲单位设置为当量,同时当量单位为1mm,脉冲默认速度设置值为10(mm/s),脉冲默认速 +度加速时间(ms)设置值为100(ms),脉冲默认速度减速时间(ms)设置值为200(ms) ,脉冲起始速度、 +终止速度设置值为0(mm/s) ;则表示当脉冲指令处于加速阶段时,脉冲频率加速每增加10mm/s所用的时间 +为100ms,而当处于减速阶段时,脉冲频率减速每减小10mm/s所用的时间为200ms。 +如果速度由0Hz加速至50mm/s,则加速时间一共为50(mm/s)/10(mm/s)*100ms=500ms;同样速 +度由50mm/sHz加速为0Hz,则减速时间一共为50(mm/s)/10(mm/s)*200ms =1000ms。 +起始速度 +0mm/s +加速时间100ms 减速时间200ms +终止速度 +0mm/s +加速斜率K1= +10mm/s +100ms += 100mm/s +减速斜率K2= +-10mm/s +200ms += -50mm/s +10 +mm/s +t0 + +【注】 : +※1:此处三个参数主要用来定义加减速时间的斜率,脉冲具体的每一段的加减速时间全部通过设定好的 +斜率进行计算。 +※2:脉冲加速斜率由0加速到默认速度所需的脉冲默认速度加速时间决定;脉冲减速斜率由默认速度减速 +到0所需的脉冲默认速度减速时间决定。 +※3:此参数适用于PLSR、PLSF、DRVI、DRV A、ZRN指令。 +※4:起始速度与终止速度必须要小于设定的额定速度。 +※5:脉冲默认速度与设定的脉冲频率之间没有直接的关系,脉冲默认速度只为提供加减速斜率使用,但 +当脉冲频率设定为0时,按默认速度发脉冲。 +===== 手册第51页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 41 + +(二)第 1套参数——补间加减速时间(ms) +【补间加减速时间(ms)】:是指当设定了【齿轮间隙正向补偿】与【齿轮间隙负向补偿】时,间隙 +补偿的加减速时间。此加减速时间的加速时间与减速时间相同(不可分别设置) ,脉冲加速斜率由0加速 +到【脉冲默认速度】所需的【补间加减速时间(ms)】决定;脉冲减速斜率由【脉冲默认速度】减速到0 +所需的【补间加减速时间(ms)】决定。参数配置表如下: + +向右移动脉冲段 +t0 +f +齿轮间隙正向补偿 +频率 +补偿所用时间 +向左移动脉冲段 +t0 +f +齿轮间隙负向补偿 +频率 补偿所用时间 + +【注】 : +※1:此参数设置值为加速时间或者减速时间,加速时间与减速时间相同。 +※2:间隙补偿脉冲量的脉冲加速斜率由0加速到默认速度所需的补间加减速时间决定;脉冲减速斜率由默 +认速度减速到0所需的补间加减速时间决定。 +※3:此参数适用于PLSR、DRVI、DRV A指令。 +(三)第 1套参数——脉冲加减速模式 +【脉冲加减速模式】 :是指脉冲开始按照起始速度加速至设定频率过程中的加速模式和脉冲开始按照 +设定频率减速至起始频率过程中的减速模式。参数配置表如下: + +脉冲加减速模式:包含直线加减速、S曲线加减速和正弦曲线加减速三种模式,下面我们分别来了解 +一下这三种加减速模式。 +【直线加减速】 :是指脉冲在启动时,由起始速度开始加速至设定脉冲频率的速度变化成一条直线,由 +设定脉冲频率开始减速至终止频率的速度变化成一条直线,如下图所示: +加速直线 减速直线 +起始频率 终止频率 +设定频率 +t +Hz + + +===== 手册第52页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +42 + +【S曲线加减速】 :是指脉冲在启动时,由起始速度开始加速至设定脉冲频率的速度变化成一条S形曲 +线,由设定脉冲频率开始减速至终止频率的速度变化成一条S形曲线,如下图所示: +加速曲线 减速曲线 +起始频率 终止频率 +设定频率 +t +Hz + +【正弦曲线加减速】 :是指脉冲在启动时,由起始速度开始加速至设定脉冲频率的速度变化成一条正 +弦曲线,由设定脉冲频率开始减速至终止频率的速度变化成一条正弦曲线,如下图所示: +加速曲线 减速曲线 +起始频率 终止频率 +设定频率 +t +Hz + +正弦曲线加减速将脉冲段的启动加速段与停止减速段可以按照正弦波曲线加减速,这样的脉冲波形更 +加适合步进与伺服电机的接收,可以提高步进与伺服电机的运行性能;详细使用方式可以参考S形曲线加 +减速。 +【注】 :此参数适用于PLSR、DRVI、DRV A指令。 +(四)第 1套参数——最高速度 +【最高速度】 :是指程序中所有脉冲指令在执行第一套参数时,最高的脉冲个数输出频率不能够超过 +最高速度的设定值;如果超过最高速度的设定值将会按照最高速度的设定值进行运行。参数配置表如 +下: + +【注】 : +※1:最高速度设定值单位随脉冲单位设置为脉冲个数或者当量(1mm、0.1mm、0.01mm、1um)而变 +化。 +※2:在含有脉冲指令的程序中,务必要设置最高速度,默认值为100000;XD/XL全系列PLC的脉冲输出 +最高频率为100KHz,最高速度设置值不能超过此值。 +※3:当脉冲单位设置为当量时,转化为脉冲个数输出频率往往会比较大,极有可能会超出最高速度设置 +值,请注意! +※4:此参数适用于PLSR、DRVI、DRV A指令。 + + +===== 手册第53页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 43 + +(五)第 1套参数——起始速度/终止速度 +【起始速度】与【终止速度】是指脉冲指令开始执行时的起始频率与结束时的结束频率。一般脉冲 +的起始速度与终止速度都为0,但是一些特殊的场合需要脉冲刚开始指令从非0的速度开始加速(或者减 +速) ,脉冲结束的时候为非0的速度。参数配置表如下: + +例如,需要发送这样一段脉冲:脉冲总数为30000个脉冲,从1000Hz开始加速,经过100ms加速至 +5000Hz,发送至减速阶段时由5000Hz开始减速,经过50ms减速至2000Hz脉冲要刚好发送结束。起始速度 +与终止速度配置如下(其它配置省略) : + +脉冲参数配置 +t0 +频率 +1000 +5000 +2000 + +脉冲波形图 +【注】 : +※1:起始速度与终止速度设定值单位随脉冲单位设置为脉冲个数或者当量(1mm、0.1mm、0.01mm、 +1um)而变化。 +※2:起始速度与终止速度的设定值要小于设定的最高频率。 +※3:当脉冲单位设置为当量时,转化为脉冲个数输出频率往往会比较大,极有可能会超出最高速度设置 +值,请注意! +※4:在含有脉冲指令的程序中,务必要设置起始速度与终止速度;此参数的默认值为0。 +※5:此参数适用于 PLSR、PLSF、DRVI、DRV A、ZRN指令。 +※6:起始速度和终止速度在配置加减速时间参数的前提下生效。 +(六)第 1套参数——随动FOLLOW指令参数设置 +随动 FOLLOW指令主要用于从轴伺服电机或者步进电机随着主轴电机的状态运行(即从轴电机的运 +行状态要与主轴电机的运行状态保持一致) 。性能参数主要包括【FOLLOW性能参数】和【FOLLOW前馈 +补偿】 。 +FOLLOW指令是随动功能,通过编码器或者手摇脉冲发生器的脉冲反馈,PLC测量输入的脉冲实时 +脉冲频率以及脉冲个数,通过乘系数与除系数之间的比例关系,输出对应的,脉冲频率与脉冲个数控制 +步进或者伺服电机。 + 【FOLLOW性能参数】功能类似于伺服驱动器的刚性功能,当此参数设置值越小时,随动刚性越小 +(延时大) ;此参数设置值越大时,随动刚性越大(延时小) 。 + +===== 手册第54页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +44 + +【FOLLOW前馈补偿参数】是指从接收脉冲到发出脉冲,会有一定的延时,为了减少因此产生的滞 +后效应,可通过修改前馈补偿参数来进行补偿,让脉冲输出有一定的超前,来抵消这个滞后效应。但是 +如果前馈参数设置较大容易进入死循环,进而导致随动结束时电机不停的抖动。 +参数配置表如下: + +(七)第 1套参数——脉冲频率刷新时间 +脉冲频率刷新时间可由用户指定,100us或 1ms可选,默认为 1ms刷新一次。 + +(八)第 1套参数——回归速度VH +本参数的功能及使用同“脉冲公共参数——回归速度 VH(建议使用参数块) ” ,优先选用本参数。 + +【注】:本参数仅对V3.4.6及以上固件版本PLC有效。 +(九)第 1套参数——爬行速度VC +本参数的功能及使用同“脉冲公共参数——爬行速度 VC(建议使用参数块) ” ,优先选用本参数。 + +【注】:本参数仅对V3.4.6及以上固件版本PLC有效。 + + + +===== 手册第55页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 45 + +1-2-1-4. 脉冲中断标号 +【脉冲中断标号】 :是指多段脉冲输出指令 PLSR 最多可以配置 100 段,在执行过程中每执行完一段 +脉冲后, 都会立即产生一个中断 (除了PLSR指令发送脉冲结束后会进入脉冲中断外, 相对单段定位DRVI、 +绝对单段定位 DRV A发送完脉冲也会进入第一个脉冲中断,建议在中断程序中导通条件加一个导通PLSR +指令的常开开关) 。 +注意:配置的每一段脉冲段都有唯一对应的脉冲中断标号,无论脉冲配置表中的跳转设置如何配置, +只要当前段脉冲发送完成就执行该脉冲段的脉冲中断程序。如下表: +中断标号 对应脉冲轴 说明 +I60**(I6000~I6099) PLS+0(脉冲) Y0 轴脉冲 100 段中断处理子表地址 +I61**(I6100~I6199) PLS+1(脉冲) Y1 轴脉冲 100 段中断处理子表地址 +I62**(I6200~I6299) PLS+2(脉冲) Y2 轴脉冲 100 段中断处理子表地址 +I63**(I6300~I6399) PLS+3(脉冲) Y3 轴脉冲 100 段中断处理子表地址 +I64**(I6400~I6499) PLS+4(脉冲) Y4 轴脉冲 100 段中断处理子表地址 +I65**(I6500~I6599) PLS+5(脉冲) Y5 轴脉冲 100 段中断处理子表地址 +I66**(I6600~I6699) PLS+6(脉冲) Y6 轴脉冲 100 段中断处理子表地址 +I67**(I6700~I6799) PLS+7(脉冲) Y7 轴脉冲 100 段中断处理子表地址 +I68**(I6800~I6899) PLS+8(脉冲) Y10 轴脉冲 100 段中断处理子表地址 +I69**(I6900~I6999) PLS+9(脉冲) Y11轴脉冲 100段中断处理子表地址 +例 1:现配置连续的八段脉冲从第一段依次执行,脉冲端口为Y0,则产生的脉冲中断如下图: +t0 +频率 +I6000 I6001 I6002 I6003 I6004 I6005 I6006 I6007 +第1段 第2段 第3段 第4段 第5段 第6段 第7段 第8段 + +例 2:如果配置表中配置了六段脉冲指令,脉冲端口为 Y0,但是不是连续发送,脉冲配置表如下: + + +===== 手册第56页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +46 + +由上面的脉冲参数配置表中可知,脉冲指令 PLSR指令执行时,脉冲依次发送的顺序为:第 1段脉 +冲、第 3 段脉冲、第 5 段脉冲、第 2 段脉冲、第 6 段脉冲、第 4段脉冲,则产生的脉冲中断依次为 +I6000、I6002、I6004、I6001、I6005、I6003,如下图所示: +t0 +频率 +I6000 I6002 I6004 I6001 I6005 I6003 +1000 +2000 +3000 +4000 +5000 +6000 +1000 +3000 +5000 +6000 +4000 +2000 + +注意:脉冲中断程序与外部中断程序格式相同: +FEND +IRET +I6000 +DINC +SM0 +D0 +主程序 +END + + +===== 手册第57页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 47 + +1-2-1-5. 脉冲监控常用线圈/寄存器 +1)正在发脉冲标志位 +序号 线圈 轴数 说明 +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 +2)脉冲发送方向标志位 +序号 线圈 轴数 说明 +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 +3)机械回原点完成标志位 +序号 线圈 轴数 说明 +1 SM1004 Y0 当机械回原完成后线圈置 ON,利用线圈的上升沿判 +断脉冲发送是否结束。 +t0 +频率 +SM1004 +机械回原 + +注意:3.4.7及以上固件版本普通机型、3.7.2及以上 +固件版本网口机型、3.6.1b 及以上固件版本总线机型 +支持机械回原点完成标志。 +2 SM1024 Y1 +3 SM1044 Y2 +4 SM1064 Y3 +5 SM1084 Y4 +6 SM1104 Y5 +7 SM1124 Y6 +8 SM1144 Y7 +9 SM1164 Y10 +10 SM1184 Y11 + +===== 手册第58页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +48 + +4)高速脉冲特殊数据寄存器HSD(掉电记忆) +编号 功能 说明 轴数 +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 位 +===== 手册第59页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 49 + +1-2-2.多段脉冲输出 [PLSR] +1-2-2-1. 指令用法介绍 +1)指令概述 +多段脉冲输出指令。 +多段脉冲输出[PLSR] +16位指令 - 32 位指令 PLSR +执行条件 上升/下降沿线圈触发 适用机型 XD/XL系列(不含 XD1/XL1) +固件要求 - 软件要求 - +2)操作数 +操作数 作用 类型 +S0 指定脉冲数据起始地址 32位,双字 +S1 指定用户参数块起始地址 32位,双字 +S2 指定系统参数块(0~4) 32位,双字 +D 指定脉冲输出端口编号 位 +3)适用软元件 +操 +作 +数 +字软元件 位软元件 +系统 常数 模块 系统 +D FD SD SFD TD CD DX DY DM DS K/H ID QD X Y M S T C Dn.m +S0 ● ● ● ● ● ● ● ● +S1 ● ● ● ● ● ● ● ● +S2 ● ● ● ● ● ● ● ● ● +D ● +注:D 表示 D、HD;TD表示 TD、HTD、ETD;CD表示 CD、HCD、HSCD、HSD;DM 表示 DM、DHM;DS表示 DS、 +DHS。M表示 M、HM、SM;S表示 S、HS;T表示 T、HT、ET;C表示 C、HC、HSC。 +4)功能和动作 +《指令形式》 +PLSR HD0 HD100 K1 +RST M0 +M0 +SM1000 +S1· S2· D· +Y0 +S0· + +⚫ S0指定【数据起始地址】 ,详见1-2-1-1节。 +⚫ S1指定【用户参数地址】 ,详见1-2-1-2节。 +⚫ S2指定【系统参数块】的编号,K0~K4可选,详见 1-2-1-3 节。 +⚫ D 指定【脉冲输出端子】 ,各型号可选脉冲输出端子见1-1 节。 +⚫ 脉冲频率范围:1Hz~100KHz(XD3-24T4/32T4的 Y2、Y3 为 10KHz);数值变大为加速,数值变 +小为减速,与脉冲方向无关。 +⚫ 脉冲个数:K-2,147,483,648 ~ K2,147,483,647;当设定为负数时表示反向发脉冲。 +⚫ 加减速在系统参数中设置,指令适用的系统参数由S2指定,指令加减速时间计算方式详见 1-2-1- +3节默认速度参数设置。 +⚫ 当 M0由 OFF→ON时,执行脉冲指令 PLSR,即使 M0断开,脉冲仍然发送,直至发送完成。 +⚫ 要想中断脉冲输出,请使用 STOP指令。 +⚫ 发送脉冲过程中,Y0对应的发脉冲标志位 SM1000置 ON,脉冲发送完毕,SM1000置 OFF。 +===== 手册第60页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +50 + +⚫ Y0 端子的累计脉冲个数保存在 HSD0(双字)中,当前段脉冲个数保存在 SD1002(双字)中, +更多关于脉冲的特殊寄存器见 1-6节。 +⚫ PLSR 指令每段的脉冲频率在脉冲指令执行过程中修改后立即生效!其他参数修改后不会立即生 +效,只会在下一次条件导通时才生效。 +⚫ 绝对模式时,如果设定的脉冲个数和累计脉冲数(HSD0)相等时,SM1000无动作、不会有下降 +沿。 +【注】 :PLC可输出 100KHz~200KHz的高速脉冲(XD3-24T4/32T4的 Y2、Y3为 10KHz),但无法保 +证所有伺服都正常运行,请在输出端和 24V电源之间接入约 500Ω电阻,电阻功率需>2W。 +5)指令的添加及配置 +(1)PLSR指令写法如下: +PLSR HD0 HD100 K1 +RST M0 +M0 +SM1000 +S1· S2· D· +Y0 +S0· + +(2)写程序并下载后,在指令上右击,选择“PLSR指令参数配置” ,打开多段脉冲配置框。 + +(3)PLSR配置脉冲与频率有两种方法 +方法一:点击添加,在表格中添加脉冲频率、脉冲个数、等待条件等,添加完后先写入 PLC,再点击 +确定。 + +方法二:在触摸屏或其他设备中配置,根据数据起始地址,找到对应段数的存放脉冲频率和个数的寄 +存器。如本例的起始地址 S0为 HD0,由本手册1-2-1-1 节中的表格可知,第一段的脉冲频率和脉冲个数分 + +===== 手册第61页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 51 + +别为 HD10、HD12,第二段为 HD20和 HD22,以此类推。除此之外,HD0脉冲总段数也必须设置,否则 +程序运行会报错。数据起始地址的具体说明详见本手册 1-2-1-1 节。 + +数据起始地址(S0)说明: +地址 说明 数值 备注 +HD0(双字) (S0+0) 脉冲总段数(1~100) 4 +HD2(8个字) (S0+2) 保留 0 +HD10(双字) (S0+10) 第 1段脉冲频率(#1) 2000 +第 1 段 +HD12(双字) (S0+12) 第 1段脉冲个数(#1) 3000 +HD14(S0+14) +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +256 +HD15(双字) (S0+15) 常数值/寄存器编号(等待条件的) (#1) 100 +第 1 段 HD17(S0+17) +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD18(双字) (S0+18) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD20(双字) (S0+20) 第 2段脉冲频率(#2) 2800 第 2 段 HD22(双字) (S0+22) 第 2段脉冲个数(#2) 7000 + +===== 手册第62页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +52 + +地址 说明 数值 备注 +HD24(S0+24) 等待条件,等待条件寄存器类型(#2) 517 +HD25(双字) (S0+25) 常数值或者寄存器编号(等待条件的) (#2) 100 +HD27(S0+27) 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD28(双字) (S0+28) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 +HD30(双字) (S0+30) 第 3段脉冲频率(#3) 1200 +第 3 段 +HD32(双字) (S0+32) 第 3段脉冲个数(#3) 999999999 +HD34(S0+34) 等待条件,等待条件寄存器类型(#3) 768 +HD35(双字) (S0+35) 常数值或者寄存器编号(等待条件的) (#3) 2000 +HD37(S0+37)(S0+37) 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD38(双字) (S0+38) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 +HD40(双字) (S0+40) 第 4段脉冲频率(#3) 3000 +第 4 段 +HD42(双字) (S0+42 第 4段脉冲个数(#3) 999999999 +HD44(S0+44) 等待条件,等待条件寄存器类型(#3) 1028 +HD45(双字) (S0+45) 常数值或者寄存器编号(等待条件的) (#3) 2 +HD47(S0+47) 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD48(双字) (S0+48) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 +…… …… …… …… +S0+N*10+0(双字) 第 N 段脉冲频率 +S0+N*10+2(双字) 第 N 段脉冲个数 +S0+N*10+4 等待条件,等待条件寄存器类型 +S0+N*10+5(双字) 常数值或者寄存器编号(等待条件) +S0+N*10+7 跳转寄存器的类型 +S0+N*10+8(双字) 常数值或者寄存器编号(跳转寄存器) +(4)配置方向与加减速时间 +单击参数,在公共参数中配置方向端子,根据使用的系统参数块配置加减速时间。该程序举例中系统 +参数块为 K1,即代表使用第一套参数,在第一套参数中配置默认速度、加减速时间。 +【注】 : +※1:加减速时间与默认速度构成加减速时间的斜率。例如默认速度为1000Hz,加减速时间为 10ms,即频 +率每变化 1000Hz需要 10ms,直到变为设定速度为止。 +※2:第 1-4套参数不可频繁修改,需频繁修改或在触摸屏上设置加减速时间,请使用第0套参数,参数地 +址可在软件中查看,参见图 4。 + +图 1 + +===== 手册第63页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 53 + + +图 2 + +图 3 + +图 4 + +===== 手册第64页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +54 + + +图 5 +6)指令模式 +3000 +10000 +50000 +20000 60000 5000 +f +f2 +f1 +f3 +Ut + +脉冲波形图 + +脉冲指令参数配置表 + + +===== 手册第65页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 55 + +7)具体实现 +以下曲线都是在曲线加速时间为 0的情况下设定多段脉冲输出情况。 +(1)脉冲段完成方式的划分 +第1段 +第2段 第3段 +第4段 + +⚫ 多段脉冲完成方式波形图按照上图所示进行分段。 +⚫ 除最后一段脉冲外,每个脉冲段都是由上升或下降部分和平稳部分构成。 +⚫ 最后一段脉冲有上升或下降部分、平稳部分、上升或下降部分构成。 +(2)脉冲段后续方式的划分 +第1段 第2段 第3段 第4段 + +⚫ 多段脉冲后续方式波形图按照上图所示进行分段。 +⚫ 发送本段个数脉冲完成时,已切换到后续段速度,在此处分段除第一段脉冲外,每个脉冲段都是 +由平稳部分、上升或下降部分构成。 +⚫ 第一段脉冲有上升或下降部分、平稳部分、上升或下降部分构成。 +(3)单段脉冲波形 +⚫ 脉冲个数足够多的情况:脉冲能到达用户设定的最高频率,波形形状为梯形; +单段脉冲 + +⚫ 脉冲个数少的情况:脉冲波形为三角形; +用户设定频率 +终止频率 +起始频率 + +(4)1 段脉冲发送(不是最后一段) +Tu +T +Svb +V + +⚫ V:用户设定当前段的频率; +⚫ S:当前段脉冲个数; +⚫ Vb:当前段起始频率; +⚫ T:当前脉冲段发送的时间。 +⚫ Tu:脉冲上升/下降段时间(Tu = (V-VB) / K,K 为上升下降斜率) 。 +===== 手册第66页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +56 + +(5)最后一段脉冲 +Vb +Vm +Ve +Vm +Vb +Ve +1 2 3 +1 2 +3 + +⚫ 最后一段脉冲:由上升/下降段和平稳段和上升/下降段,共 3部分组成。 +(6)脉冲个数为0 段 +⚫ 如果当前段脉冲频率为 0或者脉冲个数为 0,则按照脉冲默认速度发送脉冲。 +(7)动态修改当前脉冲频率 +⚫ 非最后一段 +脉冲个数足够多的情况 脉冲个数较少的情况 +用户设定当前目标频率 +前一时刻用户设定目标频率 + +当用户改变当前脉冲频率的后,按照上升下降斜率加减速到目标频率。 +⚫ 最后一段 +目标频率更改 +用户设定当前目标频率 +前一时刻用户设定目标频率 + +当用户改变当前脉冲频率后,PLC重新计算脉冲曲线,然后按照新的脉冲曲线形状发送。 + +===== 手册第67页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 57 + +1-2-2-2. 指令用法举例 +1)例1 +现需要发送连续的 3段脉冲,脉冲端子为 Y0,脉冲方向端子为 Y2,每段的脉冲频率、脉冲数与加减 +速如下表所示: +名称 频率设定值(Hz) 脉冲数设定值 +第 1 段脉冲 1000 2000 +第 2 段脉冲 200 1000 +第 3 段脉冲 2000 6000 +加减速 每 100ms频率变化 1000Hz +(1)脉冲发送波形图 +t0 +频率 +200 +1000 +2000 + + + +(2)脉冲指令 +PLSR HD0 HD100 K1 +RST M0 +M0 +SM1000 +Y0 + +(3)上位机软件配置方法 +⚫ 脉冲段配置 + + +===== 手册第68页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +58 + +⚫ 脉冲配置参数 + +脉冲配置参数一 + +脉冲配置参数二 + +脉冲配置参数三 + +===== 手册第69页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 59 + + +脉冲配置参数四 + +⚫ 脉冲数据地址分配如下表: +地址 说明 数值 +HD0(双字) 脉冲总段数(1~100) 3 +HD2(8个字) 保留 0 +HD10(双字) 脉冲频率(#1) 1000 +HD12(双字) 脉冲个数(#1) 2000 +HD14 +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +0 +HD15(双字) 常数值/寄存器编号(等待条件的) (#1) 0 +HD17 +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD+18(双字) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD+20(双字) 脉冲频率(#2) 200 +HD+22(双字) 脉冲个数(#2) 1000 + +===== 手册第70页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +60 + +地址 说明 数值 +HD+24 等待条件,等待条件寄存器类型(#2) 0 +HD+25(双字) 常数值或者寄存器编号(等待条件的) (#2) 0 +HD+27 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD+28(双字) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 +HD+30(双字) 脉冲频率(#3) 2000 +HD+32(双字) 脉冲个数(#3) 6000 +HD+34 等待条件,等待条件寄存器类型(#3) 0 +HD+35(双字) 常数值或者寄存器编号(等待条件的) (#3) 0 +HD+37 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD+38(双字) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 + +⚫ 系统参数块起始地址: +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑;1:负逻辑,默认为 0 +Bit 2:启用软限位功能 +0:不启用;1:启用 默认为 0 +Bit 3:机械回原点方向 +0:负向;1:正向 默认为 0 +Bit 10~8:脉冲单位 +Bit8:0:脉冲个数,1:当量 + 000:脉冲个数 + 001:微米 + 011:忽米 + 101:丝米 + 111:毫米 + 默认为 000 +Bit13:脉冲类型 +0:单向脉冲,1:AB相脉冲;默认为 0 +Bit15:插补坐标模式 +0:十字坐标;默认为 0 +0 +公 +共 +参 +数 +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 0 +SFD902 脉冲数/1转低 16 位 1 +SFD903 脉冲数/1转高 16 位 0 +SFD904 移动量/1转低 16 位 1 +SFD905 移动量/1转高 16 位 0 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 2 +SFD907 方向延时时间 默认是 20,单位:ms 20 +SFD908 齿轮间隙正向补偿 0 +SFD909 齿轮间隙负向补偿 0 +SFD910 电气原点位置低 16位 0 +SFD911 电气原点位置高 16位 0 +===== 手册第71页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 61 + +SFD912 信号端子开关状态设置 +Bit0:原点信号开关状态设置 +Bit1:Z 相开关状态设置 +Bit2:正极限开关状态设置 +Bit3:负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻辑); +默认为 0 +0 +SFD914 Z 相端子设定 Bit0~bit7:指定 X 端子的编号,0xFF 为 +无端子(中断) 0xFF +SFD915 极限端子设定 +Bit7~bit0:指定正极限的 X 端子编号, +0xFF为无端子 +Bit15~bit8:指定负极限的 X 端子编号, +0xFF为无端子 +FFFF +SFD917 归零清除 CLR 信号输出端子设 +定 +Bit0~Bit7:指定 Y 端子的编号,0xFF 为 +无端子 0xFF +SFD918 回归速度 VH 低 16位 0 +SFD919 回归速度 VH 高 16位 0 +SFD922 爬行速度 VC低 16 位 0 +SFD923 爬行速度 VC高 16 位 0 +SFD924 机械原点位置低 16位 0 +SFD925 机械原点位置高 16位 0 +SFD926 Z 相个数 0 +SFD927 CLR信号延时时间 默认 20,单位:ms 20 +SFD930 软限位正极限值 低 16 位 0 +SFD931 高 16 位 0 +SFD932 软限位负极限值 低 16 位 0 +SFD933 高 16 位 0 +… +SFD950 脉冲默认速度低 16位 速度为 0 的时候采用默认速度发送脉冲 1000 +第 +一 +套 +参 +数 +SFD951 脉冲默认速度高 16位 0 +SFD952 脉冲默认速度加速时间 100 +SFD953 脉冲默认速度减速时间 100 +SFD954 补间加减速时间 0 +SFD955 脉冲加减速模式 +Bit 1~0:加减速段加减速方式 +00:直线加减速 +01:S曲线加减速 +10:正弦曲线加减速 +11:保留 +Bit 15~2:保留 + +SFD956 最高速度限制低 16位 3392 +SFD957 最高速度限制高 16位 3 +SFD958 起始速度低 16位 0 +SFD959 起始速度高 16位 0 +SFD960 终止速度低 16位 0 +SFD961 终止速度高 16位 0 +===== 手册第72页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +62 + +SFD962 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick +50 +SFD963 Follow前馈补偿参数 0~100,百分比 0 +… +【注】 : +※1:由于 PLSR脉冲输出指令需要配置的参数较多,建议用户直接通过脉冲配置参数表直接进行配置,这 +样不容易出现错误; +※2: 如客户需要自己在触摸屏上设定每段脉冲的脉冲频率和脉冲数时, 可以先通过脉冲配置表进行配置 (必 +须进行配置) ; 配置完后在梯形图中给每段的脉冲频率和脉冲个数通过DMOV指令结合寄存器赋值 (S0+N*10+0、 +S0+N*10+2)即可。 +例如本例中: +DMOV HD200 HD10 //HD200 为触摸屏第 1段脉冲频率设定 +DMOV HD202 HD12 //HD202 为触摸屏第 1段脉冲个数设定 +DMOV HD204 HD20 //HD204 为触摸屏第 2段脉冲频率设定 +DMOV HD206 HD22 //HD206 为触摸屏第 2段脉冲个数设定 +DMOV HD208 HD30 //HD208 为触摸屏第 3段脉冲频率设定 +DMOV HD210 HD32 //HD210 为触摸屏第 3段脉冲个数设定 +也可通过触摸屏直接给寄存器 HD10、HD12、HD20、HD22、HD30、HD32设定脉冲频率和脉冲个数数值。 +2)例2 +工位原点 A点(X0) B点(X1) C点(X2) + +如上图所示,现需要正向走 3 段工位,由于 A、B、C三点的位置是不确定(会随机变动) ,且要求从 +工位原点到 A 点、A 点到 B 点、B 点到 C 点三段的移动速度都不一样但都是固定的,在这种情况下我们 +可以通过对 PLSR指令的功能配置来实现;我们可以分别在A、B、C三点的位置安装一个接近开关,接入 +PLC 的 X0、X1、X2 三个端子(如果是 PLC 的外部中断输入端子则会更快) ;脉冲端子为Y0,方向端子 +由 Y2 来控制,每段的脉冲频率、加减速时间与对应的寄存器如下表所示: +名称 频率设定值 +(Hz) +脉冲数设定值 +原点至 A点 1000 999999999 +A 点至 B点 3000 999999999 +B点至 C点 2000 999999999 +加减速时间 每 100ms频率变化 1000Hz +注意:由于每段的脉冲数目是不定的,所以每段的脉冲数目设定一个很大的值,保证工位足可以碰到 +各位置的接近开关;当到达 C点位置时通过脉冲 STOP指令执行急停。 + +===== 手册第73页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 63 + +(1)示意图 +0 A点(X0) B点(X1) C点(X2) +M0 +频率f +2000Hz +1000Hz +3000Hz + +(2)脉冲指令 +PLSR HD0 HD100 K1 +RST M0 +M0 +SM1000 +X2 +STOP Y0 K1 +Y0 + + + + +(3)上位机软件配置方法 +⚫ 脉冲段配置 + + + +===== 手册第74页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +64 + +⚫ 脉冲配置参数 + +脉冲配置参数一 + +脉冲配置参数二 + +脉冲配置参数三 + +===== 手册第75页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 65 + + +脉冲配置参数四 +⚫ 脉冲数据地址分配如下表: +地址 说明 数值 +HD0(双字) 脉冲总段数(1~100) 3 +HD2(8个字) 保留 0 +HD10(双字) 脉冲频率(#1) 1000 +HD12(双字) 脉冲个数(#1) 999999999 +HD14 +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +1028 +HD15(双字) 常数值/寄存器编号(等待条件的) (#1) 0 +HD17 +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD+18(双字) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD+20(双字) 脉冲频率(#2) 3000 +HD+22(双字) 脉冲个数(#2) 999999999 +HD+24 等待条件,等待条件寄存器类型(#2) 1028 + +===== 手册第76页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +66 + +地址 说明 数值 +HD+25(双字) 常数值或者寄存器编号(等待条件的) (#2) 1 +HD+27 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD+28(双字) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 +HD+30(双字) 脉冲频率(#3) 2000 +HD+32(双字) 脉冲个数(#3) 999999999 +HD+34 等待条件,等待条件寄存器类型(#3) 1028 +HD+35(双字) 常数值或者寄存器编号(等待条件的) (#3) 2 +HD+37 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD+38(双字) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 + +⚫ 系统参数块起始地址: +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑;1:负逻辑,默认为 0 +Bit 2:启用软限位功能 +0:不启用;1:启用 默认为 0 +Bit 3:机械回原点方向 +0:负向;1:正向 默认为 0 +Bit 10~8:脉冲单位 +Bit8:0:脉冲个数,1:当量 + 000:脉冲个数 + 001:微米 + 011:忽米 + 101:丝米 + 111:毫米 + 默认为 000 +Bit13:脉冲类型 +0:单向脉冲,1:AB相脉冲;默认为 0 +Bit15:插补坐标模式 +0:十字坐标; 默认为 0 +0 +公 +共 +参 +数 +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 +SFD902 脉冲数/1转低 16位 1 +SFD903 脉冲数/1转高 16位 0 +SFD904 移动量/1转低 16位 1 +SFD905 移动量/1转高 16位 0 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 2 +SFD907 方向延时时间 默认是 20,单位:ms 20 +SFD908 齿轮间隙正向补偿 0 +SFD909 齿轮间隙负向补偿 0 +SFD910 电气原点位置低 16 位 0 +SFD911 电气原点位置高 16 位 0 +===== 手册第77页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 67 + +SFD912 信号端子开关状态设置 +Bit0:原点信号开关状态设置 +Bit1:Z 相开关状态设置 +Bit2:正极限开关状态设置 +Bit3:负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻 +辑);默认为 0 +0 +SFD914 Z 相端子设定 Bit0~Bit7:指定 X 端子的编号,0xFF 为 +无端子(中断) 0xFF +SFD915 极限端子设定 +Bit7~Bit0:指定正极限的 X 端子编号, +0xFF为无端子 +Bit15~Bit8:指定负极限的 X 端子编号, +0xFF为无端子 +FFFF +SFD917 归零清除 CLR信号输出端子设 +定 +Bit0~Bit7:指定 Y 端子的编号,0xFF 为 +无端子 0xFF +SFD918 回归速度 VH 低 16位 0 +SFD919 回归速度 VH 高 16位 0 +SFD922 爬行速度 VC低 16位 0 +SFD923 爬行速度 VC高 16位 0 +SFD924 机械原点位置低 16 位 0 +SFD925 机械原点位置高 16 位 0 +SFD926 Z 相个数 0 +SFD927 CLR信号延时时间 默认 20,单位:ms 20 +SFD930 软限位正极限值 低 16 位 0 +SFD931 高 16 位 0 +SFD932 软限位负极限值 低 16 位 0 +SFD933 高 16 位 0 +… +SFD950 脉冲默认速度低 16 位 速度为 0 的时候采用默认速度发送脉冲 1000 +第 +一 +套 +参 +数 +SFD951 脉冲默认速度高 16 位 0 +SFD952 脉冲默认速度加速时间 100 +SFD953 脉冲默认速度减速时间 100 +SFD954 补间加减速时间 0 +SFD955 脉冲加减速模式 +Bit 1~0:加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +Bit 15~2:保留 +0 +SFD956 最高速度限制低 16 位 3392 +SFD957 最高速度限制高 16 位 3 +SFD958 起始速度低 16位 0 +SFD959 起始速度高 16位 0 +SFD960 终止速度低 16位 0 +SFD961 终止速度高 16位 0 +===== 手册第78页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +68 + +SFD962 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick +50 +SFD963 Follow前馈补偿参数 0~100,百分比 0 +… +【注】 : +※1:由于 PLSR脉冲输出指令需要配置的参数较多,建议用户直接通过脉冲配置参数表直接进行配置,这 +样不容易出现错误; +※2:如客户需要自己在触摸屏上设定每段脉冲的脉冲频率和脉冲数时, 可以先通过脉冲配置表进行配置 (必 +须进行配置) ; 配置完后在梯形图中给每段的脉冲频率和脉冲个数通过DMOV指令结合寄存器赋值 (S0+N*10+0、 +S0+N*10+2)即可。 +例如本例中: +DMOV HD200 HD10 //HD200 为触摸屏第 1段脉冲频率设定 +DMOV HD202 HD12 //HD202 为触摸屏第 1段脉冲个数设定 +DMOV HD204 HD20 //HD204 为触摸屏第 2段脉冲频率设定 +DMOV HD206 HD22 //HD206 为触摸屏第 2段脉冲个数设定 +DMOV HD208 HD30 //HD208 为触摸屏第 3段脉冲频率设定 +DMOV HD210 HD32 //HD210 为触摸屏第 3段脉冲个数设定 +也可通过触摸屏直接给寄存器 HD10、HD12、HD20、HD22、HD30、HD32设定脉冲频率和脉冲个数数值。 +3)例3 +现需要发这样四段脉冲:第 1 段的脉冲频率为 2000Hz,脉冲数目为 3000;第 1 段脉冲发送完后延时 +100ms 开始发送第 2 段脉冲,第 2 段脉冲频率为 2800Hz,脉冲数目为 4000;第 2 段脉冲发送完后等待线 +圈 M100, 当线圈M100置位后立即开始发送第3段脉冲, 第3段脉冲频率为 1200Hz, 脉冲数目为999999999, +从脉冲开始发送ACT延时 2s后立即切换到第4段脉冲; 第4段脉冲频率为3000Hz, 脉冲数目为999999999, +当外部信号触发输入点 X2 时立即减速停止脉冲;脉冲加速斜率为每加速 1000Hz为 80ms,脉冲减速斜率 +为每减速 1000Hz为 120ms,脉冲方向端子为 Y2。 +(1)脉冲波形如下图所示: +t0 +频率 +2000 +2800 +3000 +1200 +延时100ms ACT延时 +时间 +M100 +X2 等待线圈M100 + +(2)脉冲指令 +PLSR HD0 HD100 K1 +M0 +Y0 + + +===== 手册第79页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 69 + +(3)脉冲数据配置 +⚫ 脉冲段配置 + +脉冲数据配置(相对模式) + +脉冲数据配置(绝对模式) +⚫ 系统参数块配置 + +系统参数块配置一 + +===== 手册第80页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +70 + + +系统参数块配置二 + +系统参数块配置三 + +系统参数块配置四 + + +===== 手册第81页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 71 + +⚫ 脉冲数据地址分配如下表(相对模式) : +地址 说明 数值 +HD0(双字) 脉冲总段数(1~100) 4 +HD2(8个字) 保留 0 +HD10(双字) 脉冲频率(#1) 2000 +HD12(双字) 脉冲个数(#1) 3000 +HD14 +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +256 +HD15(双字) 常数值/寄存器编号(等待条件的) (#1) 100 +HD17 +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD+18(双字) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD+20(双字) 脉冲频率(#2) 2800 +HD+22(双字) 脉冲个数(#2) 7000 +HD+24 等待条件,等待条件寄存器类型(#2) 517 +HD+25(双字) 常数值或者寄存器编号(等待条件的) (#2) 100 +HD+27 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD+28(双字) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 +HD+30(双字) 脉冲频率(#3) 1200 +HD+32(双字) 脉冲个数(#3) 999999999 +HD+34 等待条件,等待条件寄存器类型(#3) 768 +HD+35(双字) 常数值或者寄存器编号(等待条件的) (#3) 2000 +HD+37 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD+38(双字) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 +HD+40(双字) 脉冲频率(#3) 3000 +HD+42(双字) 脉冲个数(#3) 999999999 +HD+44 等待条件,等待条件寄存器类型(#3) 1028 +HD+45(双字) 常数值或者寄存器编号(等待条件的) (#3) 2 +HD+47 跳转类型,跳转寄存器的类型(等待条件的) (#3) 0 +HD+48(双字) 常数值或者寄存器编号(跳转寄存器的) (#3) 0 + +===== 手册第82页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +72 + +⚫ 系统参数块起始地址: +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑;1:负逻辑,默认为 0 +Bit 2:启用软限位功能 +0:不启用;1:启用 默认为 0 +Bit 3:机械回原点方向 +0:负向;1:正向 默认为 0 +Bit 10~8:脉冲单位 +bit8:0:脉冲个数,1:当量 + 000:脉冲个数 + 001:微米 + 011:忽米 + 101:丝米 + 111:毫米 + 默认为 000 +Bit13:脉冲类型 +0:单向脉冲,1:AB相脉冲;默认为 0 +Bit15:插补坐标模式 +0:十字坐标;默认为 0 +0 +公 +共 +参 +数 +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 +SFD902 脉冲数/1转低 16位 1 +SFD903 脉冲数/1转高 16位 0 +SFD904 移动量/1转低 16位 1 +SFD905 移动量/1转高 16位 0 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 2 +SFD907 方向延时时间 默认是 20,单位:ms 20 +SFD908 齿轮间隙正向补偿 0 +SFD909 齿轮间隙负向补偿 0 +SFD910 电气原点位置低 16 位 0 +SFD911 电气原点位置高 16 位 0 +SFD912 信号端子开关状态设置 +Bit0:原点信号开关状态设置 +Bit1:Z 相开关状态设置 +Bit2:正极限开关状态设置 +Bit3:负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻 +辑);默认为 0 +0 +SFD914 Z 相端子设定 Bit0~bit7:指定 X 端子的编号,0xFF 为 +无端子(中断) 0xFF +SFD915 极限端子设定 +Bit7~bit0:指定正极限的 X 端子编号, +0xFF为无端子 +Bit15~bit8:指定负极限的 X 端子编号, +0xFF为无端子 +FFFF +SFD917 归零清除 CLR信号输出端子设 +定 +Bit0~bit7:指定 Y 端子的编号,0xFF 为 +无端子 0xFF +SFD918 回归速度 VH 低 16位 0 +===== 手册第83页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 73 + +SFD919 回归速度 VH 高 16位 0 +SFD922 爬行速度 VC低 16位 0 +SFD923 爬行速度 VC高 16位 0 +SFD924 机械原点位置低 16 位 0 +SFD925 机械原点位置高 16 位 0 +SFD926 Z 相个数 0 +SFD927 CLR信号延时时间 默认 20,单位:ms 20 +SFD930 软限位正极限值 低 16 位 0 +SFD931 高 16 位 0 +SFD932 软限位负极限值 低 16 位 0 +SFD933 高 16 位 0 +… +SFD950 脉冲默认速度低 16 位 速度为 0 的时候采用默认速度发送脉冲 1000 +第 +一 +套 +参 +数 +SFD951 脉冲默认速度高 16 位 0 +SFD952 脉冲默认速度加速时间 100 +SFD953 脉冲默认速度减速时间 100 +SFD954 补间加减速时间 0 +SFD955 脉冲加减速模式 +bit 1~0: 加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +bit 15~2:保留 +0 +SFD956 最高速度限制低 16 位 3392 +SFD957 最高速度限制高 16 位 3 +SFD958 起始速度低 16位 0 +SFD959 起始速度高 16位 0 +SFD960 终止速度低 16位 0 +SFD961 终止速度高 16位 0 +SFD962 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick 50 +SFD963 Follow前馈补偿参数 0~100,百分比 0 +… +【注】 : +※1:由于 PLSR脉冲输出指令需要配置的参数较多,建议用户直接通过脉冲配置参数表直接进行配置,这 +样不容易出现错误; +※2:如客户需要自己在触摸屏上设定每段脉冲的脉冲频率和脉冲数时, 可以先通过脉冲配置表进行配置 (必 +须进行配置) ; 配置完后在梯形图中给每段的脉冲频率和脉冲个数通过DMOV指令结合寄存器赋值 (S0+N*10+0、 +S0+N*10+2)即可。 +例如本例中: +DMOV HD200 HD10 //HD200 为触摸屏第 1段脉冲频率设定 +DMOV HD202 HD12 //HD202 为触摸屏第 1段脉冲个数设定 +DMOV HD204 HD20 //HD204 为触摸屏第 2段脉冲频率设定 +DMOV HD206 HD22 //HD206 为触摸屏第 2段脉冲个数设定 +DMOV HD208 HD30 //HD208 为触摸屏第 3段脉冲频率设定 +===== 手册第84页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +74 + +DMOV HD210 HD32 //HD210 为触摸屏第 3段脉冲个数设定 +DMOV HD212 HD40 //HD212 为触摸屏第 4段脉冲频率设定 +DMOV HD214 HD42 //HD214 为触摸屏第 4段脉冲个数设定 +也可通过触摸屏直接给寄存器 HD10、HD12、HD20、HD22、HD30、HD32、HD40、HD42 设定脉冲频率 +和脉冲个数数值。 +4)例4 +有如下传动机构,一台伺服驱动器(电子齿轮比默认1:1)控制一台伺服电机(编码器2500线) ,通 +过减速比为 1:2 的减速机连接到滚珠丝杠上,滚珠丝杠的螺距为10mm,滚珠丝杠带动一个能左右移动的 +工作台。现需要将工作台从左往右移动200mm,紧接着再反向移动 200mm,速度均为 20mm/s,加速时间 +为 100ms,减速时间为 200ms,脉冲方向端子为 Y2。机械结构与脉冲运行示意图分别如下图: + + 伺服电机 减速 +机构 +丝杠螺距:10mm负载轴 +机械结构示意图 +通过上面的内容,我们可以计算出: +脉冲数(1转)= 20000 = 2500 * 4 * 2 +1 +移动量(1转)= 螺距 = 10mm +20mm/s = * 20000 = 40000pulse/s 20mm +10mm + +最高脉冲输出频率为 40K/s,远小于 100K/s,所以可以正常运行。 + +(1)脉冲波形如下图所示: +t +20mm/s +20mm/s +t1 +t2 +t1 t2 +0 +t1=100ms +t2=200ms + +(2)脉冲指令 +PLSR HD0 HD100 K1 +M0 +Y0 + + +===== 手册第85页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 75 + +(3)脉冲数据配置 +⚫ 脉冲段配置 + +脉冲数据配置(相对模式) + +脉冲数据配置(绝对模式) +⚫ 系统参数块配置(相对) + +系统参数块配置一 + +===== 手册第86页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +76 + + +系统参数块配置二 + +系统参数块配置三 + +系统参数块配置四 +⚫ 脉冲数据地址分配如下表(相对模式) : +地址 说明 数值 +HD0(双字) 脉冲总段数(1~100) 2 +HD2(8个字) 保留 0 + +===== 手册第87页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 77 + +地址 说明 数值 +HD10(双字) 脉冲频率(#1) 20 +HD12(双字) 脉冲个数(#1) 200 +HD14 +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +0 +HD15(双字) 常数值/寄存器编号(等待条件的) (#1) 0 +HD17 +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD+18(双字) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD+20(双字) 脉冲频率(#2) 20 +HD+22(双字) 脉冲个数(#2) -200 +HD+24 等待条件,等待条件寄存器类型(#2) 0 +HD+25(双字) 常数值或者寄存器编号(等待条件的) (#2) 0 +HD+27 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD+28(双字) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 +⚫ 系统参数块起始地址: +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑;1:负逻辑,默认为 0 +Bit 2:启用软限位功能 +0:不启用;1:启用 默认为 0 +Bit 3:机械回原点方向 +0:负向;1:正向 默认为 0 +Bit 10~8:脉冲单位 +bit8:0:脉冲个数,1:当量 + 000:脉冲个数 + 001:微米 + 011:忽米 + 101:丝米 + 111:毫米 + 默认为 000 +Bit15:插补坐标模式 +0:十字坐标;默认为 0 +1792 +公 +共 +参 +数 +===== 手册第88页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +78 + +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 0 +SFD902 脉冲数/1转低 16位 20000 SFD903 脉冲数/1转高 16位 +SFD904 移动量/1转低 16位 10 SFD905 移动量/1转高 16位 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 2 +SFD907 方向延时时间 默认是 20,单位:ms 20 +SFD908 齿轮间隙正向补偿 0 +SFD909 齿轮间隙负向补偿 0 +SFD910 电气原点位置低 16 位 0 +SFD911 电气原点位置高 16 位 0 +SFD912 信号端子开关状态设置 +Bit0:原点信号开关状态设置 +Bit1:Z 相开关状态设置 +Bit2:正极限开关状态设置 +Bit3:负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻 +辑);默认为 0 +0 +SFD914 Z 相端子设定 Bit0~bit7:指定 X 端子的编号,0xFF 为 +无端子(中断) 0xFF +SFD915 极限端子设定 +Bit7~bit0:指定正极限的 X 端子编号, +0xFF为无端子 +Bit15~bit8:指定负极限的 X 端子编号, +0xFF为无端子 +FFFF +SFD917 归零清除 CLR信号输出端子设 +定 +Bit0~bit7:指定 Y 端子的编号,0xFF 为 +无端子 0xFF +SFD918 回归速度 VH 低 16位 0 +SFD919 回归速度 VH 高 16位 0 +SFD922 爬行速度 VC低 16位 0 +SFD923 爬行速度 VC高 16位 0 +SFD924 机械原点位置低 16 位 0 +SFD925 机械原点位置高 16 位 0 +SFD926 Z 相个数 0 +SFD927 CLR信号延时时间 默认 20,单位:ms 20 +SFD930 软限位正极限值 低 16 位 0 SFD931 高 16 位 +SFD932 软限位负极限值 低 16 位 0 SFD933 高 16 位 +… +SFD950 脉冲默认速度低 16 位 速度为 0 的时候采用默认速度发送脉冲 20 第 +一 +套 +参 +数 +SFD951 脉冲默认速度高 16 位 0 +SFD952 脉冲默认速度加速时间 100 +SFD953 脉冲默认速度减速时间 200 +SFD954 补间加减速时间 0 +===== 手册第89页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 79 + +SFD955 脉冲加减速模式 +bit 1~0: 加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +bit 15~2:保留 + +SFD956 最高速度限制低 16 位 100 +SFD957 最高速度限制高 16 位 0 +SFD958 起始速度低 16位 0 +SFD959 起始速度高 16位 0 +SFD960 终止速度低 16位 0 +SFD961 终止速度高 16位 0 +SFD962 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick +50 +SFD963 Follow前馈补偿参数 0~100,百分比 0 +【注】 : +※1:由于 PLSR脉冲输出指令需要配置的参数较多,建议用户直接通过脉冲配置参数表直接进行配置,这 +样不容易出现错误; +※2:如客户需要自己在触摸屏上设定每段脉冲的脉冲频率和脉冲数时, 可以先通过脉冲配置表进行配置 (必 +须进行配置) ; 配置完后在梯形图中给每段的脉冲频率和脉冲个数通过DMOV指令结合寄存器赋值 (S0+N*10+0、 +S0+N*10+2)即可。 +例如本例中: +DMOV HD200 HD10 //HD200 为触摸屏第 1段移动速度设定 +DMOV HD202 HD12 //HD202 为触摸屏第 1段移动长度设定 +DMOV HD204 HD20 //HD204 为触摸屏第 2段移动速度设定 +DMOV HD206 HD22 //HD206 为触摸屏第 2段移动长度设定 +也可通过触摸屏直接给寄存器 HD10、HD12、HD20、HD22设定脉冲频率和脉冲个数数值。 +5)例5 +有如下传动机构,一台伺服驱动器(电子齿轮比默认1:1)控制一台伺服电机(编码器2500线) ,通 +过减速比为 1:2的减速机连接到滚珠丝杠上,滚珠丝杠的螺距为 5mm,滚珠丝杠带动一个能左右移动的 +工作台。现需要将工作台在 A 位置与 B位置之间进行往复运动,A 位置到 B位置之间的距离为 200mm, +A 位置到 B 位置速度为 20mm/s,B 位置到 A 位置速度为 30mm/s,加速时间均为 100ms,减速时间均为 +200ms;由于滚珠丝杆存在机械间隙误差,A→B→A 存在的机械间隙误差为 3mm,B→A→B 存在的机械 +间隙误差为 2mm,脉冲方向端子为 Y2。机械结构与脉冲运行示意图分别如下图: + 伺服电机 减速 +机构 +负载轴 +BA + +机械结构示意图 +通过上面的内容,我们可以计算出: +脉冲数(1转)= 20000 = 2500 * 4 * 2 +1 +移动量(1转)= 螺距 = 5mm +20mm/s = * 20000 = 80000pulse/s 20mm +5mm +30mm/s = * 20000 = 120000pulse/s 30mm +5mm + +===== 手册第90页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +80 + +脉冲数(1转)= 20000 = 2500 * 4 * 2 +1 +移动量(1转)= 螺距 = 5mm +20mm/s = * 20000 = 80000pulse/s 20mm +5mm +30mm/s = * 20000 = 120000pulse/s 30mm +5mm + +由于正向运动与反向运动时的加减速时间均相同,而最高脉冲频率不相同,所以它们的加减速斜率 +不相同: +正向加速斜率:100ms变化 80000Hz,正向减速斜率:200ms变化 80000Hz;反向加速斜率:100ms +变化 120000Hz,反向减速斜率:200ms变化 120000Hz。 +这里需要两组不同的加减速斜率,所以我们要设置两套参数。 +最高脉冲输出频率分别为 80K/s与 120K/s。 +(1)脉冲波形如下图所示: +t0 t1 +频率 +20mm/s +30mm/s +t2 +t3 t1 t2 +t3 +t1=100ms +t2=200ms +t3=30ms + +(2)脉冲指令 +PLSR HD0 HD50 K1 Y0 +M0 +( R ) +SM1000 +M1 +SM1000 HSD2 K200 +D ( S ) +M1 +M1 +PLSR HD100 HD150 K2 Y0 +( R ) +M0 +HSD2 K0 +D ( S ) +M0 + + +===== 手册第91页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 81 + +(3)脉冲数据配置 +⚫ 脉冲段配置 + +脉冲数据配置(相对模式) + + +===== 手册第92页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +82 + + +脉冲数据配置(绝对模式) +⚫ 系统参数块配置(相对) + +系统参数块配置一 + +系统参数块配置二 + +===== 手册第93页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 83 + + +系统参数块配置三 + +系统参数块配置四 + +系统参数块配置五 + + + + + +===== 手册第94页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +84 + +⚫ 脉冲数据地址分配如下表(相对模式) : +地址 说明 数值 +HD0(双字) 脉冲总段数(1~100) 2 +HD2(8个字) 保留 0 +HD10(双字) 脉冲频率(#1) 20 +HD12(双字) 脉冲个数(#1) 200 +HD14 +bit15~bit8:等待条件(#1) +H00:脉冲发送完成 +H01:wait时间 +H02:wait信号 +H03:ACT时间 +H04:EXT信号 +H05:EXT信号或者脉冲发送完成 +bit7~bit0:等待条件寄存器类型 + H00:常数 + H01:D + H02:HD + H03:FD + H04:X + H05:M +H06:HM +0 +HD15(双字) 常数值/寄存器编号(等待条件的) (#1) 0 +HD17 +bit7~bit0:跳转寄存器的类型 + H00:常数 + H01:D + H02:HD +H03:FD +0 +HD+18(双字) 常数值/寄存器编号(跳转寄存器的) (#1) 0 +HD+20(双字) 脉冲频率(#2) 20 +HD+22(双字) 脉冲个数(#2) -200 +HD+24 等待条件,等待条件寄存器类型(#2) 0 +HD+25(双字) 常数值或者寄存器编号(等待条件的) (#2) 0 +HD+27 跳转类型,跳转寄存器的类型(等待条件的) (#2) 0 +HD+28(双字) 常数值或者寄存器编号(跳转寄存器的) (#2) 0 + + + + + + + + +===== 手册第95页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 85 + +⚫ 系统参数块起始地址: +SFD900 脉冲参数设定 +Bit 1:脉冲方向逻辑 +0:正逻辑;1:负逻辑,默认为 0 +Bit 2:启用软限位功能 +0:不启用;1:启用 默认为 0 +Bit 3:机械回原点方向 +0:负向;1:正向 默认为 0 +Bit 10~8:脉冲单位 +bit8:0:脉冲个数,1:当量 + 000:脉冲个数 + 001:微米 + 011:忽米 + 101:丝米 + 111:毫米 + 默认为 000 +Bit15:插补坐标模式 +0:十字坐标;默认为 0 +1792 +公 +共 +参 +数 +SFD901 脉冲发送模式 Bit 0:脉冲发送模式 +0:完成模式;1:后续模式;默认为 0 0 +SFD902 脉冲数/1转低 16位 20000 +SFD903 脉冲数/1转高 16位 0 +SFD904 移动量/1转低 16位 5 +SFD905 移动量/1转高 16位 0 +SFD906 脉冲方向端子 指定 Y 端子的编号,0xFF为无端子 2 +SFD907 方向延时时间 默认是 20,单位:ms 20 +SFD908 齿轮间隙正向补偿 0 +SFD909 齿轮间隙负向补偿 0 +SFD910 电气原点位置低 16 位 0 +SFD911 电气原点位置高 16 位 0 +SFD912 信号端子开关状态设置 +Bit0: 原点信号开关状态设置 +Bit1: Z 相开关状态设置 +Bit2: 正极限开关状态设置 +Bit3: 负极限开关状态设置 +0:常开(正逻辑),1:常闭(负逻 +辑);默认为 0 +0 +SFD914 Z 相端子设定 Bit0~bit7:指定 X 端子的编号,0xFF 为 +无端子(中断) 0xFF +SFD915 极限端子设定 +Bit7~bit0:指定正极限的 X 端子编号, +0xFF为无端子 +Bit15~bit8:指定负极限的 X 端子编号, +0xFF为无端子 +FFFF +SFD917 归零清除 CLR信号输出端子设 +定 +Bit0~bit7:指定 Y 端子的编号,0xFF 为 +无端子 0xFF +SFD918 回归速度 VH 低 16位 0 +SFD919 回归速度 VH 高 16位 0 +SFD922 爬行速度 VC低 16位 0 +===== 手册第96页 ===== +1.脉冲输出 XD/XL系列可编程控制器用户手册[定位控制篇] +86 + +SFD923 爬行速度 VC高 16位 0 +SFD924 机械原点位置低 16 位 0 +SFD925 机械原点位置高 16 位 0 +SFD926 Z 相个数 0 +SFD927 CLR信号延时时间 默认 20,单位:ms 20 +SFD930 软限位正极限值 低 16 位 0 +SFD931 高 16 位 0 +SFD932 软限位负极限值 低 16 位 0 +SFD933 高 16 位 0 +… +SFD950 脉冲默认速度低 16 位 速度为 0 的时候采用默认速度发送脉冲 20 +第 +一 +套 +参 +数 +SFD951 脉冲默认速度高 16 位 0 +SFD952 脉冲默认速度加速时间 100 +SFD953 脉冲默认速度减速时间 200 +SFD954 补间加减速时间 30 +SFD955 脉冲加减速模式 +bit 1~0: 加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +bit 15~2:保留 +0 +SFD956 最高速度限制低 16 位 50 +SFD957 最高速度限制高 16 位 0 +SFD958 起始速度低 16位 0 +SFD959 起始速度高 16位 0 +SFD960 终止速度低 16位 0 +SFD961 终止速度高 16位 0 +SFD962 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick 50 +SFD963 Follow前馈补偿参数 0~100,百分比 0 +… +SFD970 脉冲默认速度低 16 位 +速度为 0 的时候采用默认速度发送脉冲 +30 +第 +二 +套 +参 +数 +SFD971 脉冲默认速度高 16 位 0 +SFD972 脉冲默认速度加速时间 100 +SFD973 脉冲默认速度减速时间 200 +SFD974 补间加减速时间 30 +SFD975 脉冲加减速模式 +bit 1~0: 加减速段加减速方式 + 00:直线加减速 + 01:S曲线加减速 + 10:正弦曲线加减速 + 11:保留 +bit 15~2:保留 +0 +SFD976 最高速度限制低 16 位 50 +SFD977 最高速度限制高 16 位 0 +SFD978 起始速度低 16位 0 +===== 手册第97页 ===== +XD/XL系列可编程控制器用户手册[定位控制篇] 1.脉冲输出 + 87 + +SFD979 起始速度高 16位 0 +SFD980 终止速度低 16位 0 +SFD981 终止速度高 16位 0 +SFD982 Follow性能参数 1~100,100表示时间常数为 1 个 Tick, +1 表示时间常数为 100个 Tick 50 +SFD983 Follow前馈补偿参数 0~100,百分比 0 +… +【注】 : +※1:由于 PLSR脉冲输出指令需要配置的参数较多,建议用户直接通过脉冲配置参数表直接进行配置,这 +样不容易出现错误; +※2:如客户需要自己在触摸屏上设定每段脉冲的脉冲频率和脉冲数时, 可以先通过脉冲配置表进行配置 (必 +须进行配置) ; 配置完后在梯形图中给每段的脉冲频率和脉冲个数通过DMOV指令结合寄存器赋值 (S0+N*10+0、 +S0+N*10+2)即可。 +例如本例中: +DMOV HD200 HD10 //HD200 为触摸屏第 1段脉冲频率设定 +DMOV HD202 HD12 //HD202 为触摸屏第 1段脉冲个数设定 +DMOV HD204 HD20 //HD204 为触摸屏第 2段脉冲频率设定 +DMOV HD206 HD22 //HD206 为触摸屏第 3段脉冲个数设定 +也可通过触摸屏直接给寄存器 HD10、HD12、HD20、HD22设定脉冲频率和脉冲个数数值。 +