Parcourir la source

工程单位/脉冲当量换算落地(余数累计真机验证)+ 位置链路闭环 + AB 启动预热

本批完成内容:

工程单位换算(新增 plsr_position 模块):
- 单位代码校验、脉冲数/转与移动量/转等效配置校验。
- 工程单位 ↔ 脉冲双向换算(相对/绝对模式),带溢出检查。
- 段目标按等效配置换算为脉冲,换算余数跨段累计,
  避免每段独立取整造成累计位置误差。

任务解析(plsr_job):
- S2 等效配置(单位代码/脉冲每转/移动量每转)解析与校验。
- 段目标支持工程单位形式(相对/绝对),换算失败返回
  PLSR_RESULT_POSITION_OVERFLOW 并给出解析块定位。

位置链路(plsr_core):
- logicalPosition/taskPulses/totalPulses 随运动实时累加,
  带 int64 溢出检查(溢出发 COUNTER_FAULT)。
- HSD 掉电检查点:运动开始/完成/停止时保存位置与有效标志。
- 软急停、正/负限位 I 事件处理接入状态机。

HAL(AB 启动预热):
- 首次启动保持"吞周期"流程建立 00 相位;完成预热后
  段间/调频重定相直接从已验证的 00 边界交还 AF,
  不再吞掉用户周期。(待 AB 上板验证)

host 测试扩展:
- 单位换算(含余数累计)、等效配置解析、位置累加/溢出、
  AB 预热状态机断言,全量测试通过。

上板验证(逻辑分析仪实测):
- 两段各 1001 工程单位 → 段1 输出 1501 脉冲、段2 输出 1502
  脉冲(余数累计正确),总计 3003 脉冲。
- 稳定频率 1500Hz,无毛刺,起步/收尾平滑,时长约 2.2s。
master
ywh il y a 1 mois
Parent
révision
6c61682617
24 fichiers modifiés avec 1510 ajouts et 36 suppressions
  1. +2
    -1
      Core/Src/main.c
  2. +6
    -0
      EWARM/Modbus.ewp
  3. +4
    -0
      PLSR/Inc/plc_device.h
  4. +8
    -0
      PLSR/Inc/plsr_address_map.h
  5. +2
    -0
      PLSR/Inc/plsr_hal_f407.h
  6. +4
    -0
      PLSR/Inc/plsr_job.h
  7. +56
    -0
      PLSR/Inc/plsr_position.h
  8. +4
    -0
      PLSR/Inc/plsr_self_test.h
  9. +1
    -0
      PLSR/Inc/plsr_types.h
  10. +54
    -0
      PLSR/Src/plc_device.c
  11. +366
    -13
      PLSR/Src/plsr_core.c
  12. +43
    -4
      PLSR/Src/plsr_hal_f407.c
  13. +163
    -16
      PLSR/Src/plsr_job.c
  14. +6
    -1
      PLSR/Src/plsr_path.c
  15. +227
    -0
      PLSR/Src/plsr_position.c
  16. +49
    -0
      PLSR/Src/plsr_self_test.c
  17. +13
    -0
      PLSR/Test/run_host_tests.ps1
  18. +10
    -0
      PLSR/Test/test_plc_device.c
  19. +20
    -1
      PLSR/Test/test_plsr_core.c
  20. +331
    -0
      PLSR/Test/test_plsr_hal.c
  21. +48
    -0
      PLSR/Test/test_plsr_job.c
  22. +93
    -0
      PLSR/Test/test_plsr_position.c
  23. BIN
      tmp/pdfs/xinjie_page_30.png
  24. BIN
      tmp/pdfs/xinjie_page_31.png

+ 2
- 1
Core/Src/main.c Voir le fichier

@@ -202,7 +202,8 @@ int main(void)

/* 上电自测:延时 1s 后由 Q0/Q1 输出三段 AB 正交周期(验证后关闭)。 */
HAL_Delay(1000U);
(void)PlsrSelfTestQueue();
// (void)PlsrSelfTestQueue();
(void)PlsrEquivalentSelfTestQueue();
OSStart();
/* USER CODE END 2 */



+ 6
- 0
EWARM/Modbus.ewp Voir le fichier

@@ -1290,6 +1290,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_profile.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_position.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_hal_f407.h</name>
</file>
@@ -1314,6 +1317,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_profile.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_position.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_hal_f407.c</name>
</file>


+ 4
- 0
PLSR/Inc/plc_device.h Voir le fichier

@@ -49,9 +49,13 @@ PLC_DEVICE_RESULT PlcDevicePublishSm(uint8_t axis,

PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value);
PLC_DEVICE_RESULT PlcDeviceWriteSd(uint16_t address, int32_t value);
PLC_DEVICE_RESULT PlcDeviceReadSdDword(uint16_t lowAddress, int32_t *value);
PLC_DEVICE_RESULT PlcDevicePublishSd(uint8_t axis,
uint8_t item,
int32_t value);
PLC_DEVICE_RESULT PlcDevicePublishSdDword(uint8_t axis,
uint8_t lowItem,
int32_t value);

PLC_DEVICE_RESULT PlcDeviceGetEventAddress(uint8_t axis,
uint16_t segmentNumber,


+ 8
- 0
PLSR/Inc/plsr_address_map.h Voir le fichier

@@ -22,6 +22,14 @@ extern "C" {
#define PLSR_SD_AXIS_ITEM_COUNT (12U)
#define PLSR_EVENT_AXIS_ITEM_COUNT (100U)

#define PLSR_SD_ITEM_SEGMENT (0U)
#define PLSR_SD_ITEM_SEGMENT_PULSES (2U)
#define PLSR_SD_ITEM_SEGMENT_EQUIV (4U)
#define PLSR_SD_ITEM_FREQUENCY (6U)
#define PLSR_SD_ITEM_SPEED (8U)
#define PLSR_SD_ITEM_ERROR_CODE (10U)
#define PLSR_SD_ITEM_ERROR_BLOCK (11U)

typedef struct
{
uint16_t hsdRuntimeBase;


+ 2
- 0
PLSR/Inc/plsr_hal_f407.h Voir le fichier

@@ -38,7 +38,9 @@ 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);
uint32_t PlsrHwGetCurrentFrequencyHz(uint8_t axis);
int64_t PlsrHwGetEmittedPulses(uint8_t axis);
uint8_t PlsrHwIsAbStartupPriming(uint8_t axis);

/* 每 1ms tick 推进 HAL 状态机(DIR 延时等)。 */
void PlsrHwTick(uint8_t axis);


+ 4
- 0
PLSR/Inc/plsr_job.h Voir le fichier

@@ -1,6 +1,7 @@
#ifndef PLSR_JOB_H
#define PLSR_JOB_H

#include "plsr_position.h"
#include "plsr_types.h"
#include <stdint.h>

@@ -137,7 +138,10 @@ typedef struct
PLSR_DATA_REF s0;
PLSR_DATA_REF s1;
PLSR_S2_SNAPSHOT s2;
PLSR_EQUIVALENT_CONFIG equivalent;
PLSR_SEGMENT_SNAPSHOT segments[PLSR_MAX_SEGMENTS];
uint32_t inputDefaultSpeed;
uint32_t inputMaximumSpeed;
uint32_t timerClockHz;
uint32_t pairedTimerClockHz;
uint16_t segmentCount;


+ 56
- 0
PLSR/Inc/plsr_position.h Voir le fichier

@@ -0,0 +1,56 @@
#ifndef PLSR_POSITION_H
#define PLSR_POSITION_H

#include "plsr_types.h"
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct
{
uint32_t pulsesPerRevolution;
uint32_t movementPerRevolution;
uint8_t unitCode;
} PLSR_EQUIVALENT_CONFIG;

uint8_t PlsrPositionUnitCodeIsValid(uint8_t unitCode);
uint8_t PlsrPositionUsesEquivalent(const PLSR_EQUIVALENT_CONFIG *config);
PLSR_RESULT PlsrPositionValidateEquivalent(
const PLSR_EQUIVALENT_CONFIG *config);

/* 带余数的相对距离换算。remainder/newRemainder 的分母恒为
* movementPerRevolution,允许负值以保证正反向运动可相互抵消。 */
PLSR_RESULT PlsrPositionUnitsToPulses(
const PLSR_EQUIVALENT_CONFIG *config,
int32_t units,
int64_t remainder,
int64_t *pulses,
int64_t *newRemainder);

PLSR_RESULT PlsrPositionAbsoluteUnitsToPulses(
const PLSR_EQUIVALENT_CONFIG *config,
int32_t units,
int64_t *pulses);

PLSR_RESULT PlsrPositionPulsesToUnits(
const PLSR_EQUIVALENT_CONFIG *config,
int64_t pulses,
int64_t *units);

PLSR_RESULT PlsrPositionSpeedToPulseFrequency(
const PLSR_EQUIVALENT_CONFIG *config,
uint32_t speed,
uint32_t *frequencyHz);

PLSR_RESULT PlsrPositionPulseFrequencyToSpeed(
const PLSR_EQUIVALENT_CONFIG *config,
uint32_t frequencyHz,
uint32_t *speed);

#ifdef __cplusplus
}
#endif

#endif /* PLSR_POSITION_H */

+ 4
- 0
PLSR/Inc/plsr_self_test.h Voir le fichier

@@ -11,6 +11,10 @@ extern "C" {
* 用于 P3b-2 上板验证,验证完成后应关闭。 */
PLSR_RESULT PlsrSelfTestQueue(void);

/* P4-2 当量换算上板自测:Q0=PULSE、Y4=DIR,两段各1工程单位;
* 3脉冲/2单位比例下应依次输出1、2个脉冲。 */
PLSR_RESULT PlsrEquivalentSelfTestQueue(void);

#ifdef __cplusplus
}
#endif


+ 1
- 0
PLSR/Inc/plsr_types.h Voir le fichier

@@ -166,6 +166,7 @@ typedef struct
uint32_t illegalTransitionCount;
uint32_t pendingEvents;
int64_t logicalPosition;
int64_t taskPulses;
int64_t totalPulses;
uint8_t busy;
uint8_t pulseActive;


+ 54
- 0
PLSR/Src/plc_device.c Voir le fichier

@@ -549,6 +549,7 @@ PLC_DEVICE_RESULT PlcDevicePublishSm(uint8_t axis,
PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value)
{
int32_t *source;
uint32_t interruptState;

if (value == NULL)
{
@@ -560,7 +561,9 @@ PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value)
return PLC_DEVICE_INVALID_ADDRESS;
}

interruptState = PlcDeviceEnterCritical();
*value = *source;
PlcDeviceExitCritical(interruptState);
return PLC_DEVICE_OK;
}

