#include "plsr_profile.h" #include #include /* 正弦表: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; } static uint32_t PlsrProfileSqrtU64(uint64_t value) { uint64_t bit = UINT64_C(1) << 62U; uint64_t root = 0UL; while (bit > value) { bit >>= 2U; } while (bit != 0UL) { if (value >= root + bit) { value -= root + bit; root = (root >> 1U) + bit; } else { root >>= 1U; } bit >>= 2U; } return (uint32_t)root; } /* 每刷新步的斜率增量(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 PlsrProfileRequestStop(PLSR_PROFILE_STATE *state) { if ((state == NULL) || (state->started == 0U)) { return PLSR_RESULT_INVALID_ARGUMENT; } state->stopFrequencyHz = 0U; state->decelTargetHz = 0U; if (state->decelSlopeHzPerMs == 0UL) { state->frequencyQ32 = 0UL; state->phase = PLSR_PROFILE_PHASE_DONE; } else { 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; } /* 虚拟发射计数校准到硬件实际计数。 * ARPE 预装载使硬件频率切换滞后于 profile 理想频率(约一个周期), * 不校准会导致虚拟计数提前到达 totalPulses:profile DONE 时硬件仍欠发, * PWM 以 DONE 瞬间的冻结频率补发剩余脉冲(段尾低频平台/宽脉冲)。 * 每刷新周期由 core 在 Step 前调用,偏差不跨周期累积。 */ void PlsrProfileSyncPulses(PLSR_PROFILE_STATE *state, uint64_t hwPulses) { if (state == NULL) { return; } state->emittedPulsesQ32 = hwPulses << 32U; } uint32_t PlsrProfileGetInitialOutputFrequency( const PLSR_PROFILE_STATE *state) { uint64_t accelerationHzPerSecond; uint64_t pulseDiscriminant; uint64_t targetSquare; uint64_t currentSquare; uint64_t denominator; uint32_t currentHz; uint32_t endHz; uint32_t result; if (state == NULL) { return 0UL; } currentHz = (uint32_t)(state->frequencyQ32 >> 32U); if ((state->phase != PLSR_PROFILE_PHASE_ACCEL) || (state->curveMode != PLSR_PROFILE_CURVE_LINEAR) || (state->accelSlopeHzPerMs == 0UL) || (currentHz >= state->targetFrequencyHz)) { return currentHz; } accelerationHzPerSecond = (uint64_t)state->accelSlopeHzPerMs * UINT64_C(1000); currentSquare = (uint64_t)currentHz * currentHz; targetSquare = (uint64_t)state->targetFrequencyHz * state->targetFrequencyHz; pulseDiscriminant = currentSquare + UINT64_C(2) * accelerationHzPerSecond; if (pulseDiscriminant <= targetSquare) { /* 整个首脉冲都处于线性加速段:f1^2=f0^2+2a。 */ endHz = PlsrProfileSqrtU64(pulseDiscriminant); result = (currentHz + endHz) / 2UL; } else { /* 不到一个脉冲就到达目标频率:剩余相位按目标频率运行。 */ uint64_t gap = (uint64_t)state->targetFrequencyHz - currentHz; denominator = gap * gap + UINT64_C(2) * accelerationHzPerSecond; result = (uint32_t)((UINT64_C(2) * accelerationHzPerSecond * state->targetFrequencyHz) / denominator); } if (result == 0UL) { result = 1UL; } if (result > state->targetFrequencyHz) { result = state->targetFrequencyHz; } return result; } uint32_t PlsrProfileGetBrakingOutputFrequency( const PLSR_PROFILE_STATE *state, uint64_t pulsesRemaining) { uint64_t decelerationHzPerSecond; uint64_t twiceDeceleration; uint64_t stopSquare; uint64_t targetSquare; uint64_t brakingSquare; uint64_t brakingPulses; uint64_t startSquare; uint64_t endSquare; uint32_t startHz; uint32_t endHz; uint32_t result; if ((state == NULL) || (pulsesRemaining == 0UL)) { return 0UL; } if ((state->curveMode != PLSR_PROFILE_CURVE_LINEAR) || (state->decelSlopeHzPerMs == 0UL)) { return state->targetFrequencyHz; } decelerationHzPerSecond = (uint64_t)state->decelSlopeHzPerMs * UINT64_C(1000); twiceDeceleration = UINT64_C(2) * decelerationHzPerSecond; stopSquare = (uint64_t)state->stopFrequencyHz * state->stopFrequencyHz; targetSquare = (uint64_t)state->targetFrequencyHz * state->targetFrequencyHz; if (targetSquare <= stopSquare) { return state->targetFrequencyHz; } /* 先比较制动距离,再做乘法。这样超长路径直接保持目标频率, * 同时避免 2*a*n 在极端脉冲数下发生 uint64_t 溢出。 */ brakingSquare = targetSquare - stopSquare; brakingPulses = brakingSquare / twiceDeceleration; if ((brakingSquare % twiceDeceleration) != 0UL) { brakingPulses++; } if (pulsesRemaining > brakingPulses) { return state->targetFrequencyHz; } startSquare = stopSquare + twiceDeceleration * pulsesRemaining; endSquare = stopSquare + twiceDeceleration * (pulsesRemaining - 1UL); startHz = PlsrProfileSqrtU64(startSquare); endHz = PlsrProfileSqrtU64(endSquare); result = (startHz + endHz) / 2UL; if (result < state->stopFrequencyHz) { result = state->stopFrequencyHz; } if (result > state->targetFrequencyHz) { result = state->targetFrequencyHz; } if (result == 0UL) { result = 1UL; } return result; }