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

复段间固定多出 7~8 ms 的问题

codex/plsr-2026-minimal
ywh преди 1 месец
родител
ревизия
71c92a8de2
променени са 1 файла, в които са добавени 572 реда и са изтрити 31 реда
  1. +572
    -31
      PLSR/Src/plsr.c

+ 572
- 31
PLSR/Src/plsr.c Целия файл

@@ -62,6 +62,8 @@
#define PLSR_PROFILE_STARTUP_BUDGET (400U)
#define PLSR_PROFILE_REFILL_TARGET (800U)
#define PLSR_PROFILE_REFILL_BUDGET (200U)
#define PLSR_TIMED_START_BUILD_BUDGET (32U)
#define PLSR_TIMED_START_READY_ITEMS PLSR_PROFILE_STARTUP_BUDGET
#define PLSR_PROFILE_LOW_WATER (100U)
#define PLSR_PROFILE_LOW_WATER_CLEAR (200U)
#define PLSR_UNDERRUN_HOLD_TIME_US (2000UL)
@@ -141,6 +143,28 @@ typedef struct
volatile uint8_t generatorComplete;
} PLSR_PROFILE_QUEUE;

/* A WAIT_TIME boundary is a cold start, but its planner work is predictable
while the source segment is still running. Build that next stream in the
inactive queue bank and publish it only after the startup horizon is ready.
The boundary path then contains no planner loop. */
typedef struct
{
PLSR_PROFILE_ENTRY firstRun;
uint64_t magnitude;
uint32_t sourceEpoch;
uint32_t queueGeneration;
uint16_t generatedItems;
uint8_t sourceSegment;
uint8_t nextSegment;
uint8_t positive;
uint8_t directionLevel;
uint8_t directionChanged;
uint8_t queueBank;
volatile uint8_t building;
volatile uint8_t valid;
volatile uint8_t pendingActivation;
} PLSR_TIMED_START;

typedef struct
{
uint64_t magnitude;
@@ -260,6 +284,7 @@ static PLSR_SHORT_PROFILE PlsrShortProfile;
static PLSR_PROFILE_QUEUE PlsrProfileQueues[2];
static volatile uint8_t PlsrProfileQueueBank;
#define PlsrProfileQueue (PlsrProfileQueues[PlsrProfileQueueBank])
static PLSR_TIMED_START PlsrTimedStart;
static PLSR_HANDOFF_PLAN PlsrHandoffPlan;
static volatile uint64_t PlsrCountedObservedPublished;
static volatile uint8_t PlsrCountedHandoffStaged;
@@ -359,7 +384,8 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile,
uint8_t segmentNumber,
uint32_t startFrequencyHz,
uint32_t targetFrequencyHz,
uint64_t pulseCount);
uint64_t pulseCount,
uint8_t currentPositive);
static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile,
PLSR_PROFILE_ENTRY *entry);
static uint8_t PlsrShortProfileTakeRunLimited(
@@ -372,6 +398,12 @@ static PLSR_PLATFORM_SERVICE_RESULT PlsrReplanPulseDir(
uint32_t targetHz,
uint32_t totalPulses);
static void PlsrProfileQueueReset(void);
static void PlsrProfileQueueResetBankLocked(uint8_t bank);
static void PlsrInvalidateTimedStartLocked(void);
static void PlsrServiceTimedStartPreparation(void);
static PLSR_PLATFORM_SERVICE_RESULT PlsrTryStartPreparedTimedSegment(
uint8_t segmentNumber);
static uint8_t PlsrStartPreparedTimedOutput(void);
static void PlsrProfileQueueInvalidateGeneration(void);
static uint8_t PlsrProfileQueueBegin(
const PLSR_SHORT_PROFILE *producerProfile,
@@ -1702,13 +1734,13 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile,
uint8_t segmentNumber,
uint32_t startFrequencyHz,
uint32_t targetFrequencyHz,
uint64_t pulseCount)
uint64_t pulseCount,
uint8_t currentPositive)
{
PLSR_MOTION_BLOCK block;
PLSR_PLANNER_STATUS status;
const PLSR_SEGMENT_CONFIG *segment;
uint8_t nextSegment = 0U;
uint8_t currentPositive;
uint8_t nextPositive;

(void)memset(profile, 0, sizeof(*profile));
@@ -1741,7 +1773,6 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile,
}
if (nextSegment != 0U)
{
currentPositive = PlsrCountPositive;
if (PlsrActiveConfig.positionMode == PLSR_POSITION_RELATIVE)
{
currentPositive = (segment->pulses >= 0L) ? 1U : 0U;
@@ -1749,11 +1780,12 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile,
(PlsrActiveConfig.segments[nextSegment - 1U].pulses >= 0L)
? 1U : 0U;
}
else if ((segmentNumber == PlsrCurrentSegment)
&& (PlsrPredictNextDirection(nextSegment,
&nextPositive) != 0U))
else if ((int64_t)PlsrActiveConfig.segments[nextSegment - 1U].pulses
!= (int64_t)segment->pulses)
{
/* direction resolved from the live absolute position */
nextPositive =
((int64_t)PlsrActiveConfig.segments[nextSegment - 1U].pulses
> (int64_t)segment->pulses) ? 1U : 0U;
}
else
{
@@ -1915,6 +1947,8 @@ static PLSR_PLATFORM_SERVICE_RESULT PlsrReplanPulseDir(
PlsrPlatformExitCritical(criticalState);
return PLSR_PLATFORM_SERVICE_DEFERRED;
}
/* The replacement stream owns the inactive bank from this point. */
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);

if (totalPulses < committedPulses)
@@ -1993,7 +2027,8 @@ static PLSR_PLATFORM_SERVICE_RESULT PlsrReplanPulseDir(
if ((tailFrequencyHz == 0UL)
|| (PlsrPrepareShortProfile(&replacement, segmentNumber,
tailFrequencyHz, targetHz,
replacementPulses) == 0U))
replacementPulses,
PlsrCountPositive) == 0U))
{
return PLSR_PLATFORM_SERVICE_FAILED;
}
@@ -2145,25 +2180,40 @@ static void PlsrProfileRecordPlannerStatus(
}
}

