소스 검색

改用bkp存储,存储公共参数、段参数、记录信息

Signed-off-by: hanyongwei <2043702190@qq.com>
dev1
hanyongwei 3 주 전
부모
커밋
fabb0f833f
22개의 변경된 파일2667개의 추가작업 그리고 2485개의 파일을 삭제
  1. +0
    -2
      .gitignore
  2. +5
    -4
      app/main.c
  3. +3
    -3
      drivers/Source/stm32f4xx_it.c
  4. +1591
    -984
      iar/plsr.dep
  5. +24
    -34
      iar/plsr.ewp
  6. +0
    -9
      iar/plsr.ewt
  7. +10
    -403
      plsr/accel_curve/plsr_accel_curve.c
  8. +41
    -148
      plsr/accel_curve/plsr_accel_curve.h
  9. +15
    -2
      plsr/command/plsr_command.c
  10. +1
    -1
      plsr/command/plsr_command.h
  11. +7
    -7
      plsr/param/plsr_param.c
  12. +9
    -6
      plsr/param/plsr_param.h
  13. +0
    -91
      plsr/path_plan/plsr_path_plan.c
  14. +0
    -49
      plsr/path_plan/plsr_path_plan.h
  15. +24
    -48
      plsr/plsr.c
  16. +6
    -6
      plsr/plsr.h
  17. +133
    -62
      plsr/pulse_driver/plsr_pulse_driver.c
  18. +9
    -13
      plsr/pulse_driver/plsr_pulse_driver.h
  19. +610
    -375
      plsr/run_control/plsr_run_control.c
  20. +24
    -78
      plsr/run_control/plsr_run_control.h
  21. +135
    -97
      plsr/signal_io/plsr_signal_io.c
  22. +20
    -63
      plsr/signal_io/plsr_signal_io.h

+ 0
- 2
.gitignore 파일 보기

@@ -21,5 +21,3 @@ build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/
plsr/test/
tools/
.cursor/
*.pdf
3. 定位控制手册.pdf

+ 5
- 4
app/main.c 파일 보기

@@ -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();


+ 3
- 3
drivers/Source/stm32f4xx_it.c 파일 보기

@@ -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();
}



+ 1591
- 984
iar/plsr.dep
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


+ 24
- 34
iar/plsr.ewp 파일 보기

@@ -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>


+ 0
- 9
iar/plsr.ewt 파일 보기

@@ -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
- 403
plsr/accel_curve/plsr_accel_curve.c 파일 보기

@@ -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) */
/*============================================================================*/


+ 41
- 148
plsr/accel_curve/plsr_accel_curve.h 파일 보기

@@ -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

+ 15
- 2
plsr/command/plsr_command.c 파일 보기

@@ -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();
}
}

+ 1
- 1
plsr/command/plsr_command.h 파일 보기

@@ -83,7 +83,7 @@ void PlsrCommandSetFault(uint16_t code);

/**
* @brief 清故障为 0
* @note 调用时机:START 前、CLR 控制位、Init
* @note 调用时机:START 前、CLR 控制位(Init 不再清,以便上电恢复)
*/
void PlsrCommandClearFault(void);



+ 7
- 7
plsr/param/plsr_param.c 파일 보기

@@ -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)


+ 9
- 6
plsr/param/plsr_param.h 파일 보기

@@ -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);



+ 0
- 91
plsr/path_plan/plsr_path_plan.c 파일 보기

@@ -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);
}

+ 0
- 49
plsr/path_plan/plsr_path_plan.h 파일 보기

@@ -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

+ 24
- 48
plsr/plsr.c 파일 보기

@@ -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 独立 */
}
}

+ 6
- 6
plsr/plsr.h 파일 보기

@@ -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);



+ 133
- 62
plsr/pulse_driver/plsr_pulse_driver.c 파일 보기

@@ -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;
}



+ 9
- 13
plsr/pulse_driver/plsr_pulse_driver.h 파일 보기

@@ -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);


+ 610
- 375
plsr/run_control/plsr_run_control.c
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


+ 24
- 78
plsr/run_control/plsr_run_control.h 파일 보기

@@ -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

+ 135
- 97
plsr/signal_io/plsr_signal_io.c 파일 보기

@@ -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();
}
}
}



+ 20
- 63
plsr/signal_io/plsr_signal_io.h 파일 보기

@@ -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

불러오는 중...
취소
저장