【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 警告deepseek
| @@ -1284,6 +1284,9 @@ | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_job.h</name> | <name>$PROJ_DIR$\..\PLSR\Inc\plsr_job.h</name> | ||||
| </file> | </file> | ||||
| <file> | |||||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_path.h</name> | |||||
| </file> | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | ||||
| </file> | </file> | ||||
| @@ -1296,6 +1299,9 @@ | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_job.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_job.c</name> | ||||
| </file> | </file> | ||||
| <file> | |||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_path.c</name> | |||||
| </file> | |||||
| <file> | <file> | ||||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name> | <name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name> | ||||
| </file> | </file> | ||||
| @@ -23,15 +23,20 @@ typedef enum | |||||
| PLC_DEVICE_RESULT PlcDeviceInit(void); | 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 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 PlcDeviceLoadSfd(void); | ||||
| PLC_DEVICE_RESULT PlcDeviceSaveSfd(void); | PLC_DEVICE_RESULT PlcDeviceSaveSfd(void); | ||||
| PLC_DEVICE_RESULT PlcDeviceResetSfdDefaults(void); | PLC_DEVICE_RESULT PlcDeviceResetSfdDefaults(void); | ||||
| @@ -13,7 +13,6 @@ PLSR_RESULT PlsrInit(void); | |||||
| void PlsrTask(void *argument); | void PlsrTask(void *argument); | ||||
| void PlsrProcess(void); | void PlsrProcess(void); | ||||
| PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request); | |||||
| PLSR_RESULT PlsrPostCall(const PLSR_CALL *call); | PLSR_RESULT PlsrPostCall(const PLSR_CALL *call); | ||||
| PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); | PLSR_RESULT PlsrPostCommand(const PLSR_COMMAND *command); | ||||
| PLSR_RESULT PlsrPostEvent(uint8_t axis, uint32_t eventMask); | 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_RESULT PlsrGetLastParseDetail(uint8_t axis, | ||||
| PLSR_PARSE_DETAIL *detail); | 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_RESULT PlsrStateTransition(uint8_t axis, | ||||
| PLSR_STATE target, | PLSR_STATE target, | ||||
| PLSR_TRANSITION_REASON reason); | PLSR_TRANSITION_REASON reason); | ||||
| @@ -124,7 +124,7 @@ typedef struct | |||||
| uint16_t accelerationMs; | uint16_t accelerationMs; | ||||
| uint16_t decelerationMs; | uint16_t decelerationMs; | ||||
| uint16_t gapAccelerationMs; | uint16_t gapAccelerationMs; | ||||
| uint16_t directionDelayUs; | |||||
| uint16_t directionDelayMs; | |||||
| uint8_t curveMode; | uint8_t curveMode; | ||||
| uint8_t follow; | uint8_t follow; | ||||
| uint8_t feedforwardPercent; | uint8_t feedforwardPercent; | ||||
| @@ -0,0 +1,71 @@ | |||||
| #ifndef PLSR_PATH_H | |||||
| #define PLSR_PATH_H | |||||
| #include "plsr_job.h" | |||||
| #include "plsr_types.h" | |||||
| #include <stdint.h> | |||||
| #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 */ | |||||
| @@ -8,15 +8,21 @@ | |||||
| extern "C" { | extern "C" { | ||||
| #endif | #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 | 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; | } PLSR_HSD_DATA; | ||||
| typedef struct | typedef struct | ||||
| { | { | ||||
| int32_t config[PLSR_SFD_CONFIG_COUNT]; | |||||
| uint16_t config[PLSR_SFD_CONFIG_COUNT]; | |||||
| } PLSR_SFD_DATA; | } PLSR_SFD_DATA; | ||||
| typedef enum | typedef enum | ||||
| @@ -129,8 +129,9 @@ typedef enum | |||||
| #define PLSR_EVENT_WAIT_BEGIN (1UL << 10U) | #define PLSR_EVENT_WAIT_BEGIN (1UL << 10U) | ||||
| #define PLSR_EVENT_WAIT_COMPLETE (1UL << 11U) | #define PLSR_EVENT_WAIT_COMPLETE (1UL << 11U) | ||||
| #define PLSR_EVENT_JOB_COMPLETE (1UL << 12U) | #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 \ | #define PLSR_EVENT_CRITICAL_MASK (PLSR_EVENT_SOFTWARE_EMERGENCY \ | ||||
| | PLSR_EVENT_LIMIT_POSITIVE \ | | PLSR_EVENT_LIMIT_POSITIVE \ | ||||
| | PLSR_EVENT_LIMIT_NEGATIVE \ | | PLSR_EVENT_LIMIT_NEGATIVE \ | ||||
| @@ -177,8 +178,10 @@ typedef struct | |||||
| uint8_t jobValid; | uint8_t jobValid; | ||||
| uint8_t s2Set; | uint8_t s2Set; | ||||
| uint8_t speedClamped; | uint8_t speedClamped; | ||||
| uint8_t positionOverflow; | |||||
| uint16_t segmentCount; | uint16_t segmentCount; | ||||
| uint16_t startSegment; | uint16_t startSegment; | ||||
| uint16_t currentSegment; | |||||
| } PLSR_STATUS; | } PLSR_STATUS; | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| @@ -26,6 +26,8 @@ static uint8_t PlcSfdOperationActive; | |||||
| static uint32_t PlcHsdChangeCounter; | static uint32_t PlcHsdChangeCounter; | ||||
| static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult; | static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult; | ||||
| static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult; | static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult; | ||||
| static uint8_t PlcRestoredHsdPositionValid; | |||||
| static uint8_t PlcRestoredHsdLastBusy; | |||||
| static uint32_t PlcDeviceEnterCritical(void) | static uint32_t PlcDeviceEnterCritical(void) | ||||
| { | { | ||||
| @@ -90,7 +92,7 @@ static void PlcDeviceEndSfdOperation(void) | |||||
| PlcDeviceExitCritical(interruptState); | 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) | 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)); | (void)memset(PlcSmFlags, 0, sizeof(PlcSmFlags)); | ||||
| PlcSfdOperationActive = 0U; | PlcSfdOperationActive = 0U; | ||||
| PlcHsdChangeCounter = 0UL; | PlcHsdChangeCounter = 0UL; | ||||
| PlcRestoredHsdPositionValid = 0U; | |||||
| PlcRestoredHsdLastBusy = 0U; | |||||
| PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData); | PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData); | ||||
| PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED) | PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED) | ||||
| @@ -181,6 +185,12 @@ PLC_DEVICE_RESULT PlcDeviceInit(void) | |||||
| { | { | ||||
| return PLC_DEVICE_PERSISTENCE_ERROR; | 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); | PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); | ||||
| PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; | PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; | ||||
| @@ -193,9 +203,9 @@ PLC_DEVICE_RESULT PlcDeviceInit(void) | |||||
| return PLC_DEVICE_OK; | 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) | if (value == NULL) | ||||
| { | { | ||||
| @@ -211,7 +221,7 @@ PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value) | |||||
| return PLC_DEVICE_OK; | 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) | if ((address < PLSR_HSD_CONFIG_START) | ||||
| || (address >= PLSR_HSD_CONFIG_START + PLSR_HSD_CONFIG_COUNT)) | || (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; | 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) | 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; | 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; | uint32_t interruptState; | ||||
| uint16_t offset; | 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); | offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); | ||||
| interruptState = PlcDeviceEnterCritical(); | 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); | PlcDeviceExitCritical(interruptState); | ||||
| *value = (int64_t)combined; | |||||
| *value = (int32_t)combined; | |||||
| return PLC_DEVICE_OK; | 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; | uint32_t interruptState; | ||||
| uint16_t offset; | uint16_t offset; | ||||
| @@ -280,17 +291,48 @@ PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value) | |||||
| return PLC_DEVICE_INVALID_ADDRESS; | return PLC_DEVICE_INVALID_ADDRESS; | ||||
| } | } | ||||
| rawValue = (uint64_t)value; | |||||
| rawValue = (uint32_t)value; | |||||
| offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); | offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); | ||||
| interruptState = PlcDeviceEnterCritical(); | 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++; | PlcHsdChangeCounter++; | ||||
| PlcHsdDirty = 1U; | PlcHsdDirty = 1U; | ||||
| PlcDeviceExitCritical(interruptState); | PlcDeviceExitCritical(interruptState); | ||||
| return PLC_DEVICE_OK; | return PLC_DEVICE_OK; | ||||
| } | } | ||||
| uint8_t PlcDeviceGetRestoredHsdPositionValid(void) | |||||
| { | |||||
| return PlcRestoredHsdPositionValid; | |||||
| } | |||||
| uint8_t PlcDeviceGetRestoredHsdLastBusy(void) | |||||
| { | |||||
| return PlcRestoredHsdLastBusy; | |||||
| } | |||||
| PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) | PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) | ||||
| { | { | ||||
| PLSR_HSD_DATA snapshot; | PLSR_HSD_DATA snapshot; | ||||
| @@ -323,7 +365,7 @@ PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) | |||||
| return PLC_DEVICE_OK; | 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; | uint32_t interruptState; | ||||
| @@ -348,7 +390,7 @@ PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value) | |||||
| return PLC_DEVICE_OK; | 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; | uint32_t interruptState; | ||||
| @@ -1,6 +1,7 @@ | |||||
| #include "plsr_core.h" | #include "plsr_core.h" | ||||
| #include "plc_device.h" | #include "plc_device.h" | ||||
| #include "plsr_address_map.h" | #include "plsr_address_map.h" | ||||
| #include "plsr_path.h" | |||||
| #include "plsr_resource.h" | #include "plsr_resource.h" | ||||
| #include <string.h> | #include <string.h> | ||||
| @@ -25,12 +26,14 @@ typedef struct | |||||
| uint32_t illegalTransitionCount; | uint32_t illegalTransitionCount; | ||||
| int64_t logicalPosition; | int64_t logicalPosition; | ||||
| int64_t totalPulses; | int64_t totalPulses; | ||||
| PLSR_PATH_CONTEXT path; | |||||
| uint8_t hasLastCommand; | uint8_t hasLastCommand; | ||||
| uint8_t done; | uint8_t done; | ||||
| uint8_t directionPositive; | uint8_t directionPositive; | ||||
| uint8_t immediateStopPending; | uint8_t immediateStopPending; | ||||
| uint8_t positionValid; | uint8_t positionValid; | ||||
| uint8_t jobValid; | uint8_t jobValid; | ||||
| uint8_t positionOverflow; | |||||
| } PLSR_AXIS; | } PLSR_AXIS; | ||||
| typedef struct | 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, | static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, | ||||
| PLSR_STATE target) | PLSR_STATE target) | ||||
| { | { | ||||
| @@ -141,7 +161,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, | |||||
| case PLSR_STATE_ACCEL: | case PLSR_STATE_ACCEL: | ||||
| case PLSR_STATE_RUN: | 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_DECEL) | ||||
| || (target == PLSR_STATE_WAIT) | || (target == PLSR_STATE_WAIT) | ||||
| || (target == PLSR_STATE_COMPLETED) | || (target == PLSR_STATE_COMPLETED) | ||||
| @@ -150,7 +171,8 @@ static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current, | |||||
| : 0U; | : 0U; | ||||
| case PLSR_STATE_DECEL: | 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_WAIT) | ||||
| || (target == PLSR_STATE_PAUSED) | || (target == PLSR_STATE_PAUSED) | ||||
| || (target == PLSR_STATE_COMPLETED) | || (target == PLSR_STATE_COMPLETED) | ||||
| @@ -223,7 +245,11 @@ PLSR_RESULT PlsrStateTransition(uint8_t axis, | |||||
| { | { | ||||
| axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; | axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; | ||||
| axisObject->immediateStopPending = 0U; | axisObject->immediateStopPending = 0U; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| PlsrResourceRelease(&axisObject->lease); | PlsrResourceRelease(&axisObject->lease); | ||||
| /* 运动已结束:记录 lastBusy=0,保证掉电后恢复时位置仍可信。 */ | |||||
| (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 0U); | |||||
| (void)PlcDeviceCheckpointHsd(); | |||||
| } | } | ||||
| PlsrPublishAxis(axis); | PlsrPublishAxis(axis); | ||||
| return PLSR_RESULT_OK; | return PLSR_RESULT_OK; | ||||
| @@ -360,6 +386,7 @@ static uint8_t PlsrPopHighestPriorityCommand(PLSR_COMMAND_SLOT *slot) | |||||
| return 1U; | return 1U; | ||||
| } | } | ||||
| #ifdef PLSR_HOST_TEST | |||||
| PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) | PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) | ||||
| { | { | ||||
| PLSR_COMMAND command; | PLSR_COMMAND command; | ||||
| @@ -383,6 +410,7 @@ PLSR_RESULT PlsrPostStart(const PLSR_START_REQUEST *request) | |||||
| command.argument = 0; | command.argument = 0; | ||||
| return PlsrQueueCommand(&command, request, NULL); | return PlsrQueueCommand(&command, request, NULL); | ||||
| } | } | ||||
| #endif /* PLSR_HOST_TEST */ | |||||
| PLSR_RESULT PlsrPostCall(const PLSR_CALL *call) | PLSR_RESULT PlsrPostCall(const PLSR_CALL *call) | ||||
| { | { | ||||
| @@ -515,6 +543,12 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject, | |||||
| { | { | ||||
| PlsrResourceRelease(&axisObject->lease); | PlsrResourceRelease(&axisObject->lease); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ | |||||
| (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); | |||||
| (void)PlcDeviceCheckpointHsd(); | |||||
| } | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -568,6 +602,19 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, | |||||
| axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; | axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; | ||||
| axisObject->immediateStopPending = 0U; | axisObject->immediateStopPending = 0U; | ||||
| axisObject->done = 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, | result = PlsrStateTransition(call->dAxis, | ||||
| PLSR_STATE_ACCEL, | PLSR_STATE_ACCEL, | ||||
| PLSR_TRANSITION_START); | PLSR_TRANSITION_START); | ||||
| @@ -576,6 +623,12 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, | |||||
| axisObject->jobValid = 0U; | axisObject->jobValid = 0U; | ||||
| PlsrResourceRelease(&axisObject->lease); | PlsrResourceRelease(&axisObject->lease); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ | |||||
| (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); | |||||
| (void)PlcDeviceCheckpointHsd(); | |||||
| } | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -600,6 +653,7 @@ static PLSR_RESULT PlsrStopImmediate(uint8_t axis) | |||||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_IMMEDIATE); | PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_IMMEDIATE); | ||||
| axisObject->pendingTerminal = PLSR_STATE_STOPPED; | axisObject->pendingTerminal = PLSR_STATE_STOPPED; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| if ((axisObject->state == PLSR_STATE_WAIT) | if ((axisObject->state == PLSR_STATE_WAIT) | ||||
| || (axisObject->state == PLSR_STATE_PAUSED)) | || (axisObject->state == PLSR_STATE_PAUSED)) | ||||
| { | { | ||||
| @@ -633,6 +687,7 @@ static PLSR_RESULT PlsrStopDecel(uint8_t axis) | |||||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_DECEL); | PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_DECEL); | ||||
| axisObject->pendingTerminal = PLSR_STATE_STOPPED; | axisObject->pendingTerminal = PLSR_STATE_STOPPED; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| if ((axisObject->state == PLSR_STATE_WAIT) | if ((axisObject->state == PLSR_STATE_WAIT) | ||||
| || (axisObject->state == PLSR_STATE_PAUSED)) | || (axisObject->state == PLSR_STATE_PAUSED)) | ||||
| { | { | ||||
| @@ -664,6 +719,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis) | |||||
| if (axisObject->state == PLSR_STATE_WAIT) | if (axisObject->state == PLSR_STATE_WAIT) | ||||
| { | { | ||||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); | PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| return PlsrStateTransition(axis, | return PlsrStateTransition(axis, | ||||
| PLSR_STATE_PAUSED, | PLSR_STATE_PAUSED, | ||||
| PLSR_TRANSITION_DECEL_COMPLETE); | PLSR_TRANSITION_DECEL_COMPLETE); | ||||
| @@ -677,6 +733,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis) | |||||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); | PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); | ||||
| axisObject->pendingTerminal = PLSR_STATE_PAUSED; | axisObject->pendingTerminal = PLSR_STATE_PAUSED; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| if (axisObject->state == PLSR_STATE_DECEL) | if (axisObject->state == PLSR_STATE_DECEL) | ||||
| { | { | ||||
| return PLSR_RESULT_OK; | return PLSR_RESULT_OK; | ||||
| @@ -743,10 +800,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) | |||||
| { | { | ||||
| axisObject->logicalPosition = slot->command.argument; | axisObject->logicalPosition = slot->command.argument; | ||||
| axisObject->positionValid = 1U; | 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; | result = PLSR_RESULT_OK; | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -760,10 +817,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot) | |||||
| { | { | ||||
| axisObject->logicalPosition = 0; | axisObject->logicalPosition = 0; | ||||
| axisObject->positionValid = 1U; | 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; | result = PLSR_RESULT_OK; | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -854,6 +911,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) | |||||
| PlsrSetStopReason(axisObject, | PlsrSetStopReason(axisObject, | ||||
| PLSR_STOP_REASON_SOFTWARE_EMERGENCY); | PLSR_STOP_REASON_SOFTWARE_EMERGENCY); | ||||
| axisObject->done = 0U; | axisObject->done = 0U; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| (void)PlsrStateTransition(axis, | (void)PlsrStateTransition(axis, | ||||
| PLSR_STATE_STOPPED, | PLSR_STATE_STOPPED, | ||||
| PLSR_TRANSITION_STOP); | PLSR_TRANSITION_STOP); | ||||
| @@ -870,6 +928,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) | |||||
| PlsrSetStopReason(axisObject, | PlsrSetStopReason(axisObject, | ||||
| PLSR_STOP_REASON_LIMIT_POSITIVE); | PLSR_STOP_REASON_LIMIT_POSITIVE); | ||||
| axisObject->pendingTerminal = PLSR_STATE_STOPPED; | axisObject->pendingTerminal = PLSR_STATE_STOPPED; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| if ((axisObject->state == PLSR_STATE_WAIT) | if ((axisObject->state == PLSR_STATE_WAIT) | ||||
| || (axisObject->state == PLSR_STATE_PAUSED)) | || (axisObject->state == PLSR_STATE_PAUSED)) | ||||
| { | { | ||||
| @@ -896,6 +955,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) | |||||
| PlsrSetStopReason(axisObject, | PlsrSetStopReason(axisObject, | ||||
| PLSR_STOP_REASON_LIMIT_NEGATIVE); | PLSR_STOP_REASON_LIMIT_NEGATIVE); | ||||
| axisObject->pendingTerminal = PLSR_STATE_STOPPED; | axisObject->pendingTerminal = PLSR_STATE_STOPPED; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| if ((axisObject->state == PLSR_STATE_WAIT) | if ((axisObject->state == PLSR_STATE_WAIT) | ||||
| || (axisObject->state == PLSR_STATE_PAUSED)) | || (axisObject->state == PLSR_STATE_PAUSED)) | ||||
| { | { | ||||
| @@ -920,12 +980,75 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) | |||||
| : PLSR_ERROR_COUNTER_FAULT; | : PLSR_ERROR_COUNTER_FAULT; | ||||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); | PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); | ||||
| axisObject->done = 0U; | axisObject->done = 0U; | ||||
| PlsrPathTerminate(&axisObject->path); | |||||
| (void)PlsrStateTransition(axis, | (void)PlsrStateTransition(axis, | ||||
| PLSR_STATE_ERROR, | PLSR_STATE_ERROR, | ||||
| PLSR_TRANSITION_FAULT); | 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) | static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) | ||||
| { | { | ||||
| PLSR_AXIS *axisObject = &PlsrAxes[axis]; | PLSR_AXIS *axisObject = &PlsrAxes[axis]; | ||||
| @@ -988,6 +1111,19 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) | |||||
| PLSR_TRANSITION_WAIT_COMPLETE); | 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) | if (((events & PLSR_EVENT_JOB_COMPLETE) != 0UL) | ||||
| && (PlsrStateIsBusy(axisObject->state) != 0U) | && (PlsrStateIsBusy(axisObject->state) != 0U) | ||||
| && (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED) | && (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED) | ||||
| @@ -1002,7 +1138,9 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) | |||||
| PLSR_RESULT PlsrInit(void) | PLSR_RESULT PlsrInit(void) | ||||
| { | { | ||||
| int64_t restoredPosition; | |||||
| int32_t restoredPosition; | |||||
| uint8_t restoredPositionValid; | |||||
| uint8_t restoredLastBusy; | |||||
| uint8_t axis; | uint8_t axis; | ||||
| (void)memset(PlsrAxes, 0, sizeof(PlsrAxes)); | (void)memset(PlsrAxes, 0, sizeof(PlsrAxes)); | ||||
| @@ -1011,18 +1149,23 @@ PLSR_RESULT PlsrInit(void) | |||||
| PlsrResourceInit(); | PlsrResourceInit(); | ||||
| PlsrInitialized = 1U; | PlsrInitialized = 1U; | ||||
| restoredPositionValid = PlcDeviceGetRestoredHsdPositionValid(); | |||||
| restoredLastBusy = PlcDeviceGetRestoredHsdLastBusy(); | |||||
| for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) | for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) | ||||
| { | { | ||||
| PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED; | PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED; | ||||
| PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED; | PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED; | ||||
| PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE; | 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; | PlsrAxes[axis].logicalPosition = restoredPosition; | ||||
| } | |||||
| /* 只有上次正常停机且保存了位置有效标志,才允许绝对定位; | |||||
| * 运动中掉电(lastBusy=1)时位置不可信。 */ | |||||
| if ((restoredPositionValid != 0U) && (restoredLastBusy == 0U)) | |||||
| { | |||||
| PlsrAxes[axis].positionValid = 1U; | PlsrAxes[axis].positionValid = 1U; | ||||
| } | } | ||||
| if (PlsrStateTransition(axis, | if (PlsrStateTransition(axis, | ||||
| @@ -1074,6 +1217,25 @@ void PlsrProcess(void) | |||||
| PlsrProcessNormalEvents(axis, events); | 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) | void PlsrTask(void *argument) | ||||
| @@ -1127,6 +1289,7 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status) | |||||
| : PLSR_DIRECTION_POINT_NONE; | : PLSR_DIRECTION_POINT_NONE; | ||||
| status->positionValid = axisObject->positionValid; | status->positionValid = axisObject->positionValid; | ||||
| status->jobValid = axisObject->jobValid; | status->jobValid = axisObject->jobValid; | ||||
| status->positionOverflow = axisObject->positionOverflow; | |||||
| status->s2Set = (axisObject->jobValid != 0U) ? axisObject->job.s2Set : 0U; | status->s2Set = (axisObject->jobValid != 0U) ? axisObject->job.s2Set : 0U; | ||||
| status->speedClamped = (axisObject->jobValid != 0U) | status->speedClamped = (axisObject->jobValid != 0U) | ||||
| ? axisObject->job.speedClamped | ? axisObject->job.speedClamped | ||||
| @@ -1137,6 +1300,10 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status) | |||||
| status->startSegment = (axisObject->jobValid != 0U) | status->startSegment = (axisObject->jobValid != 0U) | ||||
| ? axisObject->job.startSegment | ? axisObject->job.startSegment | ||||
| : 0U; | : 0U; | ||||
| status->currentSegment = (axisObject->jobValid != 0U) | |||||
| ? PlsrPathGetCurrentSegment( | |||||
| &axisObject->path) | |||||
| : 0U; | |||||
| PlsrCoreExitCritical(interruptState); | PlsrCoreExitCritical(interruptState); | ||||
| return PLSR_RESULT_OK; | return PLSR_RESULT_OK; | ||||
| } | } | ||||
| @@ -188,23 +188,20 @@ static PLSR_RESULT PlsrReadFixedWord(uint8_t useHsd, | |||||
| uint16_t *value, | uint16_t *value, | ||||
| PLSR_PARSE_DETAIL *detail) | PLSR_PARSE_DETAIL *detail) | ||||
| { | { | ||||
| int32_t rawValue = 0; | |||||
| PLC_DEVICE_RESULT deviceResult; | 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, | PlsrSetDetail(detail, | ||||
| PLSR_RESULT_INVALID_S2, | PLSR_RESULT_INVALID_S2, | ||||
| PLSR_PARSE_BLOCK_S2, | PLSR_PARSE_BLOCK_S2, | ||||
| address, | address, | ||||
| rawValue, | |||||
| 0, | |||||
| 0U); | 0U); | ||||
| return PLSR_RESULT_INVALID_S2; | return PLSR_RESULT_INVALID_S2; | ||||
| } | } | ||||
| *value = (uint16_t)rawValue; | |||||
| return PLSR_RESULT_OK; | return PLSR_RESULT_OK; | ||||
| } | } | ||||
| @@ -523,7 +520,7 @@ static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call, | |||||
| snapshot->directionPoint = (uint8_t)word; | snapshot->directionPoint = (uint8_t)word; | ||||
| result = PlsrReadFixedWord(0U, | result = PlsrReadFixedWord(0U, | ||||
| (uint16_t)(commonBase + 7U), | (uint16_t)(commonBase + 7U), | ||||
| &snapshot->s2.directionDelayUs, | |||||
| &snapshot->s2.directionDelayMs, | |||||
| detail); | detail); | ||||
| if (result != PLSR_RESULT_OK) return result; | if (result != PLSR_RESULT_OK) return result; | ||||
| @@ -0,0 +1,456 @@ | |||||
| #include "plsr_path.h" | |||||
| #include <string.h> | |||||
| /* 等待寄存器类型编码(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; | |||||
| } | |||||
| @@ -3,7 +3,7 @@ | |||||
| #include <string.h> | #include <string.h> | ||||
| #define PLSR_HSD_BACKUP_MAGIC (0x504C4853UL) | #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_A_OFFSET (0x0100UL) | ||||
| #define PLSR_BACKUP_SLOT_STRIDE (0x0200UL) | #define PLSR_BACKUP_SLOT_STRIDE (0x0200UL) | ||||
| #define PLSR_SFD_FLASH_MAGIC (0x504C5346UL) | #define PLSR_SFD_FLASH_MAGIC (0x504C5346UL) | ||||
| @@ -16,6 +16,12 @@ | |||||
| #define PLSR_SFD_PARAMETER_SET_OFFSET (50U) | #define PLSR_SFD_PARAMETER_SET_OFFSET (50U) | ||||
| #define PLSR_SFD_PARAMETER_SET_STRIDE (20U) | #define PLSR_SFD_PARAMETER_SET_STRIDE (20U) | ||||
| #define PLSR_SFD_DEFAULT_MAX_SPEED (100000UL) | #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 | 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) | static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) | ||||
| { | { | ||||
| uint16_t axisOffset; | uint16_t axisOffset; | ||||
| @@ -179,10 +211,35 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) | |||||
| { | { | ||||
| axisOffset = (uint16_t)((uint16_t)axis * PLSR_SFD_AXIS_STRIDE); | 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; | for (parameterSet = 0U; | ||||
| parameterSet < PLSR_SFD_PARAMETER_SET_COUNT; | parameterSet < PLSR_SFD_PARAMETER_SET_COUNT; | ||||
| @@ -191,14 +248,26 @@ static void PlsrPersistenceApplySfdDefaults(PLSR_SFD_DATA *data) | |||||
| parameterOffset = (uint16_t)( | parameterOffset = (uint16_t)( | ||||
| axisOffset + PLSR_SFD_PARAMETER_SET_OFFSET | axisOffset + PLSR_SFD_PARAMETER_SET_OFFSET | ||||
| + (uint16_t)parameterSet * PLSR_SFD_PARAMETER_SET_STRIDE); | + (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) | static PLSR_PERSISTENCE_RESULT PlsrPersistenceBeginSfdOperation(void) | ||||
| { | { | ||||
| #ifdef PLSR_HOST_TEST | #ifdef PLSR_HOST_TEST | ||||
| @@ -324,6 +393,7 @@ static void PlsrPersistenceCopyHsdFromVolatile( | |||||
| { | { | ||||
| uint16_t index; | uint16_t index; | ||||
| destination->metadata = source->metadata; | |||||
| for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++) | for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++) | ||||
| { | { | ||||
| destination->runtime[index] = source->runtime[index]; | destination->runtime[index] = source->runtime[index]; | ||||
| @@ -385,7 +455,7 @@ PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data) | |||||
| if ((validA == 0U) && (validB == 0U)) | if ((validA == 0U) && (validB == 0U)) | ||||
| { | { | ||||
| (void)memset(data, 0, sizeof(*data)); | |||||
| PlsrPersistenceApplyHsdDefaults(data); | |||||
| return PLSR_PERSISTENCE_DEFAULTED; | return PLSR_PERSISTENCE_DEFAULTED; | ||||
| } | } | ||||
| @@ -29,6 +29,7 @@ $tests = @( | |||||
| "$workspacePath\PLSR\Src\plsr_persistence.c" | "$workspacePath\PLSR\Src\plsr_persistence.c" | ||||
| "$workspacePath\PLSR\Src\plsr_resource.c" | "$workspacePath\PLSR\Src\plsr_resource.c" | ||||
| "$workspacePath\PLSR\Src\plsr_job.c" | "$workspacePath\PLSR\Src\plsr_job.c" | ||||
| "$workspacePath\PLSR\Src\plsr_path.c" | |||||
| "$workspacePath\PLSR\Src\plsr_core.c" | "$workspacePath\PLSR\Src\plsr_core.c" | ||||
| "$workspacePath\PLSR\Test\test_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_persistence.c" | ||||
| "$workspacePath\PLSR\Src\plsr_resource.c" | "$workspacePath\PLSR\Src\plsr_resource.c" | ||||
| "$workspacePath\PLSR\Src\plsr_job.c" | "$workspacePath\PLSR\Src\plsr_job.c" | ||||
| "$workspacePath\PLSR\Src\plsr_path.c" | |||||
| "$workspacePath\PLSR\Src\plsr_core.c" | "$workspacePath\PLSR\Src\plsr_core.c" | ||||
| "$workspacePath\PLSR\Test\test_plsr_job.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" | |||||
| ) | |||||
| } | } | ||||
| ) | ) | ||||
| @@ -40,10 +40,10 @@ static void TestAddressMap(void) | |||||
| static void TestHsdAndPersistence(void) | static void TestHsdAndPersistence(void) | ||||
| { | { | ||||
| uint16_t value16; | |||||
| int32_t value32; | 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(); | PlsrPersistenceTestResetStorage(); | ||||
| TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | ||||
| @@ -51,38 +51,95 @@ static void TestHsdAndPersistence(void) | |||||
| TEST_CHECK(PlcDeviceIsHsdDirty() != 0U); | TEST_CHECK(PlcDeviceIsHsdDirty() != 0U); | ||||
| TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 11) == PLC_DEVICE_OK); | 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(0U, 1) == PLC_DEVICE_READ_ONLY); | ||||
| TEST_CHECK(PlcDeviceWriteHsdConfig(459U, 1) == PLC_DEVICE_INVALID_ADDRESS); | 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(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); | ||||
| TEST_CHECK(PlcDeviceIsHsdDirty() == 0U); | TEST_CHECK(PlcDeviceIsHsdDirty() == 0U); | ||||
| TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 22) == PLC_DEVICE_OK); | 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); | TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); | ||||
| PlsrPersistenceTestCorruptNewestHsd(); | PlsrPersistenceTestCorruptNewestHsd(); | ||||
| TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | ||||
| TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_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) | static void TestSfd(void) | ||||
| { | { | ||||
| int32_t value; | |||||
| uint16_t value; | |||||
| PlsrPersistenceTestResetStorage(); | PlsrPersistenceTestResetStorage(); | ||||
| TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | ||||
| @@ -99,6 +156,15 @@ static void TestSfd(void) | |||||
| TEST_CHECK(value == 0x86A0); | TEST_CHECK(value == 0x86A0); | ||||
| TEST_CHECK(PlcDeviceReadSfd(957U, &value) == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceReadSfd(957U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == 1); | 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(PlcDeviceReadSfd(1297U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == 10); | TEST_CHECK(value == 10); | ||||
| TEST_CHECK(PlsrPersistenceLoadSfd(NULL) | TEST_CHECK(PlsrPersistenceLoadSfd(NULL) | ||||
| @@ -107,11 +173,11 @@ static void TestSfd(void) | |||||
| == PLSR_PERSISTENCE_INVALID_ARGUMENT); | == PLSR_PERSISTENCE_INVALID_ARGUMENT); | ||||
| TEST_CHECK(PlcDeviceWriteSfd(900U, 123) == PLC_DEVICE_OK); | 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(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == 123); | TEST_CHECK(value == 123); | ||||
| TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); | 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(899U, &value) == PLC_DEVICE_INVALID_ADDRESS); | ||||
| TEST_CHECK(PlcDeviceReadSfd(1420U, &value) == PLC_DEVICE_INVALID_ADDRESS); | TEST_CHECK(PlcDeviceReadSfd(1420U, &value) == PLC_DEVICE_INVALID_ADDRESS); | ||||
| TEST_CHECK(PlcDeviceIsSfdDirty() != 0U); | TEST_CHECK(PlcDeviceIsSfdDirty() != 0U); | ||||
| @@ -123,17 +189,17 @@ static void TestSfd(void) | |||||
| TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == 123); | TEST_CHECK(value == 123); | ||||
| TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); | 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(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); | TEST_CHECK(PlcDeviceSaveSfd() == PLC_DEVICE_OK); | ||||
| PlsrPersistenceTestCorruptNewestSfd(); | PlsrPersistenceTestCorruptNewestSfd(); | ||||
| TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); | ||||
| TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == 123); | TEST_CHECK(value == 123); | ||||
| TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); | ||||
| TEST_CHECK(value == -456); | |||||
| TEST_CHECK(value == 0xB456U); | |||||
| TEST_CHECK(PlcDeviceWriteSfd(900U, 1001) == PLC_DEVICE_OK); | TEST_CHECK(PlcDeviceWriteSfd(900U, 1001) == PLC_DEVICE_OK); | ||||
| PlsrPersistenceTestSetSfdFault(PLSR_TEST_SFD_FAULT_ERASE); | PlsrPersistenceTestSetSfdFault(PLSR_TEST_SFD_FAULT_ERASE); | ||||
| @@ -215,6 +281,8 @@ int main(void) | |||||
| { | { | ||||
| TestAddressMap(); | TestAddressMap(); | ||||
| TestHsdAndPersistence(); | TestHsdAndPersistence(); | ||||
| TestHsdDefaults(); | |||||
| TestHsdMetaPersistence(); | |||||
| TestSfd(); | TestSfd(); | ||||
| TestSmAndSd(); | TestSmAndSd(); | ||||
| @@ -344,12 +344,60 @@ static void TestQueueAndInputValidation(void) | |||||
| TEST_CHECK(TestGetStatus(0U).lastCommandResult == PLSR_RESULT_OK); | 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) | int main(void) | ||||
| { | { | ||||
| TestResourceManager(); | TestResourceManager(); | ||||
| TestStateMachineAndCommands(); | TestStateMachineAndCommands(); | ||||
| TestLimitWaitAndPairs(); | TestLimitWaitAndPairs(); | ||||
| TestQueueAndInputValidation(); | TestQueueAndInputValidation(); | ||||
| TestPositionCheckpointing(); | |||||
| (void)printf("PASS: %u PLSR core checks\n", TestCount); | (void)printf("PASS: %u PLSR core checks\n", TestCount); | ||||
| return EXIT_SUCCESS; | return EXIT_SUCCESS; | ||||
| @@ -387,6 +387,53 @@ static void TestCoreSubmission(void) | |||||
| CHECK(detail.result == PLSR_RESULT_OK); | 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) | int main(void) | ||||
| { | { | ||||
| PlsrPersistenceTestResetStorage(); | PlsrPersistenceTestResetStorage(); | ||||
| @@ -396,6 +443,7 @@ int main(void) | |||||
| TestValidationFailures(); | TestValidationFailures(); | ||||
| TestDynamicReferences(); | TestDynamicReferences(); | ||||
| TestOutputAndDivider(); | TestOutputAndDivider(); | ||||
| TestDefaultSfdStartable(); | |||||
| TestCoreSubmission(); | TestCoreSubmission(); | ||||
| if (TestFailures != 0) | if (TestFailures != 0) | ||||
| @@ -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 <stdio.h> | |||||
| #include <string.h> | |||||
| #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; | |||||
| } | |||||