|
|
@@ -0,0 +1,531 @@ |
|
|
|
|
|
#include "plsr_profile.h" |
|
|
|
|
|
#include <math.h> |
|
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
|
|
/* 正弦表:256 项,sin(pi*i/256) 的 Q16 表示。 */ |
|
|
|
|
|
static const uint16_t PlsrProfileSineTable[256] = |
|
|
|
|
|
{ |
|
|
|
|
|
0, 804, 1608, 2412, 3216, 4019, 4821, 5623, |
|
|
|
|
|
6424, 7224, 8022, 8820, 9616, 10411, 11204, 11996, |
|
|
|
|
|
12785, 13573, 14359, 15143, 15924, 16703, 17479, 18253, |
|
|
|
|
|
19024, 19792, 20557, 21320, 22078, 22834, 23586, 24335, |
|
|
|
|
|
25080, 25821, 26558, 27291, 28020, 28745, 29466, 30182, |
|
|
|
|
|
30893, 31600, 32303, 33000, 33692, 34380, 35062, 35738, |
|
|
|
|
|
36410, 37076, 37736, 38391, 39040, 39683, 40320, 40951, |
|
|
|
|
|
41576, 42194, 42806, 43412, 44011, 44604, 45190, 45769, |
|
|
|
|
|
46341, 46906, 47464, 48015, 48559, 49095, 49624, 50146, |
|
|
|
|
|
50660, 51166, 51665, 52156, 52639, 53114, 53581, 54040, |
|
|
|
|
|
54491, 54934, 55368, 55794, 56212, 56621, 57022, 57414, |
|
|
|
|
|
57798, 58172, 58538, 58896, 59244, 59583, 59914, 60235, |
|
|
|
|
|
60547, 60851, 61145, 61429, 61705, 61971, 62228, 62476, |
|
|
|
|
|
62714, 62943, 63162, 63372, 63572, 63763, 63944, 64115, |
|
|
|
|
|
64277, 64429, 64571, 64704, 64827, 64940, 65043, 65137, |
|
|
|
|
|
65220, 65294, 65358, 65413, 65457, 65492, 65516, 65531, |
|
|
|
|
|
65535, 65531, 65516, 65492, 65457, 65413, 65358, 65294, |
|
|
|
|
|
65220, 65137, 65043, 64940, 64827, 64704, 64571, 64429, |
|
|
|
|
|
64277, 64115, 63944, 63763, 63572, 63372, 63162, 62943, |
|
|
|
|
|
62714, 62476, 62228, 61971, 61705, 61429, 61145, 60851, |
|
|
|
|
|
60547, 60235, 59914, 59583, 59244, 58896, 58538, 58172, |
|
|
|
|
|
57798, 57414, 57022, 56621, 56212, 55794, 55368, 54934, |
|
|
|
|
|
54491, 54040, 53581, 53114, 52639, 52156, 51665, 51166, |
|
|
|
|
|
50660, 50146, 49624, 49095, 48559, 48015, 47464, 46906, |
|
|
|
|
|
46341, 45769, 45190, 44604, 44011, 43412, 42806, 42194, |
|
|
|
|
|
41576, 40951, 40320, 39683, 39040, 38391, 37736, 37076, |
|
|
|
|
|
36410, 35738, 35062, 34380, 33692, 33000, 32303, 31600, |
|
|
|
|
|
30893, 30182, 29466, 28745, 28020, 27291, 26558, 25821, |
|
|
|
|
|
25080, 24335, 23586, 22834, 22078, 21320, 20557, 19792, |
|
|
|
|
|
19024, 18253, 17479, 16703, 15924, 15143, 14359, 13573, |
|
|
|
|
|
12785, 11996, 11204, 10411, 9616, 8820, 8022, 7224, |
|
|
|
|
|
6424, 5623, 4821, 4019, 3216, 2412, 1608, 804, |
|
|
|
|
|
}; |
|
|
|
|
|
static uint64_t PlsrProfileHzToQ32(uint32_t hz) |
|
|
|
|
|
{ |
|
|
|
|
|
return (uint64_t)hz << 32U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 每刷新步的斜率增量(Q32.32),支持 0.1ms 刷新(1000/refreshHz 可能 < 1)。 */ |
|
|
|
|
|
static uint64_t PlsrProfileSlopePerStepQ32(uint32_t slopeHzPerMs, |
|
|
|
|
|
uint32_t refreshHz) |
|
|
|
|
|
{ |
|
|
|
|
|
return PlsrProfileHzToQ32(slopeHzPerMs) * UINT64_C(1000) |
|
|
|
|
|
/ (uint64_t)refreshHz; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 从当前频率减速到目标频率所需的脉冲数(Q32.32 精度)。 */ |
|
|
|
|
|
static uint64_t PlsrProfileDecelPulsesQ32(uint32_t frequencyHz, |
|
|
|
|
|
uint32_t targetHz, |
|
|
|
|
|
uint32_t slopeHzPerMs) |
|
|
|
|
|
{ |
|
|
|
|
|
uint64_t numerator; |
|
|
|
|
|
uint64_t denominator; |
|
|
|
|
|
|
|
|
|
|
|
if (frequencyHz <= targetHz) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0UL; |
|
|
|
|
|
} |
|
|
|
|
|
numerator = (uint64_t)frequencyHz * (uint64_t)frequencyHz |
|
|
|
|
|
- (uint64_t)targetHz * (uint64_t)targetHz; |
|
|
|
|
|
denominator = 2000UL * (uint64_t)slopeHzPerMs; |
|
|
|
|
|
if (denominator == 0UL) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0UL; |
|
|
|
|
|
} |
|
|
|
|
|
return (numerator / denominator) * PLSR_PROFILE_Q32_ONE |
|
|
|
|
|
+ ((numerator % denominator) * PLSR_PROFILE_Q32_ONE) |
|
|
|
|
|
/ denominator; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 直线增量初始化。 */ |
|
|
|
|
|
static void PlsrProfileInitLinearDelta(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
uint32_t slopeHzPerMs, |
|
|
|
|
|
uint32_t refreshHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->deltaPerStepQ32 = PlsrProfileSlopePerStepQ32(slopeHzPerMs, |
|
|
|
|
|
refreshHz); |
|
|
|
|
|
state->jerkPerStepQ32 = 0UL; |
|
|
|
|
|
state->curveStep = 0U; |
|
|
|
|
|
state->curveStepLimit = 0U; |
|
|
|
|
|
state->sPhase = 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* S 形/正弦增量初始化:增量先升后降(S形)或正弦半波。 */ |
|
|
|
|
|
static void PlsrProfileInitCurveDelta(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
uint32_t frequencyGapHz, |
|
|
|
|
|
uint32_t slopeHzPerMs, |
|
|
|
|
|
uint32_t refreshHz, |
|
|
|
|
|
uint8_t curveMode) |
|
|
|
|
|
{ |
|
|
|
|
|
uint64_t linearPerStep = PlsrProfileSlopePerStepQ32(slopeHzPerMs, |
|
|
|
|
|
refreshHz); |
|
|
|
|
|
uint64_t integerPerStepHz; |
|
|
|
|
|
uint64_t rampSteps; |
|
|
|
|
|
uint64_t jerkQ32; |
|
|
|
|
|
|
|
|
|
|
|
integerPerStepHz = ((uint64_t)slopeHzPerMs * UINT64_C(1000)) |
|
|
|
|
|
/ (uint64_t)refreshHz; |
|
|
|
|
|
if (integerPerStepHz == 0UL) |
|
|
|
|
|
{ |
|
|
|
|
|
integerPerStepHz = 1UL; |
|
|
|
|
|
} |
|
|
|
|
|
rampSteps = ((uint64_t)frequencyGapHz + integerPerStepHz - 1UL) |
|
|
|
|
|
/ integerPerStepHz; |
|
|
|
|
|
|
|
|
|
|
|
if (curveMode == PLSR_PROFILE_CURVE_SINE) |
|
|
|
|
|
{ |
|
|
|
|
|
/* 正弦:时间同直线,峰值增量 = 直线增量 * pi/2(面积匹配)。 */ |
|
|
|
|
|
state->deltaPerStepQ32 = 0UL; |
|
|
|
|
|
state->jerkPerStepQ32 = 0UL; |
|
|
|
|
|
state->curveStep = 0U; |
|
|
|
|
|
state->curveStepLimit = (uint32_t)rampSteps; |
|
|
|
|
|
state->sPhase = 0U; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* S 形:增量 0→峰值→0,总时间 2 倍直线时间。 */ |
|
|
|
|
|
jerkQ32 = linearPerStep / rampSteps; |
|
|
|
|
|
state->deltaPerStepQ32 = 0UL; |
|
|
|
|
|
state->jerkPerStepQ32 = jerkQ32; |
|
|
|
|
|
state->curveStep = 0U; |
|
|
|
|
|
state->curveStepLimit = (uint32_t)(rampSteps * 2UL); |
|
|
|
|
|
state->sPhase = 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 进入加速段(直线或曲线)。 */ |
|
|
|
|
|
static void PlsrProfileBeginAccel(PLSR_PROFILE_STATE *state) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t gapHz; |
|
|
|
|
|
|
|
|
|
|
|
if (state->targetFrequencyHz <= state->frequencyQ32 >> 32U) |
|
|
|
|
|
{ |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (state->curveMode == PLSR_PROFILE_CURVE_LINEAR) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileInitLinearDelta(state, |
|
|
|
|
|
state->accelSlopeHzPerMs, |
|
|
|
|
|
state->refreshHz); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
gapHz = state->targetFrequencyHz |
|
|
|
|
|
- (uint32_t)(state->frequencyQ32 >> 32U); |
|
|
|
|
|
PlsrProfileInitCurveDelta(state, |
|
|
|
|
|
gapHz, |
|
|
|
|
|
state->accelSlopeHzPerMs, |
|
|
|
|
|
state->refreshHz, |
|
|
|
|
|
state->curveMode); |
|
|
|
|
|
} |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_ACCEL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 进入减速段:减速到 decelTargetHz(默认=终止速度,降频时=新目标)。 */ |
|
|
|
|
|
static void PlsrProfileBeginDecel(PLSR_PROFILE_STATE *state) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t gapHz; |
|
|
|
|
|
|
|
|
|
|
|
if (state->frequencyQ32 >> 32U <= state->decelTargetHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->frequencyQ32 = PlsrProfileHzToQ32(state->decelTargetHz); |
|
|
|
|
|
if (state->decelTargetHz == state->stopFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_DONE; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
/* 降频到新目标:转匀速继续。 */ |
|
|
|
|
|
state->targetFrequencyHz = state->decelTargetHz; |
|
|
|
|
|
state->decelTargetHz = state->stopFrequencyHz; |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (state->curveMode == PLSR_PROFILE_CURVE_LINEAR) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileInitLinearDelta(state, |
|
|
|
|
|
state->decelSlopeHzPerMs, |
|
|
|
|
|
state->refreshHz); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
gapHz = (uint32_t)(state->frequencyQ32 >> 32U) |
|
|
|
|
|
- state->decelTargetHz; |
|
|
|
|
|
PlsrProfileInitCurveDelta(state, |
|
|
|
|
|
gapHz, |
|
|
|
|
|
state->decelSlopeHzPerMs, |
|
|
|
|
|
state->refreshHz, |
|
|
|
|
|
state->curveMode); |
|
|
|
|
|
} |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_DECEL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 曲线模式下推进增量(S形三角增量 / 正弦查表)。 */ |
|
|
|
|
|
static uint64_t PlsrProfileAdvanceDelta(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
uint64_t linearPerStepQ32) |
|
|
|
|
|
{ |
|
|
|
|
|
uint64_t delta; |
|
|
|
|
|
|
|
|
|
|
|
if (state->curveMode == PLSR_PROFILE_CURVE_SINE) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t index = ((uint32_t)state->curveStep * 256U) |
|
|
|
|
|
/ (uint32_t)state->curveStepLimit; |
|
|
|
|
|
|
|
|
|
|
|
if (index > 255U) |
|
|
|
|
|
{ |
|
|
|
|
|
index = 255U; |
|
|
|
|
|
} |
|
|
|
|
|
delta = ((uint64_t)PlsrProfileSineTable[index] |
|
|
|
|
|
* linearPerStepQ32) / 65536UL; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
if (state->sPhase == 0U) |
|
|
|
|
|
{ |
|
|
|
|
|
state->deltaPerStepQ32 += state->jerkPerStepQ32; |
|
|
|
|
|
if ((state->curveStepLimit != 0U) |
|
|
|
|
|
&& (state->curveStep >= state->curveStepLimit / 2U)) |
|
|
|
|
|
{ |
|
|
|
|
|
state->sPhase = 1U; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
if (state->deltaPerStepQ32 > state->jerkPerStepQ32) |
|
|
|
|
|
{ |
|
|
|
|
|
state->deltaPerStepQ32 -= state->jerkPerStepQ32; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
state->deltaPerStepQ32 = 0UL; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
delta = state->deltaPerStepQ32; |
|
|
|
|
|
} |
|
|
|
|
|
state->curveStep++; |
|
|
|
|
|
return delta; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PLSR_RESULT PlsrProfileStart(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
const PLSR_PROFILE_REQUEST *request, |
|
|
|
|
|
int64_t pulses, |
|
|
|
|
|
uint32_t refreshHz) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t startHz; |
|
|
|
|
|
|
|
|
|
|
|
if ((state == NULL) || (request == NULL)) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
if ((pulses <= 0) || (refreshHz == 0UL) |
|
|
|
|
|
|| (request->targetFrequencyHz == 0UL)) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
state->refreshHz = refreshHz; |
|
|
|
|
|
state->totalPulses = pulses; |
|
|
|
|
|
state->targetFrequencyHz = request->targetFrequencyHz; |
|
|
|
|
|
if (request->targetFrequencyHz > request->maxFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->targetFrequencyHz = request->maxFrequencyHz; |
|
|
|
|
|
} |
|
|
|
|
|
state->startFrequencyHz = request->startFrequencyHz; |
|
|
|
|
|
state->stopFrequencyHz = request->stopFrequencyHz; |
|
|
|
|
|
state->decelTargetHz = request->stopFrequencyHz; |
|
|
|
|
|
state->accelSlopeHzPerMs = request->accelSlopeHzPerMs; |
|
|
|
|
|
state->decelSlopeHzPerMs = request->decelSlopeHzPerMs; |
|
|
|
|
|
state->curveMode = request->curveMode; |
|
|
|
|
|
startHz = request->startFrequencyHz; |
|
|
|
|
|
if (startHz > state->targetFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
startHz = state->targetFrequencyHz; |
|
|
|
|
|
} |
|
|
|
|
|
state->frequencyQ32 = PlsrProfileHzToQ32(startHz); |
|
|
|
|
|
state->emittedPulsesQ32 = 0UL; |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_ACCEL; |
|
|
|
|
|
state->started = 1U; |
|
|
|
|
|
|
|
|
|
|
|
if (startHz >= state->targetFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
} |
|
|
|
|
|
else if (request->accelSlopeHzPerMs == 0UL) |
|
|
|
|
|
{ |
|
|
|
|
|
state->frequencyQ32 = PlsrProfileHzToQ32(state->targetFrequencyHz); |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileBeginAccel(state); |
|
|
|
|
|
} |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PLSR_RESULT PlsrProfileStep(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
uint32_t *frequencyHz, |
|
|
|
|
|
uint8_t *completed) |
|
|
|
|
|
{ |
|
|
|
|
|
uint64_t emittedThisStep; |
|
|
|
|
|
uint64_t remainingQ32; |
|
|
|
|
|
uint64_t decelNeeded; |
|
|
|
|
|
uint64_t linearPerStepQ32; |
|
|
|
|
|
uint64_t deltaQ32; |
|
|
|
|
|
uint32_t currentHz; |
|
|
|
|
|
|
|
|
|
|
|
if ((state == NULL) || (frequencyHz == NULL) || (completed == NULL)) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
*completed = 0U; |
|
|
|
|
|
if (state->phase == PLSR_PROFILE_PHASE_DONE) |
|
|
|
|
|
{ |
|
|
|
|
|
*frequencyHz = 0U; |
|
|
|
|
|
*completed = 1U; |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
linearPerStepQ32 = PlsrProfileSlopePerStepQ32( |
|
|
|
|
|
(state->phase == PLSR_PROFILE_PHASE_ACCEL) |
|
|
|
|
|
? state->accelSlopeHzPerMs |
|
|
|
|
|
: state->decelSlopeHzPerMs, |
|
|
|
|
|
state->refreshHz); |
|
|
|
|
|
|
|
|
|
|
|
switch (state->phase) |
|
|
|
|
|
{ |
|
|
|
|
|
case PLSR_PROFILE_PHASE_ACCEL: |
|
|
|
|
|
deltaQ32 = PlsrProfileAdvanceDelta(state, linearPerStepQ32); |
|
|
|
|
|
state->frequencyQ32 += deltaQ32; |
|
|
|
|
|
if ((state->frequencyQ32 |
|
|
|
|
|
>= PlsrProfileHzToQ32(state->targetFrequencyHz)) |
|
|
|
|
|
|| ((state->curveMode != PLSR_PROFILE_CURVE_LINEAR) |
|
|
|
|
|
&& (state->curveStep >= state->curveStepLimit))) |
|
|
|
|
|
{ |
|
|
|
|
|
state->frequencyQ32 = |
|
|
|
|
|
PlsrProfileHzToQ32(state->targetFrequencyHz); |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
case PLSR_PROFILE_PHASE_CRUISE: |
|
|
|
|
|
/* 剩余脉冲不足以按当前频率完成减速时开始减速(Q32 精度)。 */ |
|
|
|
|
|
remainingQ32 = PlsrProfileHzToQ32( |
|
|
|
|
|
(uint32_t)state->totalPulses) |
|
|
|
|
|
- state->emittedPulsesQ32; |
|
|
|
|
|
decelNeeded = PlsrProfileDecelPulsesQ32( |
|
|
|
|
|
(uint32_t)(state->frequencyQ32 >> 32U), |
|
|
|
|
|
state->stopFrequencyHz, |
|
|
|
|
|
state->decelSlopeHzPerMs); |
|
|
|
|
|
if (remainingQ32 <= decelNeeded) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileBeginDecel(state); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
case PLSR_PROFILE_PHASE_DECEL: |
|
|
|
|
|
deltaQ32 = PlsrProfileAdvanceDelta(state, linearPerStepQ32); |
|
|
|
|
|
state->frequencyQ32 -= deltaQ32; |
|
|
|
|
|
if (state->frequencyQ32 |
|
|
|
|
|
<= PlsrProfileHzToQ32(state->decelTargetHz)) |
|
|
|
|
|
{ |
|
|
|
|
|
state->frequencyQ32 = |
|
|
|
|
|
PlsrProfileHzToQ32(state->decelTargetHz); |
|
|
|
|
|
if (state->decelTargetHz == state->stopFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_DONE; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
/* 降频到新目标:转匀速继续。 */ |
|
|
|
|
|
state->targetFrequencyHz = state->decelTargetHz; |
|
|
|
|
|
state->decelTargetHz = state->stopFrequencyHz; |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_CRUISE; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* 发射本步脉冲:频率(Hz) / 刷新率。 */ |
|
|
|
|
|
currentHz = (uint32_t)(state->frequencyQ32 >> 32U); |
|
|
|
|
|
emittedThisStep = state->frequencyQ32 / (uint64_t)state->refreshHz; |
|
|
|
|
|
if ((state->emittedPulsesQ32 + emittedThisStep) |
|
|
|
|
|
>= PlsrProfileHzToQ32((uint32_t)state->totalPulses)) |
|
|
|
|
|
{ |
|
|
|
|
|
state->emittedPulsesQ32 = |
|
|
|
|
|
PlsrProfileHzToQ32((uint32_t)state->totalPulses); |
|
|
|
|
|
state->phase = PLSR_PROFILE_PHASE_DONE; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
state->emittedPulsesQ32 += emittedThisStep; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
*frequencyHz = currentHz; |
|
|
|
|
|
if (state->phase == PLSR_PROFILE_PHASE_DONE) |
|
|
|
|
|
{ |
|
|
|
|
|
*completed = 1U; |
|
|
|
|
|
*frequencyHz = 0U; |
|
|
|
|
|
} |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PLSR_RESULT PlsrProfileRetarget(PLSR_PROFILE_STATE *state, |
|
|
|
|
|
uint32_t newTargetFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t currentHz; |
|
|
|
|
|
|
|
|
|
|
|
if (state == NULL) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
if (newTargetFrequencyHz == 0UL) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
currentHz = (uint32_t)(state->frequencyQ32 >> 32U); |
|
|
|
|
|
if (newTargetFrequencyHz == state->targetFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |
|
|
|
|
|
state->targetFrequencyHz = newTargetFrequencyHz; |
|
|
|
|
|
|
|
|
|
|
|
if (newTargetFrequencyHz > currentHz) |
|
|
|
|
|
{ |
|
|
|
|
|
/* 升频:加速/匀速 → 加速;减速中 → 取消减速转加速。 */ |
|
|
|
|
|
if ((state->phase == PLSR_PROFILE_PHASE_DECEL) |
|
|
|
|
|
|| (state->phase == PLSR_PROFILE_PHASE_CRUISE) |
|
|
|
|
|
|| (state->phase == PLSR_PROFILE_PHASE_DONE)) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileBeginAccel(state); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
/* 降频:先减速到新目标,再转匀速继续(最终仍减速到终止速度)。 */ |
|
|
|
|
|
if ((state->phase == PLSR_PROFILE_PHASE_ACCEL) |
|
|
|
|
|
|| (state->phase == PLSR_PROFILE_PHASE_CRUISE)) |
|
|
|
|
|
{ |
|
|
|
|
|
state->decelTargetHz = newTargetFrequencyHz; |
|
|
|
|
|
PlsrProfileBeginDecel(state); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PLSR_RESULT PlsrProfilePlan(const PLSR_PROFILE_REQUEST *request, |
|
|
|
|
|
int64_t pulses, |
|
|
|
|
|
PLSR_PROFILE_PLAN *plan) |
|
|
|
|
|
{ |
|
|
|
|
|
double target; |
|
|
|
|
|
double start; |
|
|
|
|
|
double stop; |
|
|
|
|
|
double accelSlope; |
|
|
|
|
|
double decelSlope; |
|
|
|
|
|
double accelTime; |
|
|
|
|
|
double decelTime; |
|
|
|
|
|
double accelPulses; |
|
|
|
|
|
double decelPulses; |
|
|
|
|
|
double cruisePulses; |
|
|
|
|
|
double cruiseTime; |
|
|
|
|
|
double peak; |
|
|
|
|
|
|
|
|
|
|
|
if ((request == NULL) || (plan == NULL) || (pulses <= 0)) |
|
|
|
|
|
{ |
|
|
|
|
|
return PLSR_RESULT_INVALID_ARGUMENT; |
|
|
|
|
|
} |
|
|
|
|
|
(void)memset(plan, 0, sizeof(*plan)); |
|
|
|
|
|
|
|
|
|
|
|
target = (double)request->targetFrequencyHz; |
|
|
|
|
|
if (target > (double)request->maxFrequencyHz) |
|
|
|
|
|
{ |
|
|
|
|
|
target = (double)request->maxFrequencyHz; |
|
|
|
|
|
} |
|
|
|
|
|
start = (double)request->startFrequencyHz; |
|
|
|
|
|
if (start > target) |
|
|
|
|
|
{ |
|
|
|
|
|
start = target; |
|
|
|
|
|
} |
|
|
|
|
|
stop = (double)request->stopFrequencyHz; |
|
|
|
|
|
accelSlope = (double)request->accelSlopeHzPerMs; |
|
|
|
|
|
decelSlope = (double)request->decelSlopeHzPerMs; |
|
|
|
|
|
|
|
|
|
|
|
accelTime = (target > start) ? (target - start) / accelSlope : 0.0; |
|
|
|
|
|
decelTime = (target > stop) ? (target - stop) / decelSlope : 0.0; |
|
|
|
|
|
accelPulses = (start + target) * accelTime / 2000.0; |
|
|
|
|
|
decelPulses = (target + stop) * decelTime / 2000.0; |
|
|
|
|
|
|
|
|
|
|
|
if ((accelPulses + decelPulses) >= (double)pulses) |
|
|
|
|
|
{ |
|
|
|
|
|
/* 三角曲线:求峰值频率。 */ |
|
|
|
|
|
double sum = (double)pulses * 1000.0 |
|
|
|
|
|
+ start * start / (2.0 * accelSlope) |
|
|
|
|
|
+ stop * stop / (2.0 * decelSlope); |
|
|
|
|
|
double denom = 1.0 / (2.0 * accelSlope) |
|
|
|
|
|
+ 1.0 / (2.0 * decelSlope); |
|
|
|
|
|
|
|
|
|
|
|
peak = (denom > 0.0) ? sqrt(sum / denom) : target; |
|
|
|
|
|
accelTime = (peak > start) ? (peak - start) / accelSlope : 0.0; |
|
|
|
|
|
decelTime = (peak > stop) ? (peak - stop) / decelSlope : 0.0; |
|
|
|
|
|
accelPulses = (start + peak) * accelTime / 2000.0; |
|
|
|
|
|
decelPulses = (peak + stop) * decelTime / 2000.0; |
|
|
|
|
|
plan->peakFrequencyHz = (uint32_t)peak; |
|
|
|
|
|
plan->triangular = 1U; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
cruisePulses = (double)pulses - accelPulses - decelPulses; |
|
|
|
|
|
cruiseTime = (target > 0.0) ? cruisePulses * 1000.0 / target : 0.0; |
|
|
|
|
|
plan->cruiseTimeMs = (uint32_t)cruiseTime; |
|
|
|
|
|
plan->peakFrequencyHz = (uint32_t)target; |
|
|
|
|
|
plan->triangular = 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
plan->accelTimeMs = (uint32_t)accelTime; |
|
|
|
|
|
plan->decelTimeMs = (uint32_t)decelTime; |
|
|
|
|
|
plan->totalTimeMs = plan->accelTimeMs + plan->cruiseTimeMs |
|
|
|
|
|
+ plan->decelTimeMs; |
|
|
|
|
|
plan->totalPulses = pulses; |
|
|
|
|
|
return PLSR_RESULT_OK; |
|
|
|
|
|
} |