本批完成内容: 四轴并发自测(P6): - 新增 PlsrFourAxisSelfTestQueue:Q0~Q3 四路独立 PULSE/DIR 任务(1000Hz/1000、2000Hz/2000、3000Hz/3000、4000Hz/4000 脉冲),每轴独立 S0/S1 基址与 SFD 配置(Q4~Q7 方向点)。 - main.c 上电自测入口切换为四轴并发测试(验证后可删除)。 状态机修复: - start==target 或零加速时直接进入 CRUISE 的段,补发 ACCEL_COMPLETE 事件,避免轴状态卡在 ACCEL 整段。 host 测试扩展: - 四轴并发启动/计数相关断言(HAL 测试 +90 项)。 上板验证(逻辑分析仪实测): - Q0/Q1/Q2/Q3 = 1000/2000/3000/4000Hz,上升沿 1000/2000/3000/4000 全部精确,占空比 50%,时长约 1s, 四路独立无干扰。 - 同时验证 84MHz 定时器时钟修正有效:Q1(TIM13)、Q3 (TIM14)频率精确无减半。master
| @@ -202,9 +202,8 @@ int main(void) | |||
| /* 上电自测:延时 1s 后由 Q0/Q1 输出三段 AB 正交周期(验证后关闭)。 */ | |||
| HAL_Delay(1000U); | |||
| /* P5 board test: Q0=PULSE, Q4=DIR, controlled stop at +500. */ | |||
| //(void)PlsrProtectionSelfTestQueue(); | |||
| (void)PlsrProtectionSelfTestQueue(); | |||
| /* P6 board test: Q0..Q3 run four independent PULSE/DIR jobs. */ | |||
| (void)PlsrFourAxisSelfTestQueue(); | |||
| OSStart(); | |||
| /* USER CODE END 2 */ | |||
| @@ -19,6 +19,9 @@ PLSR_RESULT PlsrEquivalentSelfTestQueue(void); | |||
| * distance is deliberately longer than the +500-pulse soft limit. */ | |||
| PLSR_RESULT PlsrProtectionSelfTestQueue(void); | |||
| /* P6 board test: start four independent PULSE/DIR axes together. */ | |||
| PLSR_RESULT PlsrFourAxisSelfTestQueue(void); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| @@ -1782,6 +1782,12 @@ static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis, | |||
| axisObject->profileWasAccel = | |||
| (axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) ? 1U : 0U; | |||
| PlsrPublishAxis(axis); | |||
| if (axisObject->profileWasAccel == 0U) | |||
| { | |||
| /* start==target or zero acceleration enters CRUISE directly. The | |||
| * axis state must not remain stuck in ACCEL for the whole segment. */ | |||
| (void)PlsrPostEvent(axis, PLSR_EVENT_ACCEL_COMPLETE); | |||
| } | |||
| return PLSR_RESULT_OK; | |||
| } | |||
| @@ -12,10 +12,12 @@ | |||
| * 段3:1000Hz / -500 周期(B 超前 A,验证反向) | |||
| * 数据源为静态数组,仅自测使用(正式 D 设备适配器见 Modbus 阶段)。 */ | |||
| #define SELF_TEST_WORD_CAPACITY (64U) | |||
| #define SELF_TEST_WORD_CAPACITY (192U) | |||
| #define SELF_TEST_S0_BASE (10U) | |||
| #define SELF_TEST_S1_BASE (60U) | |||
| #define SELF_TEST_DIR_POINT (4U) | |||
| #define SELF_TEST_SFD_AXIS_STRIDE (130U) | |||
| #define SELF_TEST_SFD_SET_OFFSET (50U) | |||
| static uint16_t SelfTestWords[3][SELF_TEST_WORD_CAPACITY]; | |||
| @@ -234,3 +236,108 @@ PLSR_RESULT PlsrProtectionSelfTestQueue(void) | |||
| call.outputModeOverride = PLSR_OUTPUT_PULSE_DIR; | |||
| return PlsrPostCall(&call); | |||
| } | |||
| PLSR_RESULT PlsrFourAxisSelfTestQueue(void) | |||
| { | |||
| static const uint16_t s0Base[PLSR_AXIS_COUNT] = | |||
| { | |||
| 10U, 40U, 70U, 100U | |||
| }; | |||
| static const uint16_t s1Base[PLSR_AXIS_COUNT] = | |||
| { | |||
| 160U, 164U, 168U, 172U | |||
| }; | |||
| static const uint32_t frequencyHz[PLSR_AXIS_COUNT] = | |||
| { | |||
| 1000UL, 2000UL, 3000UL, 4000UL | |||
| }; | |||
| static const int32_t pulseCount[PLSR_AXIS_COUNT] = | |||
| { | |||
| 1000, 2000, 3000, 4000 | |||
| }; | |||
| PLSR_CALL call; | |||
| PLSR_COMMAND command; | |||
| PLSR_RESULT result; | |||
| uint16_t commonBase; | |||
| uint16_t setBase; | |||
| uint8_t axis; | |||
| (void)memset(SelfTestWords, 0, sizeof(SelfTestWords)); | |||
| for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) | |||
| { | |||
| commonBase = (uint16_t)(900U | |||
| + (uint16_t)axis | |||
| * SELF_TEST_SFD_AXIS_STRIDE); | |||
| setBase = (uint16_t)(commonBase + SELF_TEST_SFD_SET_OFFSET); | |||
| /* Pulse unit, PULSE/DIR, no limit input, Q4..Q7 as DIR. */ | |||
| (void)PlcDeviceWriteSfd(commonBase, 0U); | |||
| SelfTestWriteSfdDword((uint16_t)(commonBase + 2U), 1UL); | |||
| SelfTestWriteSfdDword((uint16_t)(commonBase + 4U), 1UL); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(commonBase + 6U), | |||
| (uint16_t)(SELF_TEST_DIR_POINT + axis)); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(commonBase + 7U), 10U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(commonBase + 12U), 0U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(commonBase + 15U), 0xFFFFU); | |||
| /* K1 has no ramp so all four channels keep an exact fixed rate. */ | |||
| SelfTestWriteSfdDword(setBase, frequencyHz[axis]); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 2U), 0U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 3U), 0U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 4U), 0U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 5U), 0U); | |||
| SelfTestWriteSfdDword((uint16_t)(setBase + 6U), 100000UL); | |||
| SelfTestWriteSfdDword((uint16_t)(setBase + 8U), | |||
| frequencyHz[axis]); | |||
| SelfTestWriteSfdDword((uint16_t)(setBase + 10U), 0UL); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 12U), 50U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 13U), 0U); | |||
| (void)PlcDeviceWriteSfd((uint16_t)(setBase + 14U), 0U); | |||
| SelfTestWriteSfdDword((uint16_t)(setBase + 16U), 2000UL); | |||
| SelfTestWriteSfdDword((uint16_t)(setBase + 18U), 200UL); | |||
| SelfTestWriteDword(PLSR_DEVICE_D, s0Base[axis], 1U); | |||
| SelfTestWriteDword(PLSR_DEVICE_D, | |||
| (uint32_t)s0Base[axis] + 10UL, | |||
| frequencyHz[axis]); | |||
| SelfTestWriteDword(PLSR_DEVICE_D, | |||
| (uint32_t)s0Base[axis] + 12UL, | |||
| (uint32_t)pulseCount[axis]); | |||
| SelfTestWriteDword(PLSR_DEVICE_D, s1Base[axis], 0U); | |||
| /* Make the board-test result independent of a previously restored | |||
| * Backup SRAM position. */ | |||
| (void)memset(&command, 0, sizeof(command)); | |||
| command.sequence = 0xA500UL + axis; | |||
| command.axis = axis; | |||
| command.opcode = PLSR_CMD_SET_POSITION; | |||
| command.argument = 0; | |||
| result = PlsrPostCommand(&command); | |||
| if (result != PLSR_RESULT_QUEUED) | |||
| { | |||
| return result; | |||
| } | |||
| (void)memset(&call, 0, sizeof(call)); | |||
| call.sequence = 0xA600UL + axis; | |||
| call.source.context = NULL; | |||
| call.source.validateWords = SelfTestValidateWords; | |||
| call.source.readWord = SelfTestReadWord; | |||
| call.source.readBit = SelfTestReadBit; | |||
| call.s0.device = PLSR_DEVICE_D; | |||
| call.s0.address = s0Base[axis]; | |||
| call.s1.device = PLSR_DEVICE_D; | |||
| call.s1.address = s1Base[axis]; | |||
| call.s2.type = PLSR_OPERAND_CONSTANT; | |||
| call.s2.constant = 1; | |||
| call.dAxis = axis; | |||
| call.outputModeOverride = PLSR_OUTPUT_PULSE_DIR; | |||
| result = PlsrPostCall(&call); | |||
| if (result != PLSR_RESULT_QUEUED) | |||
| { | |||
| return result; | |||
| } | |||
| } | |||
| return PLSR_RESULT_QUEUED; | |||
| } | |||
| @@ -3,6 +3,7 @@ | |||
| #include "plsr_hal_f407.h" | |||
| #include "plsr_job.h" | |||
| #include "plsr_persistence.h" | |||
| #include "plsr_resource.h" | |||
| #include "plsr_self_test.h" | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| @@ -1243,6 +1244,94 @@ static void TestProtectionSelfTest(void) | |||
| CHECK(eventRecord.lastReason == PLSR_STOP_REASON_LIMIT_POSITIVE); | |||
| } | |||
| static void TestFourAxisSelfTest(void) | |||
| { | |||
| static const uint32_t expectedFrequency[PLSR_AXIS_COUNT] = | |||
| { | |||
| 1000UL, 2000UL, 3000UL, 4000UL | |||
| }; | |||
| static const int32_t expectedPulses[PLSR_AXIS_COUNT] = | |||
| { | |||
| 1000, 2000, 3000, 4000 | |||
| }; | |||
| PLC_DEVICE_EVENT_RECORD eventRecord; | |||
| PLSR_RESOURCE_STATUS resources; | |||
| PLSR_STATUS status; | |||
| int32_t hsdPulses; | |||
| int subTick; | |||
| int ticks; | |||
| uint8_t axis; | |||
| TestResetEnvironment(); | |||
| CHECK(PlsrFourAxisSelfTestQueue() == PLSR_RESULT_QUEUED); | |||
| PlsrProcess(); | |||
| for (ticks = 0; ticks < 10; ticks++) | |||
| { | |||
| PlsrProcess(); | |||
| } | |||
| for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) | |||
| { | |||
| CHECK(PlsrGetStatus(axis, &status) == PLSR_RESULT_OK); | |||
| CHECK(status.state == PLSR_STATE_RUN); | |||
| CHECK(status.outputMode == PLSR_OUTPUT_PULSE_DIR); | |||
| CHECK(status.directionPoint == (uint8_t)(4U + axis)); | |||
| CHECK(status.directionPositive != 0U); | |||
| CHECK(PlsrHwGetState(axis) == PLSR_HW_STATE_RUNNING); | |||
| CHECK(PlsrHwGetCurrentFrequencyHz(axis) | |||
| == expectedFrequency[axis]); | |||
| } | |||
| /* A 0.25ms base slot produces 1/2/3/4kHz update ratios while all four | |||
| * hardware channels are active concurrently for one simulated second. */ | |||
| for (subTick = 0; subTick < 4000; subTick++) | |||
| { | |||
| if ((subTick & 3) == 0) | |||
| { | |||
| PlsrHwTestTriggerUpdate(0U); | |||
| } | |||
| if ((subTick & 1) == 0) | |||
| { | |||
| PlsrHwTestTriggerUpdate(1U); | |||
| } | |||
| if ((subTick & 3) != 3) | |||
| { | |||
| PlsrHwTestTriggerUpdate(2U); | |||
| } | |||
| PlsrHwTestTriggerUpdate(3U); | |||
| if ((subTick & 3) == 3) | |||
| { | |||
| PlsrProcess(); | |||
| } | |||
| } | |||
| for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) | |||
| { | |||
| CHECK(PlsrGetStatus(axis, &status) == PLSR_RESULT_OK); | |||
| CHECK(status.state == PLSR_STATE_COMPLETED); | |||
| CHECK(status.stopReason == PLSR_STOP_REASON_NORMAL_COMPLETE); | |||
| CHECK(status.done != 0U); | |||
| CHECK(status.logicalPosition == expectedPulses[axis]); | |||
| CHECK(status.taskPulses == expectedPulses[axis]); | |||
| CHECK(status.totalPulses == expectedPulses[axis]); | |||
| CHECK(PlsrHwGetState(axis) == PLSR_HW_STATE_IDLE); | |||
| CHECK(PlcDeviceReadHsdDword((uint16_t)(axis * 4U), | |||
| &hsdPulses) == PLC_DEVICE_OK); | |||
| CHECK(hsdPulses == expectedPulses[axis]); | |||
| CHECK(PlcDeviceReadEvent((uint16_t)(6000U | |||
| + (uint16_t)axis * 100U), | |||
| &eventRecord) == PLC_DEVICE_OK); | |||
| CHECK(eventRecord.count == 1UL); | |||
| CHECK(eventRecord.pending != 0U); | |||
| CHECK(eventRecord.lastReason == PLSR_STOP_REASON_NORMAL_COMPLETE); | |||
| } | |||
| PlsrResourceGetStatus(&resources); | |||
| CHECK(resources.outputMask == 0UL); | |||
| CHECK(resources.highMask == 0U); | |||
| CHECK(PlsrResourceCheckInvariant() != 0U); | |||
| } | |||
| static void TestStopStopsHardware(void) | |||
| { | |||
| TEST_MEMORY memory; | |||
| @@ -1304,6 +1393,7 @@ int main(void) | |||
| TestProductionSelfTestStartsAb(); | |||
| TestEquivalentSelfTest(); | |||
| TestProtectionSelfTest(); | |||
| TestFourAxisSelfTest(); | |||
| TestStopStopsHardware(); | |||
| if (TestFailures != 0) | |||