From ec0472dbd2948b95140deb6c4b5ce3c994c22b14 Mon Sep 17 00:00:00 2001 From: hanyongwei <2043702190@qq.com> Date: Sat, 15 Aug 2026 15:40:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8E=E7=BB=AD=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E4=B8=8E=E5=AE=8C=E6=88=90=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E7=9A=84=E4=B8=8D=E5=90=8C=E5=9C=A8=E4=BA=8E=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E6=98=AF=E6=AE=B5=E5=BC=80=E5=A7=8B=E6=97=B6?= =?UTF-8?q?=E5=8A=A0=E5=87=8F=E9=80=9F=E5=88=B0=E7=9B=AE=E6=A0=87=E9=A2=91?= =?UTF-8?q?=E7=8E=87=EF=BC=8C=E5=90=8E=E7=BB=AD=E6=96=B9=E5=BC=8F=E6=98=AF?= =?UTF-8?q?=E5=89=8D=E4=B8=80=E6=AE=B5=E5=AE=8C=E6=88=90=E6=97=B6=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E5=8A=A0=E5=87=8F=E9=80=9F=E5=88=B0=E5=90=8E=E4=B8=80?= =?UTF-8?q?=E6=AE=B5=E7=9A=84=E7=9B=AE=E6=A0=87=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plsr/accel_curve/plsr_accel_curve.c | 3 +- plsr/accel_curve/plsr_accel_curve.h | 4 +- plsr/param/plsr_param.h | 8 +- plsr/pulse_driver/plsr_pulse_driver.c | 11 +++ plsr/pulse_driver/plsr_pulse_driver.h | 2 + plsr/run_control/plsr_run_control.c | 131 +++++++++++++++++++++----- plsr/run_control/plsr_run_control.h | 2 +- 7 files changed, 130 insertions(+), 31 deletions(-) diff --git a/plsr/accel_curve/plsr_accel_curve.c b/plsr/accel_curve/plsr_accel_curve.c index 9cda636..fd14599 100644 --- a/plsr/accel_curve/plsr_accel_curve.c +++ b/plsr/accel_curve/plsr_accel_curve.c @@ -490,9 +490,10 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, } /* - * 无独立减速终点(完成模式非末段:f_end == f_tgt): + * 无独立段末终点(完成方式非末段:f_end == f_tgt): * 脉冲不够时不 FitPeak、不减速,全程按原目标斜率加速; * 脉冲用尽后由上层把当前频率链到下一段继续加。 + * 后续方式非末段 f_end=下一段目标,不走此分支。 * 末段(f_end 为终止速度)不够时仍三角压峰。 */ if ((f_end == f_tgt) && (acc_n > total_pulses)) diff --git a/plsr/accel_curve/plsr_accel_curve.h b/plsr/accel_curve/plsr_accel_curve.h index 613de9d..ccd3667 100644 --- a/plsr/accel_curve/plsr_accel_curve.h +++ b/plsr/accel_curve/plsr_accel_curve.h @@ -3,7 +3,9 @@ * @brief 预估加/匀/减脉冲;相内按时间走直线/S/正弦 * * 相界按预估脉冲划分;预估与运行时一致:shape(t/T) + 脉冲起点锁频递推。 - * 末段脉冲不够:降低峰值做三角。非末段不够:不减速,沿斜率加到脉冲用尽。 + * 完成方式非末段 f_end==f_tgt:不够则不减速,沿斜率加到脉冲用尽。 + * 后续方式非末段 f_end=下一段目标:段末过渡占用本段脉冲;不够则压峰/压缩过渡。 + * 末段(完成/后续)脉冲不够:降低峰值做三角到终止速度。 */ #ifndef PLSR_ACCEL_CURVE_H #define PLSR_ACCEL_CURVE_H diff --git a/plsr/param/plsr_param.h b/plsr/param/plsr_param.h index 56c3f29..46c0f4c 100644 --- a/plsr/param/plsr_param.h +++ b/plsr/param/plsr_param.h @@ -65,8 +65,8 @@ typedef enum { } PlsrDirLogic_e; typedef enum { - PLSR_SEND_COMPLETE = 0, /* 本段发完再规划下一段加减速 */ - PLSR_SEND_FOLLOW = 1 /* 本段末速对准下一段频率,同向可不停表 */ + PLSR_SEND_COMPLETE = 0, /* 完成:上一段发完后,本段开头再加减速到本段频率 */ + PLSR_SEND_FOLLOW = 1 /* 后续:上一段发完时频率已到本段目标;同向可不停表 */ } PlsrSendMode_e; typedef enum { @@ -102,8 +102,8 @@ typedef struct { uint16_t seg_count; uint16_t start_seg; /* 1-based */ uint32_t default_speed; /* 脉冲默认速度 Hz:定加减速斜率 K;段频为 0 时也作段目标 */ - uint32_t start_speed; /* 脉冲起始速度:完成模式首段从该频起跳再升降 */ - uint32_t end_speed; /* 脉冲终止速度:末段减速落到该频后再停到 0 */ + uint32_t start_speed; /* 脉冲起始速度:首段(或断链重启)从该频起跳 */ + uint32_t end_speed; /* 脉冲终止速度:末段减速落到该频后再停 */ uint16_t accel_ms; /* 默认速度加速时间 ms(定加速斜率) */ uint16_t decel_ms; /* 默认速度减速时间 ms(定减速斜率) */ } PlsrCfg_t; diff --git a/plsr/pulse_driver/plsr_pulse_driver.c b/plsr/pulse_driver/plsr_pulse_driver.c index f165d4d..c1d72c1 100644 --- a/plsr/pulse_driver/plsr_pulse_driver.c +++ b/plsr/pulse_driver/plsr_pulse_driver.c @@ -514,3 +514,14 @@ void PlsrPulseDriverArmOnePulseStop(void) /* 下一 UPDATE 后计数器停止,不再多开一拍 */ htim->Instance->CR1 |= TIM_CR1_OPM; } + +void PlsrPulseDriverClearOnePulseStop(void) +{ + TIM_HandleTypeDef *htim = s_active_htim; + + if (htim == (TIM_HandleTypeDef *)0) + { + return; + } + htim->Instance->CR1 &= (uint32_t)(~TIM_CR1_OPM); +} diff --git a/plsr/pulse_driver/plsr_pulse_driver.h b/plsr/pulse_driver/plsr_pulse_driver.h index a53fbfc..a5970f0 100644 --- a/plsr/pulse_driver/plsr_pulse_driver.h +++ b/plsr/pulse_driver/plsr_pulse_driver.h @@ -40,6 +40,8 @@ void PlsrPulseDriverApplyPending(void); void PlsrPulseDriverStop(void); /** 当前拍结束后停止计数,避免多出一个上升沿 */ void PlsrPulseDriverArmOnePulseStop(void); +/** 取消单脉冲停表(后续模式同向衔接时用) */ +void PlsrPulseDriverClearOnePulseStop(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 29d0f5d..44be53b 100644 --- a/plsr/run_control/plsr_run_control.c +++ b/plsr/run_control/plsr_run_control.c @@ -1,17 +1,22 @@ /** * @file plsr_run_control.c - * @brief 完成模式直线加减速(手册梯形/三角) + * @brief 完成方式 / 后续方式 运行控制(加减速形状由 accel_mode 决定) * - * 完成方式要点: - * - 段开始:阶跃到起速,再按时间直线升/降到本段目标(相时间按脉冲周期 µs 累加,避免 10ms 台阶) - * - 升降相按时间走完,不按脉冲数切相 - * - 剩余脉冲 <= 预留减速脉冲时进入减速;非末段不预留减速 - * - 末段减到终止速度后停机(落到 0) + * 完成方式(PLSR_SEND_COMPLETE): + * - 上一段发完后,再在本段开头加/减速到本段目标频率 + * - 非末段:加速→匀速(f_end=本段目标,无段末过渡);末段再减到终止速度 + * - 段间停表,用 s_chain_freq 把当前频传给下一段作起速 + * + * 后续方式(PLSR_SEND_FOLLOW): + * - 上一段脉冲发完时,频率已到达下一段目标(过渡占用本段末尾脉冲) + * - 首段:起速→本段目标→再过渡到下一段目标 + * - 中间段:已在本段目标上匀速→段末过渡到下一段目标 + * - 末段:匀速→减到终止速度后停 + * - 同向无等待时不停表衔接;换向/等待则停表再启 * * 【硬约束】脉冲 UPDATE ISR(PlsrRunControlOnPulseIsr)内禁止重计算: * - 禁止 FitRampTimeMs / EstimatePulses 等 O(N) 或查表迭代 - * - 禁止 S 曲线、正弦等将来更重的轮廓递推/拟合 - * - ISR 只做:计数、O(1) 插值取频、改预装载 ARR;耗时规划放在 BeginSeg/任务上下文 + * - ISR 只做:计数、O(1) 形状取频、改预装载 ARR;耗时规划放在 BeginSeg/任务上下文 */ #include "plsr_run_control.h" #include "plsr_path_plan.h" @@ -30,9 +35,9 @@ typedef enum { } RcState_e; typedef enum { - PH_APPROACH = 0, /* 起速 → 本段目标/峰值,按时间 */ + PH_APPROACH = 0, /* 起速 → 本段目标/峰值 */ PH_CONST, /* 匀速 */ - PH_DECEL /* → 终止速度,按时间 */ + PH_DECEL /* 段末过渡 → f_end(可升可降:完成=止速;后续=下一段目标) */ } RunPhase_e; static volatile RcState_e s_state; @@ -62,6 +67,35 @@ static uint8_t s_pwm_on; static void PlsrRunControlAfterSegDone(void); +/** + * 后续方式:本段末拍后能否同向不停表直接进下一段。 + * (有下一段、同向、且本段无等待。)ISR 内 O(1)。 + */ +static uint8_t PlsrRunControlWillFollowKeep(uint16_t cur_seg) +{ + PlsrCfg_t *cfg = PlsrParamGetCfg(); + int16_t next0; + + if (cfg->send_mode != PLSR_SEND_FOLLOW) + { + return 0U; + } + next0 = PlsrPathPlanNextSeg(cur_seg); + if (next0 < 0) + { + return 0U; + } + if (PlsrPathPlanIsForward((uint16_t)next0, s_acc_pulse) != s_forward) + { + return 0U; + } + if (PlsrPathPlanGetWaitMs(cur_seg) > 0U) + { + return 0U; + } + return 1U; +} + static INT32U PlsrRunControlMsToTicks(uint32_t ms) { INT32U t; @@ -172,6 +206,11 @@ static void PlsrRunControlEnterDecel(uint32_t from_hz, uint32_t remain_pulses) uint32_t den; uint32_t ref_ms; uint32_t f_end; + uint32_t old_dec_n; + uint32_t old_t_dec; + + old_dec_n = s_accel_plan.dec_n; + old_t_dec = s_accel_plan.t_dec_ms; PlsrRunControlEnterPhase(PH_DECEL, from_hz); f_end = s_accel_plan.f_end; @@ -191,6 +230,17 @@ static void PlsrRunControlEnterDecel(uint32_t from_hz, uint32_t remain_pulses) return; } + /* + * 规划阶段已按 dec_n 拟合过 T(含 S/正弦)且剩余脉冲仍等于规划值: + * 直接沿用,避免 ISR 内 O(1) 平均公式把形状时间改歪。 + * 脉冲被改频/截断等导致 remain 变化时,再走下面 O(1) 回退。 + */ + if ((old_t_dec > 0U) && (old_dec_n > 0U) && (remain_pulses == old_dec_n)) + { + s_accel_plan.t_dec_ms = old_t_dec; + return; + } + /* 参数斜率决定的理想减速时间 */ df = (from_hz > f_end) ? (from_hz - f_end) : (f_end - from_hz); if (f_end > from_hz) @@ -414,11 +464,21 @@ static void PlsrRunControlPlanSeg(uint32_t total, uint32_t f_from, { f_lo = 1U; } - PlsrPulseDriverLockPscRange(f_lo, f_hi); + /* + * 后续同向不停表时 PWM 仍在跑:禁止重锁 PSC(只改了映像、硬件 PSC 未变, + * 会导致 ARR 按新 PSC 算错频)。完成方式段间已停表,可安全重锁。 + */ + if (s_pwm_on == 0U) + { + PlsrPulseDriverLockPscRange(f_lo, f_hi); + } - s_cur_freq = f_from; s_approach_from = f_from; s_decel_from = s_accel_plan.f_tgt; + if (s_pwm_on == 0U) + { + s_cur_freq = f_from; + } if (f_from == s_accel_plan.f_tgt) { @@ -445,6 +505,22 @@ static void PlsrRunControlPlanSeg(uint32_t total, uint32_t f_from, } PlsrRunControlEnterPhase(PH_APPROACH, f_from); } + + /* 段末过渡时间按 dec_n 拟合(后续对准下一段 / 完成末段到止速) */ + if ((s_accel_plan.dec_n > 0U) && + (s_accel_plan.f_tgt != s_accel_plan.f_end)) + { + s_accel_plan.t_dec_ms = + PlsrAccelCurveFitRampTimeMs(s_accel_plan.f_tgt, + s_accel_plan.f_end, + s_accel_plan.dec_n, + s_accel_plan.t_dec_ms, + s_accel_plan.mode); + if (s_accel_plan.t_dec_ms < 1U) + { + s_accel_plan.t_dec_ms = 1U; + } + } } static void PlsrRunControlFinishAll(void) @@ -482,36 +558,31 @@ static void PlsrRunControlGotoNextOrFinish(uint16_t cur_seg) static void PlsrRunControlAfterSegDone(void) { PlsrCfg_t *cfg = PlsrParamGetCfg(); - int16_t next0; uint16_t wait_ms; - uint8_t follow; uint8_t keep_pwm; /* 先脱离 RUN,避免脉冲 ISR 重入 */ s_state = RC_IDLE; - next0 = PlsrPathPlanNextSeg(s_cur_seg); - follow = (cfg->send_mode == PLSR_SEND_FOLLOW) ? 1U : 0U; - keep_pwm = 0U; + keep_pwm = PlsrRunControlWillFollowKeep(s_cur_seg); - if (follow == 0U) + if (cfg->send_mode == PLSR_SEND_COMPLETE) { s_chain_freq = s_cur_freq; s_chain_valid = 1U; } - if ((follow != 0U) && (next0 >= 0) && - (PlsrPathPlanIsForward((uint16_t)next0, s_acc_pulse) == s_forward)) + if (keep_pwm != 0U) { + /* 后续同向:频率已在下一段目标,不停表直接 BeginSeg */ s_follow_cont = 1U; - keep_pwm = 1U; } else { PlsrPulseDriverStop(); s_follow_cont = 0U; s_pwm_on = 0U; - if (follow != 0U) + if (cfg->send_mode == PLSR_SEND_FOLLOW) { s_cur_freq = 0U; s_chain_valid = 0U; @@ -521,6 +592,7 @@ static void PlsrRunControlAfterSegDone(void) wait_ms = PlsrPathPlanGetWaitMs(s_cur_seg); if (wait_ms > 0U) { + /* WillFollowKeep 在有等待时已为 0;此处兜底停表 */ if (keep_pwm != 0U) { PlsrPulseDriverStop(); @@ -602,6 +674,7 @@ static void PlsrRunControlBeginSeg(uint16_t seg_0) if (s_follow_cont != 0U) { + /* 同向不停表:上一段末速应已是本段目标 */ f_from = s_cur_freq; } else if ((cfg->send_mode == PLSR_SEND_COMPLETE) && (s_chain_valid != 0U)) @@ -615,6 +688,10 @@ static void PlsrRunControlBeginSeg(uint16_t seg_0) if (cfg->send_mode == PLSR_SEND_FOLLOW) { + /* + * 后续:段末 f_end 对准下一段目标(末段对准终止速度)。 + * AccelCurvePlan 据此预留段末过渡脉冲;中间段 f_from≈f_tgt → 仅匀速+过渡。 + */ if (is_last == 0U) { PlsrSeg_t *nseg = PlsrParamGetSeg((uint16_t)next0); @@ -659,7 +736,9 @@ static void PlsrRunControlBeginSeg(uint16_t seg_0) if (s_follow_cont != 0U) { + PlsrPulseDriverClearOnePulseStop(); PlsrRunControlRefreshProfile(0U); + PlsrPulseDriverApplyPending(); s_state = RC_RUN; s_follow_cont = 0U; } @@ -878,10 +957,14 @@ void PlsrRunControlOnPulseIsr(void) s_cur_freq = s_pending_freq; - /* 最后一拍:下一 UPDATE 停表,避免再多出一个上升沿 */ + /* 最后一拍:完成方式 / 后续末段或断链时 OPM 停表,避免多一个上升沿。 + * 后续同向不停表衔接时禁止 OPM,否则会在段界把定时器停掉。 */ if (s_done == (s_target - 1)) { - PlsrPulseDriverArmOnePulseStop(); + if (PlsrRunControlWillFollowKeep(s_cur_seg) == 0U) + { + PlsrPulseDriverArmOnePulseStop(); + } } if ((s_phase == PH_APPROACH) || (s_phase == PH_DECEL)) diff --git a/plsr/run_control/plsr_run_control.h b/plsr/run_control/plsr_run_control.h index eda7666..649fe45 100644 --- a/plsr/run_control/plsr_run_control.h +++ b/plsr/run_control/plsr_run_control.h @@ -1,6 +1,6 @@ /** * @file plsr_run_control.h - * @brief 运行控制:完成/后续模式;当前先保证完成模式直线梯形 + * @brief 运行控制:完成方式 / 后续方式(加减速形状见 accel_mode) */ #ifndef PLSR_RUN_CONTROL_H #define PLSR_RUN_CONTROL_H