@@ -576,12 +579,63 @@ PLC_DEVICE_RESULT PlcDevicePublishSd(uint8_t axis,
uint8_t item,
int32_t value)
{
uint32_t interruptState;

if ((axis >= PLSR_AXIS_COUNT) || (item >= PLSR_SD_AXIS_ITEM_COUNT))
{
return PLC_DEVICE_INVALID_ARGUMENT;
}

interruptState = PlcDeviceEnterCritical();
PlcSdRuntime[axis][item] = value;
PlcDeviceExitCritical(interruptState);
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDeviceReadSdDword(uint16_t lowAddress, int32_t *value)
{
int32_t *source;
uint32_t rawValue;
uint32_t interruptState;
uint8_t axis;
uint8_t item;

if (value == NULL)
{
return PLC_DEVICE_NULL_POINTER;
}
source = PlcDeviceResolveSd(lowAddress, &axis, &item);
if ((source == NULL) || ((item & 1U) != 0U)
|| (item > PLSR_SD_ITEM_SPEED))
{
return PLC_DEVICE_INVALID_ADDRESS;
}
interruptState = PlcDeviceEnterCritical();
rawValue = (uint32_t)PlcSdRuntime[axis][item] & 0xFFFFUL;
rawValue |= ((uint32_t)PlcSdRuntime[axis][item + 1U] & 0xFFFFUL)
<< 16U;
PlcDeviceExitCritical(interruptState);
*value = (int32_t)rawValue;
return PLC_DEVICE_OK;
}

PLC_DEVICE_RESULT PlcDevicePublishSdDword(uint8_t axis,
uint8_t lowItem,
int32_t value)
{
uint32_t rawValue;
uint32_t interruptState;

if ((axis >= PLSR_AXIS_COUNT) || ((lowItem & 1U) != 0U)
|| (lowItem > PLSR_SD_ITEM_SPEED))
{
return PLC_DEVICE_INVALID_ARGUMENT;
}
rawValue = (uint32_t)value;
interruptState = PlcDeviceEnterCritical();
PlcSdRuntime[axis][lowItem] = (int32_t)(rawValue & 0xFFFFUL);
PlcSdRuntime[axis][lowItem + 1U] = (int32_t)(rawValue >> 16U);
PlcDeviceExitCritical(interruptState);
return PLC_DEVICE_OK;
}



+ 366
- 13
PLSR/Src/plsr_core.c Voir le fichier

@@ -7,6 +7,8 @@
#include "plsr_resource.h"
#include <string.h>

#define PLSR_CORE_SFD_AXIS_STRIDE (130U)

#ifndef PLSR_HOST_TEST
#include "stm32f4xx.h"
#include "ucos_ii.h"
@@ -17,6 +19,8 @@ typedef struct
PLSR_STATE state;
PLSR_OUTPUT_MODE outputMode;
PLSR_ERROR error;
uint16_t compatibleErrorCode;
uint16_t compatibleErrorBlock;
PLSR_STOP_REASON stopReason;
PLSR_STATE pendingTerminal;
PLSR_RESOURCE_LEASE lease;
@@ -27,7 +31,11 @@ typedef struct
PLSR_RESULT lastCommandResult;
uint32_t illegalTransitionCount;
int64_t logicalPosition;
int64_t taskPulses;
int64_t totalPulses;
int64_t segmentAccountedPulses;
int64_t equivalentCommandRemainder;
PLSR_EQUIVALENT_CONFIG equivalent;
PLSR_PATH_CONTEXT path;
PLSR_PROFILE_STATE profile;
uint8_t profileActive;
@@ -39,6 +47,7 @@ typedef struct
uint8_t positionValid;
uint8_t jobValid;
uint8_t positionOverflow;
uint8_t segmentAccountingActive;
} PLSR_AXIS;

typedef struct
@@ -61,6 +70,8 @@ static PLSR_JOB_SNAPSHOT PlsrJobScratch;
static void PlsrStopSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject);
static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis,
PLSR_AXIS *axisObject);
static void PlsrAccountHardwarePulses(uint8_t axis,
PLSR_AXIS *axisObject);

static uint32_t PlsrCoreEnterCritical(void)
{
@@ -104,6 +115,161 @@ static uint8_t PlsrStateIsPulseActive(PLSR_STATE state)
: 0U;
}

static void PlsrLoadAxisEquivalentConfig(uint8_t axis,
PLSR_EQUIVALENT_CONFIG *config)
{
uint16_t base = (uint16_t)(PLSR_SFD_CONFIG_START
+ (uint16_t)axis
* PLSR_CORE_SFD_AXIS_STRIDE);
uint16_t word;
uint16_t lowWord;
uint16_t highWord;

config->unitCode = 0U;
config->pulsesPerRevolution = 1UL;
config->movementPerRevolution = 1UL;
if (PlcDeviceReadSfd(base, &word) != PLC_DEVICE_OK)
{
return;
}
config->unitCode = (uint8_t)((word >> 8U) & 0x07U);
if ((PlcDeviceReadSfd((uint16_t)(base + 2U), &lowWord)
!= PLC_DEVICE_OK)
|| (PlcDeviceReadSfd((uint16_t)(base + 3U), &highWord)
!= PLC_DEVICE_OK))
{
config->unitCode = 0U;
return;
}
config->pulsesPerRevolution = (uint32_t)lowWord
| ((uint32_t)highWord << 16U);
if ((PlcDeviceReadSfd((uint16_t)(base + 4U), &lowWord)
!= PLC_DEVICE_OK)
|| (PlcDeviceReadSfd((uint16_t)(base + 5U), &highWord)
!= PLC_DEVICE_OK))
{
config->unitCode = 0U;
return;
}
config->movementPerRevolution = (uint32_t)lowWord
| ((uint32_t)highWord << 16U);
if (PlsrPositionValidateEquivalent(config) != PLSR_RESULT_OK)
{
config->unitCode = 0U;
config->pulsesPerRevolution = 1UL;
config->movementPerRevolution = 1UL;
}
}

static void PlsrPublishSdDword(uint8_t axis,
uint8_t lowItem,
int32_t value)
{
(void)PlcDevicePublishSdDword(axis, lowItem, value);
}

static int32_t PlsrGetCompatibleSegmentPulses(const PLSR_AXIS *axisObject)
{
int64_t signedPulses = (axisObject->directionPositive != 0U)
? axisObject->segmentAccountedPulses
: -axisObject->segmentAccountedPulses;

if (signedPulses > INT32_MAX)
{
return INT32_MAX;
}
if (signedPulses < INT32_MIN)
{
return INT32_MIN;
}
return (int32_t)signedPulses;
}

static int32_t PlsrClampCompatibleInt32(int64_t value)
{
if (value > INT32_MAX) return INT32_MAX;
if (value < INT32_MIN) return INT32_MIN;
return (int32_t)value;
}

static void PlsrPublishRuntime(uint8_t axis)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];
uint16_t currentSegment = (axisObject->jobValid != 0U)
? PlsrPathGetCurrentSegment(
&axisObject->path)
: 0U;
int32_t segmentPulses = PlsrGetCompatibleSegmentPulses(axisObject);
int32_t frequencyHz = (int32_t)PlsrHwGetCurrentFrequencyHz(axis);
int64_t segmentEquivalent64 = segmentPulses;
uint32_t speed = (uint32_t)frequencyHz;
uint16_t publishedError;
uint16_t publishedBlock;

if (PlsrPositionPulsesToUnits(&axisObject->equivalent,
segmentPulses,
&segmentEquivalent64) != PLSR_RESULT_OK)
{
axisObject->positionOverflow = 1U;
segmentEquivalent64 = (segmentPulses < 0) ? INT32_MIN : INT32_MAX;
}
if (PlsrPositionPulseFrequencyToSpeed(&axisObject->equivalent,
(uint32_t)frequencyHz,
&speed) != PLSR_RESULT_OK)
{
speed = UINT32_MAX;
}

PlsrPublishSdDword(axis,
PLSR_SD_ITEM_SEGMENT,
(int32_t)currentSegment);
PlsrPublishSdDword(axis,
PLSR_SD_ITEM_SEGMENT_PULSES,
segmentPulses);
PlsrPublishSdDword(axis,
PLSR_SD_ITEM_SEGMENT_EQUIV,
PlsrClampCompatibleInt32(segmentEquivalent64));
PlsrPublishSdDword(axis, PLSR_SD_ITEM_FREQUENCY, frequencyHz);
PlsrPublishSdDword(axis,
PLSR_SD_ITEM_SPEED,
(speed > (uint32_t)INT32_MAX) ? INT32_MAX
: (int32_t)speed);
publishedError = (axisObject->compatibleErrorCode != 0U)
? axisObject->compatibleErrorCode
: (uint16_t)axisObject->error;
publishedBlock = (axisObject->compatibleErrorCode != 0U)
? axisObject->compatibleErrorBlock
: (((axisObject->error != PLSR_ERROR_NONE)
&& (axisObject->jobValid != 0U))
? currentSegment
: 0U);
(void)PlcDevicePublishSd(axis,
PLSR_SD_ITEM_ERROR_CODE,
(int32_t)publishedError);
(void)PlcDevicePublishSd(axis,
PLSR_SD_ITEM_ERROR_BLOCK,
(int32_t)publishedBlock);
}

static void PlsrSetCompatibleEquivalentError(uint8_t axis,
PLSR_AXIS *axisObject)
{
uint16_t commonBase = (uint16_t)(PLSR_SFD_CONFIG_START
+ (uint16_t)axis
* PLSR_CORE_SFD_AXIS_STRIDE);

if ((axisObject->parseDetail.result == PLSR_RESULT_INVALID_S2)
&& (axisObject->parseDetail.block == PLSR_PARSE_BLOCK_S2)
&& ((axisObject->parseDetail.address
== (uint32_t)(commonBase + 2U))
|| (axisObject->parseDetail.address
== (uint32_t)(commonBase + 4U))))
{
axisObject->compatibleErrorCode = 2U;
axisObject->compatibleErrorBlock = 0U;
}
}

static void PlsrPublishAxis(uint8_t axis)
{
PLSR_AXIS *axisObject = &PlsrAxes[axis];
@@ -114,6 +280,7 @@ static void PlsrPublishAxis(uint8_t axis)
(void)PlcDevicePublishSm(axis,
compatibleRun,
axisObject->directionPositive);
PlsrPublishRuntime(axis);
}

static void PlsrSetStopReason(PLSR_AXIS *axis,
@@ -125,21 +292,102 @@ static void PlsrSetStopReason(PLSR_AXIS *axis,
}
}

