diff --git a/iar/plsr.dep b/iar/plsr.dep index da69f5f..d833f96 100644 --- a/iar/plsr.dep +++ b/iar/plsr.dep @@ -1509,7 +1509,7 @@ ICCARM - 299 159 195 448 426 360 212 282 289 11 9 223 12 4 2 10 3 16 0 142 17 6 7 8 184 149 221 230 231 232 13 227 1 233 229 228 226 222 224 225 219 374 392 377 70 173 387 401 368 347 39 216 + 299 159 195 448 426 360 212 282 386 289 11 9 223 12 4 2 10 3 16 0 142 17 6 7 8 184 149 221 230 231 232 13 227 1 233 229 228 226 222 224 225 219 374 392 377 70 173 387 401 368 347 39 216 @@ -1682,7 +1682,7 @@ ICCARM - 289 11 9 223 12 4 2 10 159 195 448 426 360 212 3 16 0 142 17 6 7 8 184 149 221 230 231 232 13 227 1 233 229 228 226 222 224 225 219 374 392 377 70 173 387 401 368 347 39 216 381 299 399 5 15 386 + 289 11 9 223 12 4 2 10 159 195 448 426 360 212 3 16 0 142 17 6 7 8 184 149 221 230 231 232 13 227 1 233 229 228 226 222 224 225 219 374 392 377 70 173 387 401 368 347 39 216 381 299 399 5 15 386 282 diff --git a/plsr/accel_curve/plsr_accel_curve.c b/plsr/accel_curve/plsr_accel_curve.c index ef8a7b9..7af7227 100644 --- a/plsr/accel_curve/plsr_accel_curve.c +++ b/plsr/accel_curve/plsr_accel_curve.c @@ -488,12 +488,13 @@ static uint32_t PlsrAccelCurveFitPeak(uint32_t total_pulses, if (f_want >= f_cur) { peak = f_cur + mid; + n_a = PlsrAccelCurvePulsesForRamp(f_cur, peak, a_acc); } else { peak = f_cur - mid; + n_a = PlsrAccelCurvePulsesForRamp(f_cur, peak, a_dec); } - n_a = PlsrAccelCurvePulsesForRamp(f_cur, peak, a_acc); n_d = PlsrAccelCurvePulsesForRamp(peak, f_end, a_dec); if ((n_a + n_d) <= total_pulses) @@ -602,7 +603,15 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, plan->a_acc = a_acc; plan->a_dec = a_dec; - acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_acc); + if (f_cur > f_peak) + { + /* 入口高于峰值:首相为减速,用 a_dec(与 rampTimeMs 减速分支一致) */ + acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_dec); + } + else + { + acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_acc); + } dec_n = PlsrAccelCurvePulsesForRamp(f_peak, f_end, a_dec); if (total_pulses == 0U) @@ -627,7 +636,14 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, { f_peak = PlsrAccelCurveFitPeak(total_pulses, f_cur, f_tgt, f_end, a_acc, a_dec); - acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_acc); + if (f_cur > f_peak) + { + acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_dec); + } + else + { + acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_acc); + } dec_n = PlsrAccelCurvePulsesForRamp(f_peak, f_end, a_dec); if (acc_n > total_pulses) { @@ -701,7 +717,9 @@ uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, rising = (plan->f_tgt >= plan->f_cur) ? 1U : 0U; if (plan->mode == PLSR_ACCEL_LINEAR) { - f = PlsrAccelCurveFreqKinematic(plan->f_cur, plan->a_acc, + uint32_t a_use = (rising != 0U) ? plan->a_acc : plan->a_dec; + + f = PlsrAccelCurveFreqKinematic(plan->f_cur, a_use, pulse_done, rising, plan->f_tgt); } else @@ -785,10 +803,10 @@ void PlsrAccelPulseRtBeginAcc(PlsrAccelPulseRt_t *rt, rt->f0 = plan->f_cur; rt->f1 = plan->f_tgt; rt->f = plan->f_cur; - rt->a = plan->a_acc; rt->n = 0U; rt->n_total = plan->acc_n; rt->rising = (plan->f_tgt >= plan->f_cur) ? 1U : 0U; + rt->a = (rt->rising != 0U) ? plan->a_acc : plan->a_dec; rt->mode = plan->mode; rt->active = (plan->acc_n > 0U) && (plan->f_cur != plan->f_tgt) ? 1U : 0U; PlsrAccelPulseRtSetupShape(rt, rt->n_total); diff --git a/plsr/accel_curve/plsr_accel_curve.h b/plsr/accel_curve/plsr_accel_curve.h index 718f231..942c335 100644 --- a/plsr/accel_curve/plsr_accel_curve.h +++ b/plsr/accel_curve/plsr_accel_curve.h @@ -32,7 +32,7 @@ typedef struct { PlsrAccelMode_e mode; /* 直线 / S / 正弦 */ } PlsrAccelPlan_t; -/** 配置起速 → 规划用真实起点(Jump;起速 0 也不从 0Hz 开跑) */ +/** 配置起速 → 规划用真实起点(0=Jump 起跳;非 0 且 >f_tgt 则直接用起速走减速) */ uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, uint32_t f_tgt, uint32_t default_spd, diff --git a/plsr/run_control/plsr_run_control.c b/plsr/run_control/plsr_run_control.c index d97bde7..f9f9a66 100644 --- a/plsr/run_control/plsr_run_control.c +++ b/plsr/run_control/plsr_run_control.c @@ -136,6 +136,10 @@ static uint32_t s_plan_acc_n; /* PlanSeg 锁定加速脉冲预算 static uint32_t s_plan_dec_n; /* PlanSeg 锁定减速脉冲预算 */ static uint32_t s_decel_budget; /* EnterDecel 时锁定的减速步进预算 */ +/** 本次 Start 的启动段(0-based);仅该段首次冷启动可套用「起速>目标」入口规则 */ +static uint16_t s_run_entry_seg0; +static uint8_t s_run_first_entry; + /** * 策略层解析后交给 PlanSeg 的段端点(不含 wait_type,仅频率与时间) */ @@ -667,11 +671,29 @@ static uint8_t PlsrRunControlResolveSegPlanEndpoints(uint16_t seg_idx0, uint32_t } else { - endpoints->freq_start_hz = PlsrAccelCurveResolveStartHz(cfg->start_speed, - f_tgt, - cfg->default_speed, - endpoints->accel_ms, - endpoints->decel_ms); + /* + * 停表后冷启动:仅本次运行的启动段、且目标频低于配置起速时, + * 入口直接用起速(再规划减速到目标);其余仍走起跳频率。 + */ + if ((s_run_first_entry != 0U) && + (seg_idx0 == s_run_entry_seg0) && + (cfg->start_speed >= 1U) && (f_tgt < cfg->start_speed)) + { + s_run_first_entry = 0U; + endpoints->freq_start_hz = PlsrRunControlClampSpeed(cfg->start_speed); + } + else + { + if ((s_run_first_entry != 0U) && (seg_idx0 == s_run_entry_seg0)) + { + s_run_first_entry = 0U; + } + endpoints->freq_start_hz = PlsrAccelCurveResolveStartHz(cfg->start_speed, + f_tgt, + cfg->default_speed, + endpoints->accel_ms, + endpoints->decel_ms); + } } /* @@ -1261,6 +1283,7 @@ static void PlsrRunControlFinishAll(void) s_wait_expire_req = 0U; s_act_time_ms = 0U; s_last_period_wait = 0U; + s_run_first_entry = 0U; } /** @@ -1554,6 +1577,8 @@ uint8_t PlsrRunControlStart(uint16_t start_seg_1based) s_follow_cont = 0U; s_chain_valid = 0U; s_chain_freq = 0U; + s_run_entry_seg0 = (uint16_t)(start_seg_1based - 1U); + s_run_first_entry = 1U; s_cur_freq = PlsrRunControlClampSpeed(cfg->start_speed); /* * 绝对原点:仅在首次 Start(或 ClearAccPulse 之后的第一次)锁定。 @@ -1592,6 +1617,7 @@ void PlsrRunControlStop(void) s_wait_expire_req = 0U;/*段后等待到期请求标志*/ s_act_time_ms = 0U;/*本段 ACT 定时时长*/ s_last_period_wait = 0U; + s_run_first_entry = 0U; } /** @brief 运行中改频(见 plsr_run_control.h) */ diff --git a/plsr/signal_io/plsr_signal_io.c b/plsr/signal_io/plsr_signal_io.c index 3dc8e3c..a383252 100644 --- a/plsr/signal_io/plsr_signal_io.c +++ b/plsr/signal_io/plsr_signal_io.c @@ -2,11 +2,29 @@ * @file plsr_signal_io.c * @brief WAIT/EXT 输入 + TIM5 单次延时 + OS 节拍消抖 * - * @details - * EXTI 下降沿 → pending;DebouncePoll 满约 20ms 且仍低 → fell。 - * TIM5(32 位,APB1 84MHz):有延时才开表。 - * PSC=8399 → 10kHz(1 次计数 = 100µs) - * ARR = delay_ms×10 − 1 → 一次溢出 = 设定的 ms(分辨率 0.1ms,够用) + * @details 实现要点 + * 【EXTI 输入】 + * X4=PB5 → EXTI9_5;X5=PG12 → EXTI15_10。 + * 下降沿触发 OnFalling:仅 pending=1,不在 ISR 里判有效(避免抖动/毛刺)。 + * + * 【消抖】 + * DebouncePoll(任务上下文,约 1ms 一次): + * pending 且首次 → 记 arm_tick; + * 满 PLSR_SIGNAL_DEBOUNCE_MS(20ms)且 PinIsLow → fell=1。 + * 上升沿恢复高电平则 Confirm 末尾不置 fell(pending 被清掉)。 + * + * 【TIM5 单次延时】 + * 32 位 TIM5,APB1 84MHz;仅在有延时时开表(URS+OPM)。 + * PSC=8399 → 10kHz(1 计数 = 100µs);ARR = delay_ms×10 − 1。 + * 一次 UPDATE 溢出 = delay_ms;中断内 Cancel 并 PlsrRunControlOnDelayTimer()。 + * + * 【与 run_control 配合】 + * ScheduleDelayMs :方向延时 / WAIT 时间 / ACT 定时 + * TakeExtFalling :RUN 中 EXT 切段、WAIT_COND 等 + * TakeWaitFalling :WAIT 信号段后等待 + * ClearEdges :EXT 段开跑前丢旧沿 + * ClearPending :纯 EXT 段末保留 fell、清 pending + * DelayRemainMs :ACT 脉冲先完后续装剩余时间 */ #include "plsr_signal_io.h" #include "plsr_param.h" @@ -14,34 +32,38 @@ #include "main.h" #include "ucos_ii.h" +/* ---------- 硬件引脚(与 CubeMX / 方案书一致) ---------- */ #define PLSR_X4_PORT GPIOB -#define PLSR_X4_PIN GPIO_PIN_5 +#define PLSR_X4_PIN GPIO_PIN_5 /* X4:WAIT/EXT 可选 */ #define PLSR_X5_PORT GPIOG -#define PLSR_X5_PIN GPIO_PIN_12 +#define PLSR_X5_PIN GPIO_PIN_12 /* X5:WAIT/EXT 可选 */ /* - * 84MHz / 8400 = 10kHz → 100µs/格。 - * PSC=8399 落在 16 位内;不必用 1µs(那只是为了硬凑,ms 延时用不上)。 + * TIM5 时基:84MHz / (8399+1) = 10kHz → 100µs/格。 + * PSC=8399 落在 16 位内;ms 级延时用 100µs 分辨率足够,不必 1µs 时基。 */ -#define PLSR_TIM5_PSC_100US 8399U -#define PLSR_TIM5_TICKS_PER_MS 10U /* 10×100µs = 1ms */ -#define PLSR_DELAY_MS_MAX 65535U +#define PLSR_TIM5_PSC_100US 8399U +#define PLSR_TIM5_TICKS_PER_MS 10U /* 10×100µs = 1ms */ +#define PLSR_DELAY_MS_MAX 65535U +/** OS 节拍消抖窗口(ms),与 PlsrTask tick 配合 */ #define PLSR_SIGNAL_DEBOUNCE_MS 20U +/** 单路 X 口消抖状态(pending / fell 由 ISR 与任务共享,volatile) */ typedef struct { - volatile uint8_t pending; - volatile uint8_t stamp_ok; - volatile INT32U arm_tick; - volatile uint8_t fell; + volatile uint8_t pending; /* 1=EXTI 已见下降沿,待 Confirm */ + volatile uint8_t stamp_ok; /* 1=已记 arm_tick,正在计时 */ + volatile INT32U arm_tick; /* 消抖起点 OS tick */ + volatile uint8_t fell; /* 1=消抖确认的有效下降沿,Take* 消费 */ GPIO_TypeDef *port; uint16_t pin; } PlsrEdge_t; -static PlsrEdge_t s_db_x4; /* X4 输入消抖状态(WAIT/EXT 共用) */ -static PlsrEdge_t s_db_x5; /* X5 输入消抖状态 */ -static volatile uint8_t s_delay_armed; /* 1=TIM5 单次延时正在计时 */ +static PlsrEdge_t s_db_x4; /* X4 消抖状态 */ +static PlsrEdge_t s_db_x5; /* X5 消抖状态 */ +static volatile uint8_t s_delay_armed; /* 1=TIM5 单次延时正在计时 */ +/** 毫秒 → OS tick 数(向上取整,至少 1 tick) */ static INT32U PlsrSignalIoMsToTicks(uint32_t ms) { INT32U t; @@ -58,17 +80,23 @@ static INT32U PlsrSignalIoMsToTicks(uint32_t ms) return t; } +/** @return 1=脚当前为低(有效按下/接地) */ static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) { return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; } +/** EXTI 下降沿:仅置 pending,消抖在 DebouncePoll 完成 */ static void PlsrSignalIoOnFalling(PlsrEdge_t *db) { db->pending = 1U; db->stamp_ok = 0U; } +/** + * @brief 单路消抖确认(DebouncePoll 每 tick 调用) + * @note 满 debounce_ms 仍低 → fell;否则丢弃 pending + */ static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) { INT32U need; @@ -99,6 +127,7 @@ static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) } } +/** X4/X5:下降沿 EXTI + 上拉;先关 NVIC 再配 GPIO,避免配置期间误触发 */ static void PlsrSignalIoGpioExtiInit(void) { GPIO_InitTypeDef gpio = {0}; @@ -130,6 +159,7 @@ static void PlsrSignalIoGpioExtiInit(void) HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); } +/** TIM5:预配 PSC/最大 ARR,默认停表;ScheduleDelayMs 时再装 ARR 并 OPM 启动 */ static void PlsrSignalIoTim5Init(void) { __HAL_RCC_TIM5_CLK_ENABLE(); @@ -189,7 +219,7 @@ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) delay_ms = PLSR_DELAY_MS_MAX; } - /* N ms → N×10 个 100µs 格 */ + /* N ms → N×10 个 100µs 格;OPM 计满一次 UPDATE 即到期 */ ticks = delay_ms * PLSR_TIM5_TICKS_PER_MS; TIM5->CR1 = 0U; @@ -263,6 +293,7 @@ void PlsrSignalIoClearPending(void) s_db_x5.stamp_ok = 0U; } +/** wait_x_sel / ext_x_sel:0→X4,非 0→X5 */ static PlsrEdge_t *PlsrSignalIoDbBySel(uint16_t sel_01) { return (sel_01 != 0U) ? &s_db_x5 : &s_db_x4; diff --git a/plsr/signal_io/plsr_signal_io.h b/plsr/signal_io/plsr_signal_io.h index fc2d228..9808dd8 100644 --- a/plsr/signal_io/plsr_signal_io.h +++ b/plsr/signal_io/plsr_signal_io.h @@ -3,48 +3,105 @@ * @brief WAIT/EXT 外部输入与 TIM5 单次延时 * * @details 模块职责 - * 1. 配置 X4=PB5、X5=PG12 为下降沿 EXTI(上拉空闲高,飞线接地出沿); - * 2. 消抖:EXTI 置 pending,任务侧按 OS 节拍确认 20ms 仍低 → fell; - * 3. TIM5(32 位)仅在有延时时开启:100µs 时基,ARR=ms×10−1, - * 一次计满 = 设定 ms(最长 65535ms),到期 UPDATE → OnDelayTimer。 + * 1. 配置 X4=PB5、X5=PG12 为下降沿 EXTI(内部上拉,空闲高;飞线/触点接地出有效沿); + * 2. 消抖:EXTI 仅置 pending,任务侧 DebouncePoll 按 OS 节拍确认约 20ms 仍低 → fell; + * 3. TIM5(32 位)按需单次计时:100µs 时基,ARR=ms×10−1,一次 UPDATE = 设定 ms + * (最长 65535ms);到期回调 run_control::OnDelayTimer。 + * + * 硬件与参数映射 + * X4 (PB5, EXTI9_5) / X5 (PG12, EXTI15_10) + * wait_x_sel (0x1002) → 0=X4,1=X5,供 WAIT 信号等待(PLSR_WAIT_SIGNAL) + * ext_x_sel (0x1003) → 0=X4,1=X5,供 EXT 切段(PLSR_WAIT_EXT / EXT_OR_DONE) + * WAIT 与 EXT 可独立选口,共用同一套消抖状态机。 + * + * 消抖状态机(每路 PlsrEdge_t) + * pending=1 ← EXTI 下降沿 + * DebouncePoll:首次见 pending 记 arm_tick;满 20ms 且脚仍低 → fell=1 + * Take*Falling 消费 fell;ClearPending 只清 pending/stamp,保留已确认 fell + * + * TIM5 单次延时用途(run_control 调用) + * - 方向切换延时 dir_delay_ms(RC_DIR_WAIT → Start) + * - WAIT 时间 PLSR_WAIT_TIME 段后等待 + * - ACT 时间 PLSR_WAIT_ACT 段内定时(到期 OPM 收当前拍再切段) + * - ACT 脉冲先完但定时未到:DelayRemainMs 续装剩余 ms * * 与其它模块关系 * - plsr_param :wait_x_sel / ext_x_sel - * - plsr_run_control :TakeWait/ExtFalling、ScheduleDelayMs、OnDelayTimer - * - plsr.c :周期调用 DebouncePoll - * - stm32f4xx_it :TIM5 / EXTI 转发 + * - plsr_run_control :TakeWait/ExtFalling、Schedule/CancelDelay、OnDelayTimer + * - plsr.c :PlsrTask 每 OS 节拍 DebouncePoll + * - stm32f4xx_it :TIM5 / EXTI9_5 / EXTI15_10 转发至本模块 IRQ 入口 + * + * 线程/中断约定 + * fell/pending 由 ISR 写、任务读;Take* 仅在任务上下文消费。 + * ScheduleDelayMs / CancelDelay 由任务调用;OnTim5Irq 在 TIM5 中断内回调 run_control。 */ #ifndef PLSR_SIGNAL_IO_H #define PLSR_SIGNAL_IO_H #include +/** + * @brief 初始化 X4/X5 EXTI 消抖状态与 TIM5(不启动延时) + * @note 调用时机:PlsrInit,在 RunControl 之前 + * @note 副作用:清 pending/fell;TIM5 停表、OPM 单次模式预配置 + */ void PlsrSignalIoInit(void); /** - * @brief 武装 TIM5 单次延时(ms),到期进 OnDelayTimer + * @brief 武装 TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer * @param delay_ms 0 按 1ms;上限 65535ms + * @note 重入会先 Cancel 再重装;OPM 计满自动停表 */ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); +/** @brief 停止 TIM5 单次延时,s_delay_armed=0 */ void PlsrSignalIoCancelDelay(void); /** * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) + * @note ACT 脉冲先完、定时仍走时,用于续装剩余 ACT 时间 */ uint32_t PlsrSignalIoDelayRemainMs(void); +/** + * @brief OS 节拍消抖轮询(X4/X5 各跑一遍 Confirm) + * @note 由 PlsrTask 每 tick 调用;满 PLSR_SIGNAL_DEBOUNCE_MS 且仍低才置 fell + */ void PlsrSignalIoDebouncePoll(void); +/** + * @brief 清 X4/X5 的 pending 与 fell(开跑 EXT 段、FinishAll 时用) + * @note 丢弃尚未确认的沿与已锁存沿,防误切段 + */ void PlsrSignalIoClearEdges(void); + +/** + * @brief 只清 pending/stamp,保留已确认的 fell + * @note 纯 EXT 段末:清消抖中的假沿,已确认沿留给 TakeExtFalling + */ void PlsrSignalIoClearPending(void); + +/** + * @brief 消费 WAIT 口(wait_x_sel)上已确认的下降沿 + * @return 1=本次取到有效沿并已清零 fell;0=无沿 + * @note 仅认消抖后的 fell,不读当前 GPIO 电平(防虚低误跳) + */ uint8_t PlsrSignalIoTakeWaitFalling(void); + +/** + * @brief 消费 EXT 口(ext_x_sel)上已确认的下降沿 + * @return 1=本次取到有效沿并已清零 fell;0=无沿 + * @note RUN 中 EXT/EXT_OR_DONE 切段、WAIT_COND 等路径共用 + */ uint8_t PlsrSignalIoTakeExtFalling(void); -/** @brief TIM5 UPDATE:单次延时到期 */ +/** @brief TIM5 UPDATE 中断入口:单次延时到期 → run_control */ void PlsrSignalIoOnTim5Irq(void); +/** @brief EXTI9_5 中断入口(X4 / PB5) */ void PlsrSignalIoOnExti9_5(void); + +/** @brief EXTI15_10 中断入口(X5 / PG12) */ void PlsrSignalIoOnExti15_10(void); #endif