HAL 启动参数增加 outputMode,core 会明确传递 PULSE/DIR、AB 或 CW/CCW 模式。 实现 AB 正向相序:00→10→11→01→00。 实现 AB 反向相序:00→01→11→10→00。 四次相位跳变、一个完整周期只累计一个指令脉冲。 AB 仅允许 D=Y0 或 Y2,对应 Q0/Q1、Q2/Q3。 AB 模式忽略 DIR 点和 SFD907 方向延时。 调频同时更新 A、B 两路,且不重置当前相位。 正常完成只在完整 00 周期边界停止。 紧急停止会将主机模型恢复至安全 00。 支持两组 AB 轴独立运行,停止一轴不会影响另一轴。 CW/CCW 目前明确返回 PLSR_RESULT_NOT_SUPPORTED。 目标板 AB 硬件尚未实现时也明确拒绝启动,防止误输出为单路 PWM。 同时修复两个旧边界问题: 零脉冲跳转链耗尽本轮预算时,不再误启动一个 0 脉冲硬件段。 极短段可能在 ACCEL 或 DECEL 阶段结束;现在 ACCEL/RUN/DECEL 均可正确处理段完成事件。deepseek
| @@ -24,6 +24,7 @@ typedef struct | |||
| { | |||
| uint32_t frequencyHz; /* 初始频率(起始速度,可为 0) */ | |||
| int64_t targetPulses; /* 本段目标脉冲数 */ | |||
| PLSR_OUTPUT_MODE outputMode; /* PULSE/DIR、AB 或 CW/CCW */ | |||
| uint8_t directionPoint; /* DIR 输出点(Y 点号,0xFF=无) */ | |||
| uint8_t directionPositive; | |||
| uint16_t directionDelayMs; | |||
| @@ -57,7 +58,11 @@ uint32_t PlsrHwTestGetCr1(uint8_t axis); | |||
| uint32_t PlsrHwTestGetPsc(uint8_t axis); | |||
| uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis); | |||
| uint8_t PlsrHwTestGetDirLevel(uint8_t axis); | |||
| uint8_t PlsrHwTestGetAbPhaseA(uint8_t axis); | |||
| uint8_t PlsrHwTestGetAbPhaseB(uint8_t axis); | |||
| uint8_t PlsrHwTestGetAbQuarter(uint8_t axis); | |||
| void PlsrHwTestTriggerUpdate(uint8_t axis); | |||
| void PlsrHwTestAdvanceAbQuarter(uint8_t axis); | |||
| #endif | |||
| #ifdef __cplusplus | |||
| @@ -59,7 +59,8 @@ static uint8_t PlsrInitialized; | |||
| static PLSR_JOB_SNAPSHOT PlsrJobScratch; | |||
| static void PlsrStopSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject); | |||
| static void PlsrStartSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject); | |||
| static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis, | |||
| PLSR_AXIS *axisObject); | |||
| static uint32_t PlsrCoreEnterCritical(void) | |||
| { | |||
| @@ -636,7 +637,22 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, | |||
| else | |||
| { | |||
| /* 启动当前段硬件输出与速度曲线。 */ | |||
| PlsrStartSegmentHardware(call->dAxis, axisObject); | |||
| /* 零脉冲跳转链耗尽本轮预算时没有实际运动段,等待下一次 | |||
| * PlsrPathTick 找到非零段后再启动硬件。 */ | |||
| if (axisObject->path.jumpChainPending == 0U) | |||
| { | |||
| result = PlsrStartSegmentHardware(call->dAxis, axisObject); | |||
| } | |||
| if (result != PLSR_RESULT_OK) | |||
| { | |||
| axisObject->error = PLSR_ERROR_TIMER_FAULT; | |||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); | |||
| axisObject->done = 0U; | |||
| (void)PlsrStateTransition(call->dAxis, | |||
| PLSR_STATE_ERROR, | |||
| PLSR_TRANSITION_FAULT); | |||
| return result; | |||
| } | |||
| /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ | |||
| (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); | |||
| (void)PlcDeviceCheckpointHsd(); | |||
| @@ -1008,13 +1024,15 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) | |||
| /* 启动当前段的硬件输出与速度曲线(P3a:单轴 PULSE/DIR)。 | |||
| * 由段进入 ACCEL 时调用(任务启动 + 段间推进)。 */ | |||
| static void PlsrStartSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject) | |||
| static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis, | |||
| PLSR_AXIS *axisObject) | |||
| { | |||
| const PLSR_JOB_SNAPSHOT *job = &axisObject->job; | |||
| const PLSR_SEGMENT_SNAPSHOT *segment = | |||
| &job->segments[axisObject->path.currentSegment - 1U]; | |||
| PLSR_PROFILE_REQUEST profileRequest; | |||
| PLSR_HW_START_PARAMS params; | |||
| PLSR_RESULT result; | |||
| int64_t pulses; | |||
| uint8_t positive; | |||
| @@ -1050,24 +1068,32 @@ static void PlsrStartSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject) | |||
| : 0UL; | |||
| profileRequest.curveMode = job->s2.curveMode; | |||
| if (PlsrProfileStart(&axisObject->profile, | |||
| &profileRequest, | |||
| pulses, | |||
| 1000U) == PLSR_RESULT_OK) | |||
| result = PlsrProfileStart(&axisObject->profile, | |||
| &profileRequest, | |||
| pulses, | |||
| 1000U); | |||
| if (result != PLSR_RESULT_OK) | |||
| { | |||
| axisObject->profileActive = 1U; | |||
| axisObject->profileWasAccel = | |||
| (axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) | |||
| ? 1U | |||
| : 0U; | |||
| return result; | |||
| } | |||
| params.frequencyHz = job->s2.startSpeed; | |||
| params.targetPulses = pulses; | |||
| params.outputMode = (PLSR_OUTPUT_MODE)job->outputMode; | |||
| params.directionPoint = job->directionPoint; | |||
| params.directionPositive = positive; | |||
| params.directionDelayMs = job->s2.directionDelayMs; | |||
| (void)PlsrHwStartPulse(axis, ¶ms); | |||
| result = PlsrHwStartPulse(axis, ¶ms); | |||
| if (result != PLSR_RESULT_OK) | |||
| { | |||
| axisObject->profileActive = 0U; | |||
| axisObject->profileWasAccel = 0U; | |||
| return result; | |||
| } | |||
| axisObject->profileActive = 1U; | |||
| axisObject->profileWasAccel = | |||
| (axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) ? 1U : 0U; | |||
| return PLSR_RESULT_OK; | |||
| } | |||
| /* 停止当前段的硬件输出与速度曲线。 */ | |||
| @@ -1086,6 +1112,9 @@ static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action) | |||
| switch (action) | |||
| { | |||
| case PLSR_PATH_ACTION_NEXT_SEGMENT: | |||
| /* SEGMENT_COMPLETE 表示上一段输出边界已经结束。统一收口旧段 | |||
| * profile/HAL 后再启动新段;真实 IRQ 与测试注入事件均一致。 */ | |||
| PlsrStopSegmentHardware(axis, axisObject); | |||
| if (axisObject->state == PLSR_STATE_WAIT) | |||
| { | |||
| (void)PlsrStateTransition(axis, | |||
| @@ -1103,7 +1132,16 @@ static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action) | |||
| /* 进入新段:重新启动硬件输出与速度曲线。 */ | |||
| if (axisObject->state == PLSR_STATE_ACCEL) | |||
| { | |||
| PlsrStartSegmentHardware(axis, axisObject); | |||
| if (PlsrStartSegmentHardware(axis, axisObject) | |||
| != PLSR_RESULT_OK) | |||
| { | |||
| axisObject->error = PLSR_ERROR_TIMER_FAULT; | |||
| PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); | |||
| axisObject->done = 0U; | |||
| (void)PlsrStateTransition(axis, | |||
| PLSR_STATE_ERROR, | |||
| PLSR_TRANSITION_FAULT); | |||
| } | |||
| } | |||
| break; | |||
| @@ -1208,7 +1246,9 @@ static void PlsrProcessNormalEvents(uint8_t axis, uint32_t events) | |||
| } | |||
| if (((events & PLSR_EVENT_SEGMENT_COMPLETE) != 0UL) | |||
| && (axisObject->state == PLSR_STATE_RUN) | |||
| && ((axisObject->state == PLSR_STATE_ACCEL) | |||
| || (axisObject->state == PLSR_STATE_RUN) | |||
| || (axisObject->state == PLSR_STATE_DECEL)) | |||
| && (axisObject->pendingTerminal == PLSR_STATE_UNINITIALIZED) | |||
| && (axisObject->immediateStopPending == 0U)) | |||
| { | |||
| @@ -13,6 +13,7 @@ | |||
| #define PLSR_HW_TIMER_UPDATE_BIT (0x0001U) | |||
| #define PLSR_HW_OUTPUT_POINT_COUNT (21U) | |||
| #define PLSR_HW_DBG_SNAPSHOT_COUNT (160U) | |||
| #define PLSR_HW_AB_QUARTER_COUNT (4U) | |||
| typedef struct | |||
| { | |||
| @@ -101,12 +102,14 @@ static PLSR_HW_TIMER_REGS PlsrHwTimers[PLSR_HW_AXIS_COUNT]; | |||
| typedef struct | |||
| { | |||
| PLSR_HW_STATE state; | |||
| PLSR_OUTPUT_MODE outputMode; | |||
| uint32_t currentFrequencyHz; | |||
| int64_t targetPulses; | |||
| int64_t emittedPulses; | |||
| uint16_t directionDelayRemainingMs; | |||
| uint8_t directionPoint; | |||
| uint8_t directionPositive; | |||
| uint8_t abQuarter; | |||
| } PLSR_HW_AXIS_STATE; | |||
| static PLSR_HW_AXIS_STATE PlsrHwAxes[PLSR_HW_AXIS_COUNT]; | |||
| @@ -375,6 +378,63 @@ static void PlsrHwStopPwmTimer(uint8_t axis) | |||
| PlsrHwTimerSetCen(axis, 0UL); | |||
| } | |||
| static uint8_t PlsrHwIsAbBaseAxis(uint8_t axis) | |||
| { | |||
| return ((axis == 0U) || (axis == 2U)) ? 1U : 0U; | |||
| } | |||
| #ifdef PLSR_HOST_TEST | |||
| static uint8_t PlsrHwGetPairedAxis(uint8_t axis) | |||
| { | |||
| return (uint8_t)(axis + 1U); | |||
| } | |||
| #endif | |||
| /* P3b-1:AB 正交相序先在 host 模型中闭环。真实 STM32 双定时器的 | |||
| * 同步启动、相位偏置和安全停止将在 P3b-2 接入。 */ | |||
| static void PlsrHwConfigureActiveOutput(uint8_t axis, | |||
| PLSR_OUTPUT_MODE outputMode, | |||
| uint32_t frequencyHz) | |||
| { | |||
| PlsrHwConfigurePwm(axis, frequencyHz); | |||
| #ifdef PLSR_HOST_TEST | |||
| if (outputMode == PLSR_OUTPUT_AB) | |||
| { | |||
| PlsrHwConfigurePwm(PlsrHwGetPairedAxis(axis), frequencyHz); | |||
| } | |||
| #else | |||
| (void)outputMode; | |||
| #endif | |||
| } | |||
| static void PlsrHwBeginActiveOutput(uint8_t axis, | |||
| PLSR_OUTPUT_MODE outputMode) | |||
| { | |||
| PlsrHwPwmBegin(axis); | |||
| #ifdef PLSR_HOST_TEST | |||
| if (outputMode == PLSR_OUTPUT_AB) | |||
| { | |||
| PlsrHwPwmBegin(PlsrHwGetPairedAxis(axis)); | |||
| } | |||
| #else | |||
| (void)outputMode; | |||
| #endif | |||
| } | |||
| static void PlsrHwStopActiveOutput(uint8_t axis, | |||
| PLSR_OUTPUT_MODE outputMode) | |||
| { | |||
| PlsrHwStopPwmTimer(axis); | |||
| #ifdef PLSR_HOST_TEST | |||
| if ((outputMode == PLSR_OUTPUT_AB) && (PlsrHwIsAbBaseAxis(axis) != 0U)) | |||
| { | |||
| PlsrHwStopPwmTimer(PlsrHwGetPairedAxis(axis)); | |||
| } | |||
| #else | |||
| (void)outputMode; | |||
| #endif | |||
| } | |||
| uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber) | |||
| { | |||
| /* 与资源层一致的合法输出点掩码(Q0~Q7、Q10~Q17、Q20)。 */ | |||
| @@ -465,7 +525,7 @@ PLSR_RESULT PlsrHwInit(void) | |||
| PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params) | |||
| { | |||
| PLSR_HW_AXIS_STATE *state; | |||
| uint8_t directionChanged; | |||
| uint8_t directionChanged = 0U; | |||
| if ((axis >= PLSR_HW_AXIS_COUNT) || (params == NULL)) | |||
| { | |||
| @@ -475,6 +535,27 @@ PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params) | |||
| { | |||
| return PLSR_RESULT_INVALID_ARGUMENT; | |||
| } | |||
| if ((uint32_t)params->outputMode > (uint32_t)PLSR_OUTPUT_CW_CCW) | |||
| { | |||
| return PLSR_RESULT_INVALID_ARGUMENT; | |||
| } | |||
| if ((params->outputMode == PLSR_OUTPUT_AB) | |||
| && (PlsrHwIsAbBaseAxis(axis) == 0U)) | |||
| { | |||
| return PLSR_RESULT_INVALID_AXIS; | |||
| } | |||
| if (params->outputMode == PLSR_OUTPUT_CW_CCW) | |||
| { | |||
| return PLSR_RESULT_NOT_SUPPORTED; | |||
| } | |||
| #ifndef PLSR_HOST_TEST | |||
| if (params->outputMode == PLSR_OUTPUT_AB) | |||
| { | |||
| /* P3b-1 只交付可验证的 host 相序模型;禁止目标板误输出成 | |||
| * 单路 PULSE/DIR。P3b-2 接入双定时器后移除此保护。 */ | |||
| return PLSR_RESULT_NOT_SUPPORTED; | |||
| } | |||
| #endif | |||
| state = &PlsrHwAxes[axis]; | |||
| if (state->state == PLSR_HW_STATE_RUNNING) | |||
| { | |||
| @@ -483,17 +564,37 @@ PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params) | |||
| /* 方向延时只在方向发生变化时生效(首次启动/换向/换方向点): | |||
| * 段间同向衔接不再等待 10ms,直接进入 PWM 待启动。 */ | |||
| directionChanged = (state->directionPoint == PLSR_HW_DIR_POINT_NONE) | |||
| || (state->directionPoint != params->directionPoint) | |||
| || (state->directionPositive != params->directionPositive); | |||
| if (params->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| { | |||
| directionChanged = | |||
| (state->directionPoint == PLSR_HW_DIR_POINT_NONE) | |||
| || (state->directionPoint != params->directionPoint) | |||
| || (state->directionPositive != params->directionPositive); | |||
| } | |||
| state->outputMode = params->outputMode; | |||
| state->targetPulses = params->targetPulses; | |||
| state->emittedPulses = 0; | |||
| state->currentFrequencyHz = params->frequencyHz; | |||
| state->directionPoint = params->directionPoint; | |||
| state->directionPoint = | |||
| (params->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| ? params->directionPoint | |||
| : PLSR_HW_DIR_POINT_NONE; | |||
| state->directionDelayRemainingMs = | |||
| (directionChanged != 0U) ? params->directionDelayMs : 0U; | |||
| PlsrHwSetDirLevel(axis, params->directionPositive); | |||
| ((params->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| && (directionChanged != 0U)) | |||
| ? params->directionDelayMs | |||
| : 0U; | |||
| state->abQuarter = 0U; | |||
| if (params->outputMode == PLSR_OUTPUT_PULSE_DIR) | |||
| { | |||
| PlsrHwSetDirLevel(axis, params->directionPositive); | |||
| } | |||
| else | |||
| { | |||
| state->directionPositive = | |||
| (params->directionPositive != 0U) ? 1U : 0U; | |||
| } | |||
| state->state = (state->directionDelayRemainingMs > 0U) | |||
| ? PLSR_HW_STATE_DIR_SETTLING | |||
| : PLSR_HW_STATE_PWM_PENDING; | |||
| @@ -515,18 +616,18 @@ PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz) | |||
| if (frequencyHz > 0UL) | |||
| { | |||
| /* 运行中调频:预装载写入,更新事件时生效,不触碰使能位。 */ | |||
| PlsrHwConfigurePwm(axis, frequencyHz); | |||
| PlsrHwConfigureActiveOutput(axis, state->outputMode, frequencyHz); | |||
| } | |||
| else | |||
| { | |||
| PlsrHwStopPwmTimer(axis); | |||
| PlsrHwStopActiveOutput(axis, state->outputMode); | |||
| } | |||
| } | |||
| else if ((state->state == PLSR_HW_STATE_PWM_PENDING) | |||
| && (frequencyHz > 0UL)) | |||
| { | |||
| PlsrHwConfigurePwm(axis, frequencyHz); | |||
| PlsrHwPwmBegin(axis); | |||
| PlsrHwConfigureActiveOutput(axis, state->outputMode, frequencyHz); | |||
| PlsrHwBeginActiveOutput(axis, state->outputMode); | |||
| state->state = PLSR_HW_STATE_RUNNING; | |||
| } | |||
| return PLSR_RESULT_OK; | |||
| @@ -543,7 +644,8 @@ PLSR_RESULT PlsrHwStopPulse(uint8_t axis) | |||
| state = &PlsrHwAxes[axis]; | |||
| if (state->state != PLSR_HW_STATE_IDLE) | |||
| { | |||
| PlsrHwStopPwmTimer(axis); | |||
| PlsrHwStopActiveOutput(axis, state->outputMode); | |||
| state->abQuarter = 0U; | |||
| state->state = PLSR_HW_STATE_IDLE; | |||
| } | |||
| return PLSR_RESULT_OK; | |||
| @@ -631,8 +733,10 @@ void PlsrHwTick(uint8_t axis) | |||
| case PLSR_HW_STATE_PWM_PENDING: | |||
| if (state->currentFrequencyHz > 0UL) | |||
| { | |||
| PlsrHwConfigurePwm(axis, state->currentFrequencyHz); | |||
| PlsrHwPwmBegin(axis); | |||
| PlsrHwConfigureActiveOutput(axis, | |||
| state->outputMode, | |||
| state->currentFrequencyHz); | |||
| PlsrHwBeginActiveOutput(axis, state->outputMode); | |||
| state->state = PLSR_HW_STATE_RUNNING; | |||
| } | |||
| break; | |||
| @@ -661,12 +765,18 @@ void PlsrHwOnTimerUpdate(uint8_t axis) | |||
| { | |||
| return; | |||
| } | |||
| if (state->outputMode != PLSR_OUTPUT_PULSE_DIR) | |||
| { | |||
| /* AB host 模型按四分之一周期推进,不能把任一物理定时器的 | |||
| * update 直接当成完整 AB 指令脉冲。 */ | |||
| return; | |||
| } | |||
| state->emittedPulses++; | |||
| if (state->emittedPulses >= state->targetPulses) | |||
| { | |||
| /* 更新时刻 = 周期结束:关通道即完整下降沿后停止,无额外脉冲。 */ | |||
| PlsrHwStopPwmTimer(axis); | |||
| PlsrHwStopActiveOutput(axis, state->outputMode); | |||
| state->state = PLSR_HW_STATE_DONE; | |||
| (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE); | |||
| } | |||
| @@ -710,6 +820,85 @@ uint8_t PlsrHwTestGetDirLevel(uint8_t axis) | |||
| return PlsrHwTimers[axis].dirLevel; | |||
| } | |||
| uint8_t PlsrHwTestGetAbPhaseA(uint8_t axis) | |||
| { | |||
| const PLSR_HW_AXIS_STATE *state; | |||
| if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U)) | |||
| { | |||
| return 0U; | |||
| } | |||
| state = &PlsrHwAxes[axis]; | |||
| if (state->directionPositive != 0U) | |||
| { | |||
| return ((state->abQuarter == 1U) || (state->abQuarter == 2U)) | |||
| ? 1U | |||
| : 0U; | |||
| } | |||
| return ((state->abQuarter == 2U) || (state->abQuarter == 3U)) | |||
| ? 1U | |||
| : 0U; | |||
| } | |||
| uint8_t PlsrHwTestGetAbPhaseB(uint8_t axis) | |||
| { | |||
| const PLSR_HW_AXIS_STATE *state; | |||
| if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U)) | |||
| { | |||
| return 0U; | |||
| } | |||
| state = &PlsrHwAxes[axis]; | |||
| if (state->directionPositive != 0U) | |||
| { | |||
| return ((state->abQuarter == 2U) || (state->abQuarter == 3U)) | |||
| ? 1U | |||
| : 0U; | |||
| } | |||
| return ((state->abQuarter == 1U) || (state->abQuarter == 2U)) | |||
| ? 1U | |||
| : 0U; | |||
| } | |||
| uint8_t PlsrHwTestGetAbQuarter(uint8_t axis) | |||
| { | |||
| if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U)) | |||
| { | |||
| return 0U; | |||
| } | |||
| return PlsrHwAxes[axis].abQuarter; | |||
| } | |||
| void PlsrHwTestAdvanceAbQuarter(uint8_t axis) | |||
| { | |||
| PLSR_HW_AXIS_STATE *state; | |||
| if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U)) | |||
| { | |||
| return; | |||
| } | |||
| state = &PlsrHwAxes[axis]; | |||
| if ((state->state != PLSR_HW_STATE_RUNNING) | |||
| || (state->outputMode != PLSR_OUTPUT_AB)) | |||
| { | |||
| return; | |||
| } | |||
| state->abQuarter = (uint8_t)((state->abQuarter + 1U) | |||
| % PLSR_HW_AB_QUARTER_COUNT); | |||
| if (state->abQuarter == 0U) | |||
| { | |||
| state->emittedPulses++; | |||
| if (state->emittedPulses >= state->targetPulses) | |||
| { | |||
| /* 仅在完整 00 边界停机,禁止留下半个正交周期。 */ | |||
| PlsrHwStopActiveOutput(axis, state->outputMode); | |||
| state->state = PLSR_HW_STATE_DONE; | |||
| (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE); | |||
| } | |||
| } | |||
| } | |||
| void PlsrHwTestTriggerUpdate(uint8_t axis) | |||
| { | |||
| if (axis < PLSR_HW_AXIS_COUNT) | |||
| @@ -274,6 +274,147 @@ static void TestPulseCounting(void) | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); | |||
| } | |||
| static void TestAbPhaseAndCounting(void) | |||
| { | |||
| PLSR_HW_START_PARAMS params; | |||
| uint16_t psc; | |||
| uint16_t arr; | |||
| static const uint8_t positiveA[4] = {1U, 1U, 0U, 0U}; | |||
| static const uint8_t positiveB[4] = {0U, 1U, 1U, 0U}; | |||
| static const uint8_t negativeA[4] = {0U, 1U, 1U, 0U}; | |||
| static const uint8_t negativeB[4] = {1U, 1U, 0U, 0U}; | |||
| int quarter; | |||
| (void)PlsrHwInit(); | |||
| (void)memset(¶ms, 0, sizeof(params)); | |||
| params.frequencyHz = 1000UL; | |||
| params.targetPulses = 2; | |||
| params.outputMode = PLSR_OUTPUT_AB; | |||
| params.directionPoint = 4U; /* AB 模式必须忽略独立 DIR 点。 */ | |||
| params.directionPositive = 1U; | |||
| params.directionDelayMs = 10U; /* AB 模式不得执行方向延时。 */ | |||
| CHECK(PlsrHwStartPulse(1U, ¶ms) == PLSR_RESULT_INVALID_AXIS); | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_PWM_PENDING); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == 0U); | |||
| CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) != 0U); | |||
| CHECK(PlsrCalculateTimerDivider(168000000UL, 1000UL, &psc, &arr) | |||
| == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetPsc(0U) == psc); | |||
| CHECK(PlsrHwTestGetArr(0U) == arr); | |||
| CHECK(PlsrCalculateTimerDivider(84000000UL, 1000UL, &psc, &arr) | |||
| == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetPsc(1U) == psc); | |||
| CHECK(PlsrHwTestGetArr(1U) == arr); | |||
| /* 任一物理 timer update 不能直接计作完整 AB 周期。 */ | |||
| PlsrHwTestTriggerUpdate(0U); | |||
| CHECK(PlsrHwGetEmittedPulses(0U) == 0); | |||
| /* 正向:00→10→11→01→00;四次相位跳变只计一个脉冲。 */ | |||
| for (quarter = 0; quarter < 4; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == positiveA[quarter]); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == positiveB[quarter]); | |||
| } | |||
| CHECK(PlsrHwGetEmittedPulses(0U) == 1); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); | |||
| /* 调频同时更新 A/B 两路,且不得重置正交相位。 */ | |||
| CHECK(PlsrHwSetFrequency(0U, 2000UL) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetAbQuarter(0U) == 0U); | |||
| CHECK(PlsrCalculateTimerDivider(168000000UL, 2000UL, &psc, &arr) | |||
| == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetArr(0U) == arr); | |||
| CHECK(PlsrCalculateTimerDivider(84000000UL, 2000UL, &psc, &arr) | |||
| == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetArr(1U) == arr); | |||
| for (quarter = 0; quarter < 4; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| } | |||
| CHECK(PlsrHwGetEmittedPulses(0U) == 2); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == 0U); | |||
| /* 反向:00→01→11→10→00。 */ | |||
| params.targetPulses = 1; | |||
| params.directionPositive = 0U; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); | |||
| for (quarter = 0; quarter < 4; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == negativeA[quarter]); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == negativeB[quarter]); | |||
| } | |||
| CHECK(PlsrHwGetEmittedPulses(0U) == 1); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); | |||
| /* 紧急停止即使发生在周期中间,也必须回到安全 00。 */ | |||
| params.targetPulses = 10; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| CHECK(PlsrHwTestGetAbQuarter(0U) == 1U); | |||
| CHECK(PlsrHwStopPulse(0U) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetAbQuarter(0U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == 0U); | |||
| } | |||
| static void TestTwoAbAxesIndependent(void) | |||
| { | |||
| PLSR_HW_START_PARAMS params; | |||
| int quarter; | |||
| (void)PlsrHwInit(); | |||
| (void)memset(¶ms, 0, sizeof(params)); | |||
| params.frequencyHz = 1000UL; | |||
| params.targetPulses = 1; | |||
| params.outputMode = PLSR_OUTPUT_AB; | |||
| params.directionPoint = PLSR_HW_DIR_POINT_NONE; | |||
| params.directionPositive = 1U; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwStartPulse(2U, ¶ms) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwSetFrequency(2U, 2000UL) == PLSR_RESULT_OK); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(2U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(3U) != 0U); | |||
| for (quarter = 0; quarter < 4; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| } | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); | |||
| CHECK(PlsrHwGetState(2U) == PLSR_HW_STATE_RUNNING); | |||
| CHECK(PlsrHwGetEmittedPulses(2U) == 0); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) == 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(2U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(3U) != 0U); | |||
| for (quarter = 0; quarter < 4; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(2U); | |||
| } | |||
| CHECK(PlsrHwGetState(2U) == PLSR_HW_STATE_DONE); | |||
| CHECK(PlsrHwGetEmittedPulses(2U) == 1); | |||
| } | |||
| static void TestStopAndInvalidArgs(void) | |||
| { | |||
| (void)PlsrHwInit(); | |||
| @@ -298,6 +439,11 @@ static void TestStopAndInvalidArgs(void) | |||
| CHECK(PlsrHwStartPulse(0U, NULL) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| params.targetPulses = 0; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| params.targetPulses = 1; | |||
| params.outputMode = PLSR_OUTPUT_CW_CCW; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_NOT_SUPPORTED); | |||
| params.outputMode = (PLSR_OUTPUT_MODE)99; | |||
| CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| CHECK(PlsrHwSetFrequency(4U, 1000UL) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| CHECK(PlsrHwStopPulse(4U) == PLSR_RESULT_INVALID_ARGUMENT); | |||
| } | |||
| @@ -392,6 +538,50 @@ static void TestEndToEndTwoSegments(void) | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); | |||
| } | |||
| static void TestEndToEndAbSegment(void) | |||
| { | |||
| TEST_MEMORY memory; | |||
| PLSR_CALL call; | |||
| PLSR_STATUS status; | |||
| int quarter; | |||
| TestResetEnvironment(); | |||
| (void)memset(&memory, 0, sizeof(memory)); | |||
| TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1); | |||
| TestSetSegment(&memory, 1U, 1000U, -2); | |||
| call = TestMakeCall(&memory); | |||
| call.sequence = 15UL; | |||
| call.outputModeOverride = PLSR_OUTPUT_AB; | |||
| CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(); | |||
| CHECK(status.state == PLSR_STATE_ACCEL); | |||
| CHECK(status.outputMode == PLSR_OUTPUT_AB); | |||
| CHECK(status.directionPoint == PLSR_DIRECTION_POINT_NONE); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) != 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) != 0U); | |||
| /* 负脉冲选择反向相序,完整两个周期后由同一事件链结束任务。 */ | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| CHECK(PlsrHwTestGetAbPhaseA(0U) == 0U); | |||
| CHECK(PlsrHwTestGetAbPhaseB(0U) == 1U); | |||
| for (quarter = 1; quarter < 8; quarter++) | |||
| { | |||
| PlsrHwTestAdvanceAbQuarter(0U); | |||
| } | |||
| CHECK(PlsrHwGetEmittedPulses(0U) == 2); | |||
| CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); | |||
| PlsrProcess(); | |||
| status = TestGetStatus(); | |||
| CHECK(status.state == PLSR_STATE_COMPLETED); | |||
| CHECK(status.done != 0U); | |||
| CHECK(status.highResourceMask == 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); | |||
| CHECK(PlsrHwTestGetPwmEnabled(1U) == 0U); | |||
| } | |||
| static void TestStopStopsHardware(void) | |||
| { | |||
| TEST_MEMORY memory; | |||
| @@ -438,8 +628,11 @@ int main(void) | |||
| TestDirDelaySequence(); | |||
| TestZeroFrequencyWaits(); | |||
| TestPulseCounting(); | |||
| TestAbPhaseAndCounting(); | |||
| TestTwoAbAxesIndependent(); | |||
| TestStopAndInvalidArgs(); | |||
| TestEndToEndTwoSegments(); | |||
| TestEndToEndAbSegment(); | |||
| TestStopStopsHardware(); | |||
| if (TestFailures != 0) | |||