| @@ -1509,7 +1509,7 @@ | |||||
| <inputs> | <inputs> | ||||
| <tool> | <tool> | ||||
| <name>ICCARM</name> | <name>ICCARM</name> | ||||
| <file> 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</file> | |||||
| <file> 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</file> | |||||
| </tool> | </tool> | ||||
| </inputs> | </inputs> | ||||
| </file> | </file> | ||||
| @@ -1682,7 +1682,7 @@ | |||||
| <inputs> | <inputs> | ||||
| <tool> | <tool> | ||||
| <name>ICCARM</name> | <name>ICCARM</name> | ||||
| <file> 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</file> | |||||
| <file> 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</file> | |||||
| </tool> | </tool> | ||||
| </inputs> | </inputs> | ||||
| </file> | </file> | ||||
| @@ -488,12 +488,13 @@ static uint32_t PlsrAccelCurveFitPeak(uint32_t total_pulses, | |||||
| if (f_want >= f_cur) | if (f_want >= f_cur) | ||||
| { | { | ||||
| peak = f_cur + mid; | peak = f_cur + mid; | ||||
| n_a = PlsrAccelCurvePulsesForRamp(f_cur, peak, a_acc); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| peak = f_cur - mid; | 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); | n_d = PlsrAccelCurvePulsesForRamp(peak, f_end, a_dec); | ||||
| if ((n_a + n_d) <= total_pulses) | if ((n_a + n_d) <= total_pulses) | ||||
| @@ -602,7 +603,15 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||||
| plan->a_acc = a_acc; | plan->a_acc = a_acc; | ||||
| plan->a_dec = a_dec; | 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); | dec_n = PlsrAccelCurvePulsesForRamp(f_peak, f_end, a_dec); | ||||
| if (total_pulses == 0U) | if (total_pulses == 0U) | ||||
| @@ -627,7 +636,14 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||||
| { | { | ||||
| f_peak = PlsrAccelCurveFitPeak(total_pulses, f_cur, f_tgt, f_end, | f_peak = PlsrAccelCurveFitPeak(total_pulses, f_cur, f_tgt, f_end, | ||||
| a_acc, a_dec); | 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); | dec_n = PlsrAccelCurvePulsesForRamp(f_peak, f_end, a_dec); | ||||
| if (acc_n > total_pulses) | 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; | rising = (plan->f_tgt >= plan->f_cur) ? 1U : 0U; | ||||
| if (plan->mode == PLSR_ACCEL_LINEAR) | 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); | pulse_done, rising, plan->f_tgt); | ||||
| } | } | ||||
| else | else | ||||
| @@ -785,10 +803,10 @@ void PlsrAccelPulseRtBeginAcc(PlsrAccelPulseRt_t *rt, | |||||
| rt->f0 = plan->f_cur; | rt->f0 = plan->f_cur; | ||||
| rt->f1 = plan->f_tgt; | rt->f1 = plan->f_tgt; | ||||
| rt->f = plan->f_cur; | rt->f = plan->f_cur; | ||||
| rt->a = plan->a_acc; | |||||
| rt->n = 0U; | rt->n = 0U; | ||||
| rt->n_total = plan->acc_n; | rt->n_total = plan->acc_n; | ||||
| rt->rising = (plan->f_tgt >= plan->f_cur) ? 1U : 0U; | 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->mode = plan->mode; | ||||
| rt->active = (plan->acc_n > 0U) && (plan->f_cur != plan->f_tgt) ? 1U : 0U; | rt->active = (plan->acc_n > 0U) && (plan->f_cur != plan->f_tgt) ? 1U : 0U; | ||||
| PlsrAccelPulseRtSetupShape(rt, rt->n_total); | PlsrAccelPulseRtSetupShape(rt, rt->n_total); | ||||
| @@ -32,7 +32,7 @@ typedef struct { | |||||
| PlsrAccelMode_e mode; /* 直线 / S / 正弦 */ | PlsrAccelMode_e mode; /* 直线 / S / 正弦 */ | ||||
| } PlsrAccelPlan_t; | } PlsrAccelPlan_t; | ||||
| /** 配置起速 → 规划用真实起点(Jump;起速 0 也不从 0Hz 开跑) */ | |||||
| /** 配置起速 → 规划用真实起点(0=Jump 起跳;非 0 且 >f_tgt 则直接用起速走减速) */ | |||||
| uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | ||||
| uint32_t f_tgt, | uint32_t f_tgt, | ||||
| uint32_t default_spd, | uint32_t default_spd, | ||||
| @@ -136,6 +136,10 @@ static uint32_t s_plan_acc_n; /* PlanSeg 锁定加速脉冲预算 | |||||
| static uint32_t s_plan_dec_n; /* PlanSeg 锁定减速脉冲预算 */ | static uint32_t s_plan_dec_n; /* PlanSeg 锁定减速脉冲预算 */ | ||||
| static uint32_t s_decel_budget; /* EnterDecel 时锁定的减速步进预算 */ | 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,仅频率与时间) | * 策略层解析后交给 PlanSeg 的段端点(不含 wait_type,仅频率与时间) | ||||
| */ | */ | ||||
| @@ -667,11 +671,29 @@ static uint8_t PlsrRunControlResolveSegPlanEndpoints(uint16_t seg_idx0, uint32_t | |||||
| } | } | ||||
| else | 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_wait_expire_req = 0U; | ||||
| s_act_time_ms = 0U; | s_act_time_ms = 0U; | ||||
| s_last_period_wait = 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_follow_cont = 0U; | ||||
| s_chain_valid = 0U; | s_chain_valid = 0U; | ||||
| s_chain_freq = 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); | s_cur_freq = PlsrRunControlClampSpeed(cfg->start_speed); | ||||
| /* | /* | ||||
| * 绝对原点:仅在首次 Start(或 ClearAccPulse 之后的第一次)锁定。 | * 绝对原点:仅在首次 Start(或 ClearAccPulse 之后的第一次)锁定。 | ||||
| @@ -1592,6 +1617,7 @@ void PlsrRunControlStop(void) | |||||
| s_wait_expire_req = 0U;/*段后等待到期请求标志*/ | s_wait_expire_req = 0U;/*段后等待到期请求标志*/ | ||||
| s_act_time_ms = 0U;/*本段 ACT 定时时长*/ | s_act_time_ms = 0U;/*本段 ACT 定时时长*/ | ||||
| s_last_period_wait = 0U; | s_last_period_wait = 0U; | ||||
| s_run_first_entry = 0U; | |||||
| } | } | ||||
| /** @brief 运行中改频(见 plsr_run_control.h) */ | /** @brief 运行中改频(见 plsr_run_control.h) */ | ||||
| @@ -2,11 +2,29 @@ | |||||
| * @file plsr_signal_io.c | * @file plsr_signal_io.c | ||||
| * @brief WAIT/EXT 输入 + TIM5 单次延时 + OS 节拍消抖 | * @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_signal_io.h" | ||||
| #include "plsr_param.h" | #include "plsr_param.h" | ||||
| @@ -14,34 +32,38 @@ | |||||
| #include "main.h" | #include "main.h" | ||||
| #include "ucos_ii.h" | #include "ucos_ii.h" | ||||
| /* ---------- 硬件引脚(与 CubeMX / 方案书一致) ---------- */ | |||||
| #define PLSR_X4_PORT GPIOB | #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_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 | #define PLSR_SIGNAL_DEBOUNCE_MS 20U | ||||
| /** 单路 X 口消抖状态(pending / fell 由 ISR 与任务共享,volatile) */ | |||||
| typedef struct { | 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; | GPIO_TypeDef *port; | ||||
| uint16_t pin; | uint16_t pin; | ||||
| } PlsrEdge_t; | } 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) | static INT32U PlsrSignalIoMsToTicks(uint32_t ms) | ||||
| { | { | ||||
| INT32U t; | INT32U t; | ||||
| @@ -58,17 +80,23 @@ static INT32U PlsrSignalIoMsToTicks(uint32_t ms) | |||||
| return t; | return t; | ||||
| } | } | ||||
| /** @return 1=脚当前为低(有效按下/接地) */ | |||||
| static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) | static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) | ||||
| { | { | ||||
| return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; | return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; | ||||
| } | } | ||||
| /** EXTI 下降沿:仅置 pending,消抖在 DebouncePoll 完成 */ | |||||
| static void PlsrSignalIoOnFalling(PlsrEdge_t *db) | static void PlsrSignalIoOnFalling(PlsrEdge_t *db) | ||||
| { | { | ||||
| db->pending = 1U; | db->pending = 1U; | ||||
| db->stamp_ok = 0U; | db->stamp_ok = 0U; | ||||
| } | } | ||||
| /** | |||||
| * @brief 单路消抖确认(DebouncePoll 每 tick 调用) | |||||
| * @note 满 debounce_ms 仍低 → fell;否则丢弃 pending | |||||
| */ | |||||
| static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) | static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) | ||||
| { | { | ||||
| INT32U need; | INT32U need; | ||||
| @@ -99,6 +127,7 @@ static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) | |||||
| } | } | ||||
| } | } | ||||
| /** X4/X5:下降沿 EXTI + 上拉;先关 NVIC 再配 GPIO,避免配置期间误触发 */ | |||||
| static void PlsrSignalIoGpioExtiInit(void) | static void PlsrSignalIoGpioExtiInit(void) | ||||
| { | { | ||||
| GPIO_InitTypeDef gpio = {0}; | GPIO_InitTypeDef gpio = {0}; | ||||
| @@ -130,6 +159,7 @@ static void PlsrSignalIoGpioExtiInit(void) | |||||
| HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); | HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); | ||||
| } | } | ||||
| /** TIM5:预配 PSC/最大 ARR,默认停表;ScheduleDelayMs 时再装 ARR 并 OPM 启动 */ | |||||
| static void PlsrSignalIoTim5Init(void) | static void PlsrSignalIoTim5Init(void) | ||||
| { | { | ||||
| __HAL_RCC_TIM5_CLK_ENABLE(); | __HAL_RCC_TIM5_CLK_ENABLE(); | ||||
| @@ -189,7 +219,7 @@ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) | |||||
| delay_ms = PLSR_DELAY_MS_MAX; | 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; | ticks = delay_ms * PLSR_TIM5_TICKS_PER_MS; | ||||
| TIM5->CR1 = 0U; | TIM5->CR1 = 0U; | ||||
| @@ -263,6 +293,7 @@ void PlsrSignalIoClearPending(void) | |||||
| s_db_x5.stamp_ok = 0U; | s_db_x5.stamp_ok = 0U; | ||||
| } | } | ||||
| /** wait_x_sel / ext_x_sel:0→X4,非 0→X5 */ | |||||
| static PlsrEdge_t *PlsrSignalIoDbBySel(uint16_t sel_01) | static PlsrEdge_t *PlsrSignalIoDbBySel(uint16_t sel_01) | ||||
| { | { | ||||
| return (sel_01 != 0U) ? &s_db_x5 : &s_db_x4; | return (sel_01 != 0U) ? &s_db_x5 : &s_db_x4; | ||||
| @@ -3,48 +3,105 @@ | |||||
| * @brief WAIT/EXT 外部输入与 TIM5 单次延时 | * @brief WAIT/EXT 外部输入与 TIM5 单次延时 | ||||
| * | * | ||||
| * @details 模块职责 | * @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_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 | #ifndef PLSR_SIGNAL_IO_H | ||||
| #define PLSR_SIGNAL_IO_H | #define PLSR_SIGNAL_IO_H | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| /** | |||||
| * @brief 初始化 X4/X5 EXTI 消抖状态与 TIM5(不启动延时) | |||||
| * @note 调用时机:PlsrInit,在 RunControl 之前 | |||||
| * @note 副作用:清 pending/fell;TIM5 停表、OPM 单次模式预配置 | |||||
| */ | |||||
| void PlsrSignalIoInit(void); | void PlsrSignalIoInit(void); | ||||
| /** | /** | ||||
| * @brief 武装 TIM5 单次延时(ms),到期进 OnDelayTimer | |||||
| * @brief 武装 TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer | |||||
| * @param delay_ms 0 按 1ms;上限 65535ms | * @param delay_ms 0 按 1ms;上限 65535ms | ||||
| * @note 重入会先 Cancel 再重装;OPM 计满自动停表 | |||||
| */ | */ | ||||
| void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); | void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); | ||||
| /** @brief 停止 TIM5 单次延时,s_delay_armed=0 */ | |||||
| void PlsrSignalIoCancelDelay(void); | void PlsrSignalIoCancelDelay(void); | ||||
| /** | /** | ||||
| * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) | * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) | ||||
| * @note ACT 脉冲先完、定时仍走时,用于续装剩余 ACT 时间 | |||||
| */ | */ | ||||
| uint32_t PlsrSignalIoDelayRemainMs(void); | uint32_t PlsrSignalIoDelayRemainMs(void); | ||||
| /** | |||||
| * @brief OS 节拍消抖轮询(X4/X5 各跑一遍 Confirm) | |||||
| * @note 由 PlsrTask 每 tick 调用;满 PLSR_SIGNAL_DEBOUNCE_MS 且仍低才置 fell | |||||
| */ | |||||
| void PlsrSignalIoDebouncePoll(void); | void PlsrSignalIoDebouncePoll(void); | ||||
| /** | |||||
| * @brief 清 X4/X5 的 pending 与 fell(开跑 EXT 段、FinishAll 时用) | |||||
| * @note 丢弃尚未确认的沿与已锁存沿,防误切段 | |||||
| */ | |||||
| void PlsrSignalIoClearEdges(void); | void PlsrSignalIoClearEdges(void); | ||||
| /** | |||||
| * @brief 只清 pending/stamp,保留已确认的 fell | |||||
| * @note 纯 EXT 段末:清消抖中的假沿,已确认沿留给 TakeExtFalling | |||||
| */ | |||||
| void PlsrSignalIoClearPending(void); | void PlsrSignalIoClearPending(void); | ||||
| /** | |||||
| * @brief 消费 WAIT 口(wait_x_sel)上已确认的下降沿 | |||||
| * @return 1=本次取到有效沿并已清零 fell;0=无沿 | |||||
| * @note 仅认消抖后的 fell,不读当前 GPIO 电平(防虚低误跳) | |||||
| */ | |||||
| uint8_t PlsrSignalIoTakeWaitFalling(void); | 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); | uint8_t PlsrSignalIoTakeExtFalling(void); | ||||
| /** @brief TIM5 UPDATE:单次延时到期 */ | |||||
| /** @brief TIM5 UPDATE 中断入口:单次延时到期 → run_control */ | |||||
| void PlsrSignalIoOnTim5Irq(void); | void PlsrSignalIoOnTim5Irq(void); | ||||
| /** @brief EXTI9_5 中断入口(X4 / PB5) */ | |||||
| void PlsrSignalIoOnExti9_5(void); | void PlsrSignalIoOnExti9_5(void); | ||||
| /** @brief EXTI15_10 中断入口(X5 / PG12) */ | |||||
| void PlsrSignalIoOnExti15_10(void); | void PlsrSignalIoOnExti15_10(void); | ||||
| #endif | #endif | ||||