static void PlsrInvalidateTimedStartLocked(void)
{
PlsrTimedStart.building = 0U;
PlsrTimedStart.valid = 0U;
PlsrTimedStart.pendingActivation = 0U;
}

static void PlsrProfileQueueResetBankLocked(uint8_t bank)
{
PLSR_PROFILE_QUEUE *queue = &PlsrProfileQueues[bank & 1U];

queue->generation++;
queue->readIndex = 0U;
queue->writeIndex = 0U;
queue->active = 0U;
queue->generatorComplete = 0U;
queue->producerEpoch = 0UL;
queue->repeatRemaining = 0UL;
queue->underrunDebtPulses = 0UL;
queue->producerSegment = 0U;
queue->preparedHandoffBank = 0U;
queue->haveLastSetting = 0U;
queue->producerProfile.active = 0U;
}

static void PlsrProfileQueueReset(void)
{
uint32_t criticalState = PlsrPlatformEnterCritical();
uint8_t bank;

PlsrInvalidateTimedStartLocked();
for (bank = 0U; bank < 2U; bank++)
{
PlsrProfileQueues[bank].generation++;
PlsrProfileQueues[bank].readIndex = 0U;
PlsrProfileQueues[bank].writeIndex = 0U;
PlsrProfileQueues[bank].active = 0U;
PlsrProfileQueues[bank].generatorComplete = 0U;
PlsrProfileQueues[bank].producerEpoch = 0UL;
PlsrProfileQueues[bank].repeatRemaining = 0UL;
PlsrProfileQueues[bank].underrunDebtPulses = 0UL;
PlsrProfileQueues[bank].producerSegment = 0U;
PlsrProfileQueues[bank].preparedHandoffBank = 0U;
PlsrProfileQueues[bank].haveLastSetting = 0U;
PlsrProfileQueues[bank].producerProfile.active = 0U;
PlsrProfileQueueResetBankLocked(bank);
}
PlsrProfileQueueBank = 0U;
PlsrProfileQueueLowWaterLatched = 0U;
@@ -2273,6 +2323,251 @@ static uint8_t PlsrProfileQueueAppendLocked(
return 1U;
}

static void PlsrServiceTimedStartPreparation(void)
{
PLSR_PROFILE_QUEUE *queue;
PLSR_SHORT_PROFILE profile;
PLSR_PROFILE_ENTRY entry;
PLSR_HANDOFF_PLAN *handoff;
const PLSR_SEGMENT_CONFIG *source;
uint32_t criticalState;
uint32_t sourceEpoch;
uint32_t queueGeneration;
uint32_t targetFrequencyHz;
uint32_t startFrequencyHz;
uint64_t remaining;
int64_t predictedPosition;
int64_t displacement;
uint64_t magnitude;
int32_t position;
uint16_t budget;
uint8_t sourceSegment;
uint8_t nextSegment;
uint8_t positive;
uint8_t directionLevel;
uint8_t directionChanged;
uint8_t queueBank;

if ((PlsrActiveConfig.outputMode != PLSR_OUTPUT_PULSE_DIR)
|| (PlsrStopRequested != 0U)
|| (PlsrTimedStart.pendingActivation != 0U))
{
return;
}

criticalState = PlsrPlatformEnterCritical();
sourceEpoch = PlsrSegmentEpoch;
sourceSegment = PlsrCurrentSegment;
if ((sourceSegment == 0U)
|| (sourceSegment > PlsrActiveConfig.segmentCount)
|| (PlsrActiveConfig.segments[sourceSegment - 1U].waitType
!= PLSR_WAIT_TIME))
{
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);
return;
}
if ((PlsrTimedStart.building != 0U)
|| (PlsrTimedStart.valid != 0U))
{
if ((PlsrTimedStart.sourceEpoch != sourceEpoch)
|| (PlsrTimedStart.sourceSegment != sourceSegment)
|| (PlsrTimedStart.queueBank == PlsrProfileQueueBank)
|| (PlsrProfileQueues[PlsrTimedStart.queueBank].generation
!= PlsrTimedStart.queueGeneration))
{
PlsrInvalidateTimedStartLocked();
}
else
{
PlsrPlatformExitCritical(criticalState);
if (PlsrTimedStart.valid != 0U)
{
return;
}
goto fill_timed_start;
}
}
position = PlsrPosition;
remaining = PlsrRemainingPulses;
positive = PlsrCountPositive;
PlsrPlatformExitCritical(criticalState);

if (PlsrGetNextSegment(&nextSegment) == 0U)
{
return;
}
source = &PlsrActiveConfig.segments[sourceSegment - 1U];
if (PlsrActiveConfig.positionMode == PLSR_POSITION_ABSOLUTE)
{
predictedPosition = source->pulses;
}
else if (positive != 0U)
{
predictedPosition = (int64_t)position + (int64_t)remaining;
}
else
{
predictedPosition = (int64_t)position - (int64_t)remaining;
}
if ((predictedPosition > (int64_t)INT32_MAX)
|| (predictedPosition < (int64_t)INT32_MIN))
{
return;
}
displacement = PlsrSegmentDisplacement(
nextSegment, (int32_t)predictedPosition);
if (displacement == 0)
{
return;
}
positive = (displacement > 0) ? 1U : 0U;
magnitude = (displacement < 0) ? (uint64_t)(-displacement)
: (uint64_t)displacement;
if (magnitude > 0xFFFFFFFFULL)
{
return;
}
directionLevel = positive;
if (PlsrActiveConfig.directionNegativeLogic != 0U)
{
directionLevel ^= 1U;
}
directionChanged = ((PlsrLastDirectionValid == 0U)
|| (PlsrLastDirectionOutput
!= (uint8_t)PlsrActiveConfig.directionOutput)
|| (PlsrLastDirectionLevel != directionLevel)) ? 1U : 0U;
targetFrequencyHz =
PlsrActiveConfig.segments[nextSegment - 1U].frequencyHz;
startFrequencyHz = PlsrEffectiveStartFrequency(
targetFrequencyHz, 0U, directionChanged, 0UL);
if ((PlsrPrepareShortProfile(&profile, nextSegment,
startFrequencyHz, targetFrequencyHz,
magnitude, positive) == 0U)
|| (PlsrShortProfileTakeRun(&profile, &entry) == 0U))
{
return;
}

/* Keep the seamless plan for this future segment consistent with its
possibly clipped terminal frequency. */
handoff = &PlsrPreparedHandoffPlans[PlsrPreparedHandoffBank]
[nextSegment - 1U];
(void)PlsrBuildHandoffPlan(nextSegment, &PlsrActiveConfig,
profile.endHz, handoff);

criticalState = PlsrPlatformEnterCritical();
if ((PlsrSegmentEpoch != sourceEpoch)
|| (PlsrCurrentSegment != sourceSegment))
{
PlsrPlatformExitCritical(criticalState);
return;
}
queueBank = (uint8_t)(PlsrProfileQueueBank ^ 1U);
PlsrProfileQueueResetBankLocked(queueBank);
queue = &PlsrProfileQueues[queueBank];
queue->producerEpoch = sourceEpoch + 1UL;
queue->producerSegment = nextSegment;
queue->preparedHandoffBank = PlsrPreparedHandoffBank;
queue->repeatRemaining = 0UL;
queue->underrunDebtPulses = 0UL;
queue->haveLastSetting = 0U;
PlsrCopyShortProfile(&queue->producerProfile, &profile);
queue->active = 1U;
queue->generatorComplete = (profile.active == 0U) ? 1U : 0U;

PlsrTimedStart.firstRun = entry;
PlsrTimedStart.magnitude = magnitude;
PlsrTimedStart.sourceEpoch = sourceEpoch;
PlsrTimedStart.queueGeneration = queue->generation;
PlsrTimedStart.generatedItems = 0U;
PlsrTimedStart.sourceSegment = sourceSegment;
PlsrTimedStart.nextSegment = nextSegment;
PlsrTimedStart.positive = positive;
PlsrTimedStart.directionLevel = directionLevel;
PlsrTimedStart.directionChanged = directionChanged;
PlsrTimedStart.queueBank = queueBank;
PlsrTimedStart.valid = (queue->generatorComplete != 0U) ? 1U : 0U;
PlsrTimedStart.building = (PlsrTimedStart.valid == 0U) ? 1U : 0U;
PlsrTimedStart.pendingActivation = 0U;
PlsrPlatformExitCritical(criticalState);
PlsrProfileRecordPlannerStatus(&profile);

fill_timed_start:
budget = PLSR_TIMED_START_BUILD_BUDGET;
while (budget != 0U)
{
criticalState = PlsrPlatformEnterCritical();
queueBank = PlsrTimedStart.queueBank;
queue = &PlsrProfileQueues[queueBank];
queueGeneration = PlsrTimedStart.queueGeneration;
if ((PlsrTimedStart.building == 0U)
|| (PlsrTimedStart.sourceEpoch != PlsrSegmentEpoch)
|| (PlsrTimedStart.sourceSegment != PlsrCurrentSegment)
|| (queueBank == PlsrProfileQueueBank)
|| (queue->generation != queueGeneration))
{
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);
return;
}
PlsrCopyShortProfile(&profile, &queue->producerProfile);
PlsrPlatformExitCritical(criticalState);

if (PlsrShortProfileTakeRun(&profile, &entry) == 0U)
{
criticalState = PlsrPlatformEnterCritical();
if ((PlsrTimedStart.building != 0U)
&& (queue->generation == queueGeneration))
{
queue->generatorComplete = 1U;
PlsrTimedStart.building = 0U;
PlsrTimedStart.valid = 1U;
}
PlsrPlatformExitCritical(criticalState);
return;
}

criticalState = PlsrPlatformEnterCritical();
if ((PlsrTimedStart.building == 0U)
|| (PlsrTimedStart.sourceEpoch != PlsrSegmentEpoch)
|| (queueBank == PlsrProfileQueueBank)
|| (queue->generation != queueGeneration))
{
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);
return;
}
if (PlsrProfileQueueAppendLocked(queue, &entry) == 0U)
{
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);
return;
}
PlsrCopyShortProfile(&queue->producerProfile, &profile);
if (PlsrTimedStart.generatedItems != 0xFFFFU)
{
PlsrTimedStart.generatedItems++;
}
if ((profile.active == 0U)
|| (profile.nextPeriod >= profile.pulseCount))
{
queue->generatorComplete = 1U;
}
if ((PlsrTimedStart.generatedItems
>= PLSR_TIMED_START_READY_ITEMS)
|| (queue->generatorComplete != 0U))
{
PlsrTimedStart.building = 0U;
PlsrTimedStart.valid = 1U;
PlsrPlatformExitCritical(criticalState);
return;
}
PlsrPlatformExitCritical(criticalState);
budget--;
}
}