/* 将 64 位逻辑位置发布为 HSD 的 32 位兼容值
* 超出 INT32 范围时回绕(与信捷 32 位寄存器一致)并置溢出诊断标志。 */
/* 将 64 位逻辑位置发布为 HSD 的 32 位兼容值
* 超出范围时保持最近一次合法值,只锁存诊断,禁止回绕或静默截断。 */
static void PlsrPublishPosition(uint8_t axis, PLSR_AXIS *axisObject)
{
int32_t compatValue;
int64_t equivalentPosition;
uint16_t base = (uint16_t)((uint16_t)axis
* PLSR_HSD_RUNTIME_AXIS_COUNT);

if ((axisObject->logicalPosition > INT32_MAX)
|| (axisObject->logicalPosition < INT32_MIN))
{
axisObject->positionOverflow = 1U;
return;
}
compatValue = (int32_t)axisObject->logicalPosition;
(void)PlcDevicePublishHsdDword(base, compatValue);
if ((PlsrPositionPulsesToUnits(&axisObject->equivalent,
axisObject->logicalPosition,
&equivalentPosition) != PLSR_RESULT_OK)
|| (equivalentPosition > INT32_MAX)
|| (equivalentPosition < INT32_MIN))
{
axisObject->positionOverflow = 1U;
return;
}
(void)PlcDevicePublishHsdDword((uint16_t)(base + 2U),
(int32_t)equivalentPosition);
}

static uint8_t PlsrAddInt64Checked(int64_t left,
int64_t right,
int64_t *result)
{
if ((result == NULL)
|| ((right > 0) && (left > INT64_MAX - right))
|| ((right < 0) && (left < INT64_MIN - right)))
{
return 0U;
}
*result = left + right;
return 1U;
}

/* 将 HAL 实际完成的完整脉冲/AB周期合并到64位位置。
* emittedPulses 每段从0开始,因此用 segmentAccountedPulses 做差量去重。 */
static void PlsrAccountHardwarePulses(uint8_t axis,
PLSR_AXIS *axisObject)
{
int64_t emittedPulses;
int64_t delta;
int64_t signedDelta;
int64_t newLogicalPosition;
int64_t newTaskPulses;
int64_t newTotalPulses;

if (axisObject->segmentAccountingActive == 0U)
{
return;
}
emittedPulses = PlsrHwGetEmittedPulses(axis);
if ((emittedPulses < 0)
|| (emittedPulses < axisObject->segmentAccountedPulses))
{
axisObject->positionOverflow = 1U;
(void)PlsrPostEvent(axis, PLSR_EVENT_COUNTER_FAULT);
return;
}
delta = emittedPulses - axisObject->segmentAccountedPulses;
if (delta == 0)
{
return;
}
signedDelta = (axisObject->directionPositive != 0U) ? delta : -delta;
if ((PlsrAddInt64Checked(axisObject->logicalPosition,
signedDelta,
&newLogicalPosition) == 0U)
|| (PlsrAddInt64Checked(axisObject->taskPulses,
signedDelta,
&newTaskPulses) == 0U)
|| (PlsrAddInt64Checked(axisObject->totalPulses,
delta,
&newTotalPulses) == 0U))
{
axisObject->positionOverflow = 1U;
axisObject->segmentAccountedPulses = emittedPulses;
(void)PlsrPostEvent(axis, PLSR_EVENT_COUNTER_FAULT);
return;
}
compatValue = (int32_t)(uint32_t)axisObject->logicalPosition;
(void)PlcDevicePublishHsdDword(
(uint16_t)((uint16_t)axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
compatValue);

axisObject->logicalPosition = newLogicalPosition;
axisObject->taskPulses = newTaskPulses;
axisObject->totalPulses = newTotalPulses;
axisObject->segmentAccountedPulses = emittedPulses;
PlsrPublishPosition(axis, axisObject);
PlsrPublishRuntime(axis);
}

static uint8_t PlsrTransitionIsAllowed(PLSR_STATE current,
@@ -542,11 +790,16 @@ static PLSR_RESULT PlsrStartAxis(PLSR_AXIS *axisObject,
axisObject->outputMode = start->outputMode;
axisObject->directionPositive = (start->directionPositive != 0U) ? 1U : 0U;
axisObject->error = PLSR_ERROR_NONE;
axisObject->compatibleErrorCode = 0U;
axisObject->compatibleErrorBlock = 0U;
axisObject->stopReason = PLSR_STOP_REASON_NONE;
axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
axisObject->immediateStopPending = 0U;
axisObject->done = 0U;
axisObject->jobValid = 0U;
axisObject->taskPulses = 0;
axisObject->segmentAccountedPulses = 0;
axisObject->segmentAccountingActive = 0U;
result = PlsrStateTransition(start->axis,
PLSR_STATE_ACCEL,
PLSR_TRANSITION_START);
@@ -577,6 +830,9 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,
return PLSR_RESULT_INVALID_STATE;
}

axisObject->compatibleErrorCode = 0U;
axisObject->compatibleErrorBlock = 0U;

parseContext.logicalPosition = axisObject->logicalPosition;
parseContext.positionValid = axisObject->positionValid;
result = PlsrBuildJobSnapshot(call,
@@ -585,6 +841,7 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,
&axisObject->parseDetail);
if (result != PLSR_RESULT_OK)
{
PlsrSetCompatibleEquivalentError(call->dAxis, axisObject);
return result;
}

@@ -606,13 +863,28 @@ static PLSR_RESULT PlsrStartCall(PLSR_AXIS *axisObject,

axisObject->job = PlsrJobScratch;
axisObject->jobValid = 1U;
if ((axisObject->equivalent.unitCode
!= axisObject->job.equivalent.unitCode)
|| (axisObject->equivalent.pulsesPerRevolution
!= axisObject->job.equivalent.pulsesPerRevolution)
|| (axisObject->equivalent.movementPerRevolution
!= axisObject->job.equivalent.movementPerRevolution))
{
axisObject->equivalentCommandRemainder = 0;
}
axisObject->equivalent = axisObject->job.equivalent;
axisObject->outputMode = (PLSR_OUTPUT_MODE)axisObject->job.outputMode;
axisObject->directionPositive = axisObject->job.initialDirectionPositive;
axisObject->error = PLSR_ERROR_NONE;
axisObject->compatibleErrorCode = 0U;
axisObject->compatibleErrorBlock = 0U;
axisObject->stopReason = PLSR_STOP_REASON_NONE;
axisObject->pendingTerminal = PLSR_STATE_UNINITIALIZED;
axisObject->immediateStopPending = 0U;
axisObject->done = 0U;
axisObject->taskPulses = 0;
axisObject->segmentAccountedPulses = 0;
axisObject->segmentAccountingActive = 0U;
PlsrPathBegin(&axisObject->path,
&axisObject->job,
axisObject->logicalPosition);
@@ -864,6 +1136,10 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
else
{
axisObject->totalPulses = 0;
(void)PlcDeviceSetHsdCheckpointMeta(
axisObject->positionValid,
0U);
(void)PlcDeviceCheckpointHsd();
result = PLSR_RESULT_OK;
}
break;
@@ -904,6 +1180,8 @@ static PLSR_RESULT PlsrExecuteCommand(const PLSR_COMMAND_SLOT *slot)
else
{
axisObject->error = PLSR_ERROR_NONE;
axisObject->compatibleErrorCode = 0U;
axisObject->compatibleErrorBlock = 0U;
axisObject->stopReason = PLSR_STOP_REASON_NONE;
axisObject->done = 0U;
result = PlsrStateTransition(slot->command.axis,
@@ -1034,25 +1312,75 @@ static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis,
PLSR_HW_START_PARAMS params;
PLSR_RESULT result;
int64_t pulses;
int64_t signedPulses;
int64_t targetPosition;
int64_t nextEquivalentRemainder =
axisObject->equivalentCommandRemainder;
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;
result = PlsrPositionUnitsToPulses(
&axisObject->equivalent,
segment->pulseOrTarget,
axisObject->equivalentCommandRemainder,
&signedPulses,
&nextEquivalentRemainder);
if (result != PLSR_RESULT_OK)
{
return result;
}
if (signedPulses == INT64_MIN)
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
pulses = (signedPulses < 0) ? -signedPulses : signedPulses;
positive = (signedPulses >= 0) ? 1U : 0U;
}
else
{
/* 绝对模式:P4 位置闭环后完善;P3a 按当前位置计算。 */
int64_t delta = (int64_t)segment->pulseOrTarget
- axisObject->logicalPosition;
/* 绝对模式使用实际硬件脉冲闭环更新后的逻辑位置计算位移。 */
int64_t delta;

result = PlsrPositionAbsoluteUnitsToPulses(
&axisObject->equivalent,
segment->pulseOrTarget,
&targetPosition);
if (result != PLSR_RESULT_OK)
{
return result;
}
if (((axisObject->logicalPosition > 0)
&& (targetPosition
< INT64_MIN + axisObject->logicalPosition))
|| ((axisObject->logicalPosition < 0)
&& (targetPosition
> INT64_MAX + axisObject->logicalPosition)))
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
delta = targetPosition - axisObject->logicalPosition;
if (delta == INT64_MIN)
{
return PLSR_RESULT_POSITION_OVERFLOW;
}

pulses = (delta < 0) ? -delta : delta;
positive = (delta >= 0) ? 1U : 0U;
}

if (pulses == 0)
{
/* 当量小于一个物理脉冲时保存余数并按零脉冲段推进;不启动PWM。 */
axisObject->equivalentCommandRemainder = nextEquivalentRemainder;
axisObject->directionPositive = positive;
axisObject->segmentAccountedPulses = 0;
axisObject->segmentAccountingActive = 0U;
PlsrPublishAxis(axis);
(void)PlsrPostEvent(axis, PLSR_EVENT_SEGMENT_COMPLETE);
return PLSR_RESULT_OK;
}

(void)memset(&profileRequest, 0, sizeof(profileRequest));
profileRequest.targetFrequencyHz = segment->targetFrequency;
profileRequest.startFrequencyHz = job->s2.startSpeed;
@@ -1090,18 +1418,27 @@ static PLSR_RESULT PlsrStartSegmentHardware(uint8_t axis,
axisObject->profileWasAccel = 0U;
return result;
}
axisObject->equivalentCommandRemainder = nextEquivalentRemainder;
axisObject->directionPositive = positive;
axisObject->segmentAccountedPulses = 0;
axisObject->segmentAccountingActive = 1U;
axisObject->profileActive = 1U;
axisObject->profileWasAccel =
(axisObject->profile.phase == PLSR_PROFILE_PHASE_ACCEL) ? 1U : 0U;
PlsrPublishAxis(axis);
return PLSR_RESULT_OK;
}

/* 停止当前段的硬件输出与速度曲线。 */
static void PlsrStopSegmentHardware(uint8_t axis, PLSR_AXIS *axisObject)
{
PlsrAccountHardwarePulses(axis, axisObject);
axisObject->profileActive = 0U;
axisObject->profileWasAccel = 0U;
(void)PlsrHwStopPulse(axis);
PlsrAccountHardwarePulses(axis, axisObject);
axisObject->segmentAccountingActive = 0U;
PlsrPublishRuntime(axis);
}

/* 应用路径执行器的动作:段间推进、进入等待、结束、让出、错误。 */
@@ -1293,6 +1630,7 @@ PLSR_RESULT PlsrInit(void)
PlsrAxes[axis].state = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].pendingTerminal = PLSR_STATE_UNINITIALIZED;
PlsrAxes[axis].lease.directionPoint = PLSR_DIRECTION_POINT_NONE;
PlsrLoadAxisEquivalentConfig(axis, &PlsrAxes[axis].equivalent);
if (PlcDeviceReadHsdDword(
(uint16_t)(axis * PLSR_HSD_RUNTIME_AXIS_COUNT),
&restoredPosition) == PLC_DEVICE_OK)
@@ -1328,6 +1666,13 @@ void PlsrProcess(void)
return;
}

/* 先合并 ISR 已完成的实际脉冲,确保段完成、STOP或新命令不会在
* HAL 计数清零前丢失最后一批位置增量。 */
for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++)
{
PlsrAccountHardwarePulses(axis, &PlsrAxes[axis]);
}

