diff --git a/.gitignore b/.gitignore index e6b0ab0..242a603 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ iar/settings/modbus.reggroups build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/ plsr/test/ tools/ +.cursor/ diff --git a/plsr/accel_curve/plsr_accel_curve.c b/plsr/accel_curve/plsr_accel_curve.c index 5cfc8ee..4dcc1c9 100644 --- a/plsr/accel_curve/plsr_accel_curve.c +++ b/plsr/accel_curve/plsr_accel_curve.c @@ -93,24 +93,156 @@ static uint32_t PlsrAccelCurveRampTimeMs(uint32_t f_from, return t; } +/** + * 与运行时相同:f(t)=f0+(f1-f0)*t/T(µs),t>=T 时为 f1。 + */ +static uint32_t PlsrAccelCurveFreqOnRampUs(uint32_t f0, + uint32_t f1, + uint32_t t_us, + uint32_t T_us) +{ + uint32_t r; + + if ((T_us == 0U) || (t_us >= T_us)) + { + return f1; + } + r = (uint32_t)(((uint64_t)t_us * 1000ULL) / (uint64_t)T_us); + if (r > 1000U) + { + r = 1000U; + } + if (f1 >= f0) + { + return f0 + ((f1 - f0) * r) / 1000UL; + } + return f0 - ((f0 - f1) * r) / 1000UL; +} + +/** + * 直线加减速、脉冲起点锁频时的精确脉冲数(与 ISR 累加方式一致): + * t=0 取 f0,周期 1/f,再取 f(t)……直到 t>=T。 + * 不用平均/加权经验公式,改频率后仍与运行时一致。 + */ static uint32_t PlsrAccelCurveEstimatePulses(uint32_t f_from, uint32_t f_to, uint32_t t_ms) { - uint32_t f_hi; + uint32_t T_us; + uint32_t elapsed_us; + uint32_t n; + uint32_t f; + uint32_t guard; if ((t_ms == 0U) || (f_from == f_to)) { return 0U; } - /* - * 必须用相内最高频估脉冲上限,不能用平均频。 - * 减速:起点就是 f_peak,若按 (f_peak+0)/2 估,实际前半段仍接近峰值, - * dec_n 会在 t_dec 走完前耗尽 → 段结束硬停,示波器上就是“速降”。 - * 加速:从低到高,用最高频偏保守,只会多留一点加速脉冲(到频后持平),安全。 - */ - f_hi = (f_from > f_to) ? f_from : f_to; - return (f_hi * t_ms + 999UL) / 1000UL; + + T_us = t_ms * 1000UL; + elapsed_us = 0U; + n = 0U; + guard = 0U; + + while ((elapsed_us < T_us) && (guard < 2000000UL)) + { + f = PlsrAccelCurveFreqOnRampUs(f_from, f_to, elapsed_us, T_us); + if (f < 1U) + { + f = 1U; + } + elapsed_us += (1000000UL + (f / 2UL)) / f; + n++; + guard++; + } + + return n; +} + +/** + * 拟合斜坡时间 T:使离散直线模型下的脉冲数尽量等于 max_pulses。 + * (旧逻辑取「Estimate<=max 的最大 T / 优先 t_prefer」,脉冲有余时会提前到终点再持平, + * 减速末端出现一段平肩/弯曲。) + */ +uint32_t PlsrAccelCurveFitRampTimeMs(uint32_t f_from, + uint32_t f_to, + uint32_t max_pulses, + uint32_t t_prefer_ms) +{ + uint32_t lo; + uint32_t hi; + uint32_t mid; + uint32_t best; + uint32_t best_err; + uint32_t n; + uint32_t err; + uint32_t guard; + + if ((max_pulses == 0U) || (f_from == f_to)) + { + return 0U; + } + + if (max_pulses <= 1U) + { + return (t_prefer_ms > 0U) ? t_prefer_ms : 1U; + } + + /* 放大上界直到脉冲数够到 max_pulses(或封顶) */ + hi = (t_prefer_ms > 0U) ? t_prefer_ms : 1U; + guard = 0U; + n = PlsrAccelCurveEstimatePulses(f_from, f_to, hi); + while ((n < max_pulses) && (hi < 120000U) && (guard < 24U)) + { + if (hi > (120000U / 2U)) + { + hi = 120000U; + } + else + { + hi = hi * 2U; + } + n = PlsrAccelCurveEstimatePulses(f_from, f_to, hi); + guard++; + } + + lo = 1U; + best = (t_prefer_ms > 0U) ? t_prefer_ms : hi; + n = PlsrAccelCurveEstimatePulses(f_from, f_to, best); + best_err = (n > max_pulses) ? (n - max_pulses) : (max_pulses - n); + + while (lo <= hi) + { + mid = lo + ((hi - lo) / 2UL); + n = PlsrAccelCurveEstimatePulses(f_from, f_to, mid); + err = (n > max_pulses) ? (n - max_pulses) : (max_pulses - n); + if ((err < best_err) || + ((err == best_err) && (t_prefer_ms > 0U) && + (((mid > t_prefer_ms) ? (mid - t_prefer_ms) : (t_prefer_ms - mid)) < + ((best > t_prefer_ms) ? (best - t_prefer_ms) : (t_prefer_ms - mid))))) + { + best = mid; + best_err = err; + } + if (n == max_pulses) + { + return mid; + } + if (n < max_pulses) + { + lo = mid + 1UL; + } + else + { + if (mid == 0U) + { + break; + } + hi = mid - 1UL; + } + } + + return best; } static uint32_t PlsrAccelCurveLerp(uint32_t a, uint32_t b, uint32_t ratio_permille) @@ -339,6 +471,23 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, return; } + /* + * 无独立减速终点(完成模式非末段:f_end == f_tgt): + * 脉冲不够时不 FitPeak、不减速,全程按原目标斜率加速; + * 脉冲用尽后由上层把当前频率链到下一段继续加。 + * 末段(f_end 为终止速度)不够时仍三角压峰。 + */ + if ((f_end == f_tgt) && (acc_n > total_pulses)) + { + plan->const_n = 0U; + plan->f_tgt = f_peak; + plan->t_acc_ms = t_acc; + plan->t_dec_ms = 0U; + plan->acc_n = total_pulses; + plan->dec_n = 0U; + return; + } + if ((acc_n + dec_n) > total_pulses) { /* diff --git a/plsr/accel_curve/plsr_accel_curve.h b/plsr/accel_curve/plsr_accel_curve.h index b9d12ce..853ee26 100644 --- a/plsr/accel_curve/plsr_accel_curve.h +++ b/plsr/accel_curve/plsr_accel_curve.h @@ -2,10 +2,8 @@ * @file plsr_accel_curve.h * @brief 预估加/匀/减脉冲;相内按时间走直线/S/正弦 * - * 相界(何时匀速/减速)按预估脉冲划分。 - * 相内频率形状按相内经过时间 t 与 T_acc/T_dec 计算。 - * 脉冲不够时降低峰值、保留相时间。 - * 加/减速脉冲按相内最高频估算,避免减速相高速时过早耗尽脉冲而硬停。 + * 相界按预估脉冲划分;预估与运行时一致:时间直线 + 脉冲起点锁频递推。 + * 末段脉冲不够:降低峰值做三角。非末段不够:不减速,沿斜率加到脉冲用尽。 */ #ifndef PLSR_ACCEL_CURVE_H #define PLSR_ACCEL_CURVE_H @@ -33,6 +31,12 @@ typedef enum { uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz); +/** 拟合斜坡时间(ms),使离散直线模型脉冲数尽量等于 max_pulses */ +uint32_t PlsrAccelCurveFitRampTimeMs(uint32_t f_from, + uint32_t f_to, + uint32_t max_pulses, + uint32_t t_prefer_ms); + void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, uint32_t total_pulses, uint32_t f_cur, diff --git a/plsr/pulse_driver/plsr_pulse_driver.c b/plsr/pulse_driver/plsr_pulse_driver.c index a368168..f165d4d 100644 --- a/plsr/pulse_driver/plsr_pulse_driver.c +++ b/plsr/pulse_driver/plsr_pulse_driver.c @@ -103,6 +103,11 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst htim->Init.CounterMode = TIM_COUNTERMODE_UP; htim->Init.Period = 999U; htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + /* + * ARR/CCR 预装载打开:改频只写入影子寄存器,在下一 UPDATE 生效。 + * 避免关预装载时在 ISR 里直接改 ARR/CCR 产生毛刺边沿(段越长多计越多, + * 如设定 5000 实测 5007)。软件侧超前一拍写频率补偿延迟。 + */ htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_PWM_Init(htim) != HAL_OK) { @@ -361,6 +366,9 @@ static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_i __HAL_TIM_DISABLE(htim); __HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE); + /* 清 one-pulse,保证连续发脉冲 */ + htim->Instance->CR1 &= (uint32_t)(~TIM_CR1_OPM); + __HAL_TIM_SET_PRESCALER(htim, psc); __HAL_TIM_SET_AUTORELOAD(htim, arr); __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, ccr); @@ -425,7 +433,7 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz) if ((s_psc_locked != 0U) || (psc == s_last_psc)) { - /* 同 PSC:只改 ARR(预装载),下一周期自然切换,无停表毛刺 */ + /* 预装载:本拍写影子寄存器,下一 UPDATE 自然切换,无毛刺、不多计边沿 */ __HAL_TIM_SET_AUTORELOAD(htim, arr); __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, ccr); __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE); @@ -494,3 +502,15 @@ void PlsrPulseDriverStop(void) s_last_psc = 0xFFFFFFFFUL; s_last_arr = 0xFFFFFFFFUL; } + +void PlsrPulseDriverArmOnePulseStop(void) +{ + TIM_HandleTypeDef *htim = s_active_htim; + + if (htim == (TIM_HandleTypeDef *)0) + { + return; + } + /* 下一 UPDATE 后计数器停止,不再多开一拍 */ + htim->Instance->CR1 |= TIM_CR1_OPM; +} diff --git a/plsr/pulse_driver/plsr_pulse_driver.h b/plsr/pulse_driver/plsr_pulse_driver.h index 18973dc..a53fbfc 100644 --- a/plsr/pulse_driver/plsr_pulse_driver.h +++ b/plsr/pulse_driver/plsr_pulse_driver.h @@ -38,6 +38,8 @@ void PlsrPulseDriverRequestFreq(uint32_t freq_hz); /** 在更新中断里调用:应用挂起频率 */ void PlsrPulseDriverApplyPending(void); void PlsrPulseDriverStop(void); +/** 当前拍结束后停止计数,避免多出一个上升沿 */ +void PlsrPulseDriverArmOnePulseStop(void); uint8_t PlsrPulseDriverIsPulseTim(TIM_TypeDef *instance); diff --git a/plsr/run_control/plsr_run_control.c b/plsr/run_control/plsr_run_control.c index d3e12f5..b0723cd 100644 --- a/plsr/run_control/plsr_run_control.c +++ b/plsr/run_control/plsr_run_control.c @@ -7,6 +7,11 @@ * - 升降相按时间走完,不按脉冲数切相 * - 剩余脉冲 <= 预留减速脉冲时进入减速;非末段不预留减速 * - 末段减到终止速度后停机(落到 0) + * + * 【硬约束】脉冲 UPDATE ISR(PlsrRunControlOnPulseIsr)内禁止重计算: + * - 禁止 FitRampTimeMs / EstimatePulses 等 O(N) 或查表迭代 + * - 禁止 S 曲线、正弦等将来更重的轮廓递推/拟合 + * - ISR 只做:计数、O(1) 插值取频、改预装载 ARR;耗时规划放在 BeginSeg/任务上下文 */ #include "plsr_run_control.h" #include "plsr_path_plan.h" @@ -35,6 +40,7 @@ static volatile uint8_t s_busy; static volatile uint8_t s_forward; static volatile uint16_t s_cur_seg; static volatile uint32_t s_cur_freq; +static volatile uint32_t s_pending_freq; /* ARPE 影子频率,下一 UPDATE 才成为当前拍 */ static volatile int32_t s_done; static volatile int32_t s_target; static volatile int32_t s_acc_pulse; @@ -115,15 +121,15 @@ static uint32_t PlsrRunControlClampSpeed(uint32_t spd) static void PlsrRunControlApplyOutFreq(uint32_t profile_freq, uint8_t do_start) { /* - * 完全按轮廓频率输出:0 就是停。 - * 运行中改频只挂起,等脉冲周期边界再落到硬件,避免中途改 ARR 出毛刺。 + * 运行中只写预装载(下一拍生效),避免直接改 ARR 出毛刺多计脉冲。 + * s_cur_freq = 当前正在跑的拍;s_pending_freq = 已写入影子、下一拍的频率。 */ - s_cur_freq = profile_freq; - if (profile_freq == 0U) { PlsrPulseDriverStop(); s_pwm_on = 0U; + s_cur_freq = 0U; + s_pending_freq = 0U; return; } @@ -131,9 +137,12 @@ static void PlsrRunControlApplyOutFreq(uint32_t profile_freq, uint8_t do_start) { PlsrPulseDriverStart(profile_freq); s_pwm_on = 1U; + s_cur_freq = profile_freq; + s_pending_freq = profile_freq; } else { + s_pending_freq = profile_freq; PlsrPulseDriverRequestFreq(profile_freq); } } @@ -174,6 +183,100 @@ static void PlsrRunControlEnterPhase(RunPhase_e ph, uint32_t anchor_freq) } } +static void PlsrRunControlEnterDecel(uint32_t from_hz, uint32_t remain_pulses) +{ + PlsrCfg_t *cfg = PlsrParamGetCfg(); + uint32_t t_slope = 0U; + uint32_t t_fit; + uint32_t sum_f; + uint32_t df; + uint32_t den; + uint32_t ref_ms; + uint32_t f_end; + + PlsrRunControlEnterPhase(PH_DECEL, from_hz); + f_end = s_accel_plan.f_end; + + if (remain_pulses == 0U) + { + s_accel_plan.dec_n = 0U; + s_accel_plan.t_dec_ms = 0U; + return; + } + + s_accel_plan.dec_n = remain_pulses; + + if (from_hz == f_end) + { + s_accel_plan.t_dec_ms = 0U; + return; + } + + /* 参数斜率决定的理想减速时间 */ + df = (from_hz > f_end) ? (from_hz - f_end) : (f_end - from_hz); + if (f_end > from_hz) + { + ref_ms = cfg->accel_ms; + den = (cfg->default_speed > cfg->start_speed) ? + (cfg->default_speed - cfg->start_speed) : + ((cfg->default_speed > cfg->end_speed) ? + (cfg->default_speed - cfg->end_speed) : cfg->default_speed); + } + else + { + ref_ms = cfg->decel_ms; + den = (cfg->default_speed > cfg->end_speed) ? + (cfg->default_speed - cfg->end_speed) : + ((cfg->default_speed > cfg->start_speed) ? + (cfg->default_speed - cfg->start_speed) : cfg->default_speed); + } + if (den == 0U) + { + den = df; + } + if ((ref_ms > 0U) && (den > 0U)) + { + t_slope = (df * ref_ms + den - 1UL) / den; + if (t_slope < 1U) + { + t_slope = 1U; + } + } + + /* + * 注意:本函数会在脉冲 UPDATE ISR 里调用,禁止再跑 FitRampTimeMs/ + * EstimatePulses 那种 O(N) 递推。段脉冲多时会卡死 ISR,定时器继续 + * 溢出 → 示波器上最后一段多出若干脉冲(500 准、5000 多 6~7 个)。 + * 这里只用 O(1) 平均公式拟合 T。 + */ + sum_f = from_hz + f_end; + if (sum_f == 0U) + { + sum_f = 1U; + } + if (remain_pulses > 1U) + { + t_fit = (2UL * (remain_pulses - 1UL) * 1000UL + sum_f - 1UL) / sum_f; + } + else + { + t_fit = 1U; + } + if (t_fit < 1U) + { + t_fit = 1U; + } + + if ((t_slope > 0U) && (t_slope <= t_fit)) + { + s_accel_plan.t_dec_ms = t_slope; + } + else + { + s_accel_plan.t_dec_ms = t_fit; + } +} + static void PlsrRunControlRefreshProfile(uint8_t do_start) { uint32_t next; @@ -199,7 +302,7 @@ static void PlsrRunControlRefreshProfile(uint8_t do_start) */ if ((s_phase == PH_CONST) && (p->dec_n > 0U) && (remain <= p->dec_n)) { - PlsrRunControlEnterPhase(PH_DECEL, s_cur_freq); + PlsrRunControlEnterDecel(s_cur_freq, remain); elapsed_us = 0U; } @@ -217,7 +320,7 @@ static void PlsrRunControlRefreshProfile(uint8_t do_start) PlsrRunControlEnterPhase(PH_CONST, next); if ((p->dec_n > 0U) && (remain <= p->dec_n)) { - PlsrRunControlEnterPhase(PH_DECEL, next); + PlsrRunControlEnterDecel(next, remain); elapsed_us = 0U; T_us = p->t_dec_ms * 1000UL; next = PlsrRunControlLerpUs(s_decel_from, p->f_end, @@ -226,22 +329,41 @@ static void PlsrRunControlRefreshProfile(uint8_t do_start) } else { - /* 时间线性:f = f0 + (f1-f0)*t/T,按 µs 连续变化,不是 10ms 台阶 */ + uint32_t t_cmd = elapsed_us; + if ((s_pwm_on != 0U) && (s_cur_freq >= 1U)) + { + t_cmd = elapsed_us + + ((1000000UL + (s_cur_freq / 2UL)) / s_cur_freq); + } next = PlsrRunControlLerpUs(s_approach_from, p->f_tgt, - elapsed_us, T_us); + t_cmd, T_us); } } else if (s_phase == PH_DECEL) { T_us = p->t_dec_ms * 1000UL; - if (T_us == 0U) + /* + * ARPE 超前一拍:这里算出的 next 是“再下一拍”的预装载。 + * remain==2 时预装载最后一拍 → 必须为 f_end;remain==1 最后一拍已在跑。 + */ + if ((remain <= 1U) || (T_us == 0U)) + { + next = p->f_end; + } + else if (remain == 2U) { next = p->f_end; } else { - next = PlsrRunControlLerpUs(s_decel_from, p->f_end, - elapsed_us, T_us); + uint32_t t_cmd = elapsed_us; + uint32_t per; + if (s_cur_freq >= 1U) + { + per = (1000000UL + (s_cur_freq / 2UL)) / s_cur_freq; + t_cmd = elapsed_us + per; + } + next = PlsrRunControlLerpUs(s_decel_from, p->f_end, t_cmd, T_us); } } else @@ -249,7 +371,7 @@ static void PlsrRunControlRefreshProfile(uint8_t do_start) next = p->f_tgt; if ((p->dec_n > 0U) && (remain <= p->dec_n)) { - PlsrRunControlEnterPhase(PH_DECEL, next); + PlsrRunControlEnterDecel(next, remain); elapsed_us = 0U; T_us = p->t_dec_ms * 1000UL; next = PlsrRunControlLerpUs(s_decel_from, p->f_end, @@ -257,7 +379,7 @@ static void PlsrRunControlRefreshProfile(uint8_t do_start) } } - if ((do_start != 0U) || (next != s_cur_freq)) + if ((do_start != 0U) || (next != s_pending_freq) || (s_pwm_on == 0U)) { PlsrRunControlApplyOutFreq(next, do_start); } @@ -324,6 +446,18 @@ static void PlsrRunControlPlanSeg(uint32_t total, uint32_t f_from, } else { + /* + * 加速时间也按 acc_n 拟合,避免 T 偏短导致前端几拍“又陡又弯”、 + * 后半段早就贴在目标频上。 + */ + if (s_accel_plan.acc_n > 0U) + { + s_accel_plan.t_acc_ms = + PlsrAccelCurveFitRampTimeMs(f_from, + s_accel_plan.f_tgt, + s_accel_plan.acc_n, + s_accel_plan.t_acc_ms); + } if (s_accel_plan.t_acc_ms < 1U) { s_accel_plan.t_acc_ms = 1U; @@ -340,6 +474,7 @@ static void PlsrRunControlFinishAll(void) s_busy = 0U; s_state = RC_IDLE; s_cur_freq = 0U; + s_pending_freq = 0U; s_pwm_on = 0U; s_follow_cont = 0U; s_chain_valid = 0U; @@ -521,7 +656,10 @@ static void PlsrRunControlBeginSeg(uint16_t seg_0) } else { - /* 完成:非末段停在本段目标;末段收到终止速度 */ + /* + * 完成:非末段 f_end=本段目标 → 无减速相;脉冲不够时沿加速度斜率 + * 一直加到脉冲用尽,下一段从链频继续加。仅末段收到终止速度并减速。 + */ f_end = (is_last != 0U) ? PlsrRunControlClampSpeed(cfg->end_speed) : f_tgt; } @@ -559,6 +697,7 @@ void PlsrRunControlInit(void) s_forward = 1U; s_cur_seg = 0U; s_cur_freq = 0U; + s_pending_freq = 0U; s_pwm_on = 0U; s_done = 0; s_target = 0; @@ -610,6 +749,7 @@ void PlsrRunControlStop(void) s_busy = 0U; s_state = RC_IDLE; s_cur_freq = 0U; + s_pending_freq = 0U; s_pwm_on = 0U; s_follow_cont = 0U; s_chain_valid = 0U; @@ -668,6 +808,26 @@ void PlsrRunControlTickMs(void) } /* 第一个脉冲 = 脉冲起始速度(不是目标频、不是 1Hz) */ PlsrRunControlApplyOutFreq(s_approach_from, 1U); + /* + * ARPE:立刻预装载第 2 拍频率,否则第 2 拍仍是起速(等于多锁一拍低频)。 + */ + if ((s_phase == PH_APPROACH) && (s_approach_from >= 1U) && + (s_approach_from != s_accel_plan.f_tgt)) + { + uint32_t T_us = s_accel_plan.t_acc_ms * 1000UL; + uint32_t per = (1000000UL + (s_approach_from / 2UL)) / + s_approach_from; + uint32_t f2 = PlsrRunControlLerpUs(s_approach_from, + s_accel_plan.f_tgt, + per, + T_us); + if (f2 < 1U) + { + f2 = 1U; + } + PlsrRunControlApplyOutFreq(f2, 0U); + PlsrPulseDriverApplyPending(); + } } return; } @@ -711,24 +871,11 @@ void PlsrRunControlOnPulseIsr(void) return; } - /* 上一周期结束:先落到挂起频率 */ - PlsrPulseDriverApplyPending(); - - f_prev = s_cur_freq; - /* - * 加/减速:按本脉冲周期推进相时间(µs),再算下一个频率。 - * 这样 f-t 是连续直线,而不是 OS 10ms 台阶。 + * UPDATE:上一拍结束;预装载已装入,新拍刚开始。 + * f_prev = 刚结束那一拍;随后 s_cur_freq 切到本拍(上一拍写入的 pending)。 */ - if ((s_phase == PH_APPROACH) || (s_phase == PH_DECEL)) - { - if (f_prev >= 1U) - { - s_phase_elapsed_us += (1000000UL + (f_prev / 2UL)) / f_prev; - } - PlsrRunControlRefreshProfile(0U); - PlsrPulseDriverApplyPending(); - } + f_prev = s_cur_freq; s_done++; if (s_forward != 0U) @@ -746,7 +893,24 @@ void PlsrRunControlOnPulseIsr(void) return; } - if (s_phase == PH_CONST) + s_cur_freq = s_pending_freq; + + /* 最后一拍:下一 UPDATE 停表,避免再多出一个上升沿 */ + if (s_done == (s_target - 1)) + { + PlsrPulseDriverArmOnePulseStop(); + } + + if ((s_phase == PH_APPROACH) || (s_phase == PH_DECEL)) + { + if (f_prev >= 1U) + { + s_phase_elapsed_us += (1000000UL + (f_prev / 2UL)) / f_prev; + } + PlsrRunControlRefreshProfile(0U); + PlsrPulseDriverApplyPending(); + } + else if (s_phase == PH_CONST) { remain = (uint32_t)(s_target - s_done); if ((s_accel_plan.dec_n > 0U) && (remain <= s_accel_plan.dec_n))