| @@ -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 <stdint.h> | |||
| @@ -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) | |||
| { | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -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)) | |||
| { | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||