for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++)
{
events = PlsrTakeEvents(axis, PLSR_EVENT_CRITICAL_MASK);
@@ -1369,6 +1714,13 @@ void PlsrProcess(void)
uint8_t wasAccel;

PlsrHwTick(axis);
PlsrAccountHardwarePulses(axis, axisObject);
/* 首次 AB 内部预热周期不属于用户运动,速度曲线也必须冻结;
* 否则低速起步时会在隐藏周期内提前爬升十余个刷新步。 */
if (PlsrHwIsAbStartupPriming(axis) != 0U)
{
continue;
}
if (PlsrStateIsBusy(axisObject->state) == 0U)
{
continue;
@@ -1475,6 +1827,7 @@ PLSR_RESULT PlsrGetStatus(uint8_t axis, PLSR_STATUS *status)
status->illegalTransitionCount = axisObject->illegalTransitionCount;
status->pendingEvents = axisObject->pendingEvents;
status->logicalPosition = axisObject->logicalPosition;
status->taskPulses = axisObject->taskPulses;
status->totalPulses = axisObject->totalPulses;
status->busy = PlsrStateIsBusy(axisObject->state);
status->pulseActive = PlsrStateIsPulseActive(axisObject->state);


+ 43
- 4
PLSR/Src/plsr_hal_f407.c Voir le fichier

@@ -148,6 +148,8 @@ typedef struct
uint8_t directionPositive;
uint8_t abQuarter;
uint8_t abCountAxis;
uint8_t abStartupPriming;
uint8_t abOutputPrimed;
uint16_t abActiveBasePsc;
uint16_t abActivePairPsc;
uint16_t abActiveArr;
@@ -688,6 +690,7 @@ static void PlsrHwBeginAbOutput(uint8_t axis, uint8_t debugReason)
lagStart = periodTicks / 2UL;
state->abCountAxis = lagAxis;
state->abQuarter = 0U;
state->abStartupPriming = (state->abOutputPrimed == 0U) ? 1U : 0U;

#ifndef PLSR_HOST_TEST
interruptState = __get_PRIMASK();
@@ -733,14 +736,17 @@ static void PlsrHwBeginAbOutput(uint8_t axis, uint8_t debugReason)
PlsrHwTimerSetCc1e(pairAxis, 1UL);
PlsrHwTimerSetPwmMode1(axis);
PlsrHwTimerSetPwmMode1(pairAxis);
/* OCREF 已在低电平位置稳定后再把物理引脚交还定时器。 */
PlsrHwTimerClearCc1if(axis);
PlsrHwTimerClearCc1if(pairAxis);
PlsrHwReleasePulsePin(axis);
PlsrHwReleasePulsePin(pairAxis);
if (state->abStartupPriming == 0U)
{
/* 完成过首次预热后,段间/调频重定相均从已验证的 00 边界
* 直接交还 AF,不额外吞掉用户周期。 */
PlsrHwReleasePulsePin(axis);
PlsrHwReleasePulsePin(pairAxis);
}
PlsrHwTimerSetCen(axis, 1UL);
PlsrHwTimerSetCen(pairAxis, 1UL);
/* 最后才允许落后相计数中断。 */
PlsrHwTimerClearCc1if(axis);
PlsrHwTimerClearCc1if(pairAxis);
PlsrHwTimerSetCc1ie(lagAxis, 1UL);
@@ -797,6 +803,7 @@ static void PlsrHwStopActiveOutput(uint8_t axis,
#endif
PlsrHwStopPwmTimer(axis);
PlsrHwStopPwmTimer(pairAxis);
PlsrHwAxes[axis].abStartupPriming = 0U;
#ifndef PLSR_HOST_TEST
__DMB();
if (interruptState == 0UL)
@@ -1062,6 +1069,16 @@ uint32_t PlsrHwGetTimerClockHz(uint8_t axis)
return PlsrHwAxisMap[axis].timerClockHz;
}

uint32_t PlsrHwGetCurrentFrequencyHz(uint8_t axis)
{
if ((axis >= PLSR_HW_AXIS_COUNT)
|| (PlsrHwAxes[axis].state != PLSR_HW_STATE_RUNNING))
{
return 0UL;
}
return PlsrHwAxes[axis].currentFrequencyHz;
}

/* 硬件已发出的脉冲数(profile 虚拟计数校准用,中断内递增)。 */
int64_t PlsrHwGetEmittedPulses(uint8_t axis)
{
@@ -1090,6 +1107,15 @@ int64_t PlsrHwGetEmittedPulses(uint8_t axis)
return emittedPulses;
}

uint8_t PlsrHwIsAbStartupPriming(uint8_t axis)
{
if ((axis >= PLSR_HW_AXIS_COUNT) || (PlsrHwIsAbBaseAxis(axis) == 0U))
{
return 0U;
}
return PlsrHwAxes[axis].abStartupPriming;
}

void PlsrHwTick(uint8_t axis)
{
PLSR_HW_AXIS_STATE *state;
@@ -1163,6 +1189,19 @@ void PlsrHwOnTimerUpdate(uint8_t axis)
{
return;
}
if (state->abStartupPriming != 0U)
{
/* F407 首次切换 OC 模式时 OC1REF 初态不可直接作为物理AB相。
* GPIO 保持 00 隐藏首个内部周期;落后相下降沿是真实 00
* 边界,此时再交还 AF,且该隐藏周期绝不能计入 emitted。 */
state->abStartupPriming = 0U;
state->abOutputPrimed = 1U;
#ifndef PLSR_HOST_TEST
PlsrHwReleasePulsePin(ownerAxis);
PlsrHwReleasePulsePin(PlsrHwGetPairedAxis(ownerAxis));
#endif
return;
}
state->emittedPulses++;
if (state->emittedPulses >= state->targetPulses)
{


+ 163
- 16
PLSR/Src/plsr_job.c Voir le fichier

@@ -482,6 +482,43 @@ static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call,
result = PlsrReadFixedWord(0U, commonBase, &word, detail);
if (result != PLSR_RESULT_OK) return result;
snapshot->directionActiveHigh = ((word & (1U << 1U)) != 0U) ? 1U : 0U;
snapshot->equivalent.unitCode = (uint8_t)((word >> 8U) & 0x07U);
if (PlsrPositionUnitCodeIsValid(snapshot->equivalent.unitCode) == 0U)
{
PlsrSetDetail(detail,
PLSR_RESULT_INVALID_S2,
PLSR_PARSE_BLOCK_S2,
commonBase,
snapshot->equivalent.unitCode,
0U);
return PLSR_RESULT_INVALID_S2;
}
result = PlsrReadFixedDword(0U,
(uint16_t)(commonBase + 2U),
&snapshot->equivalent.pulsesPerRevolution,
detail);
if (result != PLSR_RESULT_OK) return result;
result = PlsrReadFixedDword(0U,
(uint16_t)(commonBase + 4U),
&snapshot->equivalent.movementPerRevolution,
detail);
if (result != PLSR_RESULT_OK) return result;
if (PlsrPositionValidateEquivalent(&snapshot->equivalent)
!= PLSR_RESULT_OK)
{
uint16_t invalidAddress =
(snapshot->equivalent.pulsesPerRevolution == 0UL)
? (uint16_t)(commonBase + 2U)
: (uint16_t)(commonBase + 4U);

PlsrSetDetail(detail,
PLSR_RESULT_INVALID_S2,
PLSR_PARSE_BLOCK_S2,
invalidAddress,
0,
0U);
return PLSR_RESULT_INVALID_S2;
}
if (call->outputModeOverride == PLSR_OUTPUT_MODE_FROM_SFD)
{
snapshot->outputMode = ((word & (1U << 13U)) != 0U)
@@ -550,6 +587,8 @@ static PLSR_RESULT PlsrLoadS2(const PLSR_CALL *call,
snapshot->s2.stopSpeed = snapshot->s2.maximumSpeed;
snapshot->speedClamped = 1U;
}
snapshot->inputDefaultSpeed = snapshot->s2.defaultSpeed;
snapshot->inputMaximumSpeed = snapshot->s2.maximumSpeed;
return PLSR_RESULT_OK;
}

@@ -835,9 +874,12 @@ static PLSR_RESULT PlsrCheckInitialPosition(
{
uint8_t visited[PLSR_MAX_SEGMENTS];
int64_t position = context->logicalPosition;
int32_t movement;
int64_t movement;
int64_t targetPosition;
int64_t remainder = 0;
int32_t next;
uint16_t segment = snapshot->startSegment;
PLSR_RESULT result;

if ((snapshot->positioningMode != 0U) && (context->positionValid == 0U))
{
@@ -858,7 +900,24 @@ static PLSR_RESULT PlsrCheckInitialPosition(
visited[segment - 1U] = 1U;
if (snapshot->positioningMode == 0U)
{
movement = segmentData->pulseOrTarget;
result = PlsrPositionUnitsToPulses(&snapshot->equivalent,
segmentData->pulseOrTarget,
remainder,
&movement,
&remainder);
if (result != PLSR_RESULT_OK)
{
PlsrSetDetail(detail,
PLSR_RESULT_POSITION_OVERFLOW,
PLSR_PARSE_BLOCK_POSITION,
snapshot->s0.address
+ (uint32_t)segment
* PLSR_S0_SEGMENT_WORDS
+ 2UL,
segmentData->pulseOrTarget,
segment);
return PLSR_RESULT_POSITION_OVERFLOW;
}
if (((movement > 0) && (position > INT64_MAX - movement))
|| ((movement < 0) && (position < INT64_MIN - movement)))
{
@@ -877,10 +936,18 @@ static PLSR_RESULT PlsrCheckInitialPosition(
}
else
{
movement = (segmentData->pulseOrTarget > position)
result = PlsrPositionAbsoluteUnitsToPulses(
&snapshot->equivalent,
segmentData->pulseOrTarget,
&targetPosition);
if (result != PLSR_RESULT_OK)
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
movement = (targetPosition > position)
? 1
: ((segmentData->pulseOrTarget < position) ? -1 : 0);
position = segmentData->pulseOrTarget;
: ((targetPosition < position) ? -1 : 0);
position = targetPosition;
}
if ((movement != 0) && (snapshot->initialDirectionPositive == 0U))
{
@@ -910,6 +977,84 @@ static PLSR_RESULT PlsrCheckInitialPosition(
return PLSR_RESULT_OK;
}

static PLSR_RESULT PlsrConvertSnapshotSpeeds(
PLSR_JOB_SNAPSHOT *snapshot,
PLSR_PARSE_DETAIL *detail)
{
uint16_t segment;
PLSR_RESULT result;

result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.defaultSpeed,
&snapshot->s2.defaultSpeed);
if (result != PLSR_RESULT_OK) return result;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.maximumSpeed,
&snapshot->s2.maximumSpeed);
if (result != PLSR_RESULT_OK) return result;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.startSpeed,
&snapshot->s2.startSpeed);
if (result != PLSR_RESULT_OK) return result;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.stopSpeed,
&snapshot->s2.stopSpeed);
if (result != PLSR_RESULT_OK) return result;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.zrnHighSpeed,
&snapshot->s2.zrnHighSpeed);
if (result != PLSR_RESULT_OK) return result;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
snapshot->s2.zrnCrawlSpeed,
&snapshot->s2.zrnCrawlSpeed);
if (result != PLSR_RESULT_OK) return result;

if ((snapshot->s2.maximumSpeed < PLSR_FREQUENCY_MIN_HZ)
|| (snapshot->s2.maximumSpeed > PLSR_FREQUENCY_MAX_HZ))
{
PlsrSetDetail(detail,
PLSR_RESULT_INVALID_FREQUENCY,
PLSR_PARSE_BLOCK_S2,
0UL,
(int32_t)snapshot->s2.maximumSpeed,
0U);
return PLSR_RESULT_INVALID_FREQUENCY;
}
for (segment = 1U; segment <= snapshot->segmentCount; segment++)
{
PLSR_SEGMENT_SNAPSHOT *segmentData =
&snapshot->segments[segment - 1U];

if (segmentData->targetFrequency == 0UL)
{
continue;
}
result = PlsrPositionSpeedToPulseFrequency(
&snapshot->equivalent,
segmentData->targetFrequency,
&segmentData->targetFrequency);
if ((result != PLSR_RESULT_OK)
|| (segmentData->targetFrequency < PLSR_FREQUENCY_MIN_HZ)
|| (segmentData->targetFrequency > PLSR_FREQUENCY_MAX_HZ))
{
PlsrSetDetail(detail,
PLSR_RESULT_INVALID_FREQUENCY,
PLSR_PARSE_BLOCK_S0,
snapshot->s0.address
+ (uint32_t)segment * PLSR_S0_SEGMENT_WORDS,
(int32_t)segmentData->targetFrequency,
segment);
return PLSR_RESULT_INVALID_FREQUENCY;
}
result = PlsrValidateFrequencyDivider(snapshot,
segmentData->targetFrequency,
detail,
segment);
if (result != PLSR_RESULT_OK) return result;
}
return PLSR_RESULT_OK;
}

PLSR_RESULT PlsrBuildJobSnapshot(const PLSR_CALL *call,
const PLSR_PARSE_CONTEXT *context,
PLSR_JOB_SNAPSHOT *snapshot,
@@ -1186,14 +1331,6 @@ PLSR_RESULT PlsrBuildJobSnapshot(const PLSR_CALL *call,
segmentData->flags |= PLSR_SEGMENT_FLAG_SPEED_CLAMPED;
snapshot->speedClamped = 1U;
}
if (segmentData->targetFrequency != 0UL)
{
result = PlsrValidateFrequencyDivider(snapshot,
segmentData->targetFrequency,
detail,
segment);
if (result != PLSR_RESULT_OK) return result;
}
result = PlsrValidateWait(call, segmentData, segment, detail);
if (result != PLSR_RESULT_OK) return result;
result = PlsrValidateJump(call,
@@ -1205,6 +1342,8 @@ PLSR_RESULT PlsrBuildJobSnapshot(const PLSR_CALL *call,
if (result != PLSR_RESULT_OK) return result;
}

result = PlsrConvertSnapshotSpeeds(snapshot, detail);
if (result != PLSR_RESULT_OK) return result;
result = PlsrCheckConstantPath(snapshot, detail);
if (result != PLSR_RESULT_OK) return result;
result = PlsrCheckInitialPosition(snapshot, context, detail);
@@ -1246,14 +1385,22 @@ PLSR_RESULT PlsrResolveLiveFrequency(const PLSR_JOB_SNAPSHOT *snapshot,
NULL);
if (result != PLSR_RESULT_OK) return result;
if (rawFrequency < 0) return PLSR_RESULT_INVALID_FREQUENCY;
*frequency = (rawFrequency == 0) ? snapshot->s2.defaultSpeed
*frequency = (rawFrequency == 0) ? snapshot->inputDefaultSpeed
: (uint32_t)rawFrequency;
*clamped = 0U;
if (*frequency > snapshot->s2.maximumSpeed)
if (*frequency > snapshot->inputMaximumSpeed)
{
*frequency = snapshot->s2.maximumSpeed;
*frequency = snapshot->inputMaximumSpeed;
*clamped = 1U;
}
if (*frequency == 0UL) return PLSR_RESULT_INVALID_FREQUENCY;
result = PlsrPositionSpeedToPulseFrequency(&snapshot->equivalent,
*frequency,
frequency);
if ((result != PLSR_RESULT_OK) || (*frequency == 0UL)
|| (*frequency > PLSR_FREQUENCY_MAX_HZ))
{
return PLSR_RESULT_INVALID_FREQUENCY;
}
return PlsrValidateFrequencyDivider(snapshot, *frequency, NULL, segment);
}

+ 6
- 1
PLSR/Src/plsr_path.c Voir le fichier

@@ -88,13 +88,18 @@ static uint8_t PlsrPathSegmentIsZeroPulse(const PLSR_JOB_SNAPSHOT *job,
{
const PLSR_SEGMENT_SNAPSHOT *segmentSnapshot =
&job->segments[segment - 1U];
int64_t targetPosition;

if ((segmentSnapshot->flags & PLSR_SEGMENT_FLAG_ZERO_PULSE) != 0U)
{
return 1U;
}
if ((job->positioningMode == 1U)
&& (segmentSnapshot->pulseOrTarget == (int32_t)logicalPosition))
&& (PlsrPositionAbsoluteUnitsToPulses(
&job->equivalent,
segmentSnapshot->pulseOrTarget,
&targetPosition) == PLSR_RESULT_OK)
&& (targetPosition == logicalPosition))
{
return 1U;
}


+ 227
- 0
PLSR/Src/plsr_position.c Voir le fichier

@@ -0,0 +1,227 @@
#include "plsr_position.h"
#include <limits.h>
#include <stddef.h>

uint8_t PlsrPositionUnitCodeIsValid(uint8_t unitCode)
{
return ((unitCode == 0U) || (unitCode == 1U) || (unitCode == 3U)
|| (unitCode == 5U) || (unitCode == 7U))
? 1U
: 0U;
}

uint8_t PlsrPositionUsesEquivalent(const PLSR_EQUIVALENT_CONFIG *config)
{
return ((config != NULL) && (config->unitCode != 0U)) ? 1U : 0U;
}

PLSR_RESULT PlsrPositionValidateEquivalent(
const PLSR_EQUIVALENT_CONFIG *config)
{
if ((config == NULL)
|| (PlsrPositionUnitCodeIsValid(config->unitCode) == 0U))
{
return PLSR_RESULT_INVALID_S2;
}
if ((config->unitCode != 0U)
&& ((config->pulsesPerRevolution == 0UL)
|| (config->movementPerRevolution == 0UL)))
{
return PLSR_RESULT_INVALID_S2;
}
return PLSR_RESULT_OK;
}

PLSR_RESULT PlsrPositionUnitsToPulses(
const PLSR_EQUIVALENT_CONFIG *config,
int32_t units,
int64_t remainder,
int64_t *pulses,
int64_t *newRemainder)
{
int64_t numerator;
int64_t pulseValue;
uint32_t denominator;

if ((pulses == NULL) || (newRemainder == NULL)
|| (PlsrPositionValidateEquivalent(config) != PLSR_RESULT_OK))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}
if (config->unitCode == 0U)
{
*pulses = units;
*newRemainder = 0;
return PLSR_RESULT_OK;
}
denominator = config->movementPerRevolution;
if ((remainder <= -(int64_t)denominator)
|| (remainder >= (int64_t)denominator))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}

/* int32 * uint32 本身落在 int64 范围内;仅余数相加需要边界检查。 */
numerator = (int64_t)units
* (int64_t)config->pulsesPerRevolution;
if (((remainder > 0) && (numerator > INT64_MAX - remainder))
|| ((remainder < 0) && (numerator < INT64_MIN - remainder)))
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
numerator += remainder;
pulseValue = numerator / (int64_t)denominator;
*newRemainder = numerator % (int64_t)denominator;
*pulses = pulseValue;
return PLSR_RESULT_OK;
}

PLSR_RESULT PlsrPositionAbsoluteUnitsToPulses(
const PLSR_EQUIVALENT_CONFIG *config,
int32_t units,
int64_t *pulses)
{
int64_t ignoredRemainder;

return PlsrPositionUnitsToPulses(config,
units,
0,
pulses,
&ignoredRemainder);
}

static uint64_t PlsrPositionMagnitude(int64_t value)
{
return (value < 0) ? (uint64_t)(-(value + 1)) + 1ULL
: (uint64_t)value;
}

PLSR_RESULT PlsrPositionPulsesToUnits(
const PLSR_EQUIVALENT_CONFIG *config,
int64_t pulses,
int64_t *units)
{
uint64_t magnitude;
uint64_t quotient;
uint64_t remainder;
uint64_t whole;
uint64_t fraction;
uint64_t scaled;
uint64_t numerator;
uint64_t denominator;

if ((units == NULL)
|| (PlsrPositionValidateEquivalent(config) != PLSR_RESULT_OK))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}
if (config->unitCode == 0U)
{
*units = pulses;
return PLSR_RESULT_OK;
}

magnitude = PlsrPositionMagnitude(pulses);
numerator = config->movementPerRevolution;
denominator = config->pulsesPerRevolution;
quotient = magnitude / denominator;
remainder = magnitude % denominator;
if ((quotient != 0ULL) && (numerator > UINT64_MAX / quotient))
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
whole = quotient * numerator;
/* remainder 与 numerator 均为 uint32 范围,乘积不会超过 uint64。 */
fraction = (remainder * numerator) / denominator;
if (whole > UINT64_MAX - fraction)
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
scaled = whole + fraction;
if ((pulses >= 0) && (scaled > (uint64_t)INT64_MAX))
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
if ((pulses < 0) && (scaled > (UINT64_C(1) << 63U)))
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
if ((pulses < 0) && (scaled == (UINT64_C(1) << 63U)))
{
*units = INT64_MIN;
}
else
{
*units = (pulses < 0) ? -(int64_t)scaled : (int64_t)scaled;
}
return PLSR_RESULT_OK;
}

static PLSR_RESULT PlsrPositionScaleUnsigned(uint32_t value,
uint32_t numerator,
uint32_t denominator,
uint32_t *result)
{
uint64_t product;
uint64_t scaled;

if ((result == NULL) || (denominator == 0UL))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}
product = (uint64_t)value * (uint64_t)numerator;
/* 速度采用四舍五入;累计位置仍采用截断并由绝对累计避免漂移。 */
scaled = product / denominator;
if ((product % denominator) >= ((uint64_t)denominator + 1ULL) / 2ULL)
{
scaled++;
}
if (scaled > UINT32_MAX)
{
return PLSR_RESULT_POSITION_OVERFLOW;
}
*result = (uint32_t)scaled;
return PLSR_RESULT_OK;
}

PLSR_RESULT PlsrPositionSpeedToPulseFrequency(
const PLSR_EQUIVALENT_CONFIG *config,
uint32_t speed,
uint32_t *frequencyHz)
{
if ((frequencyHz == NULL)
|| (PlsrPositionValidateEquivalent(config) != PLSR_RESULT_OK))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}
if (config->unitCode == 0U)
{
*frequencyHz = speed;
return PLSR_RESULT_OK;
}
return PlsrPositionScaleUnsigned(speed,
config->pulsesPerRevolution,
config->movementPerRevolution,
frequencyHz);
}

PLSR_RESULT PlsrPositionPulseFrequencyToSpeed(
const PLSR_EQUIVALENT_CONFIG *config,
uint32_t frequencyHz,
uint32_t *speed)
{
if ((speed == NULL)
|| (PlsrPositionValidateEquivalent(config) != PLSR_RESULT_OK))
{
return PLSR_RESULT_INVALID_ARGUMENT;
}
if (config->unitCode == 0U)
{
*speed = frequencyHz;
return PLSR_RESULT_OK;
}
return PlsrPositionScaleUnsigned(frequencyHz,
config->movementPerRevolution,
config->pulsesPerRevolution,
speed);
}

+ 49
- 0
PLSR/Src/plsr_self_test.c Voir le fichier

@@ -70,6 +70,13 @@ static void SelfTestWriteDword(PLSR_DEVICE_TYPE device,
SelfTestWords[device][address + 1UL] = (uint16_t)(value >> 16U);
}

static void SelfTestWriteSfdDword(uint16_t address, uint32_t value)
{
(void)PlcDeviceWriteSfd(address, (uint16_t)(value & 0xFFFFUL));
(void)PlcDeviceWriteSfd((uint16_t)(address + 1U),
(uint16_t)(value >> 16U));
}

PLSR_RESULT PlsrSelfTestQueue(void)
{
PLSR_CALL call;
@@ -109,3 +116,45 @@ PLSR_RESULT PlsrSelfTestQueue(void)
call.outputModeOverride = PLSR_OUTPUT_AB;
return PlsrPostCall(&call);
}

PLSR_RESULT PlsrEquivalentSelfTestQueue(void)
{
PLSR_CALL call;

(void)memset(SelfTestWords, 0, sizeof(SelfTestWords));

/* SFD900 Bit10~8=001(1um当量);3脉冲/2单位。 */
(void)PlcDeviceWriteSfd(900U, (1U << 8U));
SelfTestWriteSfdDword(902U, 3UL);
SelfTestWriteSfdDword(904U, 2UL);
(void)PlcDeviceWriteSfd(906U, SELF_TEST_DIR_POINT);
(void)PlcDeviceWriteSfd(907U, 10U);
/* 当量换算后物理最高速度=90000Hz,不超过硬件100kHz。 */
SelfTestWriteSfdDword(956U, 60000UL);

/* 板端可观察版本:两段各1001工程单位。
* 3脉冲/2单位带余数换算后分别输出1501、1502脉冲,累计3003脉冲;
* 1500Hz附近持续约2s,避免原3脉冲自检在逻辑分析仪启动前已经结束。 */
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE, 2U);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 10U, 1000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 12U, 1001UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 20U, 1000UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S0_BASE + 22U, 1001UL);
SelfTestWriteDword(PLSR_DEVICE_D, SELF_TEST_S1_BASE, 0U);

(void)memset(&call, 0, sizeof(call));
call.sequence = 0xA5A6UL;
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 = SELF_TEST_S0_BASE;
call.s1.device = PLSR_DEVICE_D;
call.s1.address = SELF_TEST_S1_BASE;
call.s2.type = PLSR_OPERAND_CONSTANT;
call.s2.constant = 1;
call.dAxis = 0U;
call.outputModeOverride = PLSR_OUTPUT_PULSE_DIR;
return PlsrPostCall(&call);
}

