diff --git a/EWARM/Modbus.ewp b/EWARM/Modbus.ewp index fe594b9..84d63a6 100644 --- a/EWARM/Modbus.ewp +++ b/EWARM/Modbus.ewp @@ -1290,6 +1290,9 @@ $PROJ_DIR$\..\PLSR\Inc\plsr_profile.h + + $PROJ_DIR$\..\PLSR\Inc\plsr_hal_f407.h + $PROJ_DIR$\..\PLSR\Src\plsr_persistence.c @@ -1308,6 +1311,9 @@ $PROJ_DIR$\..\PLSR\Src\plsr_profile.c + + $PROJ_DIR$\..\PLSR\Src\plsr_hal_f407.c + $PROJ_DIR$\..\PLSR\Src\plsr_core.c diff --git a/PLSR/Inc/plsr_hal_f407.h b/PLSR/Inc/plsr_hal_f407.h new file mode 100644 index 0000000..d5581ca --- /dev/null +++ b/PLSR/Inc/plsr_hal_f407.h @@ -0,0 +1,64 @@ +#ifndef PLSR_HAL_F407_H +#define PLSR_HAL_F407_H + +#include "plsr_types.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PLSR_HW_AXIS_COUNT (PLSR_AXIS_COUNT) +#define PLSR_HW_DIR_POINT_NONE (0xFFU) + +typedef enum +{ + PLSR_HW_STATE_IDLE = 0, + PLSR_HW_STATE_DIR_SETTLING, /* DIR 已置位,等待方向建立延时 */ + PLSR_HW_STATE_PWM_PENDING, /* 延时已到,等待首个非零频率启动 PWM */ + PLSR_HW_STATE_RUNNING, /* PWM 输出中,更新中断计数 */ + PLSR_HW_STATE_DONE +} PLSR_HW_STATE; + +typedef struct +{ + uint32_t frequencyHz; /* 初始频率(起始速度,可为 0) */ + int64_t targetPulses; /* 本段目标脉冲数 */ + uint8_t directionPoint; /* DIR 输出点(Y 点号,0xFF=无) */ + uint8_t directionPositive; + uint16_t directionDelayMs; +} PLSR_HW_START_PARAMS; + +PLSR_RESULT PlsrHwInit(void); + +PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params); +PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz); +PLSR_RESULT PlsrHwStopPulse(uint8_t axis); +uint8_t PlsrHwIsPulseActive(uint8_t axis); +PLSR_HW_STATE PlsrHwGetState(uint8_t axis); +uint32_t PlsrHwGetTimerClockHz(uint8_t axis); + +/* 每 1ms tick 推进 HAL 状态机(DIR 延时等)。 */ +void PlsrHwTick(uint8_t axis); + +/* 输出定时器更新中断入口(生产由 IRQHandler 调用,host 由测试调用)。 */ +void PlsrHwOnTimerUpdate(uint8_t axis); + +/* 输出点(Y0~Y20)引脚映射表:DIR 点解析用。 */ +uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber); + +#ifdef PLSR_HOST_TEST +/* 模拟寄存器访问与中断触发(测试用)。 */ +uint32_t PlsrHwTestGetArr(uint8_t axis); +uint32_t PlsrHwTestGetCcr(uint8_t axis); +uint32_t PlsrHwTestGetPsc(uint8_t axis); +uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis); +uint8_t PlsrHwTestGetDirLevel(uint8_t axis); +void PlsrHwTestTriggerUpdate(uint8_t axis); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PLSR_HAL_F407_H */ diff --git a/PLSR/Src/plsr_core.c b/PLSR/Src/plsr_core.c index e86d080..7372c5e 100644 --- a/PLSR/Src/plsr_core.c +++ b/PLSR/Src/plsr_core.c @@ -1,7 +1,9 @@ #include "plsr_core.h" #include "plc_device.h" #include "plsr_address_map.h" +#include "plsr_hal_f407.h" #include "plsr_path.h" +#include "plsr_profile.h" #include "plsr_resource.h" #include @@ -27,6 +29,9 @@ typedef struct int64_t logicalPosition; int64_t totalPulses; PLSR_PATH_CONTEXT path; + PLSR_PROFILE_STATE profile; + uint8_t profileActive; + uint8_t profileWasAccel; uint8_t hasLastCommand; uint8_t done; uint8_t directionPositive; @@ -53,6 +58,9 @@ static uint32_t PlsrNextTicket; 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 uint32_t PlsrCoreEnterCritical(void) { #ifdef PLSR_HOST_TEST @@ -230,6 +238,7 @@ PLSR_RESULT PlsrStateTransition(uint8_t axis, axisObject->state = PLSR_STATE_ERROR; axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; axisObject->immediateStopPending = 0U; + PlsrStopSegmentHardware(axis, axisObject); PlsrResourceRelease(&axisObject->lease); PlsrPublishAxis(axis); return PLSR_RESULT_INVALID_STATE; @@ -246,6 +255,7 @@ PLSR_RESULT PlsrStateTransition(uint8_t axis, axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED; axisObject->immediateStopPending = 0U; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); PlsrResourceRelease(&axisObject->lease); /* 运动已结束:记录 lastBusy=0,保证掉电后恢复时位置仍可信。 */ (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 0U); @@ -625,6 +635,8 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject, } else { + /* 启动当前段硬件输出与速度曲线。 */ + PlsrStartSegmentHardware(call->dAxis, axisObject); /* 运动开始:掉电恢复时据此判定"断电时在运动中"。 */ (void)PlcDeviceSetHsdCheckpointMeta(axisObject->positionValid, 1U); (void)PlcDeviceCheckpointHsd(); @@ -654,6 +666,7 @@ static PLSR_RESULT PlsrStopImmediate(uint8_t axis) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_IMMEDIATE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -688,6 +701,7 @@ static PLSR_RESULT PlsrStopDecel(uint8_t axis) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_STOP_DECEL); axisObject->pendingTerminal = PLSR_STATE_STOPPED; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -720,6 +734,7 @@ static PLSR_RESULT PlsrPause(uint8_t axis) { PlsrSetStopReason(axisObject, PLSR_STOP_REASON_PAUSE); PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); return PlsrStateTransition(axis, PLSR_STATE_PAUSED, PLSR_TRANSITION_DECEL_COMPLETE); @@ -912,6 +927,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PLSR_STOP_REASON_SOFTWARE_EMERGENCY); axisObject->done = 0U; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); (void)PlsrStateTransition(axis, PLSR_STATE_STOPPED, PLSR_TRANSITION_STOP); @@ -929,6 +945,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PLSR_STOP_REASON_LIMIT_POSITIVE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -956,6 +973,7 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PLSR_STOP_REASON_LIMIT_NEGATIVE); axisObject->pendingTerminal = PLSR_STATE_STOPPED; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); if ((axisObject->state == PLSR_STATE_WAIT) || (axisObject->state == PLSR_STATE_PAUSED)) { @@ -981,12 +999,85 @@ static void PlsrProcessCriticalEvents(uint8_t axis, uint32_t events) PlsrSetStopReason(axisObject, PLSR_STOP_REASON_FAULT); axisObject->done = 0U; PlsrPathTerminate(&axisObject->path); + PlsrStopSegmentHardware(axis, axisObject); (void)PlsrStateTransition(axis, PLSR_STATE_ERROR, PLSR_TRANSITION_FAULT); } } +/* 启动当前段的硬件输出与速度曲线(P3a:单轴 PULSE/DIR)。 + * 由段进入 ACCEL 时调用(任务启动 + 段间推进)。 */ +static void 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; + int64_t pulses; + uint8_t positive; + + if (job->positioningMode == 0U) + { + pulses = (segment->pulseOrTarget < 0) + ? -(int64_t)segment->pulseOrTarget + : (int64_t)segment->pulseOrTarget; + positive = (segment->pulseOrTarget >= 0) ? 1U : 0U; + } + else + { + /* 绝对模式:P4 位置闭环后完善;P3a 按当前位置计算。 */ + int64_t delta = (int64_t)segment->pulseOrTarget + - axisObject->logicalPosition; + + pulses = (delta < 0) ? -delta : delta; + positive = (delta >= 0) ? 1U : 0U; + } + + (void)memset(&profileRequest, 0, sizeof(profileRequest)); + profileRequest.targetFrequencyHz = segment->targetFrequency; + profileRequest.startFrequencyHz = job->s2.startSpeed; + profileRequest.stopFrequencyHz = job->s2.stopSpeed; + profileRequest.maxFrequencyHz = job->s2.maximumSpeed; + profileRequest.accelSlopeHzPerMs = + (job->s2.accelerationMs != 0U) + ? job->s2.defaultSpeed / job->s2.accelerationMs + : 0UL; + profileRequest.decelSlopeHzPerMs = + (job->s2.decelerationMs != 0U) + ? job->s2.defaultSpeed / job->s2.decelerationMs + : 0UL; + profileRequest.curveMode = job->s2.curveMode; + + if (PlsrProfileStart(&axisObject->profile, + &profileRequest, + pulses, + 1000U) == PLSR_RESULT_OK) + { + axisObject->profileActive = 1U; + axisObject->profileWasAccel = + (axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) + ? 1U + : 0U; + } + + params.frequencyHz = job->s2.startSpeed; + params.targetPulses = pulses; + params.directionPoint = job->directionPoint; + params.directionPositive = positive; + params.directionDelayMs = job->s2.directionDelayMs; + (void)PlsrHwStartPulse(axis, ¶ms); +} + +/* 停止当前段的硬件输出与速度曲线。 */ +static void PlsrStopSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject) +{ + axisObject->profileActive = 0U; + axisObject->profileWasAccel = 0U; + (void)PlsrHwStopPulse(axis); +} + /* 应用路径执行器的动作:段间推进、进入等待、结束、让出、错误。 */ static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action) { @@ -1009,6 +1100,11 @@ static void PlsrApplyPathAction(uint8_t axis, PLSR_PATH_ACTION action) PLSR_STATE_ACCEL, PLSR_TRANSITION_START); } + /* 进入新段:重新启动硬件输出与速度曲线。 */ + if (axisObject->state == PLSR_STATE_ACCEL) + { + PlsrStartSegmentHardware(axis, axisObject); + } break; case PLSR_PATH_ACTION_ENTER_WAIT: @@ -1147,6 +1243,7 @@ PLSR_RESULT PlsrInit(void) (void)memset(PlsrCommandQueue, 0, sizeof(PlsrCommandQueue)); PlsrNextTicket = 0UL; PlsrResourceInit(); + (void)PlsrHwInit(); PlsrInitialized = 1U; restoredPositionValid = PlcDeviceGetRestoredHsdPositionValid(); @@ -1218,12 +1315,17 @@ void PlsrProcess(void) } } - /* 1ms tick:路径执行器推进(WAIT/ACT 计时、信号/EXT 轮询、跳转链)。 */ + /* 1ms tick:路径执行器推进(WAIT/ACT 计时、信号/EXT 轮询、跳转链) + * + 速度曲线推进(P2) + HAL 状态机(DIR 延时)。 */ for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { PLSR_AXIS *axisObject = &PlsrAxes[axis]; PLSR_PATH_ACTION action; + uint32_t frequencyHz; + uint8_t profileDone; + uint8_t wasAccel; + PlsrHwTick(axis); if (PlsrStateIsBusy(axisObject->state) == 0U) { continue; @@ -1235,6 +1337,29 @@ void PlsrProcess(void) { PlsrApplyPathAction(axis, action); } + + if (axisObject->profileActive != 0U) + { + wasAccel = axisObject->profileWasAccel; + (void)PlsrProfileStep(&axisObject->profile, + &frequencyHz, + &profileDone); + if (profileDone == 0U) + { + (void)PlsrHwSetFrequency(axis, frequencyHz); + } + axisObject->profileWasAccel = + (axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) + ? 1U + : 0U; + if ((wasAccel != 0U) + && (axisObject->profileWasAccel == 0U) + && (axisObject->state == PLSR_STATE_ACCEL)) + { + /* 加速完成(曲线进入匀速/减速)→ 状态机推进到 RUN。 */ + (void)PlsrPostEvent(axis, PLSR_EVENT_ACCEL_COMPLETE); + } + } } } diff --git a/PLSR/Src/plsr_hal_f407.c b/PLSR/Src/plsr_hal_f407.c new file mode 100644 index 0000000..8f89e9d --- /dev/null +++ b/PLSR/Src/plsr_hal_f407.c @@ -0,0 +1,516 @@ +#include "plsr_hal_f407.h" +#include "plsr_address_map.h" +#include "plsr_core.h" +#include "plsr_job.h" +#include + +#ifndef PLSR_HOST_TEST +#include "stm32f4xx.h" +#include "stm32f4xx_hal.h" +#endif + +#define PLSR_HW_TIMER_CHANNEL1_BIT (0x0001U) +#define PLSR_HW_TIMER_UPDATE_BIT (0x0001U) +#define PLSR_HW_OUTPUT_POINT_COUNT (21U) + +typedef struct +{ + uint32_t timerClockHz; + uint8_t directionPoint; /* 0xFF = 无 */ +#ifndef PLSR_HOST_TEST + TIM_TypeDef *timer; + GPIO_TypeDef *gpioPort; + uint16_t gpioPin; + uint8_t afMode; + IRQn_Type irq; +#endif +} PLSR_HW_AXIS_MAP; + +#ifndef PLSR_HOST_TEST +/* 输出点(Y 点号)→ GPIO 引脚:XDM-60T4-E 原理图。 + * 点号 8/9/18/19 不存在(资源层掩码 0x0013FCFF 已约束)。 */ +typedef struct +{ + GPIO_TypeDef *port; + uint16_t pin; +} PLSR_HW_OUTPUT_PIN; + +static const PLSR_HW_OUTPUT_PIN PlsrHwOutputPins[PLSR_HW_OUTPUT_POINT_COUNT] = +{ + {GPIOF, GPIO_PIN_6}, /* Y0 */ + {GPIOF, GPIO_PIN_8}, /* Y1 */ + {GPIOF, GPIO_PIN_7}, /* Y2 */ + {GPIOF, GPIO_PIN_9}, /* Y3 */ + {GPIOI, GPIO_PIN_8}, /* Y4 */ + {GPIOE, GPIO_PIN_6}, /* Y5 */ + {GPIOE, GPIO_PIN_5}, /* Y6 */ + {GPIOE, GPIO_PIN_4}, /* Y7 */ + {NULL, 0U}, /* Y8 */ + {NULL, 0U}, /* Y9 */ + {GPIOG, GPIO_PIN_7}, /* Y10 */ + {GPIOG, GPIO_PIN_6}, /* Y11 */ + {GPIOH, GPIO_PIN_9}, /* Y12 */ + {GPIOH, GPIO_PIN_8}, /* Y13 */ + {GPIOH, GPIO_PIN_7}, /* Y14 */ + {GPIOH, GPIO_PIN_6}, /* Y15 */ + {GPIOF, GPIO_PIN_11}, /* Y16 */ + {GPIOB, GPIO_PIN_0}, /* Y17 */ + {NULL, 0U}, /* Y18 */ + {NULL, 0U}, /* Y19 */ + {GPIOH, GPIO_PIN_5} /* Y20 */ +}; +#endif + +/* Q0~Q3 定时器:XDM-60T4-E。 + * PF6=TIM10_CH1(AF3)、PF7=TIM11_CH1(AF3)、PF8=TIM13_CH1(AF9)、PF9=TIM14_CH1(AF9)。 + * 定时器时钟由 RCC 实际配置计算(APB2 分频≠1 时定时器时钟×2)。 */ +static const PLSR_HW_AXIS_MAP PlsrHwAxisMap[PLSR_HW_AXIS_COUNT] = +{ +#ifndef PLSR_HOST_TEST + {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM10, GPIOF, GPIO_PIN_6, 3U, TIM1_UP_TIM10_IRQn}, + {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM13, GPIOF, GPIO_PIN_8, 9U, TIM8_UP_TIM13_IRQn}, + {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM11, GPIOF, GPIO_PIN_7, 3U, TIM1_TRG_COM_TIM11_IRQn}, + {168000000UL, PLSR_HW_DIR_POINT_NONE, TIM14, GPIOF, GPIO_PIN_9, 9U, TIM8_TRG_COM_TIM14_IRQn} +#else + {168000000UL, PLSR_HW_DIR_POINT_NONE}, + {168000000UL, PLSR_HW_DIR_POINT_NONE}, + {168000000UL, PLSR_HW_DIR_POINT_NONE}, + {168000000UL, PLSR_HW_DIR_POINT_NONE} +#endif +}; + +/* host 测试:模拟定时器寄存器。 */ +#ifdef PLSR_HOST_TEST +typedef struct +{ + uint32_t cr1; + uint32_t dier; + uint32_t sr; + uint32_t psc; + uint32_t arr; + uint32_t ccr1; + uint32_t ccer; + uint8_t dirLevel; +} PLSR_HW_TIMER_REGS; + +static PLSR_HW_TIMER_REGS PlsrHwTimers[PLSR_HW_AXIS_COUNT]; +#endif + +typedef struct +{ + PLSR_HW_STATE state; + uint32_t currentFrequencyHz; + int64_t targetPulses; + int64_t emittedPulses; + uint16_t directionDelayRemainingMs; + uint8_t directionPoint; + uint8_t directionPositive; +} PLSR_HW_AXIS_STATE; + +static PLSR_HW_AXIS_STATE PlsrHwAxes[PLSR_HW_AXIS_COUNT]; + +/* ---- 定时器寄存器访问抽象(host 模拟 / 生产真实) ---- */ + +static void PlsrHwTimerSetArr(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].arr = value; +#else + PlsrHwAxisMap[axis].timer->ARR = value; +#endif +} + +static void PlsrHwTimerSetPsc(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].psc = value; +#else + PlsrHwAxisMap[axis].timer->PSC = value; +#endif +} + +static void PlsrHwTimerSetCcr(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].ccr1 = value; +#else + PlsrHwAxisMap[axis].timer->CCR1 = value; +#endif +} + +static void PlsrHwTimerSetCen(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].cr1 = (PlsrHwTimers[axis].cr1 & ~0x0001UL) | value; +#else + if (value != 0UL) + { + PlsrHwAxisMap[axis].timer->CR1 |= TIM_CR1_CEN; + } + else + { + PlsrHwAxisMap[axis].timer->CR1 &= ~TIM_CR1_CEN; + } +#endif +} + +static void PlsrHwTimerSetCc1e(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].ccer = (PlsrHwTimers[axis].ccer & ~0x0001UL) | value; +#else + if (value != 0UL) + { + PlsrHwAxisMap[axis].timer->CCER |= TIM_CCER_CC1E; + } + else + { + PlsrHwAxisMap[axis].timer->CCER &= ~TIM_CCER_CC1E; + } +#endif +} + +static void PlsrHwTimerSetUie(uint8_t axis, uint32_t value) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].dier = (PlsrHwTimers[axis].dier & ~0x0001UL) | value; +#else + if (value != 0UL) + { + PlsrHwAxisMap[axis].timer->DIER |= TIM_DIER_UIE; + } + else + { + PlsrHwAxisMap[axis].timer->DIER &= ~TIM_DIER_UIE; + } +#endif +} + +static void PlsrHwTimerClearUif(uint8_t axis) +{ +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].sr &= ~PLSR_HW_TIMER_UPDATE_BIT; +#else + PlsrHwAxisMap[axis].timer->SR &= ~TIM_SR_UIF; +#endif +} + +/* ---- DIR 输出 ---- */ + +static void PlsrHwSetDirLevel(uint8_t axis, uint8_t positive) +{ + PLSR_HW_AXIS_STATE *state = &PlsrHwAxes[axis]; + + state->directionPositive = (positive != 0U) ? 1U : 0U; + if (state->directionPoint == PLSR_HW_DIR_POINT_NONE) + { + return; + } +#ifdef PLSR_HOST_TEST + PlsrHwTimers[axis].dirLevel = (positive != 0U) ? 1U : 0U; +#else + if (state->directionPoint < PLSR_HW_OUTPUT_POINT_COUNT) + { + const PLSR_HW_OUTPUT_PIN *pin = + &PlsrHwOutputPins[state->directionPoint]; + + if (pin->port != NULL) + { + HAL_GPIO_WritePin(pin->port, pin->pin, + (positive != 0U) ? GPIO_PIN_SET : GPIO_PIN_RESET); + } + } +#endif +} + +/* ---- PWM 启停 ---- */ + +static void PlsrHwStartPwmTimer(uint8_t axis, uint32_t frequencyHz) +{ + uint16_t psc; + uint16_t arr; + + if (PlsrCalculateTimerDivider(PlsrHwAxisMap[axis].timerClockHz, + frequencyHz, + &psc, + &arr) != PLSR_RESULT_OK) + { + return; + } + PlsrHwTimerSetPsc(axis, psc); + PlsrHwTimerSetArr(axis, arr); + PlsrHwTimerSetCcr(axis, (uint32_t)arr / 2UL); /* 50% 占空比 */ + PlsrHwTimerSetUie(axis, 1UL); + PlsrHwTimerSetCc1e(axis, 1UL); + PlsrHwTimerSetCen(axis, 1UL); +} + +static void PlsrHwStopPwmTimer(uint8_t axis) +{ + PlsrHwTimerSetCc1e(axis, 0UL); + PlsrHwTimerSetUie(axis, 0UL); + PlsrHwTimerSetCen(axis, 0UL); +} + +uint8_t PlsrHwResolveDirectionPoint(uint8_t pointNumber) +{ + /* 与资源层一致的合法输出点掩码(Q0~Q7、Q10~Q17、Q20)。 */ + const uint32_t validOutputMask = 0x0013FCFFUL; + + if (pointNumber >= PLSR_HW_OUTPUT_POINT_COUNT) + { + return 0U; + } + if ((validOutputMask & (1UL << pointNumber)) == 0UL) + { + return 0U; + } +#ifndef PLSR_HOST_TEST + if (PlsrHwOutputPins[pointNumber].port == NULL) + { + return 0U; + } +#endif + return 1U; +} + +PLSR_RESULT PlsrHwInit(void) +{ + uint8_t axis; + + (void)memset(PlsrHwAxes, 0, sizeof(PlsrHwAxes)); + for (axis = 0U; axis < PLSR_HW_AXIS_COUNT; axis++) + { + PlsrHwAxes[axis].state = PLSR_HW_STATE_IDLE; + PlsrHwAxes[axis].directionPoint = PLSR_HW_DIR_POINT_NONE; +#ifdef PLSR_HOST_TEST + (void)memset(&PlsrHwTimers[axis], 0, sizeof(PlsrHwTimers[axis])); +#else + PlsrHwTimerSetCc1e(axis, 0UL); + PlsrHwTimerSetUie(axis, 0UL); + PlsrHwTimerSetCen(axis, 0UL); +#endif + } + return PLSR_RESULT_OK; +} + +PLSR_RESULT PlsrHwStartPulse(uint8_t axis, const PLSR_HW_START_PARAMS *params) +{ + PLSR_HW_AXIS_STATE *state; + + if ((axis >= PLSR_HW_AXIS_COUNT) || (params == NULL)) + { + return PLSR_RESULT_INVALID_ARGUMENT; + } + if (params->targetPulses <= 0) + { + return PLSR_RESULT_INVALID_ARGUMENT; + } + state = &PlsrHwAxes[axis]; + if (state->state == PLSR_HW_STATE_RUNNING) + { + return PLSR_RESULT_BUSY; + } + + state->targetPulses = params->targetPulses; + state->emittedPulses = 0; + state->currentFrequencyHz = params->frequencyHz; + state->directionPoint = params->directionPoint; + state->directionDelayRemainingMs = params->directionDelayMs; + PlsrHwSetDirLevel(axis, params->directionPositive); + state->state = (params->directionDelayMs > 0U) + ? PLSR_HW_STATE_DIR_SETTLING + : PLSR_HW_STATE_PWM_PENDING; + return PLSR_RESULT_OK; +} + +PLSR_RESULT PlsrHwSetFrequency(uint8_t axis, uint32_t frequencyHz) +{ + PLSR_HW_AXIS_STATE *state; + + if (axis >= PLSR_HW_AXIS_COUNT) + { + return PLSR_RESULT_INVALID_ARGUMENT; + } + state = &PlsrHwAxes[axis]; + state->currentFrequencyHz = frequencyHz; + if (state->state == PLSR_HW_STATE_RUNNING) + { + if (frequencyHz > 0UL) + { + PlsrHwStartPwmTimer(axis, frequencyHz); + } + else + { + PlsrHwStopPwmTimer(axis); + } + } + else if ((state->state == PLSR_HW_STATE_PWM_PENDING) + && (frequencyHz > 0UL)) + { + PlsrHwStartPwmTimer(axis, frequencyHz); + state->state = PLSR_HW_STATE_RUNNING; + } + return PLSR_RESULT_OK; +} + +PLSR_RESULT PlsrHwStopPulse(uint8_t axis) +{ + PLSR_HW_AXIS_STATE *state; + + if (axis >= PLSR_HW_AXIS_COUNT) + { + return PLSR_RESULT_INVALID_ARGUMENT; + } + state = &PlsrHwAxes[axis]; + if (state->state != PLSR_HW_STATE_IDLE) + { + PlsrHwStopPwmTimer(axis); + state->state = PLSR_HW_STATE_IDLE; + } + return PLSR_RESULT_OK; +} + +uint8_t PlsrHwIsPulseActive(uint8_t axis) +{ + if (axis >= PLSR_HW_AXIS_COUNT) + { + return 0U; + } + return (PlsrHwAxes[axis].state == PLSR_HW_STATE_RUNNING) ? 1U : 0U; +} + +PLSR_HW_STATE PlsrHwGetState(uint8_t axis) +{ + if (axis >= PLSR_HW_AXIS_COUNT) + { + return PLSR_HW_STATE_IDLE; + } + return PlsrHwAxes[axis].state; +} + +uint32_t PlsrHwGetTimerClockHz(uint8_t axis) +{ + if (axis >= PLSR_HW_AXIS_COUNT) + { + return 0UL; + } + return PlsrHwAxisMap[axis].timerClockHz; +} + +void PlsrHwTick(uint8_t axis) +{ + PLSR_HW_AXIS_STATE *state; + + if (axis >= PLSR_HW_AXIS_COUNT) + { + return; + } + state = &PlsrHwAxes[axis]; + switch (state->state) + { + case PLSR_HW_STATE_DIR_SETTLING: + if (state->directionDelayRemainingMs > 0U) + { + state->directionDelayRemainingMs--; + } + if (state->directionDelayRemainingMs == 0U) + { + state->state = PLSR_HW_STATE_PWM_PENDING; + } + break; + + case PLSR_HW_STATE_PWM_PENDING: + if (state->currentFrequencyHz > 0UL) + { + PlsrHwStartPwmTimer(axis, state->currentFrequencyHz); + state->state = PLSR_HW_STATE_RUNNING; + } + break; + + default: + break; + } +} + +/* 输出定时器更新中断:每周期末触发一次(=1 个脉冲)。 */ +void PlsrHwOnTimerUpdate(uint8_t axis) +{ + PLSR_HW_AXIS_STATE *state; + + if (axis >= PLSR_HW_AXIS_COUNT) + { + return; + } + state = &PlsrHwAxes[axis]; + PlsrHwTimerClearUif(axis); + if (state->state != PLSR_HW_STATE_RUNNING) + { + return; + } + + state->emittedPulses++; + if (state->emittedPulses >= state->targetPulses) + { + /* 更新时刻 = 周期结束:关通道即完整下降沿后停止,无额外脉冲。 */ + PlsrHwStopPwmTimer(axis); + state->state = PLSR_HW_STATE_DONE; + (void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE); + } +} + +#ifdef PLSR_HOST_TEST +uint32_t PlsrHwTestGetArr(uint8_t axis) +{ + return PlsrHwTimers[axis].arr; +} + +uint32_t PlsrHwTestGetCcr(uint8_t axis) +{ + return PlsrHwTimers[axis].ccr1; +} + +uint32_t PlsrHwTestGetPsc(uint8_t axis) +{ + return PlsrHwTimers[axis].psc; +} + +uint8_t PlsrHwTestGetPwmEnabled(uint8_t axis) +{ + return ((PlsrHwTimers[axis].ccer & PLSR_HW_TIMER_CHANNEL1_BIT) != 0UL) + ? 1U + : 0U; +} + +uint8_t PlsrHwTestGetDirLevel(uint8_t axis) +{ + return PlsrHwTimers[axis].dirLevel; +} + +void PlsrHwTestTriggerUpdate(uint8_t axis) +{ + PlsrHwOnTimerUpdate(axis); +} +#endif + +#ifndef PLSR_HOST_TEST +void TIM1_UP_TIM10_IRQHandler(void) +{ + PlsrHwOnTimerUpdate(0U); +} + +void TIM8_UP_TIM13_IRQHandler(void) +{ + PlsrHwOnTimerUpdate(1U); +} + +void TIM1_TRG_COM_TIM11_IRQHandler(void) +{ + PlsrHwOnTimerUpdate(2U); +} + +void TIM8_TRG_COM_TIM14_IRQHandler(void) +{ + PlsrHwOnTimerUpdate(3U); +} +#endif diff --git a/PLSR/Test/run_host_tests.ps1 b/PLSR/Test/run_host_tests.ps1 index ebf55ee..8f7195f 100644 --- a/PLSR/Test/run_host_tests.ps1 +++ b/PLSR/Test/run_host_tests.ps1 @@ -30,6 +30,8 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_resource.c" "$workspacePath\PLSR\Src\plsr_job.c" "$workspacePath\PLSR\Src\plsr_path.c" + "$workspacePath\PLSR\Src\plsr_profile.c" + "$workspacePath\PLSR\Src\plsr_hal_f407.c" "$workspacePath\PLSR\Src\plsr_core.c" "$workspacePath\PLSR\Test\test_plsr_core.c" ) @@ -42,6 +44,8 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_resource.c" "$workspacePath\PLSR\Src\plsr_job.c" "$workspacePath\PLSR\Src\plsr_path.c" + "$workspacePath\PLSR\Src\plsr_profile.c" + "$workspacePath\PLSR\Src\plsr_hal_f407.c" "$workspacePath\PLSR\Src\plsr_core.c" "$workspacePath\PLSR\Test\test_plsr_job.c" ) @@ -54,6 +58,8 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_resource.c" "$workspacePath\PLSR\Src\plsr_job.c" "$workspacePath\PLSR\Src\plsr_path.c" + "$workspacePath\PLSR\Src\plsr_profile.c" + "$workspacePath\PLSR\Src\plsr_hal_f407.c" "$workspacePath\PLSR\Src\plsr_core.c" "$workspacePath\PLSR\Test\test_plsr_path.c" ) @@ -64,6 +70,20 @@ $tests = @( "$workspacePath\PLSR\Src\plsr_profile.c" "$workspacePath\PLSR\Test\test_plsr_profile.c" ) + }, + @{ + Name = 'test_plsr_hal' + 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_profile.c" + "$workspacePath\PLSR\Src\plsr_hal_f407.c" + "$workspacePath\PLSR\Src\plsr_core.c" + "$workspacePath\PLSR\Test\test_plsr_hal.c" + ) } ) diff --git a/PLSR/Test/test_plsr_hal.c b/PLSR/Test/test_plsr_hal.c new file mode 100644 index 0000000..93686f2 --- /dev/null +++ b/PLSR/Test/test_plsr_hal.c @@ -0,0 +1,421 @@ +#include "plc_device.h" +#include "plsr_core.h" +#include "plsr_hal_f407.h" +#include "plsr_job.h" +#include "plsr_persistence.h" +#include +#include + +#define TEST_WORD_CAPACITY (3000U) +#define TEST_S0_BASE (100U) +#define TEST_S1_BASE (200U) + +typedef struct +{ + uint16_t words[3][TEST_WORD_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; + (void)device; + 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) +{ + (void)context; + (void)device; + (void)address; + *value = 0U; + 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); +} + +static void TestSetSegment(TEST_MEMORY *memory, + uint16_t number, + uint32_t frequency, + int32_t pulses) +{ + uint32_t base = TEST_S0_BASE + (uint32_t)number * 10UL; + + TestWriteDword(memory, PLSR_DEVICE_D, base, (int32_t)frequency); + TestWriteDword(memory, PLSR_DEVICE_D, base + 2UL, pulses); + memory->words[PLSR_DEVICE_D][base + 4UL] = 0U; + TestWriteDword(memory, PLSR_DEVICE_D, base + 5UL, 0); + memory->words[PLSR_DEVICE_D][base + 7UL] = 0U; + TestWriteDword(memory, PLSR_DEVICE_D, base + 8UL, 0); +} + +static void TestResetEnvironment(void) +{ + PlsrPersistenceTestResetStorage(); + CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + 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; +} + +/* ---- HAL 单测 ---- */ + +static void TestMapping(void) +{ + (void)PlsrHwInit(); + CHECK(PlsrHwGetTimerClockHz(0U) == 168000000UL); + CHECK(PlsrHwGetTimerClockHz(1U) == 168000000UL); + CHECK(PlsrHwGetTimerClockHz(2U) == 168000000UL); + CHECK(PlsrHwGetTimerClockHz(3U) == 168000000UL); + CHECK(PlsrHwGetTimerClockHz(4U) == 0UL); + CHECK(PlsrHwResolveDirectionPoint(4U) != 0U); + CHECK(PlsrHwResolveDirectionPoint(8U) == 0U); + CHECK(PlsrHwResolveDirectionPoint(20U) != 0U); + CHECK(PlsrHwResolveDirectionPoint(21U) == 0U); +} + +static void TestDirDelaySequence(void) +{ + (void)PlsrHwInit(); + PLSR_HW_START_PARAMS params; + uint16_t psc; + uint16_t arr; + int ticks; + + (void)memset(¶ms, 0, sizeof(params)); + params.frequencyHz = 1000UL; + params.targetPulses = 100; + params.directionPoint = 4U; + params.directionPositive = 1U; + params.directionDelayMs = 10U; + + CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DIR_SETTLING); + CHECK(PlsrHwTestGetDirLevel(0U) == 1U); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); + CHECK(PlsrHwIsPulseActive(0U) == 0U); + + for (ticks = 0; ticks < 9; ticks++) + { + PlsrHwTick(0U); + } + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DIR_SETTLING); + + PlsrHwTick(0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_PWM_PENDING); + + /* 首个非零频率启动 PWM,ARR/CCR 与分频计算一致。 */ + CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 1U); + CHECK(PlsrHwIsPulseActive(0U) == 1U); + CHECK(PlsrCalculateTimerDivider(168000000UL, 1000UL, &psc, &arr) + == PLSR_RESULT_OK); + CHECK(PlsrHwTestGetArr(0U) == arr); + CHECK(PlsrHwTestGetPsc(0U) == psc); + CHECK(PlsrHwTestGetCcr(0U) == arr / 2UL); +} + +static void TestZeroFrequencyWaits(void) +{ + (void)PlsrHwInit(); + PLSR_HW_START_PARAMS params; + + (void)memset(¶ms, 0, sizeof(params)); + params.frequencyHz = 0UL; + params.targetPulses = 50; + params.directionPoint = PLSR_HW_DIR_POINT_NONE; + params.directionPositive = 1U; + params.directionDelayMs = 0U; + + CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_PWM_PENDING); + PlsrHwTick(0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_PWM_PENDING); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); + + /* 起始速度为 0:profile 升频后首个非零频率才启动 PWM。 */ + CHECK(PlsrHwSetFrequency(0U, 10UL) == PLSR_RESULT_OK); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 1U); +} + +static void TestPulseCounting(void) +{ + (void)PlsrHwInit(); + PLSR_HW_START_PARAMS params; + int pulse; + + (void)memset(¶ms, 0, sizeof(params)); + params.frequencyHz = 1000UL; + params.targetPulses = 5; + params.directionPoint = PLSR_HW_DIR_POINT_NONE; + params.directionPositive = 1U; + params.directionDelayMs = 0U; + + CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); + CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); + + for (pulse = 0; pulse < 4; pulse++) + { + PlsrHwTestTriggerUpdate(0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); + } + /* 第 5 个脉冲:到目标,停止 + 段完成事件。 */ + PlsrHwTestTriggerUpdate(0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); + CHECK(PlsrHwIsPulseActive(0U) == 0U); + + /* 停止后再触发更新中断无动作。 */ + PlsrHwTestTriggerUpdate(0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); +} + +static void TestStopAndInvalidArgs(void) +{ + (void)PlsrHwInit(); + PLSR_HW_START_PARAMS params; + + (void)memset(¶ms, 0, sizeof(params)); + params.frequencyHz = 1000UL; + params.targetPulses = 100; + params.directionPoint = PLSR_HW_DIR_POINT_NONE; + params.directionPositive = 1U; + params.directionDelayMs = 0U; + + CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_OK); + CHECK(PlsrHwSetFrequency(0U, 1000UL) == PLSR_RESULT_OK); + CHECK(PlsrHwIsPulseActive(0U) == 1U); + CHECK(PlsrHwStopPulse(0U) == PLSR_RESULT_OK); + CHECK(PlsrHwIsPulseActive(0U) == 0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_IDLE); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); + + CHECK(PlsrHwStartPulse(4U, ¶ms) == PLSR_RESULT_INVALID_ARGUMENT); + CHECK(PlsrHwStartPulse(0U, NULL) == PLSR_RESULT_INVALID_ARGUMENT); + params.targetPulses = 0; + CHECK(PlsrHwStartPulse(0U, ¶ms) == PLSR_RESULT_INVALID_ARGUMENT); + CHECK(PlsrHwSetFrequency(4U, 1000UL) == PLSR_RESULT_INVALID_ARGUMENT); + CHECK(PlsrHwStopPulse(4U) == PLSR_RESULT_INVALID_ARGUMENT); +} + +/* ---- 端到端集成:START → 硬件 → 计数 → 事件 → 段间 → 完成 ---- */ + +static void TestEndToEndTwoSegments(void) +{ + TEST_MEMORY memory; + PLSR_CALL call; + PLSR_STATUS status; + int ticks; + int pulse; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 2); + TestSetSegment(&memory, 1U, 1000U, 100); + TestSetSegment(&memory, 2U, 2000U, 200); + + call = TestMakeCall(&memory); + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + PlsrProcess(); + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_ACCEL); + CHECK(status.currentSegment == 1U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DIR_SETTLING); + + /* DIR 延时 10ms → PWM 启动(段1 起始速度 0,profile 升频后启动)。 */ + for (ticks = 0; ticks < 10; ticks++) + { + PlsrProcess(); + } + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 1U); + + /* 加速完成 → 状态机进入 RUN。 */ + for (ticks = 0; ticks < 500; ticks++) + { + PlsrProcess(); + if (TestGetStatus().state == PLSR_STATE_RUN) + { + break; + } + } + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_RUN); + CHECK(status.currentSegment == 1U); + + /* 段1 脉冲完成:100 次更新中断 → SEGMENT_COMPLETE → 段2 启动。 */ + for (pulse = 0; pulse < 100; pulse++) + { + PlsrHwTestTriggerUpdate(0U); + } + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_DONE); + PlsrProcess(); + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_ACCEL); + CHECK(status.currentSegment == 2U); + + /* 段2:DIR 延时 → PWM → 加速 → RUN。 */ + for (ticks = 0; ticks < 600; ticks++) + { + PlsrProcess(); + if (TestGetStatus().state == PLSR_STATE_RUN) + { + break; + } + } + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_RUN); + CHECK(status.currentSegment == 2U); + + /* 段2 脉冲完成 → 任务结束。 */ + for (pulse = 0; pulse < 200; pulse++) + { + PlsrHwTestTriggerUpdate(0U); + } + PlsrProcess(); + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_COMPLETED); + CHECK(status.done != 0U); + /* 终态转换后 HAL 回 IDLE(允许重新启动),脉冲已停止。 */ + CHECK(PlsrHwIsPulseActive(0U) == 0U); + CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U); +} + +static void TestStopStopsHardware(void) +{ + TEST_MEMORY memory; + PLSR_CALL call; + PLSR_COMMAND command; + PLSR_STATUS status; + int ticks; + + TestResetEnvironment(); + (void)memset(&memory, 0, sizeof(memory)); + TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1); + TestSetSegment(&memory, 1U, 1000U, 10000); + + call = TestMakeCall(&memory); + call.sequence = 20UL; + CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED); + PlsrProcess(); + for (ticks = 0; ticks < 10; ticks++) + { + PlsrProcess(); + } + CHECK(PlsrHwIsPulseActive(0U) == 1U); + + /* STOP_IMMEDIATE:硬件立即停止。 */ + command.sequence = 21UL; + command.axis = 0U; + command.opcode = PLSR_CMD_STOP_IMMEDIATE; + command.argument = 0; + CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED); + PlsrProcess(); + CHECK(PlsrHwIsPulseActive(0U) == 0U); + CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_IDLE); + + CHECK(PlsrPostEvent(0U, PLSR_EVENT_STOP_IMMEDIATE_DONE) + == PLSR_RESULT_OK); + PlsrProcess(); + status = TestGetStatus(); + CHECK(status.state == PLSR_STATE_STOPPED); +} + +int main(void) +{ + TestMapping(); + TestDirDelaySequence(); + TestZeroFrequencyWaits(); + TestPulseCounting(); + TestStopAndInvalidArgs(); + TestEndToEndTwoSegments(); + TestStopStopsHardware(); + + if (TestFailures != 0) + { + (void)printf("FAIL: %d of %d PLSR HAL checks failed\n", + TestFailures, + TestChecks); + return 1; + } + (void)printf("PASS: %d PLSR HAL checks\n", TestChecks); + return 0; +}