#if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \
&& !defined(PLSR_HOST_TEST)
static uint32_t PlsrProfileTimingNow(void)
@@ -2646,6 +2941,195 @@ static void PlsrMaybePlanBoundaryRamp(uint32_t expectedEpoch)
}
}

static uint8_t PlsrStartPreparedTimedOutput(void)
{
PLSR_PROFILE_QUEUE *queue;
PLSR_PROFILE_ENTRY firstRun;
uint32_t criticalState;
uint32_t firstFrequencyHz;
uint32_t secondFrequencyHz;
uint32_t actualFrequencyHz;
uint32_t queueReadIndex;
uint32_t queueWriteIndex;
uint8_t queueBank;

criticalState = PlsrPlatformEnterCritical();
queueBank = PlsrTimedStart.queueBank;
queue = &PlsrProfileQueues[queueBank];
if ((PlsrTimedStart.pendingActivation == 0U)
|| (PlsrTimedStart.valid == 0U)
|| (PlsrCurrentSegment != PlsrTimedStart.nextSegment)
|| (PlsrSegmentEpoch != PlsrTimedStart.sourceEpoch + 1UL)
|| (queueBank == PlsrProfileQueueBank)
|| (queue->generation != PlsrTimedStart.queueGeneration)
|| (queue->producerEpoch != PlsrSegmentEpoch)
|| (queue->producerSegment != PlsrCurrentSegment))
{
PlsrPlatformExitCritical(criticalState);
return 0U;
}
firstRun = PlsrTimedStart.firstRun;
PlsrProfileQueueResetBankLocked(PlsrProfileQueueBank);
PlsrProfileQueueBank = queueBank;
PlsrCopyShortProfile(&PlsrShortProfile, &queue->producerProfile);
PlsrTimedStart.pendingActivation = 0U;
PlsrTimedStart.valid = 0U;
PlsrTimedStart.building = 0U;
PlsrPlatformExitCritical(criticalState);

PlsrSegmentClockStarted = 1U;
PlsrSegmentElapsedMs = 0UL;
PlsrRamp.active = 0U;
PlsrCountedHandoffStaged = 0U;
if (PlsrStageCountedHandoff() == 0U)
{
return 0U;
}

firstFrequencyHz = firstRun.setting.actualFrequencyHz;
secondFrequencyHz = firstFrequencyHz;
criticalState = PlsrPlatformEnterCritical();
queueReadIndex = queue->readIndex;
queueWriteIndex = queue->writeIndex;
if (queueReadIndex != queueWriteIndex)
{
secondFrequencyHz = queue->entries[
queueReadIndex & PLSR_PROFILE_QUEUE_MASK]
.setting.actualFrequencyHz;
}
queue->lastSetting = firstRun.setting;
queue->haveLastSetting = 1U;
PlsrPlatformExitCritical(criticalState);

if (secondFrequencyHz > firstFrequencyHz)
{
PlsrRunStatus = PLSR_STATUS_ACCELERATING;
}
else if (secondFrequencyHz < firstFrequencyHz)
{
PlsrRunStatus = PLSR_STATUS_DECELERATING;
}
else
{
PlsrRunStatus = PLSR_STATUS_RUNNING;
}
PlsrCurrentFrequencyHz = firstFrequencyHz;
PlsrQueuedFrequencyHz = secondFrequencyHz;
PlsrDiagnosticQueuedExpectedHz = firstRun.setting.actualFrequencyHz;
PlsrCountedObservedPublished = PlsrPlatformObservedPulses(
(uint8_t)PlsrActiveConfig.pulseOutput);
PlsrProfileQueueMinimumDepth = PlsrProfileQueueCount();
PlsrProfileQueueLowWaterLatched = 0U;
PlsrPulseActive = 1U;
PlsrExecutor.generation++;
PlsrExecutor.mode = (queue->generatorComplete != 0U)
? PLSR_EXEC_STEP_TABLE
: PLSR_EXEC_STREAM;
if (PlsrPlatformStartCountedStreamPrepared(
(uint8_t)PlsrActiveConfig.pulseOutput,
&firstRun.setting, firstRun.repeatCount,
&actualFrequencyHz) == 0U)
{
PlsrPulseActive = 0U;
PlsrExecutor.mode = PLSR_EXEC_IDLE;
PlsrProfileQueueReset();
PlsrShortProfile.active = 0U;
return 0U;
}
PlsrCurrentFrequencyHz = actualFrequencyHz;
PlsrDiagnosticRecordFrequency(firstRun.requestedFrequencyHz,
firstRun.setting.actualFrequencyHz,
actualFrequencyHz);
return 1U;
}