+ 13
- 0
PLSR/Test/run_host_tests.ps1 Voir le fichier

@@ -14,9 +14,17 @@ if (-not (Test-Path -LiteralPath $outputDirectory))
}

$tests = @(
@{
Name = 'test_plsr_position'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Test\test_plsr_position.c"
)
},
@{
Name = 'test_plc_device'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Test\test_plc_device.c"
@@ -25,6 +33,7 @@ $tests = @(
@{
Name = 'test_plsr_core'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
@@ -39,6 +48,7 @@ $tests = @(
@{
Name = 'test_plsr_job'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
@@ -53,6 +63,7 @@ $tests = @(
@{
Name = 'test_plsr_path'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"
@@ -67,6 +78,7 @@ $tests = @(
@{
Name = 'test_plsr_profile'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plsr_profile.c"
"$workspacePath\PLSR\Test\test_plsr_profile.c"
)
@@ -74,6 +86,7 @@ $tests = @(
@{
Name = 'test_plsr_hal'
Sources = @(
"$workspacePath\PLSR\Src\plsr_position.c"
"$workspacePath\PLSR\Src\plc_device.c"
"$workspacePath\PLSR\Src\plsr_persistence.c"
"$workspacePath\PLSR\Src\plsr_resource.c"


+ 10
- 0
PLSR/Test/test_plc_device.c Voir le fichier

@@ -269,6 +269,16 @@ static void TestSmAndSd(void)
TEST_CHECK(PlcDeviceWriteSd(1071U, 0) == PLC_DEVICE_READ_ONLY);
TEST_CHECK(PlcDeviceReadSd(1012U, &value) == PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDevicePublishSd(4U, 0U, 0) == PLC_DEVICE_INVALID_ARGUMENT);
TEST_CHECK(PlcDevicePublishSdDword(2U, 2U, -123456)
== PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadSdDword(1042U, &value) == PLC_DEVICE_OK);
TEST_CHECK(value == -123456);
TEST_CHECK(PlcDeviceReadSdDword(1043U, &value)
== PLC_DEVICE_INVALID_ADDRESS);
TEST_CHECK(PlcDevicePublishSdDword(2U, 9U, 0)
== PLC_DEVICE_INVALID_ARGUMENT);
TEST_CHECK(PlcDeviceReadSdDword(1070U, &value)
== PLC_DEVICE_INVALID_ADDRESS);

TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
TEST_CHECK(PlcDeviceReadSm(1040U, &state) == PLC_DEVICE_OK);


+ 20
- 1
PLSR/Test/test_plsr_core.c Voir le fichier

@@ -348,6 +348,7 @@ static void TestPositionCheckpointing(void)
{
PLSR_STATUS status;
int32_t hsdPosition;
int32_t hsdEquivalent;

/* 1. SET_POSITION 后立即写入 HSD 检查点。 */
TestReset();
@@ -360,6 +361,8 @@ static void TestPositionCheckpointing(void)
TEST_CHECK(PlcDeviceIsHsdDirty() == 0U);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
TEST_CHECK(hsdPosition == 500);
TEST_CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
TEST_CHECK(hsdEquivalent == 500);

/* 2. 模拟重启:位置与 positionValid 应恢复到上次正常停机的检查点。 */
TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK);
@@ -377,7 +380,7 @@ static void TestPositionCheckpointing(void)
TEST_CHECK(status.positionValid == 0U);
TEST_CHECK(status.logicalPosition == 500);

/* 4. 64位位置超出 INT32 范围:对外回绕并置溢出诊断。 */
/* 4. 64位位置超出 INT32 范围:保留内部值、HSD保持最近合法值。 */
TEST_CHECK(TestPostCommand(2U,
0U,
PLSR_CMD_SET_POSITION,
@@ -387,8 +390,24 @@ static void TestPositionCheckpointing(void)
status = TestGetStatus(0U);
TEST_CHECK(status.lastCommandResult == PLSR_RESULT_OK);
TEST_CHECK(status.positionOverflow != 0U);
TEST_CHECK(status.logicalPosition == INT64_C(0x100000000));
TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
TEST_CHECK(hsdPosition == 500);
TEST_CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
TEST_CHECK(hsdEquivalent == 500);

/* 5. 清零位置重新建立有效坐标,并解除兼容发布溢出锁存。 */
TEST_CHECK(TestPostCommand(3U, 0U, PLSR_CMD_CLEAR_POSITION, 0)
== PLSR_RESULT_QUEUED);
PlsrProcess();
status = TestGetStatus(0U);
TEST_CHECK(status.logicalPosition == 0);
TEST_CHECK(status.positionValid != 0U);
TEST_CHECK(status.positionOverflow == 0U);
TEST_CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
TEST_CHECK(hsdPosition == 0);
TEST_CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
TEST_CHECK(hsdEquivalent == 0);
}

int main(void)


+ 331
- 0
PLSR/Test/test_plsr_hal.c Voir le fichier

@@ -81,6 +81,14 @@ static void TestWriteDword(TEST_MEMORY *memory,
memory->words[device][address + 1UL] = (uint16_t)(raw >> 16U);
}

static void TestWriteSfdDword(uint16_t address, uint32_t value)
{
CHECK(PlcDeviceWriteSfd(address, (uint16_t)(value & 0xFFFFUL))
== PLC_DEVICE_OK);
CHECK(PlcDeviceWriteSfd((uint16_t)(address + 1U),
(uint16_t)(value >> 16U)) == PLC_DEVICE_OK);
}

static void TestSetSegment(TEST_MEMORY *memory,
uint16_t number,
uint32_t frequency,
@@ -104,6 +112,20 @@ static void TestResetEnvironment(void)
CHECK(PlsrInit() == PLSR_RESULT_OK);
}

static void TestCompleteFirstAbPrime(uint8_t axis)
{
int quarter;

CHECK(PlsrHwIsAbStartupPriming(axis) != 0U);
for (quarter = 0; quarter < 4; quarter++)
{
PlsrHwTestAdvanceAbQuarter(axis);
}
CHECK(PlsrHwIsAbStartupPriming(axis) == 0U);
CHECK(PlsrHwGetEmittedPulses(axis) == 0);
CHECK(PlsrHwTestGetAbQuarter(axis) == 0U);
}

static PLSR_CALL TestMakeCall(TEST_MEMORY *memory)
{
PLSR_CALL call;
@@ -134,6 +156,20 @@ static PLSR_STATUS TestGetStatus(void)
return status;
}

static int32_t TestReadSdDword(uint16_t lowAddress)
{
int32_t lowWord = 0;
int32_t highWord = 0;
uint32_t rawValue;

CHECK(PlcDeviceReadSd(lowAddress, &lowWord) == PLC_DEVICE_OK);
CHECK(PlcDeviceReadSd((uint16_t)(lowAddress + 1U), &highWord)
== PLC_DEVICE_OK);
rawValue = ((uint32_t)lowWord & 0xFFFFUL)
| (((uint32_t)highWord & 0xFFFFUL) << 16U);
return (int32_t)rawValue;
}

/* ---- HAL 单测 ---- */

static void TestMapping(void)
@@ -316,6 +352,7 @@ static void TestAbPhaseAndCounting(void)
CHECK(PlsrHwTestGetCnt(0U)
== ((PlsrHwTestGetArr(0U) + 1UL) * 3UL) / 4UL);
CHECK(PlsrHwTestGetCnt(1U) == PlsrHwTestGetCcr(1U));
TestCompleteFirstAbPrime(0U);

/* 任一物理 timer update 不能直接计作完整 AB 周期。 */
PlsrHwTestTriggerUpdate(0U);
@@ -433,6 +470,9 @@ static void TestTwoAbAxesIndependent(void)
CHECK(PlsrHwTestGetPwmEnabled(2U) != 0U);
CHECK(PlsrHwTestGetPwmEnabled(3U) != 0U);

TestCompleteFirstAbPrime(0U);
TestCompleteFirstAbPrime(2U);

for (quarter = 0; quarter < 4; quarter++)
{
PlsrHwTestAdvanceAbQuarter(0U);
@@ -473,6 +513,8 @@ static void TestAbFrequencyLimits(void)
== 2UL * (PlsrHwTestGetPsc(1U) + 1UL));
CHECK(PlsrHwTestGetArr(0U) <= 65535UL);

TestCompleteFirstAbPrime(0U);

CHECK(PlsrHwSetFrequency(0U, 100000UL) == PLSR_RESULT_OK);
for (quarter = 0; quarter < 4; quarter++)
{
@@ -580,6 +622,11 @@ static void TestEndToEndTwoSegments(void)
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_ACCEL);
CHECK(status.currentSegment == 2U);
CHECK(status.logicalPosition == 100);
CHECK(status.taskPulses == 100);
CHECK(status.totalPulses == 100);
CHECK(TestReadSdDword(1000U) == 2);
CHECK(TestReadSdDword(1002U) == 0);

/* 段2:DIR 延时 → PWM → 加速 → RUN。 */
for (ticks = 0; ticks < 600; ticks++)
@@ -603,6 +650,23 @@ static void TestEndToEndTwoSegments(void)
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.done != 0U);
CHECK(status.logicalPosition == 300);
CHECK(status.taskPulses == 300);
CHECK(status.totalPulses == 300);
CHECK(TestReadSdDword(1000U) == 2);
CHECK(TestReadSdDword(1002U) == 200);
CHECK(TestReadSdDword(1004U) == 200);
CHECK(TestReadSdDword(1006U) == 0);
{
int32_t hsdPulses;
int32_t hsdEquivalent;

CHECK(PlcDeviceReadHsdDword(0U, &hsdPulses) == PLC_DEVICE_OK);
CHECK(hsdPulses == 300);
CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent)
== PLC_DEVICE_OK);
CHECK(hsdEquivalent == 300);
}
/* 终态转换后 HAL 回 IDLE(允许重新启动),脉冲已停止。 */
CHECK(PlsrHwIsPulseActive(0U) == 0U);
CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U);
@@ -633,6 +697,8 @@ static void TestEndToEndAbSegment(void)
CHECK(PlsrHwTestGetPwmEnabled(0U) != 0U);
CHECK(PlsrHwTestGetPwmEnabled(1U) != 0U);

TestCompleteFirstAbPrime(0U);

/* 负脉冲选择反向相序,完整两个周期后由同一事件链结束任务。 */
PlsrHwTestAdvanceAbQuarter(0U);
CHECK(PlsrHwTestGetAbPhaseA(0U) == 0U);
@@ -647,11 +713,222 @@ static void TestEndToEndAbSegment(void)
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.done != 0U);
CHECK(status.directionPositive == 0U);
CHECK(status.logicalPosition == -2);
CHECK(status.taskPulses == -2);
CHECK(status.totalPulses == 2);
CHECK(TestReadSdDword(1002U) == -2);
CHECK(TestReadSdDword(1004U) == -2);
CHECK(status.highResourceMask == 0U);
CHECK(PlsrHwTestGetPwmEnabled(0U) == 0U);
CHECK(PlsrHwTestGetPwmEnabled(1U) == 0U);
}

static void TestPositionOnImmediateStop(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_COMMAND command;
PLSR_STATUS status;
int pulse;
int tick;

TestResetEnvironment();
(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1);
TestSetSegment(&memory, 1U, 1000U, 100);
call = TestMakeCall(&memory);
call.sequence = 30UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
for (tick = 0; tick < 10; tick++)
{
PlsrProcess();
}
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);

for (pulse = 0; pulse < 37; pulse++)
{
PlsrHwTestTriggerUpdate(0U);
}
PlsrProcess();
status = TestGetStatus();
CHECK(status.logicalPosition == 37);
CHECK(status.taskPulses == 37);
CHECK(status.totalPulses == 37);

command.sequence = 31UL;
command.axis = 0U;
command.opcode = PLSR_CMD_STOP_IMMEDIATE;
command.argument = 0;
CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED);
PlsrProcess();
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);
CHECK(status.logicalPosition == 37);
CHECK(status.taskPulses == 37);
CHECK(status.totalPulses == 37);
CHECK(TestReadSdDword(1002U) == 37);

command.sequence = 32UL;
command.opcode = PLSR_CMD_CLEAR_TOTAL;
CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED);
PlsrProcess();
status = TestGetStatus();
CHECK(status.logicalPosition == 37);
CHECK(status.totalPulses == 0);
}

static void TestAbsolutePositionAccounting(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_COMMAND command;
PLSR_STATUS status;
int32_t hsdPosition;
int pulse;
int tick;

TestResetEnvironment();
command.sequence = 40UL;
command.axis = 0U;
command.opcode = PLSR_CMD_SET_POSITION;
command.argument = 100;
CHECK(PlsrPostCommand(&command) == PLSR_RESULT_QUEUED);
PlsrProcess();

(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1);
TestSetSegment(&memory, 1U, 1000U, 130);
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S1_BASE, 1);
call = TestMakeCall(&memory);
call.sequence = 41UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
for (tick = 0; tick < 10; tick++)
{
PlsrProcess();
}
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);
for (pulse = 0; pulse < 30; pulse++)
{
PlsrHwTestTriggerUpdate(0U);
}
PlsrProcess();
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.logicalPosition == 130);
CHECK(status.taskPulses == 30);
CHECK(status.totalPulses == 30);
CHECK(status.positionValid != 0U);
CHECK(PlcDeviceReadHsdDword(0U, &hsdPosition) == PLC_DEVICE_OK);
CHECK(hsdPosition == 130);
}

