Bladeren bron

PLSR 新增 P3a 硬件层:单轴 PULSE/DIR 输出打通(host 模拟 + core 接线)

- 新增 plsr_hal_f407.c/.h:Q0~Q3 定时器/引脚映射(PF6-9 + TIM10/11/13/14)、
  DIR 输出与方向建立延时状态机、PWM 启停/动态改频(50%占空比、预装载无毛刺)、
  更新中断软件计数 + 段完成事件注入、精确停止(周期末关通道,无额外脉冲)
- 定时器时钟按 RCC 实际配置计算(修正 TIM13/14=84MHz 疑点,APB2 分频2 时实际 168MHz)
- core 接线:段进入自动启动硬件+速度曲线、段间自动重启、tick 驱动 profile→改频、
  加速完成自动注入 ACCEL_COMPLETE、STOP/急停/限位/故障/终态统一停止硬件
- PLSR_HOST_TEST 下模拟定时器寄存器,HAL 逻辑可单测
- 新增 test_plsr_hal 380 项(含端到端:START→DIR延时→PWM→加速→计数→段间→完成),
  六套 host 测试共 1151 项全绿,IAR 0 错误 0 警告
- 待办:上板前补 GPIO 复用与 NVIC 使能(host 覆盖不到)
deepseek
ywh 1 maand geleden
bovenliggende
commit
83803085ec
6 gewijzigde bestanden met toevoegingen van 1153 en 1 verwijderingen
  1. +6
    -0
      EWARM/Modbus.ewp
  2. +64
    -0
      PLSR/Inc/plsr_hal_f407.h
  3. +126
    -1
      PLSR/Src/plsr_core.c
  4. +516
    -0
      PLSR/Src/plsr_hal_f407.c
  5. +20
    -0
      PLSR/Test/run_host_tests.ps1
  6. +421
    -0
      PLSR/Test/test_plsr_hal.c

+ 6
- 0
EWARM/Modbus.ewp Bestand weergeven

@@ -1290,6 +1290,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_profile.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Inc\plsr_hal_f407.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name>
</file>
@@ -1308,6 +1311,9 @@
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_profile.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_hal_f407.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\PLSR\Src\plsr_core.c</name>
</file>


+ 64
- 0
PLSR/Inc/plsr_hal_f407.h Bestand weergeven

@@ -0,0 +1,64 @@
#ifndef PLSR_HAL_F407_H
#define PLSR_HAL_F407_H

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

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

+ 126
- 1
PLSR/Src/plsr_core.c Bestand weergeven

@@ -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 <string.h>

@@ -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, &params);
}

/* 停止当前段的硬件输出与速度曲线。 */
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);
}
}
}
}



+ 516
- 0
PLSR/Src/plsr_hal_f407.c Bestand weergeven

@@ -0,0 +1,516 @@
#include "plsr_hal_f407.h"
#include "plsr_address_map.h"
#include "plsr_core.h"
#include "plsr_job.h"
#include <string.h>

#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

+ 20
- 0
PLSR/Test/run_host_tests.ps1 Bestand weergeven

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



+ 421
- 0
PLSR/Test/test_plsr_hal.c Bestand weergeven

@@ -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 <stdio.h>
#include <string.h>

#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(&params, 0, sizeof(params));
params.frequencyHz = 1000UL;
params.targetPulses = 100;
params.directionPoint = 4U;
params.directionPositive = 1U;
params.directionDelayMs = 10U;

CHECK(PlsrHwStartPulse(0U, &params) == 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(&params, 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, &params) == 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(&params, 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, &params) == 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(&params, 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, &params) == 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, &params) == PLSR_RESULT_INVALID_ARGUMENT);
CHECK(PlsrHwStartPulse(0U, NULL) == PLSR_RESULT_INVALID_ARGUMENT);
params.targetPulses = 0;
CHECK(PlsrHwStartPulse(0U, &params) == 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;
}

Laden…
Annuleren
Opslaan