From ad2a6c7d7693755aa4ddab72dd64a6df0f5628b3 Mon Sep 17 00:00:00 2001 From: hanyongwei <2043702190@qq.com> Date: Mon, 31 Aug 2026 13:35:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E9=83=A8=E5=88=86=E5=86=97?= =?UTF-8?q?=E4=BD=99=E8=AE=BE=E8=AE=A12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plsr/accel_curve/plsr_accel_curve.c | 102 +++--------------- plsr/accel_curve/plsr_accel_curve.h | 8 -- plsr/pulse_driver/plsr_pulse_driver.c | 103 +++++++++---------- plsr/run_control/plsr_run_control.c | 143 ++++---------------------- plsr/run_control/plsr_run_control.h | 6 -- plsr/signal_io/plsr_signal_io.c | 139 +++++++++++++------------ plsr/signal_io/plsr_signal_io.h | 23 +---- 7 files changed, 159 insertions(+), 365 deletions(-) diff --git a/plsr/accel_curve/plsr_accel_curve.c b/plsr/accel_curve/plsr_accel_curve.c index 8c54807..239904a 100644 --- a/plsr/accel_curve/plsr_accel_curve.c +++ b/plsr/accel_curve/plsr_accel_curve.c @@ -1,50 +1,6 @@ /** * @file plsr_accel_curve.c - * @brief 脉冲域规划/取频:直线 frequency_hz^2=start_frequency_hz^2±2an;S/正弦按时间轴剖面 - * - * ============================================================================ - * 【阅读指南 — 先看这个再往下翻 if/else】 - * ============================================================================ - * - * 本文件只做一件事:给定「段入口 start_frequency_hz、目标 target_frequency_hz、出口 end_frequency_hz、总脉冲 N、 - * 曲线模式 curve_mode」,算出三相预算 accel_pulses / constant_pulses / decel_pulses,以及运行时每拍该什么频率。 - * - * 一、名词(全文件统一) - * start_frequency_hz 段入口频率(规划后实际起点,常由 run_control 的 ResolveStartHz 给出) - * target_frequency_hz 段目标峰值(段表频率;脉冲不够时会被 FitPeak 压低) - * end_frequency_hz 段出口频率(run_control 按 wait/止速 解析后传入) - * accel_pulses 从 start_frequency_hz 爬到 target_frequency_hz 需要的脉冲个数 - * constant_pulses 保持 target_frequency_hz 的匀速脉冲个数 - * decel_pulses 从 target_frequency_hz 收到 end_frequency_hz 的脉冲个数 - * accel_rate_hz_per_s 加速斜率 Hz/s ≈ default_speed*1000/acceleration_time_ms - * decel_rate_hz_per_s 减速斜率 Hz/s ≈ default_speed*1000/deceleration_time_ms - * - * 二、三种曲线模式(curve_mode) - * LINEAR 直线:f_n = sqrt(start_frequency_hz^2 ± 2*a*pulse_count);ISR 里 SquareStep ±1Hz 逼近 - * S 时间域 jerk 梯形;规划/ISR 用离散 Δt=1/frequency_hz 仿真;ISR 查预建表 - * SINE raised-cosine 时间剖面;同上,查预建表 - * - * 三、主流程(函数调用链) - * ResolveStartHz / ResolveEndHz → 起跳/落地(配置起止速=0 时用) - * PlsrAccelCurvePlan → 估 acc/dec/const;不够则砍匀速再降峰 - * PlsrAccelPrebuildFrequencyTables → S/正弦预建频率表(任务上下文,勿在 ISR) - * PlsrAccelCurveFreqAtPulse → 按「已完成脉冲数」取频(任务侧 RefreshProfile) - * PlsrAccelNextFrequency → ISR 每 UPDATE 前进一步,返回下一拍频率 - * - * 四、Plan 里 if/else 决策树(见 PlsrAccelCurvePlan 内注释) - * total=0 → 全 0 返回 - * acc+dec <= total → 有匀速:const = total - acc - dec - * acc+dec > total → 无匀速:FitPeak 降 target_frequency_hz 直到 acc+dec 放得下 - * - * 五、FreqAtPulse 的 completed_segment_pulses 含义(易混) - * completed_segment_pulses = 已完成脉冲数(不是「当前第几拍」) - * done=0 → 第 1 拍频率 = start_frequency_hz;done=1 → 第 2 拍 ≈ sqrt(start_frequency_hz^2+2a) … - * - * @details 模块职责 - * 实现 plsr_accel_curve.h 全部 API。核心路径: - * Plan → 估 accel_pulses/decel_pulses → 不够则先砍 constant_pulses,再 FitPeak 降峰; - * BeginAcceleration/BeginDeceleration/BeginConstantSpeed + NextFrequency - * 供 ISR 每个脉冲更新频率。 + * @brief 脉冲域规划/取频:直线 frequency_hz^2=start_frequency_hz^2±2an;S/正弦按时间 */ #include "plsr_accel_curve.h" #include @@ -334,7 +290,7 @@ static uint32_t PlsrAccelCurveCalculateLinearRampPulses(uint32_t start_frequency } /*============================================================================*/ -/* S / 正弦:时间轴剖面(加速度梯形 / raised-cosine) */ +/* S / 正弦 */ /*============================================================================*/ /** @@ -427,7 +383,7 @@ static uint32_t PlsrAccelCurveDeltaHzFromJerk(uint32_t acceleration_hz_per_s, ui } /** - * 时刻 elapsed_time_us 的 S 剖面频率(相对进度 0..frequency_difference_hz,再映射到 start_frequency_hz→end_frequency_hz)。 + * 时刻 elapsed_time_us 的频率(相对进度 0..frequency_difference_hz,再映射到 start_frequency_hz→end_frequency_hz)。 */ static uint32_t PlsrAccelCurveSCurveFrequencyAtTime(uint32_t start_frequency_hz, uint32_t end_frequency_hz, uint32_t acceleration_hz_per_s, uint32_t jerk_time_us, uint32_t constant_accel_time_us, @@ -539,8 +495,7 @@ static uint32_t PlsrAccelCurveSineFrequencyAtTime(uint32_t start_frequency_hz, u } /** - * 与 ISR / 建表一致:逐拍 Δt=1/frequency_hz 推进时间剖面,得到 S/正弦斜坡脉冲数。 - * (旧版 ∫frequency_hz dt 解析估算已废弃,避免与离散运行不一致。) + * 与 ISR / 建表一致:逐拍 Δt=1/frequency_hz 推进时间,得到 S/正弦斜坡脉冲数。 */ static uint32_t PlsrAccelCurveSimulateRampPulses(uint32_t start_frequency_hz, uint32_t end_frequency_hz, uint32_t acceleration_hz_per_s, @@ -762,21 +717,6 @@ static uint32_t PlsrAccelCurveFitTargetToPulseBudget(uint32_t total_pulses, /** * @brief 规划一整段脉冲域(见 plsr_accel_curve.h) - * - * 【输入】total_pulses=N, start_frequency_hz, target_frequency_hz, end_frequency_hz, default_speed_hz, accel/deceleration_time_ms, curve_mode - * 【输出】plan 里填 accel_pulses, constant_pulses, decel_pulses, start_frequency_hz, target_frequency_hz(可能被降峰), end_frequency_hz, accel_rate_hz_per_s, decel_rate_hz_per_s - * - * 步骤概要: - * 1) 钳频;start_frequency_hz/end_frequency_hz<1 时用 ResolveStart/EndHz 补起跳/落地 - * 2) 算 accel_rate_hz_per_s、decel_rate_hz_per_s(default 为 0 时回退为 |频差|*1000/时间) - * 3) 估 accel_pulses(start_frequency_hz→peak_frequency_hz)、decel_pulses(peak_frequency_hz→end_frequency_hz);选 accel_rate_hz_per_s 还是 decel_rate_hz_per_s 看升降方向 - * 4) 分配 constant_pulses 或 FitPeak 三角波 - * - * 波形形状记忆: - * target_frequency_hz(peak_frequency_hz) ───── const ─────┐ - * / \\ dec - * start_frequency_hz end_frequency_hz - * accel_pulses constant_pulses decel_pulses */ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, uint32_t total_pulses, @@ -980,15 +920,6 @@ static uint32_t PlsrAccelCurveTimedFrequencyAtPulse( /** * @brief 按已完成脉冲数取频(任务侧;ISR 用 PlsrAccelNextFrequency) - * - * 【分段逻辑 — 按 completed_segment_pulses 落在哪一段】 - * - * completed_segment_pulses ∈ [0, accel_pulses) → 加速相(start_frequency_hz → target_frequency_hz) - * completed_segment_pulses ∈ [accel_pulses, accel_pulses+constant_pulses) → 匀速 target_frequency_hz - * completed_segment_pulses ∈ [accel_pulses+constant_pulses, …) → 减速相(target_frequency_hz → end_frequency_hz) - * - * 注意 completed_segment_pulses 是「已经发完的个数」: - * Start 后第 1 拍进行中时 done 仍为 0,FreqAtPulse(0)=start_frequency_hz。 */ uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, uint32_t completed_segment_pulses) @@ -1084,7 +1015,7 @@ uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, /*============================================================================*/ /** - * @brief 为 S/正弦准备时间剖面(加速度梯形 / raised-cosine) + * @brief 为 S/正弦准备下一拍频率 */ static uint32_t PlsrAccelNextFrequencyTimed(PlsrAccelRuntime_t *runtime); @@ -1179,7 +1110,7 @@ static void PlsrAccelRuntimeSimulateOnePulse(PlsrAccelRuntime_t *runtime) } /** - * 将本相离散剖面写入 frequency_table[],返回写入长度;*table_stride_out 为抽样步长。 + * 将本相离散频率写入 frequency_table[],返回写入长度;*table_stride_out 为抽样步长。 */ static uint32_t PlsrAccelRuntimeBuildFrequencyTable(PlsrAccelRuntime_t *runtime, uint32_t *frequency_table, @@ -1317,7 +1248,7 @@ static void PlsrAccelRuntimeSelectFrequencyTable(PlsrAccelRuntime_t *runtime, runtime->frequency_table_id = (table_length > 0U) ? frequency_table_id : 0U; } -/** @brief 进入加速相(见 plsr_accel_curve.h) */ +/** @brief 进入加速相 */ void PlsrAccelBeginAcceleration(PlsrAccelRuntime_t *runtime, const PlsrAccelPlan_t *plan) { @@ -1336,7 +1267,7 @@ void PlsrAccelBeginAcceleration(PlsrAccelRuntime_t *runtime, s_acceleration_table_stride); } -/** @brief 进入减速相(见 plsr_accel_curve.h) */ +/** @brief 进入减速相 */ void PlsrAccelBeginDeceleration(PlsrAccelRuntime_t *runtime, const PlsrAccelPlan_t *plan) { @@ -1356,7 +1287,7 @@ void PlsrAccelBeginDeceleration(PlsrAccelRuntime_t *runtime, s_deceleration_table_stride); } -/** @brief 进入匀速相(见 plsr_accel_curve.h) */ +/** @brief 进入匀速相 */ void PlsrAccelBeginConstantSpeed(PlsrAccelRuntime_t *runtime, const PlsrAccelPlan_t *plan) { @@ -1467,7 +1398,7 @@ static uint32_t PlsrAccelNextFrequencyLinear(PlsrAccelRuntime_t *runtime) } /** - * S/正弦时间域步进:上一拍周期推进 t,再按剖面取下一拍频率。 + * S/正弦时间域步进:上一拍周期推进 t,再按时间取下一拍频率。 */ static uint32_t PlsrAccelNextFrequencyTimed(PlsrAccelRuntime_t *runtime) { @@ -1514,7 +1445,7 @@ static uint32_t PlsrAccelNextFrequencyTimed(PlsrAccelRuntime_t *runtime) return frequency_hz; } -/*仿真离散脉冲剖面,算出该段加减速一共需要输出多少个脉冲*/ +/*仿真离散脉冲频率,算出该段加减速一共需要输出多少个脉冲*/ static uint32_t PlsrAccelCurveSimulateRampPulses(uint32_t start_frequency_hz, uint32_t end_frequency_hz, uint32_t acceleration_hz_per_s, PlsrAccelMode_e curve_mode) @@ -1554,16 +1485,7 @@ static uint32_t PlsrAccelCurveSimulateRampPulses(uint32_t start_frequency_hz, ui } /** - * @brief ISR 热路径:本相再前进 1 脉冲,返回「下一拍应写入 ARR 的频率」 - * - * 调用时机:每个 PWM UPDATE 中断里,在本段已发脉冲数加一之后。 - * - * 模式分支: - * LINEAR → SquareStep:用 frequency_hz±1 逼近 sqrt(start_frequency_hz^2±2an),不全量开方 - * S/SINE+表 → 查 s_acceleration_frequency_table / s_deceleration_frequency_table(Prebuild 填好),table_index=(pulse_count-1)/table_stride - * S/SINE无表 → 兜底 TimeStep(不应出现在正常路径) - * - * pulse_count 含义:本相已 Step 次数;BeginAcc 后第 1 次 Step 时 pulse_count 变为 1,对应「第 2 拍」频率。 + * @brief ISR 热路径:本相再前进 1 脉冲,返回下一拍应写入 ARR 的频率 */ uint32_t PlsrAccelNextFrequency(PlsrAccelRuntime_t *runtime) { diff --git a/plsr/accel_curve/plsr_accel_curve.h b/plsr/accel_curve/plsr_accel_curve.h index 0f32beb..f1fd23c 100644 --- a/plsr/accel_curve/plsr_accel_curve.h +++ b/plsr/accel_curve/plsr_accel_curve.h @@ -4,14 +4,6 @@ * * @details 按起/峰/止频率、斜率时间与总脉冲规划三相脉冲预算,并在脉冲序号上取频。 * 不读 wait_type,不碰 TIM/GPIO。 - * - * 直线:下一频率由起点频率、变化率和已完成脉冲数计算(ISR 增量逼近)。 - * S/正弦:开相时预计算频率表;ISR 只按脉冲序号查表(匀速相不改频)。 - * 规划脉冲数与建表使用同一套离散逐拍仿真。 - * - * 术语(全模块统一): - * 字段使用完整名称和单位,例如 accel_pulses、start_frequency_hz, - * 避免阅读代码时反复猜测 acc_n、f_cur、a_acc 等数学缩写。 */ #ifndef PLSR_ACCEL_CURVE_H #define PLSR_ACCEL_CURVE_H diff --git a/plsr/pulse_driver/plsr_pulse_driver.c b/plsr/pulse_driver/plsr_pulse_driver.c index 827c175..d496bd4 100644 --- a/plsr/pulse_driver/plsr_pulse_driver.c +++ b/plsr/pulse_driver/plsr_pulse_driver.c @@ -33,13 +33,6 @@ TIM_HandleTypeDef htim13; #define PLSR_Y3_PORT GPIOF #define PLSR_Y3_PIN GPIO_PIN_9 -/* - * ---------- 模块静态状态 ---------- - * g_plsr_output_frequency_hz/psc/arr — 上次量化结果;匀速热路径比较后可跳过写寄存器 - * s_active_htim — 当前选中脉冲路 HAL 句柄 - * s_psc_locked / locked_psc — 段内 PSC 锁;跨度过大时 locked=0 - * s_req_freq / pending — ISR 无法换 PSC 时的挂起改频 - */ volatile uint32_t g_plsr_output_frequency_hz; volatile uint8_t g_plsr_pwm_running; uint8_t g_plsr_direction_forward; @@ -155,13 +148,18 @@ static void PlsrPulseDriverOcForceLowKeepRun(TIM_HandleTypeDef *htim) return; } - ccmr = htim->Instance->CCMR1; - ccmr &= (uint32_t)(~TIM_CCMR1_OC1M); - ccmr |= TIM_OCMODE_FORCED_INACTIVE; + ccmr = htim->Instance->CCMR1;//Output Compare 1 Mode,输出比较 1 模式 + //- CC1E=1:OC 通道输出到 GPIO 引脚; + //- CC1E=0:通道输出关闭,引脚由 GPIO 寄存器控制。 + ccmr &= (uint32_t)(~TIM_CCMR1_OC1M);//把[6:4]变成0 + ccmr |= TIM_OCMODE_FORCED_INACTIVE;//写入0b100 强制无效模式到OC1M域 htim->Instance->CCMR1 = ccmr; htim->Instance->CCER |= TIM_CCER_CC1E; } +/** + * Forced inactive 拉低,关掉并清标志 + */ static void PlsrPulseDriverOcHoldLow(TIM_HandleTypeDef *htim) { uint32_t ccmr; @@ -197,7 +195,7 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) __HAL_RCC_GPIOF_CLK_ENABLE(); gpio.Mode = GPIO_MODE_AF_PP; - /* 下拉:通道若短暂高阻时保持低,避免上拉把「已高」盖住导致看不到开表上升沿 */ + /* 下拉:通道若短暂高阻时保持低,避免上拉导致看不到开表上升沿 */ gpio.Pull = GPIO_PULLDOWN; gpio.Speed = GPIO_SPEED_FREQ_HIGH; @@ -250,6 +248,7 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst * ARR/CCR 预装载打开:改频只写入影子寄存器,在下一 UPDATE 生效。 * 避免关预装载时在 ISR 里直接改 ARR/CCR 产生毛刺边沿(段越长多计越多, * 如设定 5000 实测 5007)。软件侧超前一拍写频率补偿延迟。 + * polarity极性 */ htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_PWM_Init(htim) != HAL_OK) @@ -259,7 +258,7 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst oc.OCMode = TIM_OCMODE_PWM1; oc.Pulse = 500U; - oc.OCPolarity = TIM_OCPOLARITY_HIGH; + oc.OCPolarity = TIM_OCPOLARITY_HIGH;//0高电平有效,1低电平有效 oc.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(htim, &oc, TIM_CHANNEL_1) != HAL_OK) { @@ -270,7 +269,7 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst PlsrPulseDriverOcHoldLow(htim); } -/** @brief 驱动初始化(见 plsr_pulse_driver.h) */ +/** @brief 驱动初始化 */ void PlsrPulseDriverInit(void) { g_plsr_output_frequency_hz = 0U; @@ -295,7 +294,7 @@ void PlsrPulseDriverInit(void) PlsrPulseDriverTimInitOne(&htim13, TIM13); } -/** @brief 写方向脚(见 plsr_pulse_driver.h) */ +/** @brief 写方向脚 */ void PlsrPulseDriverSetDir(uint8_t forward) { uint8_t level; @@ -314,7 +313,7 @@ void PlsrPulseDriverSetDir(uint8_t forward) g_plsr_direction_valid = 1U; } -/** @brief 方向脚拉低(见 plsr_pulse_driver.h) */ +/** @brief 方向脚拉低 */ void PlsrPulseDriverClearDir(void) { HAL_GPIO_WritePin(PLSR_Y3_PORT, PLSR_Y3_PIN, GPIO_PIN_RESET); @@ -322,7 +321,7 @@ void PlsrPulseDriverClearDir(void) g_plsr_direction_valid = 0U; } -/** @brief 是否脉冲 TIM(见 plsr_pulse_driver.h) */ +/** @brief 是否脉冲 TIM */ uint8_t PlsrPulseDriverIsPulseTim(TIM_TypeDef *instance) { return ((instance == TIM10) || (instance == TIM11) || (instance == TIM13)) ? 1U : 0U; @@ -333,7 +332,7 @@ uint8_t PlsrPulseDriverIsPulseTim(TIM_TypeDef *instance) /*============================================================================*/ /* - * STM32 定时器频率换算(本模块约定) + * STM32 定时器频率换算 * clk_hz — TIM 计数时钟(APB2=168MHz 或 APB1=84MHz) * psc — 预分频寄存器值,实际分频 = PSC+1 * arr — 自动重装载,一个 PWM 周期 = (ARR+1)×(PSC+1) 个计数 @@ -365,7 +364,7 @@ static void PlsrPulseDriverCalcArrWithPsc(uint32_t clk_hz, uint32_t freq_hz, uin { ticks = 2UL; } - + a = ticks / (psc + 1UL); if (a < 2UL) { @@ -415,7 +414,7 @@ static void PlsrPulseDriverCalcPscArr(uint32_t clk_hz, uint32_t freq_hz, ticks = 2UL; } - p = (ticks + 65535UL - 1UL) / 65535UL; + p = (ticks + 65536UL - 1UL) / 65536UL; if (p == 0UL) { p = 1UL; @@ -430,13 +429,13 @@ static void PlsrPulseDriverCalcPscArr(uint32_t clk_hz, uint32_t freq_hz, PlsrPulseDriverCalcArrWithPsc(clk_hz, freq_hz, p, arr, ccr); } -/** @brief 按频率跨度锁 PSC(见 plsr_pulse_driver.h) */ +/** @brief 按频率跨度锁 PSC */ void PlsrPulseDriverLockPscRange(uint32_t f_min_hz, uint32_t f_max_hz) { - uint32_t ticks_lo; - uint32_t ticks_hi; - uint32_t psc_div_lo; - uint32_t psc_div_hi; + uint32_t ticks_max; + uint32_t ticks_min; + uint32_t psc_div_max; + uint32_t psc_div_min; uint32_t div; uint32_t clk_hz = PlsrPulseDriverTimClkHz( PlsrPulseDriverHtimByY(PlsrPulseDriverSelectY())); @@ -451,35 +450,35 @@ void PlsrPulseDriverLockPscRange(uint32_t f_min_hz, uint32_t f_max_hz) } if (f_max_hz < f_min_hz) { - uint32_t t = f_min_hz; + uint32_t temp = f_min_hz; f_min_hz = f_max_hz; - f_max_hz = t; + f_max_hz = temp; } - ticks_lo = clk_hz / f_min_hz; - ticks_hi = clk_hz / f_max_hz; - if (ticks_lo < 2UL) + ticks_max = clk_hz / f_min_hz; + ticks_min = clk_hz / f_max_hz; + if (ticks_max < 2UL) { - ticks_lo = 2UL; + ticks_max = 2UL; } - if (ticks_hi < 2UL) + if (ticks_min < 2UL) { - ticks_hi = 2UL; + ticks_min = 2UL; } /* psc_div = PSC+1:低频要求 div 足够大,高频要求 div 足够小 */ - psc_div_lo = (ticks_lo + 65535UL) / 65536UL; - if (psc_div_lo < 1UL) + psc_div_min = (ticks_max + 65535UL) / 65536UL; + if (psc_div_min < 1UL) { - psc_div_lo = 1UL; + psc_div_min = 1UL; } - psc_div_hi = ticks_hi / 2UL; - if (psc_div_hi < 1UL) + psc_div_max = ticks_min / 2UL; + if (psc_div_max < 1UL) { - psc_div_hi = 1UL; + psc_div_max = 1UL; } - if (psc_div_lo > psc_div_hi) + if (psc_div_min > psc_div_max) { /* * 单一 PSC 无法同时覆盖 f_min~f_max(例如 1Hz~很高频)。 @@ -490,12 +489,12 @@ void PlsrPulseDriverLockPscRange(uint32_t f_min_hz, uint32_t f_max_hz) return; } - div = psc_div_lo; + div = psc_div_min; s_locked_psc = div - 1UL; s_psc_locked = 1U; } -/** @brief 解除 PSC 锁(见 plsr_pulse_driver.h) */ +/** @brief 解除 PSC 锁 */ void PlsrPulseDriverUnlockPsc(void) { s_psc_locked = 0U; @@ -688,13 +687,13 @@ static void PlsrPulseDriverStartCommon(uint32_t freq_hz) } } -/** @brief 换向延时期间预装寄存器、CEN 保持关(见 plsr_pulse_driver.h) */ +/** @brief 换向延时期间预装寄存器、CEN 保持关 */ void PlsrPulseDriverPrepare(uint32_t freq_hz) { PlsrPulseDriverLoadRegs(freq_hz); } -/** @brief 任务上下文改频(见 plsr_pulse_driver.h) */ +/** @brief 任务上下文改频 */ void PlsrPulseDriverSetFreq(uint32_t freq_hz) { uint32_t psc; @@ -766,7 +765,7 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz) s_last_arr = arr; } -/** @brief ISR 改频(见 plsr_pulse_driver.h) */ +/** @brief ISR 改频 */ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz) { uint32_t psc; @@ -864,19 +863,19 @@ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz) s_last_ccr = ccr; } -/** @brief 丢弃挂起(见 plsr_pulse_driver.h) */ +/** @brief 丢弃挂起 */ void PlsrPulseDriverClearPending(void) { s_req_pending = 0U; } -/** @brief 取消冷启动首拍保护(见 plsr_pulse_driver.h) */ +/** @brief 取消冷启动首拍保护*/ void PlsrPulseDriverClearStartPeriodProtect(void) { s_protect_start_period = 0U; } -/** @brief 消费挂起改频(见 plsr_pulse_driver.h) */ +/** @brief 消费挂起改频 */ void PlsrPulseDriverApplyPending(void) { uint32_t f; @@ -895,13 +894,13 @@ void PlsrPulseDriverApplyPending(void) PlsrPulseDriverSetFreq(f); } -/** @brief 开 PWM + UPDATE 中断(见 plsr_pulse_driver.h) */ +/** @brief 开 PWM + UPDATE 中断 */ void PlsrPulseDriverStart(uint32_t freq_hz) { PlsrPulseDriverStartCommon(freq_hz); } -/** @brief 停三路(见 plsr_pulse_driver.h) */ +/** @brief 停三路 */ void PlsrPulseDriverStop(void) { s_req_pending = 0U; @@ -963,7 +962,7 @@ void PlsrPulseDriverArmActStop(void) } /** - * @brief 段末停表:OPM + 末拍 CC1 下降沿立刻拉低(见 plsr_pulse_driver.h) + * @brief 段末停表:OPM + 末拍 CC1 下降沿立刻拉低 */ void PlsrPulseDriverArmSegEndStop(void) { @@ -994,7 +993,7 @@ void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) PlsrPulseDriverOcForceLowKeepRun(htim); } -/** @brief 取消 OPM(见 plsr_pulse_driver.h) */ +/** @brief 取消 OPM */ void PlsrPulseDriverClearOnePulseStop(void) { TIM_HandleTypeDef *htim = s_active_htim; @@ -1007,7 +1006,7 @@ void PlsrPulseDriverClearOnePulseStop(void) PlsrPulseDriverClearSegEndFallArm(htim); } -/** @brief OPM 段末 UPDATE 后立刻拉低,消除 CNT 回零 PWM1 假上升沿(见 plsr_pulse_driver.h) */ +/** @brief OPM 段末 UPDATE 后立刻拉低,消除 CNT 回零 PWM1 假上升沿 */ void PlsrPulseDriverHoldOutputLow(void) { TIM_HandleTypeDef *htim = s_active_htim; diff --git a/plsr/run_control/plsr_run_control.c b/plsr/run_control/plsr_run_control.c index 58f9499..2d8fa28 100644 --- a/plsr/run_control/plsr_run_control.c +++ b/plsr/run_control/plsr_run_control.c @@ -1,92 +1,6 @@ /** * @file plsr_run_control.c * @brief 运行控制:等待策略 + 开段端点 + 执行驱动 - * - * ============================================================================ - * 【阅读指南 — 两层状态机 + 一条 ISR 主链】 - * ============================================================================ - * - * 第一层:段级状态 RcState_e(整段生命周期) - * RC_IDLE 空闲,等 START - * RC_DIR_WAIT 换向后等 dir_delay_ms(TIM5),到期再开 PWM - * RC_RUN 正在发脉冲(OnPulseIsr 推进) - * RC_WAIT_COND 本段脉冲发完,等时间/信号/EXT 再跳下一段 - * - * 第二层:段内相 RunPhase_e(单段里的加减速,只在 RC_RUN 下有效) - * PH_APPROACH 从 freq_start 爬到 f_tgt - * PH_CONST 匀速 f_tgt(ISR 不改 ARR) - * PH_DECEL 从 f_tgt 收到 freq_end - * - * 主调用链(从 START 到一段脉冲): - * Start → BeginSeg → ResolveSegPlanEndpoints(算起/止频) - * → PlanSeg → accel_curve Plan + Prebuild - * → RefreshProfile(1) 开表 - * → [DIR_WAIT?] → OnPulseIsr 循环 - * - * 关键变量: - * s_segment_pulses_done / s_segment_pulse_target 本段已发 / 目标脉冲数 - * remain = target - done(还剩几拍) - * s_phase 段内 APPROACH/CONST/DECEL - * s_stop_after_last_pulse 1=段末停表(非 FOLLOW 无缝);0=到点立刻跳下段 - * - * OnPulseIsr 决策顺序(自上而下,命中 return): - * 1) 非 RC_RUN → 直接 return - * 2) s_waiting_for_last_period → 末拍周期结束,停表,AfterSegDone - * 3) s_segment_pulses_done++,累计 g_plsr_accumulated_pulses - * 4) s_act_waiting_for_pulse_boundary → ACT 到期,任务侧切段 - * 5) s_segment_pulses_done >= target → OPM 末拍 or 立刻 AfterSegDone - * 6) 按 s_phase 改频:CONST 只进减速;APPROACH Step;DECEL Step - * - * ARPE(UPDATE ISR 写影子 → 下一周期才装入工作 ARR): - * 在 remain==k 时写入的频率供下一拍使用;进减速窗为 remain<=dec_n, - * 在 remain==1 写入 f_end,末拍才能吃到止速。勿再用 remain<=dec_n+1, - * 否则会在 remain==2 就强制止速,造成末两拍同频。 - * - * 分层(阶段1薄拆分): - * 1) 曲线(accel_curve):只吃 (f_from,f_tgt,f_end,斜率,N,mode) → 梯形/三角; - * 不读 wait_type。端点须为真实频率(>=1);配置起/止=0 由运行层 - * ResolveStart/EndHz 算起跳/落地后再传入。 - * 2) 等待策略:只决定何时打断/段后等待;统一传递 handoff: - * - 仍在输出且 freq>=1 → 链式传给下一段 - * - 已停表/输出为 0 → 下一段从配置起速(0 则起跳频率)开跑 - * 3) 本段 f_end 由等待类型 + 发送模式 + 与「下一有效段」方向关系决定: - * - EXT_OR_DONE + 下一有效段同向 + 后续 → 过渡到该段目标 - * - EXT_OR_DONE + 下一有效段同向 + 完成 → f_end=峰值,匀速跑完 - * - EXT_OR_DONE + 下一有效段反向(可跳过中间频率0且脉冲0占位)→ 规划到公共止速 - * - 真末段 → 一律规划到公共止速 - * - TIME/SIGNAL/ACT/EXT → 完成/后续相同,规划到公共止速 - * FOLLOW 另允许 EXT_OR_DONE 同向不停表衔接(直接下一段不得为无效占位)。 - * 4) PlanSeg / ChangeFreq 共用规划入口,便于后期动态改频 - * - * 【运行节拍】纯脉冲计数: - * UPDATE ISR:s_segment_pulses_done++;末拍用 ArmSegEndStop 完整收尾; - * 加/减速逐拍 PlsrAccelNextFrequency + SetFreqIsr; - * 匀速复用 ARR(不改频);remain<=dec_n 进减速。 - * 任务 TickMs:ACT(OS 节拍)/ EXT / ApplyPending; - * TIM5 one-shot:换向/WAIT/ACT/ACT剩余;ACT 到期收完当前脉冲并锁低,周期边界再切段。 - * PlsrTask:DebouncePoll + TickMs + WakePend。 - * - * 【段末频率 / ARPE】UPDATE 里写的影子 ARR 在下一周期生效; - * 减速在 remain==dec_n 切入,共 Step dec_n 次,最后一次(remain==1)写入 f_end。 - * 纯减速开表:Start(f1),后续由 ISR 顺序写入 f2…f_end(勿再预步进一拍)。 - * - * 【段末停表】非 FOLLOW-keep 时开段锁 s_stop_after_last_pulse: - * UPDATE≈上升沿;s_segment_pulses_done 到 target 时 ArmSegEndStop(OPM+CC1 末拍下降沿拉低), - * 周期 UPDATE 再 HoldOutputLow + AfterSegDone 且不再 ++。FOLLOW-keep 到点立刻衔接。 - * - * 【故障诊断 0x2006】(PlsrCommandSetFault,告警码 1~3 不停机;4 禁止启动) - * 0x01 段结束 AfterSegDone:s_segment_pulses_done != s_segment_pulse_target - * 0x02 PlanSeg 后:规划峰值达不到设定 f_tgt(典型三角) - * 0x03 加速离开 APPROACH / 减速段末:相内脉冲 ≠ 规划预算 - * 0x04 目标频率 ∉ [1,100000]:Start / BeginSeg / ChangeFreq / 运行中写频 - * - * 符号约定(与 accel_curve / param 对齐) - * seg_idx0 — 段下标 0-based(寄存器/界面段号为 1-based) - * f_tgt — 段表目标频率 Hz(规划输入,可能与规划峰值不同) - * s_segment_pulses_done — 本段已发脉冲数;s_segment_pulse_target — 本段目标脉冲数 - * g_plsr_accel_plan.accel_pulses — PlanSeg 锁定的加速相脉冲预算(0x03 诊断用) - * g_plsr_accel_runtime.total_pulses — 进入减速相时锁定的步进预算(可能因 ARPE 缩短) - * remain — 本段剩余脉冲 = s_segment_pulse_target - s_segment_pulses_done */ #include "plsr_run_control.h" #include "plsr.h" @@ -100,9 +14,9 @@ /* * ---------- 运行主状态机 RcState_e ---------- - * RC_IDLE — 无段在执行;可 Start;脉冲 ISR 不推进 - * RC_DIR_WAIT — 真换向后 TIM5 one-shot 等 dir_delay;到期 ISR 内开 PWM - * RC_RUN — 脉冲 ISR 推进;TickMs 处理 EXT / ACT(OS) + * RC_IDLE — 无段在执行 + * RC_DIR_WAIT — 真换向后 TIM5等 dir_delay;到期 ISR 内开 PWM + * RC_RUN — 脉冲 ISR 推进;TickMs 处理 EXT / ACT时间 * RC_WAIT_COND — 段后等待:TIM5 时间 或 WAIT/EXT 沿 */ typedef enum { @@ -113,7 +27,7 @@ typedef enum { } RcState_e; /* - * ---------- 段内运行相 RunPhase_e(脉冲 ISR 热路径) ---------- + * ---------- 段内运行相 RunPhase_e ---------- * PH_APPROACH — 起速 → 本段峰值 f_tgt * PH_CONST — 匀速,复用 ARR 不写寄存器 * PH_DECEL — 段末过渡 → f_end(可升可降) @@ -124,12 +38,9 @@ typedef enum { PH_DECEL } RunPhase_e; -/* 其它 PLSR 模块需要读取的运行结果只保存这一份。 */ volatile uint8_t g_plsr_busy; volatile uint16_t g_plsr_current_segment_index; volatile int32_t g_plsr_accumulated_pulses; - -/* 运行控制内部状态保持一层变量,避免 s_run.xxx 的结构体嵌套。 */ static volatile RcState_e s_state; static volatile uint8_t s_forward; static volatile int32_t s_segment_pulses_done; @@ -150,9 +61,7 @@ static uint8_t s_stop_after_last_pulse; static volatile uint8_t s_waiting_for_last_period; static OS_EVENT *s_wake_sem; -/** - * 策略层解析后交给 PlanSeg 的段端点(不含 wait_type,仅频率与时间) - */ +/* 策略层解析后交给 PlanSeg 的段端点 */ typedef struct { uint32_t freq_start_hz; /* 本段入口频率 Hz(>=1,由衔接/起速解析) */ uint32_t freq_end_hz; /* 本段出口频率 Hz(由 wait/send/方向解析) */ @@ -190,19 +99,19 @@ static void PlsrRunControlWakePost(void); static void PlsrRunControlWakeDrain(void); /** - * @brief 解析下一段索引(原 path_plan) + * @brief 解析下一段索引 * @note jump=0:末段结束,非末段顺序下一段;jump=1..N:跳到对应段;其它结束 */ -static int16_t PlsrRunControlNextSeg(uint16_t cur_seg_0based) +static int16_t PlsrRunControlNextSeg(uint16_t cur_seg_based) { uint16_t n = PlsrParamGetSegCount(); - uint16_t jump = g_plsr_segments[cur_seg_0based].jump_seg; + uint16_t jump = g_plsr_segments[cur_seg_based].jump_seg; if (jump == 0U) { - if ((n >= 1U) && (cur_seg_0based + 1U < n)) + if ((n >= 1U) && (cur_seg_based + 1U < n)) { - return (int16_t)(cur_seg_0based + 1U); + return (int16_t)(cur_seg_based + 1U); } return -1; } @@ -214,21 +123,21 @@ static int16_t PlsrRunControlNextSeg(uint16_t cur_seg_0based) } /** - * @brief 无效占位段:频率 0 且脉冲 0(不参与 EXT_OR_DONE 同向衔接/换向判断) + * @brief 判断空段,无效占位段:频率 0 且脉冲 0 */ -static uint8_t PlsrRunControlIsEmptyPlaceholderSeg(uint16_t seg_0based) +static uint8_t PlsrRunControlIsEmptyPlaceholderSeg(uint16_t seg_based) { - return ((g_plsr_segments[seg_0based].freq_hz == 0) && - (g_plsr_segments[seg_0based].pulse_cnt == 0)) ? 1U : 0U; + return ((g_plsr_segments[seg_based].freq_hz == 0) && + (g_plsr_segments[seg_based].pulse_cnt == 0)) ? 1U : 0U; } /** * @brief 从 cur 的下一段起,沿 jump 跳过「频率0且脉冲0」无效段,找下一有效段 * @return 有效段 0-based;无则 -1 */ -static int16_t PlsrRunControlNextEffectiveSeg(uint16_t cur_seg_0based) +static int16_t PlsrRunControlNextEffectiveSeg(uint16_t cur_seg_based) { - int16_t next = PlsrRunControlNextSeg(cur_seg_0based); + int16_t next = PlsrRunControlNextSeg(cur_seg_based); uint16_t guard = 0U; while ((next >= 0) && (guard < PLSR_SEG_MAX)) @@ -246,23 +155,23 @@ static int16_t PlsrRunControlNextEffectiveSeg(uint16_t cur_seg_0based) /** * @brief 判断段方向:相对看 pulse_cnt 符号;绝对看目标−局部位置 */ -static uint8_t PlsrRunControlIsForward(uint16_t seg_0based, int32_t acc_pulse) +static uint8_t PlsrRunControlIsForward(uint16_t seg_based, int32_t acc_pulse) { int32_t move; if (g_plsr_config.run_mode == PLSR_POS_ABSOLUTE) { - move = g_plsr_segments[seg_0based].pulse_cnt - acc_pulse; + move = g_plsr_segments[seg_based].pulse_cnt - acc_pulse; } else { - move = g_plsr_segments[seg_0based].pulse_cnt; + move = g_plsr_segments[seg_based].pulse_cnt; } return (move >= 0) ? 1U : 0U; } /** - * @brief 停表并清频率链(段后 WAIT / 反向切段共用) + * @brief 停表并清频率链 */ static void PlsrRunControlClearChainAndStop(void) { @@ -307,19 +216,11 @@ static void PlsrRunControlApplyKeepOrStop(uint8_t allow_act_handoff) } } -/** - * @brief 开跑瞬间武装 ACT/EXT - * @note EXT:清旧沿防误切;WAIT 信号不在此清(段中沿留给※2) - * @note ACT:写时长后武装 TIM5;到期 OPM 收当前拍,下一 UPDATE 再 ActExpire - */ +/* @brief 开始输出时:EXT 类清旧沿;ACT 类启动段内定时 */ static void PlsrRunControlArmActExtOnPulseStart(void) { uint16_t act_time_ms; - - /* - * EXT 中途切段:开跑时丢掉旧沿,避免上一段的 WAIT 沿立刻切段。 - * WAIT 信号不在这里清:段中落地的下降沿要留给※2。 - */ + if ((g_plsr_segments[g_plsr_current_segment_index].wait_type == PLSR_WAIT_EXT) || (g_plsr_segments[g_plsr_current_segment_index].wait_type == PLSR_WAIT_EXT_OR_DONE)) { diff --git a/plsr/run_control/plsr_run_control.h b/plsr/run_control/plsr_run_control.h index ea6d1a0..20c30a9 100644 --- a/plsr/run_control/plsr_run_control.h +++ b/plsr/run_control/plsr_run_control.h @@ -1,12 +1,6 @@ /** * @file plsr_run_control.h * @brief 运行控制:多段脉冲状态机(开段 → 发脉冲 → 等待/跳转 → 收尾) - * - * @details 解析发送模式、等待类型与段间频率衔接;协调 accel_curve / - * pulse_driver / signal_io / param。状态:IDLE / DIR_WAIT / RUN / WAIT_COND。 - * - * 与 accel_curve 对齐的符号: - * f_tgt — 段表目标频率 Hz;segment_pulses_done/target — 已发/目标脉冲数 */ #ifndef PLSR_RUN_CONTROL_H #define PLSR_RUN_CONTROL_H diff --git a/plsr/signal_io/plsr_signal_io.c b/plsr/signal_io/plsr_signal_io.c index ba71b43..f53afaf 100644 --- a/plsr/signal_io/plsr_signal_io.c +++ b/plsr/signal_io/plsr_signal_io.c @@ -8,7 +8,7 @@ * 下降沿触发 OnFalling:仅 pending=1,不在 ISR 里判有效(避免抖动/毛刺)。 * * 【消抖】 - * DebouncePoll(任务上下文,约 1ms 一次): + * DebouncePoll(任务上下文,约 10ms 一次): * pending 且首次 → 记 arm_tick; * 满 PLSR_SIGNAL_DEBOUNCE_MS(20ms)且 PinIsLow → fell=1。 * 上升沿恢复高电平则 Confirm 末尾不置 fell(pending 被清掉)。 @@ -32,7 +32,7 @@ #include "main.h" #include "ucos_ii.h" -/* ---------- 硬件引脚(与 CubeMX / 方案书一致) ---------- */ +/* ---------- 硬件引脚 ---------- */ #define PLSR_X4_PORT GPIOB #define PLSR_X4_PIN GPIO_PIN_5 /* X4:WAIT/EXT 可选 */ #define PLSR_X5_PORT GPIOG @@ -40,7 +40,7 @@ /* * TIM5 时基:84MHz / (8399+1) = 10kHz → 100µs/格。 - * PSC=8399 落在 16 位内;ms 级延时用 100µs 分辨率足够,不必 1µs 时基。 + * PSC=8399 落在 16 位内 */ #define PLSR_TIM5_PSC_100US 8399U #define PLSR_TIM5_TICKS_PER_MS 10U /* 10×100µs = 1ms */ @@ -59,8 +59,8 @@ typedef struct { uint16_t pin; } PlsrEdge_t; -static PlsrEdge_t s_db_x4; /* X4 消抖状态 */ -static PlsrEdge_t s_db_x5; /* X5 消抖状态 */ +static PlsrEdge_t s_debounce_x4; /* X4 消抖状态 */ +static PlsrEdge_t s_debounce_x5; /* X5 消抖状态 */ static volatile uint8_t s_delay_armed; /* 1=TIM5 单次延时正在计时 */ /** 毫秒 → OS tick 数(向上取整,至少 1 tick) */ @@ -81,49 +81,49 @@ static INT32U PlsrSignalIoMsToTicks(uint32_t ms) } /** @return 1=脚当前为低(有效按下/接地) */ -static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) +static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *debounce) { - return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; + return (HAL_GPIO_ReadPin(debounce->port, debounce->pin) == GPIO_PIN_RESET) ? 1U : 0U; } -/** EXTI 下降沿:仅置 pending,消抖在 DebouncePoll 完成 */ -static void PlsrSignalIoOnFalling(PlsrEdge_t *db) +/** EXTI 下降沿:置 pending,重置计时标志消抖在 DebouncePoll 完成 */ +static void PlsrSignalIoOnFalling(PlsrEdge_t *debounce) { - db->pending = 1U; - db->stamp_ok = 0U; + debounce->pending = 1U; + debounce->stamp_ok = 0U; } /** * @brief 单路消抖确认(DebouncePoll 每 tick 调用) * @note 满 debounce_ms 仍低 → fell;否则丢弃 pending */ -static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) +static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *debounce) { INT32U need; - if (db->pending == 0U) + if (debounce->pending == 0U) { return; } - if (db->stamp_ok == 0U) + if (debounce->stamp_ok == 0U) { - db->arm_tick = OSTimeGet(); - db->stamp_ok = 1U; + debounce->arm_tick = OSTimeGet(); + debounce->stamp_ok = 1U; return; } need = PlsrSignalIoMsToTicks(PLSR_SIGNAL_DEBOUNCE_MS); - if ((OSTimeGet() - db->arm_tick) < need) + if ((OSTimeGet() - debounce->arm_tick) < need) { return; } - db->pending = 0U; - db->stamp_ok = 0U; - if (PlsrSignalIoPinIsLow(db) != 0U) + debounce->pending = 0U; + debounce->stamp_ok = 0U; + if (PlsrSignalIoPinIsLow(debounce) != 0U) { - db->fell = 1U; + debounce->fell = 1U; } } @@ -164,14 +164,14 @@ static void PlsrSignalIoTim5Init(void) { __HAL_RCC_TIM5_CLK_ENABLE(); - TIM5->CR1 = 0U; - TIM5->DIER = 0U; - TIM5->SR = 0U; - TIM5->PSC = PLSR_TIM5_PSC_100US; - TIM5->ARR = 0xFFFFFFFFUL; - TIM5->CNT = 0U; - TIM5->EGR = TIM_EGR_UG; - TIM5->SR = 0U; + TIM5->CR1 = 0U;//定时器停止、向上计数、关闭单脉冲、普通边沿对齐 + TIM5->DIER = 0U;//DMA/中断使能寄存器 + TIM5->SR = 0U;//状态寄存器,清除所有中断标志位 + TIM5->PSC = PLSR_TIM5_PSC_100US;//8399,1Mhz + TIM5->ARR = 0xFFFFFFFFUL;//自动重装载寄存器 + TIM5->CNT = 0U;//计数值清零 + TIM5->EGR = TIM_EGR_UG;//事件产生寄存器,软件产生一次中断,不产生中断写的psc不会生效 + TIM5->SR = 0U;//EGR 的 UG 动作会把SR.UIF置 1,这里再次清零中断标志 HAL_NVIC_SetPriority(TIM5_IRQn, 7, 0); HAL_NVIC_EnableIRQ(TIM5_IRQn); @@ -181,29 +181,30 @@ void PlsrSignalIoInit(void) { s_delay_armed = 0U; - s_db_x4.port = PLSR_X4_PORT; - s_db_x4.pin = PLSR_X4_PIN; - s_db_x4.pending = 0U; - s_db_x4.stamp_ok = 0U; - s_db_x4.arm_tick = 0U; - s_db_x4.fell = 0U; + s_debounce_x4.port = PLSR_X4_PORT; + s_debounce_x4.pin = PLSR_X4_PIN; + s_debounce_x4.pending = 0U; + s_debounce_x4.stamp_ok = 0U; + s_debounce_x4.arm_tick = 0U; + s_debounce_x4.fell = 0U; - s_db_x5.port = PLSR_X5_PORT; - s_db_x5.pin = PLSR_X5_PIN; - s_db_x5.pending = 0U; - s_db_x5.stamp_ok = 0U; - s_db_x5.arm_tick = 0U; - s_db_x5.fell = 0U; + s_debounce_x5.port = PLSR_X5_PORT; + s_debounce_x5.pin = PLSR_X5_PIN; + s_debounce_x5.pending = 0U; + s_debounce_x5.stamp_ok = 0U; + s_debounce_x5.arm_tick = 0U; + s_debounce_x5.fell = 0U; PlsrSignalIoGpioExtiInit(); PlsrSignalIoTim5Init(); - + //中断初始化时可能会进入中断,读取时需要结构体合法 + //为了防止初始化终端后意外触发,需要再次清除标志位 __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X4_PIN); __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X5_PIN); - s_db_x4.fell = 0U; - s_db_x4.pending = 0U; - s_db_x5.fell = 0U; - s_db_x5.pending = 0U; + s_debounce_x4.fell = 0U; + s_debounce_x4.pending = 0U; + s_debounce_x5.fell = 0U; + s_debounce_x5.pending = 0U; } void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) @@ -266,31 +267,33 @@ uint32_t PlsrSignalIoDelayRemainMs(void) return 0U; } left_ticks = (arr + 1U) - cnt; + /*会有个小bug。假定计数剩余个数655个,则要计数65.5ms, + 该函数会向下取整,导致返回65ms。再次进来计数时就会误差0.5ms。理论最大误差1ms。*/ return left_ticks / PLSR_TIM5_TICKS_PER_MS; } void PlsrSignalIoDebouncePoll(void) { - PlsrSignalIoDebounceConfirm(&s_db_x4); - PlsrSignalIoDebounceConfirm(&s_db_x5); + PlsrSignalIoDebounceConfirm(&s_debounce_x4); + PlsrSignalIoDebounceConfirm(&s_debounce_x5); } void PlsrSignalIoClearEdges(void) { - s_db_x4.fell = 0U; - s_db_x4.pending = 0U; - s_db_x4.stamp_ok = 0U; - s_db_x5.fell = 0U; - s_db_x5.pending = 0U; - s_db_x5.stamp_ok = 0U; + s_debounce_x4.fell = 0U; + s_debounce_x4.pending = 0U; + s_debounce_x4.stamp_ok = 0U; + s_debounce_x5.fell = 0U; + s_debounce_x5.pending = 0U; + s_debounce_x5.stamp_ok = 0U; } void PlsrSignalIoClearPending(void) { - s_db_x4.pending = 0U; - s_db_x4.stamp_ok = 0U; - s_db_x5.pending = 0U; - s_db_x5.stamp_ok = 0U; + s_debounce_x4.pending = 0U; + s_debounce_x4.stamp_ok = 0U; + s_debounce_x5.pending = 0U; + s_debounce_x5.stamp_ok = 0U; } uint8_t PlsrSignalIoTakeWaitFalling(void) @@ -298,15 +301,15 @@ uint8_t PlsrSignalIoTakeWaitFalling(void) /* wait_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */ if (g_plsr_config.wait_x_sel == 0U) { - if (s_db_x4.fell != 0U) + if (s_debounce_x4.fell != 0U) { - s_db_x4.fell = 0U; + s_debounce_x4.fell = 0U; return 1U; } } - else if (s_db_x5.fell != 0U) + else if (s_debounce_x5.fell != 0U) { - s_db_x5.fell = 0U; + s_debounce_x5.fell = 0U; return 1U; } return 0U; @@ -317,15 +320,15 @@ uint8_t PlsrSignalIoTakeExtFalling(void) /* ext_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */ if (g_plsr_config.ext_x_sel == 0U) { - if (s_db_x4.fell != 0U) + if (s_debounce_x4.fell != 0U) { - s_db_x4.fell = 0U; + s_debounce_x4.fell = 0U; return 1U; } } - else if (s_db_x5.fell != 0U) + else if (s_debounce_x5.fell != 0U) { - s_db_x5.fell = 0U; + s_debounce_x5.fell = 0U; return 1U; } return 0U; @@ -351,7 +354,7 @@ void PlsrSignalIoOnExti9_5(void) if (__HAL_GPIO_EXTI_GET_IT(PLSR_X4_PIN) != 0U) { __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X4_PIN); - PlsrSignalIoOnFalling(&s_db_x4); + PlsrSignalIoOnFalling(&s_debounce_x4); } } @@ -360,6 +363,6 @@ void PlsrSignalIoOnExti15_10(void) if (__HAL_GPIO_EXTI_GET_IT(PLSR_X5_PIN) != 0U) { __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X5_PIN); - PlsrSignalIoOnFalling(&s_db_x5); + PlsrSignalIoOnFalling(&s_debounce_x5); } } diff --git a/plsr/signal_io/plsr_signal_io.h b/plsr/signal_io/plsr_signal_io.h index 9808dd8..372040a 100644 --- a/plsr/signal_io/plsr_signal_io.h +++ b/plsr/signal_io/plsr_signal_io.h @@ -14,26 +14,11 @@ * 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 调用) + * TIM5 单次延时用途 * - 方向切换延时 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、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 @@ -42,13 +27,11 @@ /** * @brief 初始化 X4/X5 EXTI 消抖状态与 TIM5(不启动延时) - * @note 调用时机:PlsrInit,在 RunControl 之前 - * @note 副作用:清 pending/fell;TIM5 停表、OPM 单次模式预配置 */ void PlsrSignalIoInit(void); /** - * @brief 武装 TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer + * @brief TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer * @param delay_ms 0 按 1ms;上限 65535ms * @note 重入会先 Cancel 再重装;OPM 计满自动停表 */ @@ -58,7 +41,7 @@ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); void PlsrSignalIoCancelDelay(void); /** - * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) + * @brief 读 TIM5 单次延时剩余 ms * @note ACT 脉冲先完、定时仍走时,用于续装剩余 ACT 时间 */ uint32_t PlsrSignalIoDelayRemainMs(void);