static void TestEquivalentRemainderAccounting(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_STATUS status;
int32_t hsdPulses;
int32_t hsdEquivalent;

TestResetEnvironment();
CHECK(PlcDeviceWriteSfd(900U, (1U << 8U)) == PLC_DEVICE_OK);
TestWriteSfdDword(902U, 3UL);
TestWriteSfdDword(904U, 2UL);
TestWriteSfdDword(956U, 60000UL);
CHECK(PlcDeviceWriteSfd(907U, 0U) == PLC_DEVICE_OK);

(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1);
TestSetSegment(&memory, 1U, 1000U, 1);
call = TestMakeCall(&memory);
call.sequence = 30UL;
call.outputModeOverride = PLSR_OUTPUT_PULSE_DIR;

/* 3脉冲/2单位:第一次1单位只输出1脉冲并保存1/2余数。 */
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);
PlsrHwTestTriggerUpdate(0U);
PlsrProcess();
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.logicalPosition == 1);
CHECK(status.taskPulses == 1);
CHECK(status.totalPulses == 1);
CHECK(PlcDeviceReadHsdDword(0U, &hsdPulses) == PLC_DEVICE_OK);
CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
CHECK(hsdPulses == 1);
CHECK(hsdEquivalent == 0);

/* 第二次1单位合并余数后输出2脉冲;两次合计精确为3脉冲/2单位。 */
call.sequence = 31UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
PlsrHwTestTriggerUpdate(0U);
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);
PlsrHwTestTriggerUpdate(0U);
PlsrProcess();
status = TestGetStatus();
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.logicalPosition == 3);
CHECK(status.taskPulses == 2);
CHECK(status.totalPulses == 3);
CHECK(PlcDeviceReadHsdDword(0U, &hsdPulses) == PLC_DEVICE_OK);
CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
CHECK(hsdPulses == 3);
CHECK(hsdEquivalent == 2);
CHECK(TestReadSdDword(1002U) == 2);
CHECK(TestReadSdDword(1004U) == 1);
}