static PLSR_PLATFORM_SERVICE_RESULT PlsrTryStartPreparedTimedSegment(
uint8_t segmentNumber)
{
PLSR_PROFILE_QUEUE *queue;
uint32_t criticalState;
uint64_t magnitude;
uint8_t positive;
uint8_t directionLevel;
uint8_t directionChanged;

criticalState = PlsrPlatformEnterCritical();
queue = &PlsrProfileQueues[PlsrTimedStart.queueBank];
if ((PlsrTimedStart.valid == 0U)
|| (PlsrTimedStart.pendingActivation != 0U)
|| (PlsrTimedStart.sourceEpoch != PlsrSegmentEpoch)
|| (PlsrTimedStart.sourceSegment != PlsrCurrentSegment)
|| (PlsrTimedStart.nextSegment != segmentNumber)
|| (PlsrTimedStart.queueBank == PlsrProfileQueueBank)
|| (queue->generation != PlsrTimedStart.queueGeneration))
{
PlsrPlatformExitCritical(criticalState);
return PLSR_PLATFORM_SERVICE_DEFERRED;
}
magnitude = PlsrTimedStart.magnitude;
positive = PlsrTimedStart.positive;
directionLevel = PlsrTimedStart.directionLevel;
directionChanged = PlsrTimedStart.directionChanged;

PlsrSegmentEpoch++;
PlsrCurrentSegment = segmentNumber;
PlsrSegmentClockStarted = 0U;
PlsrSegmentElapsedMs = 0UL;
PlsrWaitElapsedMs = 0UL;
PlsrBoundaryRampStarted = 0U;
PlsrBoundaryPending = 0U;
PlsrBoundaryWasCut = 0U;
PlsrCutRequested = 0U;
PlsrStopPulsesRemaining = 0U;
PlsrAbStopArmed = 0U;
PlsrFrequencyUpdatePending = 0U;
PlsrDeferredFrequencyPending = 0U;
PlsrCurrentFrequencyHz = 0UL;
PlsrQueuedFrequencyHz = 0UL;
PlsrHandoffPlan.valid = 0U;
PlsrShortProfile.active = 0U;
PlsrShortProfile.nextPeriod = 0U;
PlsrDirectionDelayActive = 0U;
PlsrDirectionDelayRemainingMs = 0U;
PlsrExtEdgePending = 0U;
PlsrRemainingPulses = magnitude;
PlsrCountPositive = positive;
PlsrTimedStart.pendingActivation = 1U;
PlsrPlatformExitCritical(criticalState);

PlsrExtPreviousLevel =
PlsrPlatformReadInput((uint8_t)PlsrActiveConfig.extInput);
if (PlsrPlatformPrepare((uint8_t)PlsrActiveConfig.pulseOutput,
(uint8_t)PlsrActiveConfig.directionOutput,
directionLevel,
(uint8_t)PlsrActiveConfig.outputMode,
positive) == 0U)
{
criticalState = PlsrPlatformEnterCritical();
PlsrInvalidateTimedStartLocked();
PlsrPlatformExitCritical(criticalState);
return PLSR_PLATFORM_SERVICE_FAILED;
}
PlsrDiagnosticBeginSegment(segmentNumber, magnitude, positive);
PlsrLastDirectionValid = 1U;
PlsrLastDirectionOutput = (uint8_t)PlsrActiveConfig.directionOutput;
PlsrLastDirectionLevel = directionLevel;

if ((directionChanged != 0U)
&& (PlsrActiveConfig.directionDelayMs != 0U))
{
PlsrDirectionDelayActive = 1U;
PlsrDirectionDelayRemainingMs = PlsrActiveConfig.directionDelayMs;
PlsrRunStatus = PLSR_STATUS_ACCELERATING;
return PLSR_PLATFORM_SERVICE_READY;
}
if (PlsrStartPreparedTimedOutput() == 0U)
{
return PLSR_PLATFORM_SERVICE_FAILED;
}
return PLSR_PLATFORM_SERVICE_READY;
}


