| @@ -21,5 +21,3 @@ build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/ | |||
| plsr/test/ | |||
| tools/ | |||
| .cursor/ | |||
| 3. 定位控制手册.pdf | |||
| @@ -118,8 +118,9 @@ int main(void) | |||
| } | |||
| /** | |||
| * @brief 系统时钟:HSE → PLL → 168MHz SYSCLK(APM32F407 / STM32F4 类) | |||
| * @note 须与 OS_CPU_SysTickInit 使用的主频常量一致 | |||
| * @brief 系统时钟:HSE(12MHz) → PLL → 168MHz SYSCLK(APM32F407 / STM32F4 类) | |||
| * @note 须与 OS_CPU_SysTickInit、pulse_driver 使用的主频常量一致 | |||
| * @note PLLM 须等于晶振 MHz:12MHz 用 12;误用 8 会使 SYSCLK=252MHz,脉冲频率偏 1.5 倍 | |||
| */ | |||
| void SystemClock_Config(void) | |||
| { | |||
| @@ -133,10 +134,10 @@ void SystemClock_Config(void) | |||
| RCC_OscInitStruct.HSEState = RCC_HSE_ON; | |||
| RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; | |||
| RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; | |||
| RCC_OscInitStruct.PLL.PLLM = 12; | |||
| RCC_OscInitStruct.PLL.PLLM = 12; /* HSE=12MHz → VCO in = 1MHz */ | |||
| RCC_OscInitStruct.PLL.PLLN = 336; | |||
| RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; | |||
| RCC_OscInitStruct.PLL.PLLQ = 4; | |||
| RCC_OscInitStruct.PLL.PLLQ = 4;/*不准改成7*/ | |||
| if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) | |||
| { | |||
| Error_Handler(); | |||
| @@ -225,12 +225,12 @@ void TIM2_IRQHandler(void) | |||
| } | |||
| /** | |||
| * @brief TIM3:PLSR WAIT/ACT 1ms 时基 | |||
| * @brief TIM5:PLSR 单次延时到期(换向 / WAIT / ACT) | |||
| */ | |||
| void TIM3_IRQHandler(void) | |||
| void TIM5_IRQHandler(void) | |||
| { | |||
| OSIntEnter(); | |||
| PlsrSignalIoOnTim3Irq(); | |||
| PlsrSignalIoOnTim5Irq(); | |||
| OSIntExit(); | |||
| } | |||
| @@ -359,7 +359,6 @@ | |||
| <state>$PROJ_DIR$\..\plsr\param</state> | |||
| <state>$PROJ_DIR$\..\plsr\persist</state> | |||
| <state>$PROJ_DIR$\..\plsr\pulse_driver</state> | |||
| <state>$PROJ_DIR$\..\plsr\path_plan</state> | |||
| <state>$PROJ_DIR$\..\plsr\accel_curve</state> | |||
| <state>$PROJ_DIR$\..\plsr\run_control</state> | |||
| <state>$PROJ_DIR$\..\plsr\command</state> | |||
| @@ -2850,24 +2849,12 @@ | |||
| </group> | |||
| <group> | |||
| <name>plsr</name> | |||
| <group> | |||
| <name>accel_curve</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>command</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\command\plsr_command.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\command\plsr_command.h</name> | |||
| </file> | |||
| </group> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\plsr.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\plsr.h</name> | |||
| </file> | |||
| <group> | |||
| <name>param</name> | |||
| <file> | |||
| @@ -2877,15 +2864,6 @@ | |||
| <name>$PROJ_DIR$\..\plsr\param\plsr_param.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>path_plan</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>persist</name> | |||
| <file> | |||
| @@ -2904,6 +2882,15 @@ | |||
| <name>$PROJ_DIR$\..\plsr\pulse_driver\plsr_pulse_driver.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>accel_curve</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\accel_curve\plsr_accel_curve.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>run_control</name> | |||
| <file> | |||
| @@ -2913,6 +2900,15 @@ | |||
| <name>$PROJ_DIR$\..\plsr\run_control\plsr_run_control.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>command</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\command\plsr_command.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\command\plsr_command.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>signal_io</name> | |||
| <file> | |||
| @@ -2922,12 +2918,6 @@ | |||
| <name>$PROJ_DIR$\..\plsr\signal_io\plsr_signal_io.h</name> | |||
| </file> | |||
| </group> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\plsr.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\plsr.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>ucos</name> | |||
| @@ -3109,15 +3109,6 @@ | |||
| <name>$PROJ_DIR$\..\plsr\param\plsr_param.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>path_plan</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\plsr\path_plan\plsr_path_plan.h</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>persist</name> | |||
| <file> | |||
| @@ -10,12 +10,18 @@ | |||
| * 关键约束 | |||
| * 频率钳制上限 100000Hz;运行门禁非法频率由 command/run_control 报 0x04。 | |||
| * 脉冲预算不够到目标频时:降低峰值(三角,无匀速),调用方写故障 0x02。 | |||
| * | |||
| * 符号约定(与 plsr_accel_curve.h 一致) | |||
| * f_cur/f_tgt/f_end — 段起/峰/止频率 Hz | |||
| * acc_n/dec_n/const_n — 加/减/匀速相脉冲预算(非时间 ms) | |||
| * a_acc/a_dec — 加/减速度 Hz/s | |||
| * pulse_rt — PlsrAccelPulseRt_t,ISR 逐拍改频状态机 | |||
| */ | |||
| #include "plsr_accel_curve.h" | |||
| #include <stdint.h> | |||
| /* 频率大于 100k 时暂时钳到 100k;启动门禁另报 0x04 */ | |||
| uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz) | |||
| static uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz) | |||
| { | |||
| if (freq_hz > 100000U) | |||
| { | |||
| @@ -62,17 +68,12 @@ static uint32_t PlsrAccelCurveIsqrt64(uint64_t val) | |||
| return (uint32_t)x; | |||
| } | |||
| static uint32_t PlsrAccelCurveIsqrt(uint32_t val) | |||
| { | |||
| return PlsrAccelCurveIsqrt64((uint64_t)val); | |||
| } | |||
| /** | |||
| * 起跳频率:f = sqrt(f_from^2 + 2a),a = df*1000/t_ms。 | |||
| * f_from=0 → sqrt(2a);超过 f_to 钳到 f_to。 | |||
| */ | |||
| uint32_t PlsrAccelCurveJumpFreq(uint32_t f_from, uint32_t f_to, | |||
| uint32_t t_ms) | |||
| static uint32_t PlsrAccelCurveJumpFreq(uint32_t f_from, uint32_t f_to, | |||
| uint32_t t_ms) | |||
| { | |||
| uint32_t df; | |||
| uint32_t a_hz_s; | |||
| @@ -132,8 +133,6 @@ static uint32_t PlsrAccelCurveSlopeDen(uint32_t default_spd) | |||
| static uint32_t PlsrAccelCurveRampTimeMs(uint32_t f_from, | |||
| uint32_t f_to, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms) | |||
| { | |||
| @@ -142,9 +141,6 @@ static uint32_t PlsrAccelCurveRampTimeMs(uint32_t f_from, | |||
| uint32_t ref_ms; | |||
| uint32_t t; | |||
| (void)start_spd_ref; | |||
| (void)end_spd_ref; | |||
| if (f_from == f_to) | |||
| { | |||
| return 0U; | |||
| @@ -188,8 +184,6 @@ static uint32_t PlsrAccelCurveRampTimeMs(uint32_t f_from, | |||
| uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | |||
| uint32_t f_tgt, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms) | |||
| { | |||
| @@ -204,7 +198,6 @@ uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | |||
| /* 起速 0 或非 0:都用 JumpFreq = sqrt(f_cfg^2 + 2a) */ | |||
| t_acc = PlsrAccelCurveRampTimeMs(f_cfg, f_tgt, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| return PlsrAccelCurveJumpFreq(f_cfg, f_tgt, t_acc); | |||
| } | |||
| @@ -212,8 +205,6 @@ uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | |||
| uint32_t PlsrAccelCurveResolveEndHz(uint32_t f_cfg, | |||
| uint32_t f_tgt, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms) | |||
| { | |||
| @@ -232,13 +223,11 @@ uint32_t PlsrAccelCurveResolveEndHz(uint32_t f_cfg, | |||
| if (f_cfg <= 1U) | |||
| { | |||
| t_dec = PlsrAccelCurveRampTimeMs(f_tgt, 0U, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| return PlsrAccelCurveJumpFreq(0U, f_tgt, t_dec); | |||
| } | |||
| t_dec = PlsrAccelCurveRampTimeMs(f_tgt, f_cfg, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| return PlsrAccelCurveJumpFreq(f_cfg, f_tgt, t_dec); | |||
| } | |||
| @@ -358,337 +347,6 @@ static uint32_t PlsrAccelCurveLerp(uint32_t a, uint32_t b, uint32_t ratio_permil | |||
| return a - ((a - b) * ratio_permille) / 1000UL; | |||
| } | |||
| /** | |||
| * 斜坡取频(µs 时间轴)。ISR 可用:O(1)。 | |||
| * | |||
| * 直线模式:f = f0 + (f1−f0) · t/T(64 位运算保证精度,每个点在 f-t 直线上)。 | |||
| * S / 正弦:f = f0 + (f1−f0) · shape(t/T)。 | |||
| */ | |||
| uint32_t PlsrAccelCurveFreqOnRampUs(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t t_us, | |||
| uint32_t T_us, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| uint32_t r; | |||
| if ((T_us == 0U) || (t_us >= T_us)) | |||
| { | |||
| return f1; | |||
| } | |||
| if (mode == PLSR_ACCEL_LINEAR) | |||
| { | |||
| /* | |||
| * 纯代数直线:f = f0 + (f1-f0)*t/T,用 64 位避免溢出/截断。 | |||
| * 不走 permille/Lerp,保证点在直线上。 | |||
| */ | |||
| if (f1 >= f0) | |||
| { | |||
| return f0 + (uint32_t)(((uint64_t)(f1 - f0) * (uint64_t)t_us) / (uint64_t)T_us); | |||
| } | |||
| else | |||
| { | |||
| uint32_t drop = (uint32_t)(((uint64_t)(f0 - f1) * (uint64_t)t_us) / (uint64_t)T_us); | |||
| return (drop < f0) ? (f0 - drop) : 0U; | |||
| } | |||
| } | |||
| /* S / 正弦:shape(t/T) → Lerp */ | |||
| r = (uint32_t)(((uint64_t)t_us * 1000ULL) / (uint64_t)T_us); | |||
| if (r > 1000U) | |||
| { | |||
| r = 1000U; | |||
| } | |||
| r = PlsrAccelCurveShapePermille(r, mode); | |||
| return PlsrAccelCurveLerp(f0, f1, r); | |||
| } | |||
| /** | |||
| * 斜坡取频(ms 接口;内部转 µs)。运行时相时间来自 TIM3 1ms 累加。 | |||
| */ | |||
| /** | |||
| * 斜坡取频(ms 接口,内部转 µs 调 FreqOnRampUs)。 | |||
| * 统一入口:直线/S/正弦均走 FreqOnRampUs,不再有额外特例分支。 | |||
| */ | |||
| uint32_t PlsrAccelCurveFreqOnRampMs(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t t_ms, | |||
| uint32_t T_ms, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| if (T_ms < 1U) | |||
| { | |||
| return f1; | |||
| } | |||
| return PlsrAccelCurveFreqOnRampUs(f0, f1, t_ms * 1000UL, T_ms * 1000UL, mode); | |||
| } | |||
| /** | |||
| * 减速落地频率: | |||
| * - f_end>=1:落地即止速 | |||
| * - f_end==0:返回 0(停表)。不再用「对称首档」冒充止速—— | |||
| * 那一档常算成 1Hz,示波器上会变成脉冲发完后还在 1Hz 空转。 | |||
| */ | |||
| uint32_t PlsrAccelCurveLandFreq(uint32_t f_end, | |||
| uint32_t f_peak, | |||
| uint32_t T_ms, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| (void)f_peak; | |||
| (void)T_ms; | |||
| (void)mode; | |||
| if (f_end >= 1U) | |||
| { | |||
| return f_end; | |||
| } | |||
| return 0U; | |||
| } | |||
| /** | |||
| * 斜坡第一拍频率(规划/开跑共用)。 | |||
| * f0>=1:第一拍即 f0。 | |||
| * f0==0:t=0 曲线为 0;若强行按 1Hz 计整周期(~1s),加速时间常被第一拍吃光, | |||
| * 表现为到不了目标频、曲线台阶/长拖尾。此处用 look-ahead 自洽点,且 ≥1Hz。 | |||
| */ | |||
| /** | |||
| * 起速=0 时第一拍自洽频率。 | |||
| * | |||
| * 自洽条件:f 使得 FreqOnRampUs(f0, f1, 10^6/f, T_us) = f | |||
| * | |||
| * 直线模式(f0=0):解析解 f = sqrt(f1 * 10^6 / T_us)。 | |||
| * S/正弦:迭代(取两次平均收敛)。 | |||
| */ | |||
| uint32_t PlsrAccelCurveFirstPulseFreq(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t T_us, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| uint32_t f; | |||
| uint32_t guess; | |||
| uint32_t per; | |||
| uint32_t fnew; | |||
| uint32_t i; | |||
| if (T_us == 0U) | |||
| { | |||
| f = (f1 >= 1U) ? f1 : 1U; | |||
| return f; | |||
| } | |||
| f = PlsrAccelCurveFreqOnRampUs(f0, f1, 0U, T_us, mode); | |||
| if (f >= 1U) | |||
| { | |||
| return f; | |||
| } | |||
| /* 直线 f0=0:f = sqrt(f1 * 1e6 / T_us),用整数牛顿法求 sqrt(val) */ | |||
| if ((mode == PLSR_ACCEL_LINEAR) && (f0 == 0U) && (f1 >= 1U)) | |||
| { | |||
| uint64_t val = (uint64_t)f1 * 1000000ULL / (uint64_t)T_us; | |||
| uint32_t x; | |||
| if (val == 0ULL) | |||
| { | |||
| return 1U; | |||
| } | |||
| x = (uint32_t)val; | |||
| if (x > 10000U) | |||
| { | |||
| x = 10000U; | |||
| } | |||
| for (i = 0U; i < 20U; i++) | |||
| { | |||
| uint32_t x2 = (uint32_t)((x + (uint32_t)(val / (uint64_t)x)) / 2UL); | |||
| if (x2 == x || x2 == 0U) | |||
| { | |||
| break; | |||
| } | |||
| x = x2; | |||
| } | |||
| f = (x < 1U) ? 1U : x; | |||
| if (f > f1) | |||
| { | |||
| f = f1; | |||
| } | |||
| return f; | |||
| } | |||
| /* S/正弦:迭代求自洽点,用前后平均避免震荡 */ | |||
| guess = (f1 >= 4U) ? (f1 / 4U) : 1U; | |||
| for (i = 0U; i < 16U; i++) | |||
| { | |||
| per = (1000000UL + (guess / 2UL)) / guess; | |||
| fnew = PlsrAccelCurveFreqOnRampUs(f0, f1, per, T_us, mode); | |||
| if (fnew < 1U) | |||
| { | |||
| fnew = 1U; | |||
| } | |||
| if (fnew == guess) | |||
| { | |||
| break; | |||
| } | |||
| /* 取平均避免震荡 */ | |||
| guess = (guess + fnew) / 2U; | |||
| if (guess < 1U) | |||
| { | |||
| guess = 1U; | |||
| } | |||
| } | |||
| if (guess > f1) | |||
| { | |||
| guess = f1; | |||
| } | |||
| return guess; | |||
| } | |||
| /** | |||
| * 斜坡脉冲数:与运行时 TIM3 1ms 取频一致(t=0..T-1 每毫秒贡献 f 个脉冲)。 | |||
| * 向上取整:减速段多留脉冲,才能在发完前走到 f_end。 | |||
| * 梯形面积 (f0+f1)*T/2000 会少算末段减速(约 (f_peak-f_end)/2000 个), | |||
| * 匀速被拉长,最后一段脉冲在斜坡中途用完,到不了止速。 | |||
| */ | |||
| static uint32_t PlsrAccelCurveEstimatePulses(uint32_t f_from, | |||
| uint32_t f_to, | |||
| uint32_t t_ms, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| uint32_t t; | |||
| uint32_t f; | |||
| uint32_t n; | |||
| uint32_t t_use; | |||
| uint64_t acc; | |||
| if ((t_ms == 0U) || (f_from == f_to)) | |||
| { | |||
| return 0U; | |||
| } | |||
| t_use = t_ms; | |||
| if (t_use > 120000U) | |||
| { | |||
| t_use = 120000U; | |||
| } | |||
| acc = 0ULL; | |||
| for (t = 0U; t < t_use; t++) | |||
| { | |||
| f = PlsrAccelCurveFreqOnRampMs(f_from, f_to, t, t_ms, mode); | |||
| if (f < 1U) | |||
| { | |||
| if (f_to >= 1U) | |||
| { | |||
| f = 1U; | |||
| } | |||
| else | |||
| { | |||
| f = 0U; | |||
| } | |||
| } | |||
| acc += (uint64_t)f; | |||
| } | |||
| n = (uint32_t)((acc + 999ULL) / 1000ULL); | |||
| if ((n == 0U) && (acc > 0ULL)) | |||
| { | |||
| n = 1U; | |||
| } | |||
| return n; | |||
| } | |||
| /** 匀速时间向下取整:宁可早进减速,保证斜坡走完再落到 f_end */ | |||
| static uint32_t PlsrAccelCurveConstTimeMs(uint32_t const_n, uint32_t f_hz) | |||
| { | |||
| if ((const_n == 0U) || (f_hz < 1U)) | |||
| { | |||
| return 0U; | |||
| } | |||
| return (const_n * 1000UL) / f_hz; | |||
| } | |||
| /** | |||
| * 拟合斜坡时间 T(任务上下文);使离散模型脉冲数尽量等于 max_pulses。 | |||
| */ | |||
| uint32_t PlsrAccelCurveFitRampTimeMs(uint32_t f_from, | |||
| uint32_t f_to, | |||
| uint32_t max_pulses, | |||
| uint32_t t_prefer_ms, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| uint32_t lo; | |||
| uint32_t hi; | |||
| uint32_t mid; | |||
| uint32_t best; | |||
| uint32_t best_err; | |||
| uint32_t n; | |||
| uint32_t err; | |||
| uint32_t guard; | |||
| if ((max_pulses == 0U) || (f_from == f_to)) | |||
| { | |||
| return 0U; | |||
| } | |||
| if (max_pulses <= 1U) | |||
| { | |||
| return (t_prefer_ms > 0U) ? t_prefer_ms : 1U; | |||
| } | |||
| hi = (t_prefer_ms > 0U) ? t_prefer_ms : 1U; | |||
| guard = 0U; | |||
| n = PlsrAccelCurveEstimatePulses(f_from, f_to, hi, mode); | |||
| while ((n < max_pulses) && (hi < 120000U) && (guard < 24U)) | |||
| { | |||
| if (hi > (120000U / 2U)) | |||
| { | |||
| hi = 120000U; | |||
| } | |||
| else | |||
| { | |||
| hi = hi * 2U; | |||
| } | |||
| n = PlsrAccelCurveEstimatePulses(f_from, f_to, hi, mode); | |||
| guard++; | |||
| } | |||
| lo = 1U; | |||
| best = (t_prefer_ms > 0U) ? t_prefer_ms : hi; | |||
| n = PlsrAccelCurveEstimatePulses(f_from, f_to, best, mode); | |||
| best_err = (n > max_pulses) ? (n - max_pulses) : (max_pulses - n); | |||
| while (lo <= hi) | |||
| { | |||
| mid = lo + ((hi - lo) / 2UL); | |||
| n = PlsrAccelCurveEstimatePulses(f_from, f_to, mid, mode); | |||
| err = (n > max_pulses) ? (n - max_pulses) : (max_pulses - n); | |||
| if ((err < best_err) || | |||
| ((err == best_err) && (t_prefer_ms > 0U) && | |||
| (((mid > t_prefer_ms) ? (mid - t_prefer_ms) : (t_prefer_ms - mid)) < | |||
| ((best > t_prefer_ms) ? (best - t_prefer_ms) : (t_prefer_ms - mid))))) | |||
| { | |||
| best = mid; | |||
| best_err = err; | |||
| } | |||
| if (n == max_pulses) | |||
| { | |||
| return mid; | |||
| } | |||
| if (n < max_pulses) | |||
| { | |||
| lo = mid + 1UL; | |||
| } | |||
| else | |||
| { | |||
| if (mid == 0U) | |||
| { | |||
| break; | |||
| } | |||
| hi = mid - 1UL; | |||
| } | |||
| } | |||
| return best; | |||
| } | |||
| /** | |||
| * a = default_spd * 1000 / ramp_ms(Hz/s)。ramp_ms=0 → a=0(阶跃)。 | |||
| */ | |||
| @@ -875,7 +533,7 @@ static uint32_t PlsrAccelCurveFitPeak(uint32_t total_pulses, | |||
| * 1. 钳频、解析 f_cur/f_end 若仍 <1; | |||
| * 2. 由 default_spd 与 accel/decel_ms 算 a_acc/a_dec(无默认速则频差回退); | |||
| * 3. PulsesForRamp 估 acc_n/dec_n;若和 > total → FitPeak 降峰重估; | |||
| * 4. 写出 plan 三相预算与 t_* 预览字段。 | |||
| * 4. 写出 plan 三相预算。 | |||
| */ | |||
| void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| uint32_t total_pulses, | |||
| @@ -883,14 +541,10 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| uint32_t f_tgt, | |||
| uint32_t f_end, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms, | |||
| PlsrAccelMode_e mode) | |||
| { | |||
| uint32_t t_acc; | |||
| uint32_t t_dec; | |||
| uint32_t acc_n; | |||
| uint32_t dec_n; | |||
| uint32_t f_peak; | |||
| @@ -912,13 +566,11 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| if (f_cur < 1U) | |||
| { | |||
| f_cur = PlsrAccelCurveResolveStartHz(0U, f_tgt, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| } | |||
| if (f_end < 1U) | |||
| { | |||
| f_end = PlsrAccelCurveResolveEndHz(0U, f_tgt, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| } | |||
| @@ -943,12 +595,6 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| } | |||
| f_peak = f_tgt; | |||
| t_acc = PlsrAccelCurveRampTimeMs(f_cur, f_peak, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| t_dec = PlsrAccelCurveRampTimeMs(f_peak, f_end, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| plan->f_cur = f_cur; | |||
| plan->f_end = f_end; | |||
| @@ -962,9 +608,6 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| if (total_pulses == 0U) | |||
| { | |||
| plan->const_n = 0U; | |||
| plan->t_acc_ms = 0U; | |||
| plan->t_dec_ms = 0U; | |||
| plan->t_decel_start_ms = 0U; | |||
| plan->f_tgt = f_peak; | |||
| plan->acc_n = 0U; | |||
| plan->dec_n = 0U; | |||
| @@ -975,9 +618,6 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| { | |||
| plan->const_n = 0U; | |||
| plan->f_tgt = f_peak; | |||
| plan->t_acc_ms = t_acc; | |||
| plan->t_dec_ms = 0U; | |||
| plan->t_decel_start_ms = t_acc; | |||
| plan->acc_n = total_pulses; | |||
| plan->dec_n = 0U; | |||
| return; | |||
| @@ -987,12 +627,6 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| { | |||
| f_peak = PlsrAccelCurveFitPeak(total_pulses, f_cur, f_tgt, f_end, | |||
| a_acc, a_dec); | |||
| t_acc = PlsrAccelCurveRampTimeMs(f_cur, f_peak, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| t_dec = PlsrAccelCurveRampTimeMs(f_peak, f_end, default_spd, | |||
| start_spd_ref, end_spd_ref, | |||
| accel_ms, decel_ms); | |||
| acc_n = PlsrAccelCurvePulsesForRamp(f_cur, f_peak, a_acc); | |||
| dec_n = PlsrAccelCurvePulsesForRamp(f_peak, f_end, a_dec); | |||
| if (acc_n > total_pulses) | |||
| @@ -1005,14 +639,6 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| dec_n = total_pulses - acc_n; | |||
| } | |||
| plan->const_n = 0U; | |||
| if ((t_acc == 0U) && (acc_n > 0U) && (f_cur != f_peak)) | |||
| { | |||
| t_acc = 1U; | |||
| } | |||
| if ((t_dec == 0U) && (dec_n > 0U) && (f_peak != f_end)) | |||
| { | |||
| t_dec = 1U; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| @@ -1044,11 +670,8 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| } | |||
| plan->f_tgt = f_peak; | |||
| plan->t_acc_ms = t_acc; | |||
| plan->t_dec_ms = t_dec; | |||
| plan->acc_n = acc_n; | |||
| plan->dec_n = dec_n; | |||
| plan->t_decel_start_ms = t_acc + PlsrAccelCurveConstTimeMs(plan->const_n, f_peak); | |||
| } | |||
| /** | |||
| @@ -1129,22 +752,6 @@ uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, | |||
| return PlsrAccelCurveClampFreq(f); | |||
| } | |||
| /** 兼容旧预览:用时间估算映射到脉冲再取频 */ | |||
| uint32_t PlsrAccelCurveFreqAtSegTime(const PlsrAccelPlan_t *plan, | |||
| uint32_t seg_elapsed_ms) | |||
| { | |||
| uint32_t approx_n; | |||
| uint32_t f_ref; | |||
| if (plan == (PlsrAccelPlan_t *)0) | |||
| { | |||
| return 0U; | |||
| } | |||
| f_ref = (plan->f_tgt >= 1U) ? plan->f_tgt : 1U; | |||
| approx_n = (uint32_t)(((uint64_t)seg_elapsed_ms * (uint64_t)f_ref) / 1000ULL); | |||
| return PlsrAccelCurveFreqAtPulse(plan, approx_n); | |||
| } | |||
| /*============================================================================*/ | |||
| /* 逐拍运行态(ISR) */ | |||
| /*============================================================================*/ | |||
| @@ -2,20 +2,13 @@ | |||
| * @file plsr_accel_curve.h | |||
| * @brief 脉冲域加/匀/减规划与取频(运动学:v^2 - v0^2 = 2ax) | |||
| * | |||
| * @details 模块职责 | |||
| * 纯算法层:根据起/峰/止频率、斜率时间与总脉冲,规划 acc_n/const_n/dec_n, | |||
| * 并在脉冲序号上取频。不读 wait_type,不碰 TIM/GPIO。 | |||
| * @details 按起/峰/止频率、斜率时间与总脉冲规划三相脉冲预算,并在脉冲序号上取频。 | |||
| * 不读 wait_type,不碰 TIM/GPIO。 | |||
| * | |||
| * 核心约定 | |||
| * - 加速度 a ≈ default_speed * 1000 / accel_ms(Hz/s);起/止速只定端点 | |||
| * - 直线:f(n) = sqrt(f0^2 ± 2*a*n);S/正弦:按脉冲进度 shape 插值 | |||
| * - 匀速仅当 acc_n+dec_n < total(真正到达目标峰)时出现 | |||
| * - 脉冲不够到设定峰:FitPeak 降峰(三角),调用方可写故障 0x02 | |||
| * - 开段端点由运行层 ResolveStart/EndHz 解析为真实 Hz(>=1)后再 Plan | |||
| * | |||
| * 与其它模块关系 | |||
| * - plsr_run_control :PlanSeg → Plan;ISR → PulseRtStep;Refresh → FreqAtPulse | |||
| * - plsr_param :accel_mode / default_speed / accel_ms / decel_ms | |||
| * 术语(全模块统一): | |||
| * acc_n / dec_n / const_n — 加速/减速/匀速相各需的脉冲个数(规划预算,非 ms) | |||
| * f_cur / f_tgt / f_end — 段入口频率、规划峰值、段出口频率(Hz) | |||
| * a_acc / a_dec — 加/减速度(Hz/s),≈ default_speed*1000/accel_ms | |||
| */ | |||
| #ifndef PLSR_ACCEL_CURVE_H | |||
| #define PLSR_ACCEL_CURVE_H | |||
| @@ -24,118 +17,38 @@ | |||
| #include "plsr_param.h" | |||
| /** | |||
| * 一段脉冲域规划结果(Plan 填出,运行层只读/EnterDecel 可改写 dec_n) | |||
| * acc_n/const_n/dec_n — 三相脉冲预算 | |||
| * f_cur / f_tgt / f_end — 实际起速、峰值、止速(可能已 FitPeak) | |||
| * a_acc / a_dec — Hz/s | |||
| * t_*_ms — 斜率时间估算(预览/兼容;运行热路径不依赖) | |||
| * 单段脉冲域规划结果(PlsrAccelCurvePlan 输出) | |||
| * EnterDecel 可能改写 dec_n,诊断用 run_control 另存 s_plan_dec_n。 | |||
| */ | |||
| typedef struct { | |||
| uint32_t acc_n; | |||
| uint32_t const_n; | |||
| uint32_t dec_n; | |||
| uint32_t f_cur; | |||
| uint32_t f_tgt; | |||
| uint32_t f_end; | |||
| uint32_t a_acc; | |||
| uint32_t a_dec; | |||
| uint32_t t_acc_ms; | |||
| uint32_t t_dec_ms; | |||
| uint32_t t_decel_start_ms; | |||
| PlsrAccelMode_e mode; | |||
| uint32_t acc_n; /* 加速相脉冲数:从 f_cur 爬到 f_tgt */ | |||
| uint32_t const_n; /* 匀速相脉冲数:保持 f_tgt */ | |||
| uint32_t dec_n; /* 减速相脉冲数:从 f_tgt 到 f_end */ | |||
| uint32_t f_cur; /* 段入口频率 Hz(规划后实际起点,>=1) */ | |||
| uint32_t f_tgt; /* 规划峰值 Hz(FitPeak 后可能低于段表目标) */ | |||
| uint32_t f_end; /* 段出口频率 Hz */ | |||
| uint32_t a_acc; /* 加速相加速度 Hz/s */ | |||
| uint32_t a_dec; /* 减速相加速度 Hz/s */ | |||
| PlsrAccelMode_e mode; /* 直线 / S / 正弦 */ | |||
| } PlsrAccelPlan_t; | |||
| /** 曲线相枚举(预留/文档;运行层用自有 RunPhase_e) */ | |||
| typedef enum { | |||
| PLSR_CURVE_PHASE_ACC = 0, | |||
| PLSR_CURVE_PHASE_CONST, | |||
| PLSR_CURVE_PHASE_DEC | |||
| } PlsrCurvePhase_e; | |||
| /** | |||
| * @brief 频率钳到上限 100000Hz(不抬下限) | |||
| * @note 非法低频门禁由 command/run_control 写 0x04,本函数不做 | |||
| */ | |||
| uint32_t PlsrAccelCurveClampFreq(uint32_t freq_hz); | |||
| /** | |||
| * @brief 起跳/步进频率:f = sqrt(f_from^2 + 2a),a = |f_to-f_from|*1000/t_ms | |||
| * @note f_from=0 退化为 sqrt(2a);超过 f_to 钳到 f_to;t_ms=0 直接返回 f_to | |||
| */ | |||
| uint32_t PlsrAccelCurveJumpFreq(uint32_t f_from, uint32_t f_to, | |||
| uint32_t t_ms); | |||
| /** | |||
| * @brief 配置起速 → 规划用真实起点:JumpFreq(f_cfg, f_tgt, t_acc) | |||
| * @note 起速 0 与非 0 统一走 Jump,避免配置 0 时从 0Hz 开跑 | |||
| */ | |||
| /** 配置起速 → 规划用真实起点(Jump;起速 0 也不从 0Hz 开跑) */ | |||
| uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg, | |||
| uint32_t f_tgt, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms); | |||
| /** | |||
| * @brief 配置止速 → 规划用真实终点 | |||
| * @note f_cfg<=1:按落地频率估计(避免 1Hz 拖尾);否则 JumpFreq | |||
| */ | |||
| /** 配置止速 → 规划用真实终点(f_cfg<=1 按落地估计) */ | |||
| uint32_t PlsrAccelCurveResolveEndHz(uint32_t f_cfg, | |||
| uint32_t f_tgt, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms); | |||
| /** | |||
| * @brief 斜坡取频(µs 时间轴,O(1)) | |||
| * @note 直线:代数插值;S/正弦:shape(t/T) 再 Lerp。主要用于估计/兼容,非 ISR 主路径 | |||
| */ | |||
| uint32_t PlsrAccelCurveFreqOnRampUs(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t t_us, | |||
| uint32_t T_us, | |||
| PlsrAccelMode_e mode); | |||
| /** @brief ms 接口,内部转 µs 调 FreqOnRampUs */ | |||
| uint32_t PlsrAccelCurveFreqOnRampMs(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t t_ms, | |||
| uint32_t T_ms, | |||
| PlsrAccelMode_e mode); | |||
| /** | |||
| * @brief 斜坡第一拍自洽频率(起速=0 时避免整周期 1Hz 吃光加速时间) | |||
| */ | |||
| uint32_t PlsrAccelCurveFirstPulseFreq(uint32_t f0, | |||
| uint32_t f1, | |||
| uint32_t T_us, | |||
| PlsrAccelMode_e mode); | |||
| /** | |||
| * @brief 减速落地频率:f_end>=1 返回止速;f_end==0 返回 0(停表) | |||
| */ | |||
| uint32_t PlsrAccelCurveLandFreq(uint32_t f_end, | |||
| uint32_t f_peak, | |||
| uint32_t T_ms, | |||
| PlsrAccelMode_e mode); | |||
| /** | |||
| * @brief 拟合斜坡时间 T,使离散脉冲估计尽量等于 max_pulses(任务上下文) | |||
| */ | |||
| uint32_t PlsrAccelCurveFitRampTimeMs(uint32_t f_from, | |||
| uint32_t f_to, | |||
| uint32_t max_pulses, | |||
| uint32_t t_prefer_ms, | |||
| PlsrAccelMode_e mode); | |||
| /** | |||
| * @brief 规划一整段脉冲域三相预算与实际峰值 | |||
| * @param total_pulses 本段总脉冲 | |||
| * @param f_cur/f_tgt/f_end 起/目标峰/止(真实 Hz,由策略层解析) | |||
| * @note 不够爬升则 FitPeak;结果写入 *plan | |||
| * 规划一整段脉冲域三相预算与实际峰值 | |||
| * 脉冲不够爬到 f_tgt 时 FitPeak 降峰(调用方可能写故障 0x02) | |||
| */ | |||
| void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| uint32_t total_pulses, | |||
| @@ -143,62 +56,42 @@ void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| uint32_t f_tgt, | |||
| uint32_t f_end, | |||
| uint32_t default_spd, | |||
| uint32_t start_spd_ref, | |||
| uint32_t end_spd_ref, | |||
| uint32_t accel_ms, | |||
| uint32_t decel_ms, | |||
| PlsrAccelMode_e mode); | |||
| /** | |||
| * @brief 按已完成脉冲数取频(任务/预览) | |||
| * @note 运行热路径请用 PlsrAccelPulseRt(逐拍 ±1 / Q24,可进 ISR) | |||
| */ | |||
| /** 按已完成脉冲数取频(任务/预览;ISR 热路径用 PulseRtStep) */ | |||
| uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, | |||
| uint32_t pulse_done); | |||
| /** | |||
| * @deprecated 时间轴取频;保留给旧预览,运行请用脉冲域 | |||
| */ | |||
| uint32_t PlsrAccelCurveFreqAtSegTime(const PlsrAccelPlan_t *plan, | |||
| uint32_t seg_elapsed_ms); | |||
| /** | |||
| * 逐拍运行态(ISR 热路径) | |||
| * - 直线:上一拍频率 ±1 逼近 sqrt(f0^2±2an),不全量 Isqrt64 | |||
| * - S/正弦:Begin 时算 Q24 步长,逐拍商余累加,不做 index/total 除法 | |||
| * 单相逐脉冲运行态(ISR 热路径,每来一个 UPDATE 调 Step 一次) | |||
| * 直线:±1Hz 逼近 sqrt(f0²±2an);S/正弦:Q24 相位累加 + shape 表 | |||
| */ | |||
| typedef struct { | |||
| uint32_t f; /* 当前命令频率 */ | |||
| uint32_t f0; /* 本相起点频率 */ | |||
| uint32_t f1; /* 本相终点频率 */ | |||
| uint32_t a; /* Hz/s;直线用 */ | |||
| uint32_t n; /* 本相已完成脉冲 */ | |||
| uint32_t n_total; /* 本相总脉冲(距离只在 Begin 算) */ | |||
| uint32_t phase_q24; /* S/正弦进度 0..(1<<24) */ | |||
| uint32_t step_q24; | |||
| uint32_t step_rem; /* (1<<24) % n_total */ | |||
| uint32_t rem_acc; | |||
| uint8_t rising; | |||
| uint8_t active; /* 0=匀速保持,不 Step */ | |||
| uint32_t f; /* 当前输出频率 Hz */ | |||
| uint32_t f0; /* 本相起点频率 Hz */ | |||
| uint32_t f1; /* 本相终点频率 Hz */ | |||
| uint32_t a; /* 本相加速度 Hz/s(直线用) */ | |||
| uint32_t n; /* 本相已步进脉冲数 */ | |||
| uint32_t n_total; /* 本相总步进预算(= plan 里 acc_n 或 dec_n) */ | |||
| uint32_t phase_q24; /* S/正弦:形状进度 Q24 累加器 */ | |||
| uint32_t step_q24; /* 每脉冲相位增量 = (1<<24)/n_total */ | |||
| uint32_t step_rem; /* 相位累加余数(商余法,避免每拍除法) */ | |||
| uint32_t rem_acc; /* 余数累加 */ | |||
| uint8_t rising; /* 1=频率升高,0=降低 */ | |||
| uint8_t active; /* 1=本相仍在步进;0=已到 f1 */ | |||
| PlsrAccelMode_e mode; | |||
| } PlsrAccelPulseRt_t; | |||
| /** @brief 进入加速相运行态(n=0,active 视 acc_n) */ | |||
| void PlsrAccelPulseRtBeginAcc(PlsrAccelPulseRt_t *rt, | |||
| void PlsrAccelPulseRtBeginAcc(PlsrAccelPulseRt_t *pulse_rt, | |||
| const PlsrAccelPlan_t *plan); | |||
| /** @brief 进入减速相运行态 */ | |||
| void PlsrAccelPulseRtBeginDec(PlsrAccelPulseRt_t *rt, | |||
| void PlsrAccelPulseRtBeginDec(PlsrAccelPulseRt_t *pulse_rt, | |||
| const PlsrAccelPlan_t *plan); | |||
| /** @brief 进入匀速:active=0,复用 f_tgt */ | |||
| void PlsrAccelPulseRtBeginConst(PlsrAccelPulseRt_t *rt, | |||
| void PlsrAccelPulseRtBeginConst(PlsrAccelPulseRt_t *pulse_rt, | |||
| const PlsrAccelPlan_t *plan); | |||
| /** | |||
| * @brief 本相前进 1 脉冲,返回下一拍命令频率 | |||
| * @note 匀速直接返回缓存 f;走完 n_total 后钳到 f1 并清 active | |||
| */ | |||
| uint32_t PlsrAccelPulseRtStep(PlsrAccelPulseRt_t *rt); | |||
| /** 本相前进 1 脉冲,返回下一拍命令频率 */ | |||
| uint32_t PlsrAccelPulseRtStep(PlsrAccelPulseRt_t *pulse_rt); | |||
| #endif | |||
| @@ -20,6 +20,7 @@ | |||
| #include "plsr_param.h" | |||
| #include "plsr_run_control.h" | |||
| #include "plsr.h" | |||
| #include "plsr_persist.h" | |||
| #include "modbus_rtu.h" | |||
| /* | |||
| @@ -63,6 +64,7 @@ void PlsrCommandSetFault(uint16_t code) | |||
| { | |||
| s_fault = code; | |||
| WriteHoldReg(PLSR_MON_ERR, code); | |||
| PlsrPersistNotifyMonitorChanged(); | |||
| } | |||
| /** @brief 清故障(见 plsr_command.h) */ | |||
| @@ -70,6 +72,7 @@ void PlsrCommandClearFault(void) | |||
| { | |||
| s_fault = PLSR_ERR_NONE; | |||
| WriteHoldReg(PLSR_MON_ERR, PLSR_ERR_NONE); | |||
| PlsrPersistNotifyMonitorChanged(); | |||
| } | |||
| /** @brief 读故障镜像(见 plsr_command.h) */ | |||
| @@ -84,7 +87,7 @@ void PlsrCommandInit(void) | |||
| WriteHoldReg(PLSR_CTRL_REG, 0U); | |||
| s_live_freq_req = 0U; | |||
| s_live_freq_hz = 0U; | |||
| PlsrCommandClearFault(); | |||
| /* 不清故障:上电由 PersistRestoreMonitor 恢复;无镜像时 s_fault 默认为 0 */ | |||
| PlsrCommandPublishMonitor(); | |||
| } | |||
| @@ -144,6 +147,7 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity) | |||
| if (seg != (PlsrSeg_t *)0) | |||
| { | |||
| seg->freq_hz = (int32_t)f; | |||
| PlsrPersistNotifyCommonChanged(); /* 动态改频:段表 dirty,Poll 写入 BKPSRAM */ | |||
| } | |||
| s_live_freq_hz = f; | |||
| @@ -163,6 +167,7 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity) | |||
| void PlsrCommandPoll(void) | |||
| { | |||
| uint16_t ctrl = ReadHoldReg(PLSR_CTRL_REG); | |||
| uint8_t skip_monitor = 0U; | |||
| if (ctrl != 0U) | |||
| { | |||
| @@ -188,6 +193,11 @@ void PlsrCommandPoll(void) | |||
| PlsrCommandClearFault(); | |||
| PlsrStop(); | |||
| (void)PlsrStart(PlsrParamGetStartSeg()); | |||
| /* 已进换向/WAIT 精确等待:本周期勿再 PublishMonitor,尽快 Pend */ | |||
| if (PlsrRunControlIsPreciseWait() != 0U) | |||
| { | |||
| skip_monitor = 1U; | |||
| } | |||
| } | |||
| } | |||
| @@ -199,5 +209,8 @@ void PlsrCommandPoll(void) | |||
| (void)PlsrChangeFreq(f); | |||
| } | |||
| PlsrCommandPublishMonitor(); | |||
| if (skip_monitor == 0U) | |||
| { | |||
| PlsrCommandPublishMonitor(); | |||
| } | |||
| } | |||
| @@ -83,7 +83,7 @@ void PlsrCommandSetFault(uint16_t code); | |||
| /** | |||
| * @brief 清故障为 0 | |||
| * @note 调用时机:START 前、CLR 控制位、Init | |||
| * @note 调用时机:START 前、CLR 控制位(Init 不再清,以便上电恢复) | |||
| */ | |||
| void PlsrCommandClearFault(void); | |||
| @@ -23,13 +23,13 @@ | |||
| #include "ucos_ii.h" | |||
| #include <string.h> | |||
| static PlsrCfg_t s_cfg; | |||
| static PlsrSeg_t s_seg[PLSR_SEG_MAX]; | |||
| static PlsrCfg_t s_cfg; /* 公共运行参数映像 */ | |||
| static PlsrSeg_t s_seg[PLSR_SEG_MAX]; /* 段表映像(最多 PLSR_SEG_MAX 段) */ | |||
| static OS_EVENT *s_param_sem; | |||
| static volatile uint16_t s_expect_frames; | |||
| static volatile uint16_t s_recv_frames; | |||
| static volatile uint8_t s_xfer_active; | |||
| static OS_EVENT *s_param_sem; /* 分帧收齐后唤醒 ParamBlockTask */ | |||
| static volatile uint16_t s_expect_frames; /* 本批期望总帧数 = 1 + 段数 */ | |||
| static volatile uint16_t s_recv_frames; /* 已收帧计数 */ | |||
| static volatile uint8_t s_xfer_active; /* 1=正在接收一批分帧 */ | |||
| /*============================================================================*/ | |||
| /* 运行映像 */ | |||
| @@ -416,7 +416,7 @@ void PlsrParamBlockTask(void *pArg) | |||
| (void)pArg; | |||
| for (;;) | |||
| while(1) | |||
| { | |||
| (void)OSSemPend(s_param_sem, 0U, &err); | |||
| if (err != OS_ERR_NONE) | |||
| @@ -12,14 +12,14 @@ | |||
| * - modbus_rtu / modbus_data :ReadHoldReg / WriteHoldReg | |||
| * - plsr_command :启动前 ApplyFromHold;运行中写频改 s_seg[].freq_hz | |||
| * - plsr_run_control :GetCfg / GetSeg / GetSegCount / GetStartSeg | |||
| * - plsr_persist :Apply 成功后 NotifyCommonChanged,落盘仅公共参数 | |||
| * - plsr_persist :Apply 成功后 NotifyCommonChanged,落盘公共+段表+监控 | |||
| * - PlsrParamBlockTask :独立 uC/OS 任务,等信号量后 Apply + PersistPoll | |||
| * | |||
| * 关键数据流 | |||
| * 上位机写公共帧@0x1000 → OnHoldWrite 开传输 expect=1+N | |||
| * 上位机写段帧@0x1100+ → recv++;收齐 → OSSemPost | |||
| * ParamBlockTask → ApplyFromHold → 结构体更新 → PersistNotify → 状态 OK | |||
| * 上电:InitDefault → PersistLoad 覆盖公共 → ExportToHold | |||
| * 上电:InitDefault → PersistLoad 覆盖公共+段表 → ExportToHold | |||
| * | |||
| * 协议概要 | |||
| * - 公共帧:0x1000~0x1013(20 字),0x1009=段数 N,预期总帧=1+N | |||
| @@ -86,8 +86,9 @@ typedef enum { | |||
| } PlsrDirLogic_e; | |||
| /** | |||
| * 发送方式(影响本段终止频率 f_end 与是否允许同向不停表衔接) | |||
| * COMPLETE:段末规划到公共止速;FOLLOW:无门禁时可衔接下一段目标频 | |||
| * 发送方式(与等待类型一起决定本段 f_end,并影响是否允许同向不停表衔接) | |||
| * COMPLETE:EXT_OR_DONE 同向时匀速跑完;反向或其它等待类型规划到止速 | |||
| * FOLLOW:仅 EXT_OR_DONE 同向可规划到下一段目标并允许不停表衔接;反向同其它等待 | |||
| */ | |||
| typedef enum { | |||
| PLSR_SEND_COMPLETE = 0, | |||
| @@ -113,7 +114,9 @@ typedef enum { | |||
| /** | |||
| * 段末/段内等待类型(由 run_control + signal_io 执行) | |||
| * TIME/SIGNAL/ACT/EXT 均会阻止 FOLLOW 不停表衔接(BlocksFollowKeep) | |||
| * TIME/SIGNAL/ACT/EXT:完成/后续均规划到止速;并阻止 FOLLOW 不停表衔接 | |||
| * EXT_OR_DONE:与下一段同向时后续可接目标/完成匀速跑完并可 FOLLOW 衔接; | |||
| * 与下一段反向时规划同其它等待(减速到止速,下一段从起速再爬) | |||
| */ | |||
| typedef enum { | |||
| PLSR_WAIT_TIME = 0, /* 发完后等 wait_ms(0 按 1ms),再跳转 */ | |||
| @@ -208,7 +211,7 @@ void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity); | |||
| /** | |||
| * @brief 保持寄存器 → s_cfg / s_seg | |||
| * @return 1=成功(当前恒为 1;完整校验/故障码待扩展) | |||
| * @note 副作用:NotifyCommonChanged 标记 Flash 待存 | |||
| * @note 副作用:NotifyCommonChanged 标记 BKPSRAM 待存 | |||
| */ | |||
| uint8_t PlsrParamBlockApplyFromHold(void); | |||
| @@ -1,91 +0,0 @@ | |||
| /** | |||
| * @file plsr_path_plan.c | |||
| * @brief 多段路径规划实现(只做决策,不碰 TIM/GPIO) | |||
| * | |||
| * @details 模块职责 | |||
| * NextSeg / ResolveAfterSeg:按 jump_seg 决定下一段或结束; | |||
| * IsForward:相对模式看 pulse_cnt 符号,绝对模式看「目标−相对原点位置」符号; | |||
| * GetWaitMs:仅 WAIT_TIME,0 映射为 1ms。 | |||
| * | |||
| * 关键数据流 | |||
| * AfterSegDone / CutSeg / ActExpire → ResolveAfterSeg → BeginSeg 或 FinishAll | |||
| * WillFollowKeep / CutSeg keep 判定 → IsForward(下一段, AbsLocalPos) | |||
| */ | |||
| #include "plsr_path_plan.h" | |||
| #include "plsr_param.h" | |||
| /** | |||
| * @brief 解析下一段(见 plsr_path_plan.h) | |||
| */ | |||
| int16_t PlsrPathPlanNextSeg(uint16_t cur_seg_0based) | |||
| { | |||
| PlsrSeg_t *seg = PlsrParamGetSeg(cur_seg_0based); | |||
| uint16_t n = PlsrParamGetSegCount(); | |||
| uint16_t jump = seg->jump_seg; | |||
| /* | |||
| * 跳转目标(1-based): | |||
| * 0 → 末段:结束;非末段:顺序下一段(cur+1) | |||
| * 1..N → 跳到对应段(可顺序、回跳循环、任意段) | |||
| * 其它 → 结束 | |||
| */ | |||
| if (jump == 0U) | |||
| { | |||
| /* 非末段写 0:视为顺序下一档,不是停机 */ | |||
| if ((n >= 1U) && (cur_seg_0based + 1U < n)) | |||
| { | |||
| return (int16_t)(cur_seg_0based + 1U); | |||
| } | |||
| return -1; | |||
| } | |||
| if ((jump >= 1U) && (jump <= n)) | |||
| { | |||
| return (int16_t)(jump - 1U); | |||
| } | |||
| return -1; | |||
| } | |||
| /** | |||
| * @brief 判断段方向(见 plsr_path_plan.h) | |||
| */ | |||
| uint8_t PlsrPathPlanIsForward(uint16_t seg_0based, int32_t acc_pulse) | |||
| { | |||
| PlsrSeg_t *seg = PlsrParamGetSeg(seg_0based); | |||
| PlsrCfg_t *cfg = PlsrParamGetCfg(); | |||
| int32_t move; | |||
| if (cfg->run_mode == PLSR_POS_ABSOLUTE) | |||
| { | |||
| /* move = 段目标 − 相对本次启动原点的位置 */ | |||
| move = seg->pulse_cnt - acc_pulse; | |||
| } | |||
| else | |||
| { | |||
| move = seg->pulse_cnt; | |||
| } | |||
| return (move >= 0) ? 1U : 0U; | |||
| } | |||
| /** | |||
| * @brief 段后 WAIT 时间(见 plsr_path_plan.h) | |||
| */ | |||
| uint16_t PlsrPathPlanGetWaitMs(uint16_t seg_0based) | |||
| { | |||
| PlsrSeg_t *seg = PlsrParamGetSeg(seg_0based); | |||
| /* 仅段后 WAIT 时间;0 按 1ms(手册);信号/ACT/EXT 由 run_control 处理 */ | |||
| if (seg->wait_type == PLSR_WAIT_TIME) | |||
| { | |||
| return (seg->wait_ms == 0U) ? 1U : seg->wait_ms; | |||
| } | |||
| (void)seg_0based; | |||
| return 0U; | |||
| } | |||
| /** | |||
| * @brief 段后解析下一段(见 plsr_path_plan.h) | |||
| */ | |||
| int16_t PlsrPathPlanResolveAfterSeg(uint16_t cur_seg_0based) | |||
| { | |||
| return PlsrPathPlanNextSeg(cur_seg_0based); | |||
| } | |||
| @@ -1,49 +0,0 @@ | |||
| /** | |||
| * @file plsr_path_plan.h | |||
| * @brief 多段路径规划:段序、跳转、相对/绝对方向判定 | |||
| * | |||
| * @details 模块职责 | |||
| * 只读段表与公共定位模式,回答「下一段是谁」「这一段正还是反」「段后 WAIT 多久」。 | |||
| * 不驱动 TIM/GPIO;等待类型的具体执行由 run_control + signal_io 完成。 | |||
| * | |||
| * 与其它模块关系 | |||
| * - plsr_param :GetSeg / GetSegCount / GetCfg | |||
| * - plsr_run_control :GotoNext、CutSeg、WillFollowKeep、BeginSeg 方向 | |||
| */ | |||
| #ifndef PLSR_PATH_PLAN_H | |||
| #define PLSR_PATH_PLAN_H | |||
| #include <stdint.h> | |||
| /** | |||
| * @brief 解析下一段索引 | |||
| * @param cur_seg_0based 当前段 0-based | |||
| * @return 下一段 0-based;无下一段或非法跳转返回 -1 | |||
| * @note jump=0:末段→结束;非末段→顺序下一段 | |||
| * jump=1~N:跳到对应段(可循环) | |||
| * 其它:结束 | |||
| */ | |||
| int16_t PlsrPathPlanNextSeg(uint16_t cur_seg_0based); | |||
| /** | |||
| * @brief 判断该段运动方向 | |||
| * @param seg_0based 段索引 | |||
| * @param acc_pulse 相对本次启动原点的位置(绝对模式算位移用) | |||
| * @return 1=正向,0=反向 | |||
| * @note 相对:看 pulse_cnt 符号;绝对:看 (目标−相对原点位置) 符号 | |||
| */ | |||
| uint8_t PlsrPathPlanIsForward(uint16_t seg_0based, int32_t acc_pulse); | |||
| /** | |||
| * @brief 段结束后 WAIT 时间(ms) | |||
| * @note 仅 WAIT_TIME 有效;0 按 1ms;其它等待类型返回 0(由 run_control 处理) | |||
| */ | |||
| uint16_t PlsrPathPlanGetWaitMs(uint16_t seg_0based); | |||
| /** | |||
| * @brief 段结束(或等待结束)后解析下一段 | |||
| * @note 当前语义同 NextSeg;保留独立符号便于日后插入后处理 | |||
| */ | |||
| int16_t PlsrPathPlanResolveAfterSeg(uint16_t cur_seg_0based); | |||
| #endif | |||
| @@ -10,9 +10,10 @@ | |||
| * 本文件仅依赖 plsr.h 及各子模块头文件,不被其它 plsr 模块反向引用。 | |||
| * | |||
| * 关键数据流 | |||
| * PlsrInit 顺序:Param 默认 → Persist 加载 → PulseDriver → SignalIo → | |||
| * RunControl → ExportToHold → CommandInit | |||
| * PlsrTask 循环:CommandPoll → PersistPoll → RunControlTickMs → OSTimeDly(1) | |||
| * PlsrInit 顺序:Param 默认 → PersistLoad → PulseDriver → SignalIo → | |||
| * RunControl → PersistRestoreRuntime → ExportToHold → | |||
| * CommandInit → PersistRestoreMonitor → PersistPoll | |||
| * PlsrTask 循环:TickMs → DebouncePoll → CommandPoll → PersistPoll → WakePend | |||
| */ | |||
| #include "plsr.h" | |||
| #include "plsr_pulse_driver.h" | |||
| @@ -29,12 +30,15 @@ | |||
| void PlsrInit(void) | |||
| { | |||
| PlsrParamInitDefault(); | |||
| PlsrPersistLoad(); /* 有 Flash 记录则覆盖公共参数;段表仍用默认/上位机下发 */ | |||
| PlsrPersistLoad(); /* BKPSRAM:覆盖公共+段表;累计/故障暂存 */ | |||
| PlsrPulseDriverInit(); | |||
| PlsrSignalIoInit(); | |||
| PlsrRunControlInit(); | |||
| PlsrParamBlockExportToHold(); /* 上电把运行映像摊到保持寄存器,供 Modbus 0x03 读 */ | |||
| PlsrPersistRestoreRuntime(); /* 写回累计脉冲 */ | |||
| PlsrParamBlockExportToHold(); /* 上电把运行映像摊到保持寄存器,供 Modbus 0x03 读取 */ | |||
| PlsrCommandInit(); | |||
| PlsrPersistRestoreMonitor(); /* 恢复故障码;0x3000/运行态强制空闲 */ | |||
| PlsrPersistPoll(); /* 消费 Restore 置的 dirty */ | |||
| } | |||
| /** | |||
| @@ -97,14 +101,12 @@ void PlsrOnPulseIsr(void) | |||
| void PlsrTask(void *pArg) | |||
| { | |||
| static uint8_t s_booted = 0U; | |||
| #if (PLSR_LOOP_TEST_EN != 0) | |||
| static uint8_t s_was_busy = 0U; | |||
| static uint16_t s_idle_ticks = 0U; | |||
| #endif | |||
| (void)pArg; | |||
| for (;;) | |||
| PlsrRunControlWakeInit(); /* OS 已启动,创建 WAIT/ACT 到期唤醒信号量 */ | |||
| while(1) | |||
| { | |||
| /* | |||
| * 首次进入:Modbus 从站上电常会清零保持寄存器。 | |||
| @@ -115,52 +117,26 @@ void PlsrTask(void *pArg) | |||
| OSTimeDly(30U); /* ~300ms @ 100Hz tick */ | |||
| PlsrParamBlockExportToHold(); | |||
| PlsrCommandInit(); | |||
| PlsrPersistRestoreMonitor(); /* 若仍有 pending;无则空操作 */ | |||
| s_booted = 1U; | |||
| #if (PLSR_LOOP_TEST_EN != 0) | |||
| s_was_busy = 0U; | |||
| s_idle_ticks = 0U; | |||
| #endif | |||
| } | |||
| PlsrRunControlTickMs(); /* 优先:WAIT/换向到期立刻开表 */ | |||
| PlsrSignalIoDebouncePoll(); /* OS 节拍消抖确认 */ | |||
| PlsrCommandPoll(); /* 收 START/STOP/CLR,回写 0x2000~0x2006 监控 */ | |||
| PlsrPersistPoll(); /* 公共参数有改且空闲则追加写入 Flash */ | |||
| PlsrRunControlTickMs(); /* ACT/WAIT/EXT、换向延时、ApplyPending */ | |||
| PlsrPersistPoll(); /* BKPSRAM:消费 dirty(累计脉冲限频落盘) */ | |||
| #if (PLSR_LOOP_TEST_EN != 0) | |||
| /* | |||
| * 调试循环:检测到 busy→idle 边沿后,空闲满 1s 自动从第 1 段再启。 | |||
| * 正式产品 PLSR_LOOP_TEST_EN=0 时本段不参与编译。 | |||
| * 换向 / WAIT 时间:WakePend(0) 只等 TIM5 Post(墙钟≈设定 ms)。 | |||
| * 其它:超时 1 OS tick,兼顾 EXT 轮询。 | |||
| */ | |||
| if (s_booted != 0U) | |||
| if (PlsrRunControlIsPreciseWait() != 0U) | |||
| { | |||
| uint8_t busy = PlsrIsBusy(); | |||
| if ((s_was_busy != 0U) && (busy == 0U)) | |||
| { | |||
| s_idle_ticks = 0U; | |||
| } | |||
| if (busy == 0U) | |||
| { | |||
| s_idle_ticks++; | |||
| if (s_idle_ticks >= (uint16_t)OS_TICKS_PER_SEC) | |||
| { | |||
| s_idle_ticks = 0U; | |||
| if (PlsrParamGetSegCount() >= 1U) | |||
| { | |||
| (void)PlsrStart(1U); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| s_idle_ticks = 0U; | |||
| } | |||
| s_was_busy = busy; | |||
| PlsrRunControlWakePend(0U); | |||
| } | |||
| else | |||
| { | |||
| PlsrRunControlWakePend(1U); | |||
| } | |||
| #endif | |||
| OSTimeDly(1U); /* 约 1ms 任务节拍,与 TIM3 独立 */ | |||
| } | |||
| } | |||
| @@ -13,15 +13,15 @@ | |||
| * - plsr_command :Modbus 监控区 0x2000~0x2006、控制区 0x3000 指令解析 | |||
| * - plsr_run_control:段状态机、等待策略、加减速执行、故障诊断 | |||
| * - plsr_pulse_driver:TIM PWM 脉冲 + Y3 方向 GPIO | |||
| * - plsr_signal_io :WAIT/EXT 外部输入、TIM3 1ms 时基 | |||
| * - plsr_signal_io :WAIT/EXT 外部输入、TIM5 延时 | |||
| * | |||
| * 关键数据流 | |||
| * 上电:PlsrInit → 默认参数 → Flash 覆盖公共参数 → 驱动/IO/运行控制初始化 | |||
| * → 参数导出到保持寄存器 → 指令模块初始化 | |||
| * 周期:PlsrTask → CommandPoll(START/STOP/CLR、监控回写) | |||
| * → PersistPoll(空闲时 Flash 落盘)→ RunControlTickMs(等待/切段) | |||
| * 周期:PlsrTask → CommandPoll → PersistPoll → RunControlTickMs | |||
| * → WakePend(1)(TIM5 WAIT/ACT 到期 SemPost 可立刻唤醒) | |||
| * 脉冲:TIM UPDATE 中断 → PlsrOnPulseIsr → RunControl 计数 + 逐拍改频 | |||
| * 启动:PlsrStart → RunControlStart → 路径规划 + 曲线规划 + PWM 输出 | |||
| * 启动:PlsrStart → RunControlStart → 段序/方向 + 曲线规划 + PWM 输出 | |||
| */ | |||
| #ifndef PLSR_H | |||
| #define PLSR_H | |||
| @@ -47,7 +47,7 @@ void PlsrInit(void); | |||
| /** | |||
| * @brief uC/OS-II 周期任务入口 | |||
| * @param pArg 任务参数(未使用,传 NULL) | |||
| * @note 调用时机:创建后永久循环,建议 1ms 节拍(OSTimeDly(1)) | |||
| * @note 调用时机:创建后永久循环;WAIT/ACT 到期由信号量立刻唤醒,否则最多 1 个 OS tick | |||
| * @note 副作用:轮询 Modbus 控制位、刷新监控寄存器、推进运行控制状态机、 | |||
| * 空闲时触发 Flash 持久化;首次进入延迟 ~300ms 再导出参数(避免 Modbus 清零冲掉) | |||
| */ | |||
| @@ -93,7 +93,7 @@ int32_t PlsrGetAccPulse(void); | |||
| /** | |||
| * @brief 清零累计脉冲与绝对坐标原点 | |||
| * @note 调用时机:CommandPoll 收到 CLR 控制位 | |||
| * @note 副作用:s_acc_pulse=0、s_abs_origin=0,同步清故障码 | |||
| * @note 副作用:s_acc_pulse=0、s_abs_origin=0,下次 Start 重新锁定原点 | |||
| */ | |||
| void PlsrClearAccPulse(void); | |||
| @@ -4,9 +4,10 @@ | |||
| * | |||
| * @details 模块职责 | |||
| * 实现 plsr_pulse_driver.h 全部 API。核心约束: | |||
| * - 空闲不关 CC1E:Forced active 主动拉高,避免 AF 高阻耦合假脉冲; | |||
| * - 空闲不关 CC1E:Forced inactive 主动拉低,避免 AF 高阻耦合假脉冲; | |||
| * - ARR/CCR 预装载:改频写影子寄存器,下一 UPDATE 生效,减少毛刺/多计; | |||
| * - ISR 内禁止停表换 PSC:挂起 s_req_*,任务 ApplyPending。 | |||
| * - 波形:空闲/开跑起点低;运行中 PWM1+极性高 → 每周期先高后低。 | |||
| * | |||
| * 关键数据流 | |||
| * PlanSeg → LockPscRange → DIR_WAIT → Start → ISR SetFreqIsr(只改 ARR) | |||
| @@ -35,14 +36,14 @@ TIM_HandleTypeDef htim13; | |||
| /* | |||
| * ---------- 模块静态状态 ---------- | |||
| * s_last_freq/psc/arr — 上次量化结果;匀速热路径比较后可跳过写寄存器 | |||
| * s_active_y / htim — 当前选中脉冲路 | |||
| * s_active_htim — 当前选中脉冲路 HAL 句柄 | |||
| * s_psc_locked / locked_psc — 段内 PSC 锁;跨度过大时 locked=0 | |||
| * s_req_freq / pending — ISR 无法换 PSC 时的挂起改频 | |||
| */ | |||
| static uint32_t s_last_freq; | |||
| static uint32_t s_last_psc = 0xFFFFFFFFUL; | |||
| static uint32_t s_last_arr = 0xFFFFFFFFUL; | |||
| static uint8_t s_active_y; | |||
| static uint32_t s_last_ccr; | |||
| static TIM_HandleTypeDef *s_active_htim; | |||
| static uint8_t s_psc_locked; | |||
| @@ -50,6 +51,10 @@ static uint32_t s_locked_psc; | |||
| static volatile uint32_t s_req_freq; | |||
| static volatile uint8_t s_req_pending; | |||
| /* | |||
| * 1=DIR_WAIT 已装载寄存器且 CEN 关:到期 Commit 前必须 CNT=0 再开 PWM。 | |||
| */ | |||
| static uint8_t s_regs_prepared; | |||
| /*============================================================================*/ | |||
| /* 路选择 / 时钟 / GPIO */ | |||
| @@ -114,9 +119,9 @@ static void PlsrPulseDriverDirGpioInit(void) | |||
| /** | |||
| * 停表:不关复用、不关 CC1E。 | |||
| * HAL_TIM_PWM_Stop 会清 CC1E,脚变成 AF 高阻,飞线接地的沿会耦合出极短脉冲。 | |||
| * Forced active + 通道保持使能:TIM 推挽把脚主动拉高(段间空闲高)。 | |||
| * Forced inactive + 通道保持使能:TIM 推挽把脚主动拉低(段间空闲低)。 | |||
| */ | |||
| static void PlsrPulseDriverOcHoldHigh(TIM_HandleTypeDef *htim) | |||
| static void PlsrPulseDriverOcHoldLow(TIM_HandleTypeDef *htim) | |||
| { | |||
| uint32_t ccmr; | |||
| @@ -130,7 +135,7 @@ static void PlsrPulseDriverOcHoldHigh(TIM_HandleTypeDef *htim) | |||
| ccmr = htim->Instance->CCMR1; | |||
| ccmr &= (uint32_t)(~TIM_CCMR1_OC1M); | |||
| ccmr |= TIM_OCMODE_FORCED_ACTIVE; | |||
| ccmr |= TIM_OCMODE_FORCED_INACTIVE; | |||
| htim->Instance->CCMR1 = ccmr; | |||
| htim->Instance->CCER |= TIM_CCER_CC1E; | |||
| @@ -139,17 +144,6 @@ static void PlsrPulseDriverOcHoldHigh(TIM_HandleTypeDef *htim) | |||
| TIM_CHANNEL_STATE_SET(htim, TIM_CHANNEL_1, HAL_TIM_CHANNEL_STATE_READY); | |||
| } | |||
| /** @brief 比较通道切回 PWM1 模式(开跑前) */ | |||
| static void PlsrPulseDriverOcPwm1(TIM_HandleTypeDef *htim) | |||
| { | |||
| uint32_t ccmr; | |||
| ccmr = htim->Instance->CCMR1; | |||
| ccmr &= (uint32_t)(~TIM_CCMR1_OC1M); | |||
| ccmr |= TIM_OCMODE_PWM1; | |||
| htim->Instance->CCMR1 = ccmr; | |||
| } | |||
| /** | |||
| * @brief HAL MSP:为 TIM10/11/13 配 AF GPIO 与 NVIC | |||
| * @note 由 HAL_TIM_PWM_Init 回调;优先级 5,低于 EXTI/TIM3 之外的系统中断惯例 | |||
| @@ -161,7 +155,8 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) | |||
| __HAL_RCC_GPIOF_CLK_ENABLE(); | |||
| gpio.Mode = GPIO_MODE_AF_PP; | |||
| gpio.Pull = GPIO_PULLUP; | |||
| /* 下拉:通道若短暂高阻时保持低,避免上拉把「已高」盖住导致看不到开表上升沿 */ | |||
| gpio.Pull = GPIO_PULLDOWN; | |||
| gpio.Speed = GPIO_SPEED_FREQ_HIGH; | |||
| if (htim->Instance == TIM10) | |||
| @@ -196,7 +191,7 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) | |||
| } | |||
| /** | |||
| * @brief 初始化单路脉冲 TIM:PWM1 + ARR/CCR 预装载 + 空闲 Forced 拉高 | |||
| * @brief 初始化单路脉冲 TIM:PWM1 + ARR/CCR 预装载 + 空闲 Forced 拉低 | |||
| * @param htim HAL 句柄 | |||
| * @param inst TIM10 / TIM11 / TIM13 | |||
| */ | |||
| @@ -229,8 +224,8 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst | |||
| Error_Handler(); | |||
| } | |||
| htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE; | |||
| /* 复用保持:比较通道强制有效,推挽把脚拉高 */ | |||
| PlsrPulseDriverOcHoldHigh(htim); | |||
| /* 复用保持:强制无效,推挽空闲拉低;开跑后 PWM1 每周期仍先高后低 */ | |||
| PlsrPulseDriverOcHoldLow(htim); | |||
| } | |||
| /** @brief 驱动初始化(见 plsr_pulse_driver.h) */ | |||
| @@ -239,14 +234,14 @@ void PlsrPulseDriverInit(void) | |||
| s_last_freq = 0U; | |||
| s_last_psc = 0xFFFFFFFFUL; | |||
| s_last_arr = 0xFFFFFFFFUL; | |||
| s_last_ccr = 0U; | |||
| s_psc_locked = 0U; | |||
| s_locked_psc = 0U; | |||
| s_req_pending = 0U; | |||
| s_req_freq = 0U; | |||
| s_active_y = 0U; | |||
| s_active_htim = &htim10; | |||
| PlsrPulseDriverDirGpioInit(); | |||
| /* 三路都 Init + 空闲拉高,计数器关闭;出脉冲时只 Start 选中那路 */ | |||
| /* 三路都 Init + 空闲拉低,计数器关闭;出脉冲时只 Start 选中那路 */ | |||
| PlsrPulseDriverTimInitOne(&htim10, TIM10); | |||
| PlsrPulseDriverTimInitOne(&htim11, TIM11); | |||
| PlsrPulseDriverTimInitOne(&htim13, TIM13); | |||
| @@ -286,9 +281,13 @@ uint8_t PlsrPulseDriverIsPulseTim(TIM_TypeDef *instance) | |||
| /* PSC / ARR 量化 */ | |||
| /*============================================================================*/ | |||
| /** | |||
| * @brief 在给定 PSC 下计算 ARR/CCR(约 50% 占空比) | |||
| * @note freq 钳到 [1,100000];ARR 落在 16 位合法范围 | |||
| /* | |||
| * STM32 定时器频率换算(本模块约定) | |||
| * clk_hz — TIM 计数时钟(APB2=168MHz 或 APB1=84MHz) | |||
| * psc — 预分频寄存器值,实际分频 = PSC+1 | |||
| * arr — 自动重装载,一个 PWM 周期 = (ARR+1)×(PSC+1) 个计数 | |||
| * ccr — 比较值,PWM1 模式下高电平宽度 ≈ CCR 个计数(本驱动约 50% 占空) | |||
| * 目标频率:ticks = clk_hz / freq_hz ≈ (PSC+1)×(ARR+1) | |||
| */ | |||
| static void PlsrPulseDriverCalcArrWithPsc(uint32_t clk_hz, uint32_t freq_hz, uint32_t psc, | |||
| uint32_t *arr, uint32_t *ccr) | |||
| @@ -452,10 +451,10 @@ void PlsrPulseDriverUnlockPsc(void) | |||
| s_locked_psc = 0U; | |||
| } | |||
| /** @brief 停单路:Forced active 拉高,关 CEN/UPDATE */ | |||
| /** @brief 停单路:Forced inactive 拉低,关 CEN/UPDATE */ | |||
| static void PlsrPulseDriverStopHtim(TIM_HandleTypeDef *htim) | |||
| { | |||
| PlsrPulseDriverOcHoldHigh(htim); | |||
| PlsrPulseDriverOcHoldLow(htim); | |||
| } | |||
| /*============================================================================*/ | |||
| @@ -463,11 +462,28 @@ static void PlsrPulseDriverStopHtim(TIM_HandleTypeDef *htim) | |||
| /*============================================================================*/ | |||
| /** | |||
| * @brief Start / StartFreeRun 公共路径 | |||
| * @param freq_hz 0=停表;>0 开跑 | |||
| * @param enable_update_it 1=开 UPDATE 中断(计数脉冲);0=自由跑 | |||
| * 关预装载后直写 ARR/CCR 工作寄存器,再打开预装载并复写影子。 | |||
| * 仅 UG 不可靠时(首拍 CCR 工作值仍为 0):CNT=0 也会整拍无高电平, | |||
| * 示波器上与「CNT>CCR 空等近一周期」无法区分。 | |||
| */ | |||
| static void PlsrPulseDriverForceArrCcr(TIM_HandleTypeDef *htim, | |||
| uint32_t arr, uint32_t ccr) | |||
| { | |||
| htim->Instance->CR1 &= (uint32_t)(~TIM_CR1_ARPE); | |||
| htim->Instance->CCMR1 &= (uint32_t)(~TIM_CCMR1_OC1PE); | |||
| htim->Instance->ARR = arr; | |||
| htim->Instance->CCR1 = ccr; | |||
| htim->Instance->CR1 |= TIM_CR1_ARPE; | |||
| htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE; | |||
| htim->Instance->ARR = arr; | |||
| htim->Instance->CCR1 = ccr; | |||
| } | |||
| /** | |||
| * @brief 装载 PSC/ARR/CCR,Forced 拉低,CEN=0、IT=0 | |||
| * @note 换向延时期间禁止开 CEN,避免 CNT 空转。 | |||
| */ | |||
| static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_it) | |||
| static void PlsrPulseDriverLoadRegs(uint32_t freq_hz) | |||
| { | |||
| uint32_t psc; | |||
| uint32_t arr; | |||
| @@ -482,7 +498,6 @@ static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_i | |||
| { | |||
| PlsrPulseDriverStopHtim(s_active_htim); | |||
| } | |||
| s_active_y = y; | |||
| s_active_htim = htim; | |||
| clk_hz = PlsrPulseDriverTimClkHz(htim); | |||
| @@ -492,6 +507,8 @@ static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_i | |||
| s_last_freq = 0U; | |||
| s_last_psc = 0xFFFFFFFFUL; | |||
| s_last_arr = 0xFFFFFFFFUL; | |||
| s_last_ccr = 0U; | |||
| s_regs_prepared = 0U; | |||
| return; | |||
| } | |||
| @@ -505,43 +522,109 @@ static void PlsrPulseDriverStartCommon(uint32_t freq_hz, uint8_t enable_update_i | |||
| PlsrPulseDriverCalcPscArr(clk_hz, freq_hz, &psc, &arr, &ccr); | |||
| } | |||
| if (ccr < 1U) | |||
| { | |||
| ccr = 1U; | |||
| } | |||
| s_last_freq = freq_hz; | |||
| s_last_psc = psc; | |||
| s_last_arr = arr; | |||
| s_last_ccr = ccr; | |||
| __HAL_TIM_DISABLE(htim); | |||
| __HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE); | |||
| /* 清 one-pulse,保证连续发脉冲 */ | |||
| htim->Instance->CR1 &= (uint32_t)(~TIM_CR1_OPM); | |||
| __HAL_TIM_SET_PRESCALER(htim, psc); | |||
| __HAL_TIM_SET_AUTORELOAD(htim, arr); | |||
| __HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, ccr); | |||
| __HAL_TIM_SET_COUNTER(htim, 0U); | |||
| PlsrPulseDriverForceArrCcr(htim, arr, ccr); | |||
| /* | |||
| * UG 在 Forced active 下做:脚保持高,避免 UG 在 PWM 复用脚上打出针。 | |||
| * 再切回 PWM1 并开计数。 | |||
| */ | |||
| PlsrPulseDriverOcHoldHigh(htim); | |||
| PlsrPulseDriverOcHoldLow(htim); | |||
| /* UG:PSC 生效;随后再钉 ARR/CCR/CNT,避免只进影子 */ | |||
| htim->Instance->EGR = TIM_EGR_UG; | |||
| __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE); | |||
| __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); | |||
| PlsrPulseDriverForceArrCcr(htim, arr, ccr); | |||
| htim->Instance->CNT = 0U; | |||
| s_regs_prepared = 1U; | |||
| } | |||
| /** | |||
| * @brief 开沿:先 Forced Active 出首上升沿,再切 PWM1 连续发脉冲 | |||
| * | |||
| * @note WAIT/换向后若首拍 CCR 工作值为 0,PWM1 会整拍低电平,示波器上就是 | |||
| * 「延时 + 本段第一脉冲周期」。Forced Active 与 CNT/CCR 无关,首沿必须出现。 | |||
| * Commit 内不再 UG,避免预装载影子把 CCR 又打回旧值/0。 | |||
| */ | |||
| static void PlsrPulseDriverCommitOutput(void) | |||
| { | |||
| TIM_HandleTypeDef *htim = s_active_htim; | |||
| uint32_t ccmr; | |||
| PlsrPulseDriverOcPwm1(htim); | |||
| if ((htim == (TIM_HandleTypeDef *)0) || (htim->Instance == (TIM_TypeDef *)0)) | |||
| { | |||
| return; | |||
| } | |||
| __HAL_TIM_DISABLE(htim); | |||
| __HAL_TIM_DISABLE_IT(htim, TIM_IT_UPDATE); | |||
| /* 关预装载,直写工作寄存器 */ | |||
| htim->Instance->CR1 &= (uint32_t)(~TIM_CR1_ARPE); | |||
| htim->Instance->CCMR1 &= (uint32_t)(~TIM_CCMR1_OC1PE); | |||
| htim->Instance->ARR = s_last_arr; | |||
| htim->Instance->CCR1 = (s_last_ccr < 1U) ? 1U : s_last_ccr; | |||
| htim->Instance->CNT = 0U; | |||
| /* | |||
| * 唯一的脉冲启动入口。通道必须在 CEN 前使能;TIM10/11/13 在 | |||
| * 运行中由 CC1E=0 -> 1 不保证重新建立当前 PWM1 电平,会保持无输出。 | |||
| * CNT 从 ARR 开始,下一计数节拍产生真实 UPDATE:该 UPDATE 同时建立 | |||
| * PWM1 首上升沿并进入现有脉冲 ISR 计数,边沿与 s_done 保持一一对应。 | |||
| */ | |||
| ccmr = htim->Instance->CCMR1; | |||
| ccmr &= (uint32_t)(~TIM_CCMR1_OC1M); | |||
| ccmr |= TIM_OCMODE_PWM1; | |||
| htim->Instance->CCMR1 = ccmr; | |||
| htim->Instance->CCER |= TIM_CCER_CC1E; | |||
| __HAL_TIM_ENABLE(htim); | |||
| htim->Instance->CNT = s_last_arr; | |||
| __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE); | |||
| __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); | |||
| __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE); | |||
| __HAL_TIM_ENABLE(htim); | |||
| /* 运行期改频仍走预装载 */ | |||
| htim->Instance->CR1 |= TIM_CR1_ARPE; | |||
| htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE; | |||
| htim->Instance->ARR = s_last_arr; | |||
| htim->Instance->CCR1 = (s_last_ccr < 1U) ? 1U : s_last_ccr; | |||
| s_regs_prepared = 0U; | |||
| } | |||
| if (enable_update_it != 0U) | |||
| /** | |||
| * @brief Start 公共路径(每次开沿都走 Commit,不依赖 prepared 捷径) | |||
| */ | |||
| static void PlsrPulseDriverStartCommon(uint32_t freq_hz) | |||
| { | |||
| if ((s_regs_prepared == 0U) || (freq_hz != s_last_freq) || (freq_hz == 0U)) | |||
| { | |||
| __HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE); | |||
| PlsrPulseDriverLoadRegs(freq_hz); | |||
| } | |||
| if (freq_hz != 0U) | |||
| { | |||
| PlsrPulseDriverCommitOutput(); | |||
| } | |||
| } | |||
| /** @brief 换向延时期间预装寄存器、CEN 保持关(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverPrepare(uint32_t freq_hz) | |||
| { | |||
| PlsrPulseDriverLoadRegs(freq_hz); | |||
| } | |||
| /** @brief 任务上下文改频(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverSetFreq(uint32_t freq_hz) | |||
| { | |||
| @@ -676,13 +759,6 @@ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz) | |||
| s_last_arr = arr; | |||
| } | |||
| /** @brief 挂起改频请求(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverRequestFreq(uint32_t freq_hz) | |||
| { | |||
| s_req_freq = freq_hz; | |||
| s_req_pending = 1U; | |||
| } | |||
| /** @brief 丢弃挂起(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverClearPending(void) | |||
| { | |||
| @@ -711,19 +787,14 @@ void PlsrPulseDriverApplyPending(void) | |||
| /** @brief 开 PWM + UPDATE 中断(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverStart(uint32_t freq_hz) | |||
| { | |||
| PlsrPulseDriverStartCommon(freq_hz, 1U); | |||
| } | |||
| /** @brief 开 PWM 不使能 UPDATE(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverStartFreeRun(uint32_t freq_hz) | |||
| { | |||
| PlsrPulseDriverStartCommon(freq_hz, 0U); | |||
| PlsrPulseDriverStartCommon(freq_hz); | |||
| } | |||
| /** @brief 停三路(见 plsr_pulse_driver.h) */ | |||
| void PlsrPulseDriverStop(void) | |||
| { | |||
| s_req_pending = 0U; | |||
| s_regs_prepared = 0U; | |||
| PlsrPulseDriverStopHtim(&htim10); | |||
| PlsrPulseDriverStopHtim(&htim11); | |||
| PlsrPulseDriverStopHtim(&htim13); | |||
| @@ -741,7 +812,7 @@ void PlsrPulseDriverArmOnePulseStop(void) | |||
| { | |||
| return; | |||
| } | |||
| /* 下一 UPDATE 后计数器停止,不再多开一拍 */ | |||
| /* 下一 UPDATE 后计数器停止;须在倒数第二拍尽早武装(见 run_control ISR) */ | |||
| htim->Instance->CR1 |= TIM_CR1_OPM; | |||
| } | |||
| @@ -19,8 +19,9 @@ | |||
| * Y3 → PF9 GPIO 方向(固定,不可选) | |||
| * | |||
| * 频率约定 | |||
| * 0 :停止该路 PWM(空闲 Forced active 拉高) | |||
| * 0 :停止该路 PWM(空闲 Forced inactive 拉低) | |||
| * 1~100k:按 Hz 输出;超出由 Calc 路径钳到 100k | |||
| * 波形:空闲/开跑起点低;运行中 PWM1 每周期先高后低 | |||
| */ | |||
| #ifndef PLSR_PULSE_DRIVER_H | |||
| #define PLSR_PULSE_DRIVER_H | |||
| @@ -36,7 +37,7 @@ extern TIM_HandleTypeDef htim13; | |||
| /** | |||
| * @brief 初始化三路脉冲 TIM + 方向 GPIO | |||
| * @note 调用时机:PlsrInit,在 RunControl 之前 | |||
| * @note 副作用:Y0/Y1/Y2 空闲拉高、计数器关;Y3 输出低 | |||
| * @note 副作用:Y0/Y1/Y2 空闲拉低、计数器关;Y3 输出低 | |||
| */ | |||
| void PlsrPulseDriverInit(void); | |||
| @@ -60,15 +61,16 @@ void PlsrPulseDriverLockPscRange(uint32_t f_min_hz, uint32_t f_max_hz); | |||
| void PlsrPulseDriverUnlockPsc(void); | |||
| /** | |||
| * @brief 以指定频率启动选中路 PWM,并打开 UPDATE 中断 | |||
| * @param freq_hz 0=停表;>0 开跑 | |||
| * @brief 开沿顺序:ARR/CCR → CNT=0 → UG → CCxE → CEN(CEN 后立刻有效电平) | |||
| * @note 换向延时到期应在 TIM5 延时中断内调用;勿经任务调度再开表 | |||
| */ | |||
| void PlsrPulseDriverStart(uint32_t freq_hz); | |||
| /** | |||
| * @brief 同 Start,但不使能 UPDATE 中断(自由跑,不计入脉冲 ISR) | |||
| * @brief 换向延时期间装载 ARR/CCR,Forced 低且 CEN 关 | |||
| * @note 到期在 ISR 里 Start:CNT=0+UG+CEN,首沿对齐延时结束 | |||
| */ | |||
| void PlsrPulseDriverStartFreeRun(uint32_t freq_hz); | |||
| void PlsrPulseDriverPrepare(uint32_t freq_hz); | |||
| /** | |||
| * @brief 任务上下文改频:PSC 不变则预装载 ARR;须换 PSC 则停表重装 | |||
| @@ -83,12 +85,6 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz); | |||
| */ | |||
| void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz); | |||
| /** | |||
| * @brief 请求改频(挂起),在周期边界或任务里落到硬件 | |||
| * @param freq_hz 目标 Hz;0 表示挂起停表 | |||
| */ | |||
| void PlsrPulseDriverRequestFreq(uint32_t freq_hz); | |||
| /** | |||
| * @brief 消费挂起改频(TickMs / 更新路径) | |||
| * @note s_req_freq==0 时 Stop;否则 SetFreq | |||
| @@ -99,7 +95,7 @@ void PlsrPulseDriverApplyPending(void); | |||
| void PlsrPulseDriverClearPending(void); | |||
| /** | |||
| * @brief 停止三路脉冲 TIM,空闲 Forced active 拉高 | |||
| * @brief 停止三路脉冲 TIM,空闲 Forced inactive 拉低 | |||
| * @note 不清方向脚;不清 PSC 锁(由上层 Unlock) | |||
| */ | |||
| void PlsrPulseDriverStop(void); | |||
| @@ -1,103 +1,49 @@ | |||
| /** | |||
| * @file plsr_run_control.h | |||
| * @brief 运行控制模块:多段脉冲执行状态机 | |||
| * @brief 运行控制:多段脉冲状态机(开段 → 发脉冲 → 等待/跳转 → 收尾) | |||
| * | |||
| * @details 模块职责 | |||
| * 协调路径规划、加减速曲线、脉冲驱动与外部信号,完成「开段 → 发脉冲 → | |||
| * 段后等待/跳转 → 收尾」全流程。负责解析发送模式(完成/后续)、等待类型 | |||
| * (时间/信号/ACT/EXT)及段间频率衔接策略。 | |||
| * @details 解析发送模式、等待类型与段间频率衔接;协调 accel_curve / | |||
| * pulse_driver / signal_io / param。状态:IDLE / DIR_WAIT / RUN / WAIT_COND。 | |||
| * | |||
| * 与其它模块关系 | |||
| * - plsr_path_plan :下一段索引、正反向判定(只读决策,不碰硬件) | |||
| * - plsr_accel_curve :段内梯形/三角/S/正弦脉冲域规划与 ISR 逐拍取频 | |||
| * - plsr_param :段表与公共参数只读访问 | |||
| * - plsr_pulse_driver:PWM 启停、方向、ARR/PSC 改频 | |||
| * - plsr_command :故障码 0x01~0x04 上报(PlsrCommandSetFault) | |||
| * - plsr_signal_io :TIM3 1ms、WAIT/EXT 下降沿、ACT/WAIT 超时 | |||
| * | |||
| * 关键数据流 | |||
| * Start → BeginSeg → PlanSeg → DIR_WAIT → RUN(脉冲 ISR 驱动) | |||
| * 段末 → AfterSegDone → EnterPostWaitOrNext / GotoNextOrFinish / FinishAll | |||
| * 运行中 EXT/ACT → CutSegToNext / ActExpire(可能不停表衔接下一段) | |||
| * | |||
| * 状态机概要(RcState_e) | |||
| * RC_IDLE :无段执行,可 Start | |||
| * RC_DIR_WAIT :方向已设,等 dir_delay_ms 后开 PWM | |||
| * RC_RUN :脉冲 ISR 推进 s_done,逐拍改频 | |||
| * RC_WAIT_COND :段后等待(时间到期或 WAIT/EXT 沿) | |||
| * 与 accel_curve 对齐的符号: | |||
| * f_tgt — 段表目标频率 Hz;s_done/s_target — 本段已发/目标脉冲数 | |||
| */ | |||
| #ifndef PLSR_RUN_CONTROL_H | |||
| #define PLSR_RUN_CONTROL_H | |||
| #include <stdint.h> | |||
| /** | |||
| * @brief 运行控制模块初始化 | |||
| * @note 调用时机:PlsrInit 中,PulseDriver 初始化之后 | |||
| * @note 副作用:全部静态状态复位为 RC_IDLE / 非 busy | |||
| */ | |||
| void PlsrRunControlInit(void); | |||
| /** | |||
| * @brief 从指定段启动多段序列 | |||
| * @param start_seg_1based 起始段号 1-based,越界则回退到参数 start_seg | |||
| * @return 1=已开段;0=忙、无段或首段频率非法 | |||
| * @note 调用时机:PlsrStart 转发;启动前做 0x04 频率门禁 | |||
| * @note 副作用:记录绝对坐标原点、清外部沿、进入 BeginSeg | |||
| */ | |||
| uint8_t PlsrRunControlStart(uint16_t start_seg_1based); | |||
| /** | |||
| * @brief 急停 | |||
| * @note 副作用:关 PWM、清方向/链式频率/ACT/WAIT 全部标志 | |||
| */ | |||
| void PlsrRunControlStop(void); | |||
| /** | |||
| * @brief 运行中重规划剩余脉冲的目标频率 | |||
| * @param new_tgt_hz 新目标 Hz,[1, 100000] | |||
| * @return 1=成功;0=非 RUN、非法频率或本段已完成 | |||
| * @note 副作用:remain=s_target-s_done 作为新总长,f_from=s_cur_freq 重 PlanSeg | |||
| */ | |||
| uint8_t PlsrRunControlChangeFreq(uint32_t new_tgt_hz); | |||
| /** | |||
| * @brief TIM3 1ms 回调(由 signal_io 转发) | |||
| * @note 调用时机:TIM3 UPDATE 中断,仅处理 ACT 到期置位、WAIT 时间到期置位 | |||
| * @note 副作用:置 s_act_cut_pending / s_wait_expire_req,实际切段在 TickMs | |||
| */ | |||
| void PlsrRunControlOn1ms(void); | |||
| /** | |||
| * @brief 脉冲 UPDATE 中断回调 | |||
| * @note 调用时机:每发一个脉冲;仅在 RC_RUN 有效 | |||
| * @note 副作用:s_done++、累计坐标、逐拍 Step 改频、段末 AfterSegDone | |||
| */ | |||
| uint8_t PlsrRunControlIsBusy(void); | |||
| uint16_t PlsrRunControlGetCurSeg(void); | |||
| int32_t PlsrRunControlGetAccPulse(void); | |||
| void PlsrRunControlClearAccPulse(void); | |||
| /** @brief 上电从 BKPSRAM 恢复累计脉冲(不清绝对原点锁以外的运行态) */ | |||
| void PlsrRunControlRestoreAccPulse(int32_t acc_pulse); | |||
| uint32_t PlsrRunControlGetCurFreq(void); | |||
| void PlsrRunControlTickMs(void); | |||
| /** @brief TIM5 单次延时到期(换向 / WAIT / ACT 剩余) */ | |||
| void PlsrRunControlOnDelayTimer(void); | |||
| void PlsrRunControlOnPulseIsr(void); | |||
| uint8_t PlsrRunControlChangeFreq(uint32_t freq_hz); | |||
| /** | |||
| * @brief 毫秒级任务节拍(PlsrTask 中调用) | |||
| * @note 处理:ACT/WAIT 到期切段、DIR_WAIT 超时开跑、EXT 沿 CutSeg、ApplyPending | |||
| * @brief 创建任务唤醒信号量(须在 OSInit 之后、首次 Pend/Post 之前) | |||
| */ | |||
| void PlsrRunControlTickMs(void); | |||
| /** @return 1=运动中(s_busy) */ | |||
| uint8_t PlsrRunControlIsBusy(void); | |||
| /** @return 累计脉冲有符号计数 */ | |||
| int32_t PlsrRunControlGetAccPulse(void); | |||
| /** 清零累计脉冲与绝对原点 */ | |||
| void PlsrRunControlClearAccPulse(void); | |||
| void PlsrRunControlWakeInit(void); | |||
| /** | |||
| * @return 当前段号 1-based;空闲时返回 0 | |||
| * @brief PlsrTask 休眠:被 ISR Post 立刻醒,或最多等 timeout_ticks 个 OS 节拍 | |||
| * @param timeout_ticks 与 OSSemPend 相同;0=一直等到 Post;常用 1 | |||
| */ | |||
| uint16_t PlsrRunControlGetCurSeg(void); | |||
| void PlsrRunControlWakePend(uint16_t timeout_ticks); | |||
| /** | |||
| * @return 当前输出/profile 频率 Hz(监控 0x2002 数据源) | |||
| * @brief 是否在 TIM5 单次延时等待(换向 / WAIT 时间) | |||
| * @note 此时 WakePend(0) 只等到期 Post | |||
| */ | |||
| uint32_t PlsrRunControlGetCurFreq(void); | |||
| uint8_t PlsrRunControlIsPreciseWait(void); | |||
| #endif | |||
| @@ -1,105 +1,104 @@ | |||
| /** | |||
| * @file plsr_signal_io.c | |||
| * @brief WAIT/EXT 输入与 TIM3 1ms 时基实现 | |||
| * @brief WAIT/EXT 输入 + TIM5 单次延时 + OS 节拍消抖 | |||
| * | |||
| * @details 模块职责 | |||
| * EXTI 下降沿 → pending → TIM3 每 ms 累加 wait_ms,满 20ms 且口线仍低 → fell。 | |||
| * 触发只认「确认后的 fell」,不靠平时扫口,避免脉冲耦合尖峰误切段。 | |||
| * | |||
| * 硬件 | |||
| * X4=PB5 / EXTI9_5;X5=PG12 / EXTI15_10;上拉,空闲高。 | |||
| * TIM3:PSC=83、ARR=999 → 约 1ms @ 84MHz APB1 定时器时钟惯例。 | |||
| * | |||
| * 静态状态 | |||
| * s_ms — 自由运行毫秒 | |||
| * s_db_x4/x5 — 每口 pending / wait_ms / fell | |||
| * s_htim3 — 时基句柄 | |||
| * @details | |||
| * EXTI 下降沿 → pending;DebouncePoll 满约 20ms 且仍低 → fell。 | |||
| * TIM5(32 位,APB1 84MHz):有延时才开表。 | |||
| * PSC=8399 → 10kHz(1 次计数 = 100µs) | |||
| * ARR = delay_ms×10 − 1 → 一次溢出 = 设定的 ms(分辨率 0.1ms,够用) | |||
| */ | |||
| #include "plsr_signal_io.h" | |||
| #include "plsr_param.h" | |||
| #include "plsr_run_control.h" | |||
| #include "main.h" | |||
| #include "ucos_ii.h" | |||
| #define PLSR_X4_PORT GPIOB | |||
| #define PLSR_X4_PIN GPIO_PIN_5 | |||
| #define PLSR_X5_PORT GPIOG | |||
| #define PLSR_X5_PIN GPIO_PIN_12 | |||
| #define PLSR_TIM3_PSC 83U | |||
| #define PLSR_TIM3_ARR 999U | |||
| /* | |||
| * 84MHz / 8400 = 10kHz → 100µs/格。 | |||
| * PSC=8399 落在 16 位内;不必用 1µs(那只是为了硬凑,ms 延时用不上)。 | |||
| */ | |||
| #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_SIGNAL_DEBOUNCE_MS 20U | |||
| /** | |||
| * 单路输入消抖状态 | |||
| * pending — EXTI 已见下降沿,等待 20ms 确认 | |||
| * wait_ms — 消抖累计 ms(TIM3 递增) | |||
| * fell — 确认有效,供 Take* 读清 | |||
| */ | |||
| typedef struct { | |||
| volatile uint8_t pending; | |||
| volatile uint8_t wait_ms; | |||
| volatile uint8_t stamp_ok; | |||
| volatile INT32U arm_tick; | |||
| volatile uint8_t fell; | |||
| GPIO_TypeDef *port; | |||
| uint16_t pin; | |||
| } PlsrEdge_t; | |||
| static volatile uint32_t s_ms; | |||
| static PlsrEdge_t s_db_x4; | |||
| static PlsrEdge_t s_db_x5; | |||
| 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 TIM_HandleTypeDef s_htim3; | |||
| static INT32U PlsrSignalIoMsToTicks(uint32_t ms) | |||
| { | |||
| INT32U t; | |||
| /*============================================================================*/ | |||
| /* 消抖辅助 */ | |||
| /*============================================================================*/ | |||
| if (ms == 0U) | |||
| { | |||
| return 1U; | |||
| } | |||
| t = ((INT32U)ms * (INT32U)OS_TICKS_PER_SEC + 999U) / 1000U; | |||
| if (t < 1U) | |||
| { | |||
| t = 1U; | |||
| } | |||
| return t; | |||
| } | |||
| /** @return 1=口线当前为低(接地) */ | |||
| static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) | |||
| { | |||
| return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; | |||
| } | |||
| /** @brief EXTI 入口:置 pending,清消抖计时,等待 TIM3 确认 */ | |||
| static void PlsrSignalIoOnFalling(PlsrEdge_t *db) | |||
| { | |||
| db->pending = 1U; | |||
| db->wait_ms = 0U; | |||
| db->stamp_ok = 0U; | |||
| } | |||
| /** | |||
| * @brief TIM3 每 ms 调用:pending 满 20ms 后读电平 | |||
| * @note 仍为低才置 fell;已回高则丢弃(滤耦合尖峰) | |||
| */ | |||
| static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) | |||
| { | |||
| INT32U need; | |||
| if (db->pending == 0U) | |||
| { | |||
| return; | |||
| } | |||
| if (db->wait_ms < 0xFFU) | |||
| if (db->stamp_ok == 0U) | |||
| { | |||
| db->wait_ms++; | |||
| db->arm_tick = OSTimeGet(); | |||
| db->stamp_ok = 1U; | |||
| return; | |||
| } | |||
| if (db->wait_ms < PLSR_SIGNAL_DEBOUNCE_MS) | |||
| need = PlsrSignalIoMsToTicks(PLSR_SIGNAL_DEBOUNCE_MS); | |||
| if ((OSTimeGet() - db->arm_tick) < need) | |||
| { | |||
| return; | |||
| } | |||
| db->pending = 0U; | |||
| /* 尖峰/耦合在 20ms 后已回到高,不算触发;飞线接地保持为低才算 */ | |||
| db->stamp_ok = 0U; | |||
| if (PlsrSignalIoPinIsLow(db) != 0U) | |||
| { | |||
| db->fell = 1U; | |||
| } | |||
| } | |||
| /*============================================================================*/ | |||
| /* 硬件初始化 */ | |||
| /*============================================================================*/ | |||
| /** @brief X4/X5 下降沿 EXTI + NVIC(优先级 8) */ | |||
| static void PlsrSignalIoGpioExtiInit(void) | |||
| { | |||
| GPIO_InitTypeDef gpio = {0}; | |||
| @@ -117,7 +116,6 @@ static void PlsrSignalIoGpioExtiInit(void) | |||
| gpio.Pin = PLSR_X4_PIN; | |||
| HAL_GPIO_Init(PLSR_X4_PORT, &gpio); | |||
| gpio.Pin = PLSR_X5_PIN; | |||
| HAL_GPIO_Init(PLSR_X5_PORT, &gpio); | |||
| @@ -132,45 +130,44 @@ static void PlsrSignalIoGpioExtiInit(void) | |||
| HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); | |||
| } | |||
| /** @brief TIM3 约 1ms 基时 + UPDATE 中断(优先级 9) */ | |||
| static void PlsrSignalIoTim3Init(void) | |||
| static void PlsrSignalIoTim5Init(void) | |||
| { | |||
| __HAL_RCC_TIM3_CLK_ENABLE(); | |||
| s_htim3.Instance = TIM3; | |||
| s_htim3.Init.Prescaler = PLSR_TIM3_PSC; | |||
| s_htim3.Init.CounterMode = TIM_COUNTERMODE_UP; | |||
| s_htim3.Init.Period = PLSR_TIM3_ARR; | |||
| s_htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; | |||
| s_htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; | |||
| (void)HAL_TIM_Base_Init(&s_htim3); | |||
| HAL_NVIC_SetPriority(TIM3_IRQn, 9, 0); | |||
| HAL_NVIC_EnableIRQ(TIM3_IRQn); | |||
| (void)HAL_TIM_Base_Start_IT(&s_htim3); | |||
| __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; | |||
| HAL_NVIC_SetPriority(TIM5_IRQn, 7, 0); | |||
| HAL_NVIC_EnableIRQ(TIM5_IRQn); | |||
| } | |||
| /** @brief 模块初始化(见 plsr_signal_io.h) */ | |||
| void PlsrSignalIoInit(void) | |||
| { | |||
| s_ms = 0U; | |||
| 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.wait_ms = 0U; | |||
| s_db_x4.stamp_ok = 0U; | |||
| s_db_x4.arm_tick = 0U; | |||
| s_db_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.wait_ms = 0U; | |||
| s_db_x5.stamp_ok = 0U; | |||
| s_db_x5.arm_tick = 0U; | |||
| s_db_x5.fell = 0U; | |||
| PlsrSignalIoGpioExtiInit(); | |||
| PlsrSignalIoTim3Init(); | |||
| PlsrSignalIoTim5Init(); | |||
| /* 配置 EXTI 时可能打出假沿,丢掉 */ | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X4_PIN); | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X5_PIN); | |||
| s_db_x4.fell = 0U; | |||
| @@ -179,55 +176,98 @@ void PlsrSignalIoInit(void) | |||
| s_db_x5.pending = 0U; | |||
| } | |||
| /*============================================================================*/ | |||
| /* 时基 / 沿 API */ | |||
| /*============================================================================*/ | |||
| void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) | |||
| { | |||
| uint32_t ticks; | |||
| if (delay_ms < 1U) | |||
| { | |||
| delay_ms = 1U; | |||
| } | |||
| if (delay_ms > PLSR_DELAY_MS_MAX) | |||
| { | |||
| delay_ms = PLSR_DELAY_MS_MAX; | |||
| } | |||
| /* N ms → N×10 个 100µs 格 */ | |||
| ticks = delay_ms * PLSR_TIM5_TICKS_PER_MS; | |||
| TIM5->CR1 = 0U; | |||
| TIM5->DIER = 0U; | |||
| TIM5->SR = 0U; | |||
| TIM5->PSC = PLSR_TIM5_PSC_100US; | |||
| TIM5->ARR = ticks - 1U; | |||
| TIM5->CR1 = TIM_CR1_URS; | |||
| TIM5->EGR = TIM_EGR_UG; | |||
| TIM5->SR = 0U; | |||
| TIM5->CNT = 0U; | |||
| TIM5->DIER = TIM_DIER_UIE; | |||
| s_delay_armed = 1U; | |||
| TIM5->CR1 = (uint32_t)(TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS); | |||
| } | |||
| uint32_t PlsrSignalIoGetMs(void) | |||
| void PlsrSignalIoCancelDelay(void) | |||
| { | |||
| return s_ms; | |||
| TIM5->CR1 = 0U; | |||
| TIM5->DIER = 0U; | |||
| TIM5->SR = 0U; | |||
| s_delay_armed = 0U; | |||
| } | |||
| uint32_t PlsrSignalIoDeadlineFromNow(uint32_t ms) | |||
| uint32_t PlsrSignalIoDelayRemainMs(void) | |||
| { | |||
| if (ms == 0U) | |||
| uint32_t arr; | |||
| uint32_t cnt; | |||
| uint32_t left_ticks; | |||
| if (s_delay_armed == 0U) | |||
| { | |||
| return s_ms; | |||
| return 0U; | |||
| } | |||
| return s_ms + ms; | |||
| if ((TIM5->CR1 & TIM_CR1_CEN) == 0U) | |||
| { | |||
| return 0U; | |||
| } | |||
| arr = TIM5->ARR; | |||
| cnt = TIM5->CNT; | |||
| if (cnt >= (arr + 1U)) | |||
| { | |||
| return 0U; | |||
| } | |||
| left_ticks = (arr + 1U) - cnt; | |||
| return left_ticks / PLSR_TIM5_TICKS_PER_MS; | |||
| } | |||
| uint8_t PlsrSignalIoMsReached(uint32_t deadline_ms) | |||
| void PlsrSignalIoDebouncePoll(void) | |||
| { | |||
| return ((int32_t)(s_ms - deadline_ms) >= 0) ? 1U : 0U; | |||
| PlsrSignalIoDebounceConfirm(&s_db_x4); | |||
| PlsrSignalIoDebounceConfirm(&s_db_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; | |||
| } | |||
| 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; | |||
| } | |||
| /** @brief sel 0=X4,非 0=X5 */ | |||
| static PlsrEdge_t *PlsrSignalIoDbBySel(uint16_t sel_01) | |||
| { | |||
| return (sel_01 != 0U) ? &s_db_x5 : &s_db_x4; | |||
| } | |||
| uint8_t PlsrSignalIoWaitIsOn(void) | |||
| { | |||
| PlsrCfg_t *cfg = PlsrParamGetCfg(); | |||
| return PlsrSignalIoPinIsLow(PlsrSignalIoDbBySel(cfg->wait_x_sel)); | |||
| } | |||
| uint8_t PlsrSignalIoTakeWaitFalling(void) | |||
| { | |||
| PlsrCfg_t *cfg = PlsrParamGetCfg(); | |||
| @@ -254,20 +294,18 @@ uint8_t PlsrSignalIoTakeExtFalling(void) | |||
| return 0U; | |||
| } | |||
| /*============================================================================*/ | |||
| /* 中断入口(由 stm32f4xx_it 转发) */ | |||
| /*============================================================================*/ | |||
| /** @brief TIM3:清 UIF → s_ms++ → 消抖 → RunControlOn1ms */ | |||
| void PlsrSignalIoOnTim3Irq(void) | |||
| void PlsrSignalIoOnTim5Irq(void) | |||
| { | |||
| if ((TIM3->SR & TIM_SR_UIF) != 0U) | |||
| if ((TIM5->SR & TIM_SR_UIF) != 0U) | |||
| { | |||
| TIM3->SR = (uint32_t)~TIM_SR_UIF; | |||
| s_ms++; | |||
| PlsrSignalIoDebounceConfirm(&s_db_x4); | |||
| PlsrSignalIoDebounceConfirm(&s_db_x5); | |||
| PlsrRunControlOn1ms(); | |||
| TIM5->SR = (uint32_t)~TIM_SR_UIF; | |||
| TIM5->DIER = 0U; | |||
| TIM5->CR1 = 0U; | |||
| if (s_delay_armed != 0U) | |||
| { | |||
| s_delay_armed = 0U; | |||
| PlsrRunControlOnDelayTimer(); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,93 +1,50 @@ | |||
| /** | |||
| * @file plsr_signal_io.h | |||
| * @brief WAIT/EXT 外部输入与 TIM3 1ms 时基 | |||
| * @brief WAIT/EXT 外部输入与 TIM5 单次延时 | |||
| * | |||
| * @details 模块职责 | |||
| * 1. 配置 X4=PB5、X5=PG12 为下降沿 EXTI(上拉空闲高,飞线接地出沿); | |||
| * 2. 20ms 消抖确认后锁存 fell,供 run_control 消费; | |||
| * 3. TIM3 自由运行毫秒计数,驱动 ACT/WAIT 到期与消抖确认,并转发 On1ms。 | |||
| * 2. 消抖:EXTI 置 pending,任务侧按 OS 节拍确认 20ms 仍低 → fell; | |||
| * 3. TIM5(32 位)仅在有延时时开启:100µs 时基,ARR=ms×10−1, | |||
| * 一次计满 = 设定 ms(最长 65535ms),到期 UPDATE → OnDelayTimer。 | |||
| * | |||
| * 与其它模块关系 | |||
| * - plsr_param :wait_x_sel / ext_x_sel 选择 X4 或 X5 | |||
| * - plsr_run_control :TakeWait/ExtFalling、GetMs/Deadline/MsReached、On1ms | |||
| * - stm32f4xx_it :TIM3 / EXTI9_5 / EXTI15_10 转发到本模块 On* 入口 | |||
| * | |||
| * 关键约定 | |||
| * ClearEdges — 清 pending+fell(开跑 EXT、Start) | |||
| * ClearPending— 只清消抖中假沿,保留已确认 fell(纯 EXT 发完后过滤耦合) | |||
| * Take* — 读清 fell,一次按压只切一次段 | |||
| * - plsr_param :wait_x_sel / ext_x_sel | |||
| * - plsr_run_control :TakeWait/ExtFalling、ScheduleDelayMs、OnDelayTimer | |||
| * - plsr.c :周期调用 DebouncePoll | |||
| * - stm32f4xx_it :TIM5 / EXTI 转发 | |||
| */ | |||
| #ifndef PLSR_SIGNAL_IO_H | |||
| #define PLSR_SIGNAL_IO_H | |||
| #include <stdint.h> | |||
| /** | |||
| * @brief 初始化 GPIO EXTI + TIM3 1ms | |||
| * @note 调用时机:PlsrInit;配置后清假沿 | |||
| */ | |||
| void PlsrSignalIoInit(void); | |||
| /** | |||
| * @brief 读取 TIM3 毫秒计数(自由运行,约 1ms/格) | |||
| * @return 自 Init 起累加的 ms;可能无符号环绕 | |||
| * @brief 武装 TIM5 单次延时(ms),到期进 OnDelayTimer | |||
| * @param delay_ms 0 按 1ms;上限 65535ms | |||
| */ | |||
| uint32_t PlsrSignalIoGetMs(void); | |||
| void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); | |||
| /** | |||
| * @brief 从当前 ms 起算 deadline | |||
| * @param ms 延时;0 表示立即到期(deadline=当前) | |||
| */ | |||
| uint32_t PlsrSignalIoDeadlineFromNow(uint32_t ms); | |||
| void PlsrSignalIoCancelDelay(void); | |||
| /** | |||
| * @brief 判断是否已到或过期 | |||
| * @return 1=已到(含无符号环绕比较);0=未到 | |||
| * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) | |||
| */ | |||
| uint8_t PlsrSignalIoMsReached(uint32_t deadline_ms); | |||
| uint32_t PlsrSignalIoDelayRemainMs(void); | |||
| /** | |||
| * @brief 清 WAIT/EXT 下降沿锁存(含已确认 fell 与 pending) | |||
| * @note Start / EXT 开跑时调用,避免旧沿立刻切段 | |||
| */ | |||
| void PlsrSignalIoClearEdges(void); | |||
| void PlsrSignalIoDebouncePoll(void); | |||
| /** | |||
| * @brief 只清消抖 pending,保留已确认 fell | |||
| * @note 纯 EXT 脉冲发完后过滤 Y 口耦合假沿,同时保留真沿供 TakeExt | |||
| */ | |||
| void PlsrSignalIoClearEdges(void); | |||
| void PlsrSignalIoClearPending(void); | |||
| /** | |||
| * @brief WAIT 口当前是否为低(已接地) | |||
| * @note 仅段末判断等场景读一次;不作周期轮询触发(触发只认 EXTI+消抖) | |||
| */ | |||
| uint8_t PlsrSignalIoWaitIsOn(void); | |||
| /** | |||
| * @brief 消耗一次已确认的 WAIT 口下降沿 | |||
| * @return 1=有沿并已清;0=无 | |||
| * @note ※2 段中接地保持 → 发完立刻下一段;※3 未触发则 RC_WAIT_COND 一直等 | |||
| */ | |||
| uint8_t PlsrSignalIoTakeWaitFalling(void); | |||
| /** | |||
| * @brief 消耗一次已确认的 EXT 口下降沿 | |||
| * @return 1=有沿并已清;0=无 | |||
| * @note 运行中 EXT / EXT_OR_DONE 切段;一次按压只切一次 | |||
| */ | |||
| uint8_t PlsrSignalIoTakeExtFalling(void); | |||
| /** | |||
| * @brief TIM3 UPDATE 中断入口 | |||
| * @note s_ms++、消抖确认、转发 PlsrRunControlOn1ms | |||
| */ | |||
| void PlsrSignalIoOnTim3Irq(void); | |||
| /** @brief EXTI9_5:处理 X4=PB5 下降沿 → 置 pending */ | |||
| void PlsrSignalIoOnExti9_5(void); | |||
| /** @brief TIM5 UPDATE:单次延时到期 */ | |||
| void PlsrSignalIoOnTim5Irq(void); | |||
| /** @brief EXTI15_10:处理 X5=PG12 下降沿 → 置 pending */ | |||
| void PlsrSignalIoOnExti15_10(void); | |||
| void PlsrSignalIoOnExti9_5(void); | |||
| void PlsrSignalIoOnExti15_10(void); | |||
| #endif | |||