static void TestEquivalentCompatibleError(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_STATUS status;
int32_t errorCode = -1;
int32_t errorBlock = -1;

TestResetEnvironment();
CHECK(PlcDeviceWriteSfd(900U, (1U << 8U)) == PLC_DEVICE_OK);
TestWriteSfdDword(902U, 0UL);
TestWriteSfdDword(904U, 2UL);
TestWriteSfdDword(956U, 60000UL);

(void)memset(&memory, 0, sizeof(memory));
TestWriteDword(&memory, PLSR_DEVICE_D, TEST_S0_BASE, 1);
TestSetSegment(&memory, 1U, 1000U, 1);
call = TestMakeCall(&memory);
call.sequence = 32UL;
call.outputModeOverride = PLSR_OUTPUT_PULSE_DIR;

CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
status = TestGetStatus();
CHECK(status.lastCommandResult == PLSR_RESULT_INVALID_S2);
CHECK(status.state == PLSR_STATE_IDLE);
CHECK(PlcDeviceReadSd(1010U, &errorCode) == PLC_DEVICE_OK);
CHECK(PlcDeviceReadSd(1011U, &errorBlock) == PLC_DEVICE_OK);
CHECK(errorCode == 2);
CHECK(errorBlock == 0);

/* A valid retry clears the compatible parameter error. */
TestWriteSfdDword(902U, 3UL);
call.sequence = 33UL;
CHECK(PlsrPostCall(&call) == PLSR_RESULT_QUEUED);
PlsrProcess();
CHECK(PlcDeviceReadSd(1010U, &errorCode) == PLC_DEVICE_OK);
CHECK(errorCode == 0);
CHECK(PlsrHwStopPulse(0U) == PLSR_RESULT_OK);
}

