Просмотр исходного кода

修正完成模式下直线加减速每段执行逻辑:非末端一般只有加速/减速到目标频率后就稳定输出;末端可能有加速-匀速-减速三个过程。

master
hanyongwei 1 месяц назад
Родитель
Сommit
4a498a829c
6 измененных файлов: 385 добавлений и 45 удалений
  1. +1
    -0
      .gitignore
  2. +158
    -9
      plsr/accel_curve/plsr_accel_curve.c
  3. +8
    -4
      plsr/accel_curve/plsr_accel_curve.h
  4. +21
    -1
      plsr/pulse_driver/plsr_pulse_driver.c
  5. +2
    -0
      plsr/pulse_driver/plsr_pulse_driver.h
  6. +195
    -31
      plsr/run_control/plsr_run_control.c

+ 1
- 0
.gitignore Просмотреть файл

@@ -20,3 +20,4 @@ iar/settings/modbus.reggroups
build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/
plsr/test/
tools/
.cursor/

+ 158
- 9
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)
{
/*


+ 8
- 4
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,


+ 21
- 1
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;
}

+ 2
- 0
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);



+ 195
- 31
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))


Загрузка…
Отмена
Сохранить