static uint8_t PlsrBeginSegmentOutput(uint32_t startFrequencyHz)
{
@@ -2655,17 +3139,23 @@ static uint8_t PlsrBeginSegmentOutput(uint32_t startFrequencyHz)
uint32_t secondFrequencyHz;
uint32_t actualFrequencyHz;
uint32_t profileEpoch;
uint64_t remainingPulses;
uint8_t profileSegment;
uint8_t profileHandoffBank;
uint8_t countPositive;
uint16_t fillBudget = PLSR_PROFILE_STARTUP_BUDGET;
PLSR_PROFILE_ENTRY firstRun;

PlsrSegmentClockStarted = 1U;
PlsrSegmentElapsedMs = 0UL;
profileSegment = PlsrCurrentSegment;
countPositive = PlsrCountPositive;
remainingPulses = PlsrRemainingSnapshot();
if (PlsrPrepareShortProfile(&PlsrShortProfile,
PlsrCurrentSegment, startFrequencyHz,
profileSegment, startFrequencyHz,
targetFrequencyHz,
PlsrRemainingSnapshot()) != 0U)
remainingPulses,
countPositive) != 0U)
{
PlsrRamp.active = 0U;
PlsrRefreshCurrentHandoffPlan(PlsrShortProfile.endHz);
@@ -2676,7 +3166,6 @@ static uint8_t PlsrBeginSegmentOutput(uint32_t startFrequencyHz)
firstFrequencyHz = firstRun.setting.actualFrequencyHz;
secondFrequencyHz = firstFrequencyHz;
profileEpoch = PlsrSegmentEpoch;
profileSegment = PlsrCurrentSegment;
profileHandoffBank = PlsrPreparedHandoffBank;
(void)PlsrProfileQueueBegin(&PlsrShortProfile,
profileEpoch,
@@ -3071,6 +3560,7 @@ static void PlsrTransitionToNext(uint8_t allowCarry)
{
uint8_t nextSegment;
uint32_t carryFrequencyHz = PlsrBoundaryFrequencyHz;
PLSR_PLATFORM_SERVICE_RESULT preparedResult;

if (PlsrGetNextSegment(&nextSegment) == 0U)
{
@@ -3078,6 +3568,20 @@ static void PlsrTransitionToNext(uint8_t allowCarry)
return;
}

if (allowCarry == 0U)
{
preparedResult = PlsrTryStartPreparedTimedSegment(nextSegment);
if (preparedResult == PLSR_PLATFORM_SERVICE_READY)
{
return;
}
if (preparedResult == PLSR_PLATFORM_SERVICE_FAILED)
{
PlsrEnterError(PLSR_ERROR_INVALID_RESOURCE);
return;
}
}

if (PlsrStartSegment(nextSegment, allowCarry, carryFrequencyHz) == 0U)
{
PlsrEnterError(PLSR_ERROR_INVALID_RESOURCE);
@@ -3147,7 +3651,7 @@ static uint8_t PlsrBuildHandoffPlan(uint8_t sourceSegment,
PlsrResolvedSegmentFrequency(frequencyConfig, nextSegment - 1U);
if (PlsrPrepareShortProfile(&plan->profile, nextSegment,
carryFrequencyHz, nextFrequencyHz,
plan->magnitude) == 0U)
plan->magnitude, positive) == 0U)
{
return 0U;
}
@@ -3960,7 +4464,9 @@ static PLSR_PLATFORM_QUEUE_RESULT PlsrTryContinuousHandoff(void)
static void PlsrHandleBoundary(uint8_t extEdge)
{
const PLSR_SEGMENT_CONFIG *segment;
uint32_t criticalState;
uint8_t wasCut = PlsrBoundaryWasCut;
uint8_t preserveTimedStart;

PlsrPlatformStopPulse((uint8_t)PlsrActiveConfig.pulseOutput);
PlsrBoundaryPending = 0U;
@@ -3969,7 +4475,23 @@ static void PlsrHandleBoundary(uint8_t extEdge)
PlsrCurrentFrequencyHz = 0UL;
PlsrQueuedFrequencyHz = 0UL;
PlsrShortProfile.active = 0U;
PlsrProfileQueueReset();
criticalState = PlsrPlatformEnterCritical();
preserveTimedStart =
(((PlsrTimedStart.valid != 0U)
|| (PlsrTimedStart.building != 0U))
&& (PlsrTimedStart.sourceEpoch == PlsrSegmentEpoch)
&& (PlsrTimedStart.sourceSegment == PlsrCurrentSegment)
&& (PlsrTimedStart.queueBank != PlsrProfileQueueBank)) ? 1U : 0U;
if (preserveTimedStart != 0U)
{
PlsrProfileQueueResetBankLocked(PlsrProfileQueueBank);
PlsrProfileQueueLowWaterLatched = 0U;
}
PlsrPlatformExitCritical(criticalState);
if (preserveTimedStart == 0U)
{
PlsrProfileQueueReset();
}
PlsrHandoffPlan.valid = 0U;
PlsrDeferredFrequencyPending = 0U;
PlsrAbStopArmed = 0U;
@@ -4017,7 +4539,14 @@ static void PlsrHandleBoundary(uint8_t extEdge)
{
case PLSR_WAIT_TIME:
PlsrWaitElapsedMs = 0UL;
PlsrRunStatus = PLSR_STATUS_WAITING;
if (segment->waitTimeMs == 0UL)
{
PlsrTransitionToNext(0U);
}
else
{
PlsrRunStatus = PLSR_STATUS_WAITING;
}
break;

case PLSR_WAIT_SIGNAL:
@@ -4690,6 +5219,10 @@ void PlsrPoll1ms(void)
return;
}

/* This work is bounded and targets only the inactive bank. It is done
after servicing the live queue so preparation cannot starve output. */
PlsrServiceTimedStartPreparation();

criticalState = PlsrPlatformEnterCritical();
pollEpoch = PlsrSegmentEpoch;
extLevel = PlsrPlatformReadInput((uint8_t)PlsrActiveConfig.extInput);
@@ -4734,10 +5267,17 @@ void PlsrPoll1ms(void)
if (PlsrDirectionDelayRemainingMs == 0U)
{
PlsrDirectionDelayActive = 0U;
if (PlsrBeginSegmentOutput(PlsrEffectiveStartFrequency(
PlsrActiveConfig.segments[PlsrCurrentSegment - 1U]
.frequencyHz,
0U, 1U, 0UL)) == 0U)
if (PlsrTimedStart.pendingActivation != 0U)
{
if (PlsrStartPreparedTimedOutput() == 0U)
{
PlsrEnterError(PLSR_ERROR_TIMER);
}
}
else if (PlsrBeginSegmentOutput(PlsrEffectiveStartFrequency(
PlsrActiveConfig.segments[PlsrCurrentSegment - 1U]
.frequencyHz,
0U, 1U, 0UL)) == 0U)
{
PlsrEnterError(PLSR_ERROR_TIMER);
}
@@ -5408,6 +5948,7 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress,
PlsrShadowConfig = PlsrCandidateConfig;
if (updateActiveFrequencies != 0U)
{
PlsrInvalidateTimedStartLocked();
for (index = 0U; index < PlsrActiveConfig.segmentCount; index++)
{
uint8_t frequencyChanged =


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