static void TestProductionSelfTestStartsAb(void)
{
PLSR_STATUS status;
@@ -670,6 +947,55 @@ static void TestProductionSelfTestStartsAb(void)
CHECK(PlsrHwStopPulse(0U) == PLSR_RESULT_OK);
}

static void TestEquivalentSelfTest(void)
{
PLSR_STATUS status;
int32_t hsdPulses;
int32_t hsdEquivalent;
int ticks;

TestResetEnvironment();
CHECK(PlsrEquivalentSelfTestQueue() == PLSR_RESULT_QUEUED);
PlsrProcess();

/* SFD907=10ms. Advance the simulated hardware delay before segment 1. */
for (ticks = 0; ticks < 10; ticks++)
{
PlsrProcess();
}
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);

/* 3 pulses / 2 units: 1001 units emit 1501 pulses and keep 1/2 remainder. */
for (ticks = 0; ticks < 1501; ticks++)
{
PlsrHwTestTriggerUpdate(0U);
}
PlsrProcess();
CHECK(PlsrHwGetState(0U) == PLSR_HW_STATE_RUNNING);

/* Segment 2 consumes the remainder and therefore emits 1502 pulses. */
for (ticks = 0; ticks < 1502; ticks++)
{
PlsrHwTestTriggerUpdate(0U);
}
PlsrProcess();

status = TestGetStatus();
CHECK(status.lastCommandResult == PLSR_RESULT_OK);
CHECK(status.outputMode == PLSR_OUTPUT_PULSE_DIR);
CHECK(status.state == PLSR_STATE_COMPLETED);
CHECK(status.currentSegment == 2U);
CHECK(status.logicalPosition == 3003);
CHECK(status.taskPulses == 3003);
CHECK(status.totalPulses == 3003);
CHECK(PlcDeviceReadHsdDword(0U, &hsdPulses) == PLC_DEVICE_OK);
CHECK(PlcDeviceReadHsdDword(2U, &hsdEquivalent) == PLC_DEVICE_OK);
CHECK(hsdPulses == 3003);
CHECK(hsdEquivalent == 2002);
CHECK(TestReadSdDword(1002U) == 1502);
CHECK(TestReadSdDword(1004U) == 1001);
}

static void TestStopStopsHardware(void)
{
TEST_MEMORY memory;
@@ -722,7 +1048,12 @@ int main(void)
TestStopAndInvalidArgs();
TestEndToEndTwoSegments();
TestEndToEndAbSegment();
TestPositionOnImmediateStop();
TestAbsolutePositionAccounting();
TestEquivalentRemainderAccounting();
TestEquivalentCompatibleError();
TestProductionSelfTestStartsAb();
TestEquivalentSelfTest();
TestStopStopsHardware();

if (TestFailures != 0)


+ 48
- 0
PLSR/Test/test_plsr_job.c Voir le fichier

@@ -362,6 +362,53 @@ static void TestOutputAndDivider(void)
CHECK(PlcDeviceWriteSfd(900U, 0) == PLC_DEVICE_OK);
}

static void TestEquivalentSnapshot(void)
{
TEST_MEMORY memory;
PLSR_CALL call;
PLSR_PARSE_CONTEXT context = {0, 1U};
PLSR_JOB_SNAPSHOT snapshot;
PLSR_PARSE_DETAIL detail;
uint32_t liveFrequency;
uint8_t clamped;

TestBuildValidBlocks(&memory);
TestWriteDword(&memory, PLSR_DEVICE_D, 110U, 40000);
call = TestMakeCall(&memory);
CHECK(PlcDeviceWriteSfd(900U, (1U << 8U)) == PLC_DEVICE_OK);
TestWriteSfdDword(902U, 3UL);
TestWriteSfdDword(904U, 2UL);
TestWriteSfdDword(956U, 60000UL);
TestWriteSfdDword(958U, 0UL);

CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
== PLSR_RESULT_OK);
CHECK(snapshot.equivalent.unitCode == 1U);
CHECK(snapshot.equivalent.pulsesPerRevolution == 3UL);
CHECK(snapshot.equivalent.movementPerRevolution == 2UL);
CHECK(snapshot.inputDefaultSpeed == 1000UL);
CHECK(snapshot.inputMaximumSpeed == 60000UL);
CHECK(snapshot.s2.defaultSpeed == 1500UL);
CHECK(snapshot.s2.maximumSpeed == 90000UL);
CHECK(snapshot.segments[0].targetFrequency == 60000UL);
CHECK(snapshot.segments[1].targetFrequency == 1500UL);

TestWriteDword(&memory, PLSR_DEVICE_D, 110U, 20000);
CHECK(PlsrResolveLiveFrequency(&snapshot,
1U,
&liveFrequency,
&clamped) == PLSR_RESULT_OK);
CHECK(liveFrequency == 30000UL);
CHECK(clamped == 0U);

TestWriteSfdDword(902U, 0UL);
CHECK(PlsrBuildJobSnapshot(&call, &context, &snapshot, &detail)
== PLSR_RESULT_INVALID_S2);
CHECK(detail.address == 902U);

TestConfigureAxis0K1();
}

static void TestCoreSubmission(void)
{
TEST_MEMORY memory;
@@ -443,6 +490,7 @@ int main(void)
TestValidationFailures();
TestDynamicReferences();
TestOutputAndDivider();
TestEquivalentSnapshot();
TestDefaultSfdStartable();
TestCoreSubmission();



+ 93
- 0
PLSR/Test/test_plsr_position.c Voir le fichier

@@ -0,0 +1,93 @@
#include "plsr_position.h"
#include <limits.h>
#include <stdio.h>

static int TestChecks;
static int TestFailures;

#define CHECK(condition) \
do \
{ \
TestChecks++; \
if (!(condition)) \
{ \
TestFailures++; \
(void)printf("FAIL line %d: %s\n", __LINE__, #condition); \
} \
} while (0)

int main(void)
{
PLSR_EQUIVALENT_CONFIG config = {3UL, 2UL, 1U};
PLSR_EQUIVALENT_CONFIG pulseConfig = {0UL, 0UL, 0U};
int64_t pulses;
int64_t units;
int64_t remainder;
uint32_t speed;

CHECK(PlsrPositionUnitCodeIsValid(0U) != 0U);
CHECK(PlsrPositionUnitCodeIsValid(1U) != 0U);
CHECK(PlsrPositionUnitCodeIsValid(3U) != 0U);
CHECK(PlsrPositionUnitCodeIsValid(5U) != 0U);
CHECK(PlsrPositionUnitCodeIsValid(7U) != 0U);
CHECK(PlsrPositionUnitCodeIsValid(2U) == 0U);
CHECK(PlsrPositionValidateEquivalent(&config) == PLSR_RESULT_OK);

CHECK(PlsrPositionUnitsToPulses(&config, 1, 0, &pulses, &remainder)
== PLSR_RESULT_OK);
CHECK(pulses == 1);
CHECK(remainder == 1);
CHECK(PlsrPositionUnitsToPulses(&config,
1,
remainder,
&pulses,
&remainder) == PLSR_RESULT_OK);
CHECK(pulses == 2);
CHECK(remainder == 0);

CHECK(PlsrPositionUnitsToPulses(&config, -1, 0, &pulses, &remainder)
== PLSR_RESULT_OK);
CHECK(pulses == -1);
CHECK(remainder == -1);
CHECK(PlsrPositionUnitsToPulses(&config,
-1,
remainder,
&pulses,
&remainder) == PLSR_RESULT_OK);
CHECK(pulses == -2);
CHECK(remainder == 0);

CHECK(PlsrPositionAbsoluteUnitsToPulses(&config, 10, &pulses)
== PLSR_RESULT_OK);
CHECK(pulses == 15);
CHECK(PlsrPositionPulsesToUnits(&config, 3, &units) == PLSR_RESULT_OK);
CHECK(units == 2);
CHECK(PlsrPositionPulsesToUnits(&config, -3, &units) == PLSR_RESULT_OK);
CHECK(units == -2);
CHECK(PlsrPositionSpeedToPulseFrequency(&config, 200U, &speed)
== PLSR_RESULT_OK);
CHECK(speed == 300U);
CHECK(PlsrPositionPulseFrequencyToSpeed(&config, 300U, &speed)
== PLSR_RESULT_OK);
CHECK(speed == 200U);

CHECK(PlsrPositionUnitsToPulses(&pulseConfig,
INT32_MIN,
99,
&pulses,
&remainder) == PLSR_RESULT_OK);
CHECK(pulses == INT32_MIN);
CHECK(remainder == 0);
config.pulsesPerRevolution = 0UL;
CHECK(PlsrPositionValidateEquivalent(&config) == PLSR_RESULT_INVALID_S2);

if (TestFailures != 0)
{
(void)printf("FAIL: %d of %d PLSR position checks failed\n",
TestFailures,
TestChecks);
return 1;
}
(void)printf("PASS: %d PLSR position checks\n", TestChecks);
return 0;
}

BIN
tmp/pdfs/xinjie_page_30.png Voir le fichier

Avant Après
Largeur: 1191  |  Hauteur: 1684  |  Taille: 328 KiB

BIN
tmp/pdfs/xinjie_page_31.png Voir le fichier

Avant Après
Largeur: 1191  |  Hauteur: 1684  |  Taille: 282 KiB

Chargement…
Annuler
Enregistrer