Преглед на файлове

P6 四轴并发自测落地:Q0~Q3 四路独立 PULSE/DIR 真机验证 + 零加速段状态机修复

本批完成内容:

四轴并发自测(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
ywh преди 1 месец
родител
ревизия
7750e43045
променени са 5 файла, в които са добавени 209 реда и са изтрити 4 реда
  1. +2
    -3
      Core/Src/main.c
  2. +3
    -0
      PLSR/Inc/plsr_self_test.h
  3. +6
    -0
      PLSR/Src/plsr_core.c
  4. +108
    -1
      PLSR/Src/plsr_self_test.c
  5. +90
    -0
      PLSR/Test/test_plsr_hal.c

+ 2
- 3
Core/Src/main.c Целия файл

@@ -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 */



+ 3
- 0
PLSR/Inc/plsr_self_test.h Целия файл

@@ -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


+ 6
- 0
PLSR/Src/plsr_core.c Целия файл

@@ -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;
}



+ 108
- 1
PLSR/Src/plsr_self_test.c Целия файл

@@ -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;
}

+ 90
- 0
PLSR/Test/test_plsr_hal.c Целия файл

@@ -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)


Зареждане…
Отказ
Запис