Quellcode durchsuchen

简化无用的代码

dev1
hanyongwei vor 3 Wochen
Ursprung
Commit
2073601b84
13 geänderte Dateien mit 2388 neuen und 3389 gelöschten Zeilen
  1. +781
    -1510
      iar/plsr.dep
  2. +912
    -918
      plsr/accel_curve/plsr_accel_curve.c
  3. +63
    -60
      plsr/accel_curve/plsr_accel_curve.h
  4. +11
    -15
      plsr/command/plsr_command.c
  5. +89
    -155
      plsr/param/plsr_param.c
  6. +14
    -19
      plsr/param/plsr_param.h
  7. +4
    -53
      plsr/plsr.c
  8. +10
    -18
      plsr/plsr.h
  9. +79
    -24
      plsr/pulse_driver/plsr_pulse_driver.c
  10. +10
    -4
      plsr/pulse_driver/plsr_pulse_driver.h
  11. +392
    -585
      plsr/run_control/plsr_run_control.c
  12. +1
    -12
      plsr/run_control/plsr_run_control.h
  13. +22
    -16
      plsr/signal_io/plsr_signal_io.c

+ 781
- 1510
iar/plsr.dep
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 912
- 918
plsr/accel_curve/plsr_accel_curve.c
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 63
- 60
plsr/accel_curve/plsr_accel_curve.h Datei anzeigen

@@ -5,14 +5,13 @@
* @details 按起/峰/止频率、斜率时间与总脉冲规划三相脉冲预算,并在脉冲序号上取频。
* 不读 wait_type,不碰 TIM/GPIO。
*
* 直线:f_n = sqrt(f0^2 ± 2 a n)(脉冲域,ISR 增量逼近)。
* 直线:下一频率由起点频率、变化率和已完成脉冲数计算(ISR 增量逼近)。
* S/正弦:开相时预计算频率表;ISR 只按脉冲序号查表(匀速相不改频)。
* acc_n/dec_n 与建表同一套离散逐拍仿真。
* 规划脉冲数与建表使用同一套离散逐拍仿真。
*
* 术语(全模块统一):
* 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
* 字段使用完整名称和单位,例如 accel_pulses、start_frequency_hz,
* 避免阅读代码时反复猜测 acc_n、f_cur、a_acc 等数学缩写。
*/
#ifndef PLSR_ACCEL_CURVE_H
#define PLSR_ACCEL_CURVE_H
@@ -22,87 +21,91 @@

/**
* 单段脉冲域规划结果(PlsrAccelCurvePlan 输出)
* EnterDecel 可能改写 dec_n,诊断用 run_control 另存 s_plan_dec_n
* run_control 进入减速时使用局部规划副本,不会改写这里的整段规划预算
*/
typedef struct {
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 / 正弦 */
uint32_t accel_pulses; /* 入口到目标频率需要的脉冲数 */
uint32_t constant_pulses; /* 保持目标频率的脉冲数 */
uint32_t decel_pulses; /* 目标到出口频率需要的脉冲数 */
uint32_t start_frequency_hz; /* 本段实际入口频率 */
uint32_t target_frequency_hz; /* 实际目标频率,短段可能被降低 */
uint32_t end_frequency_hz; /* 段出口频率 */
uint32_t accel_rate_hz_per_s; /* 升高频率时使用的变化率 */
uint32_t decel_rate_hz_per_s; /* 降低频率时使用的变化率 */
PlsrAccelMode_e curve_mode; /* 直线 / S / 正弦 */
} PlsrAccelPlan_t;

/** 配置起速 → 规划入口:起速>目标用起速;否则目标与起跳比较取其一 */
uint32_t PlsrAccelCurveResolveStartHz(uint32_t f_cfg,
uint32_t f_tgt,
uint32_t default_spd,
uint32_t accel_ms,
uint32_t decel_ms);
uint32_t PlsrAccelCurveResolveStartHz(uint32_t configured_frequency_hz,
uint32_t target_frequency_hz,
uint32_t default_speed_hz,
uint32_t acceleration_time_ms,
uint32_t deceleration_time_ms);

/** 配置止速 → 规划出口:止速>目标用止速;否则目标与起跳比较取其一(不默认到 0) */
uint32_t PlsrAccelCurveResolveEndHz(uint32_t f_cfg,
uint32_t f_tgt,
uint32_t default_spd,
uint32_t accel_ms,
uint32_t decel_ms);
uint32_t PlsrAccelCurveResolveEndHz(uint32_t configured_frequency_hz,
uint32_t target_frequency_hz,
uint32_t default_speed_hz,
uint32_t acceleration_time_ms,
uint32_t deceleration_time_ms);

/**
* 规划一整段脉冲域三相预算与实际峰值
* 脉冲不够爬到 f_tgt 时先 const_n=0,再 FitPeak 降峰(调用方可能写故障 0x02)
* 脉冲不够到达目标频率时取消匀速段,再降低实际目标频率。
*/
void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan,
uint32_t total_pulses,
uint32_t f_cur,
uint32_t f_tgt,
uint32_t f_end,
uint32_t default_spd,
uint32_t accel_ms,
uint32_t decel_ms,
PlsrAccelMode_e mode);
uint32_t start_frequency_hz,
uint32_t target_frequency_hz,
uint32_t end_frequency_hz,
uint32_t default_speed_hz,
uint32_t acceleration_time_ms,
uint32_t deceleration_time_ms,
PlsrAccelMode_e curve_mode);

/** 按已完成脉冲数取频(任务/预览;ISR 热路径用 PulseRtStep) */
/** 按已完成脉冲数取频(任务/预览;ISR 热路径用 PlsrAccelNextFrequency) */
uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan,
uint32_t pulse_done);
uint32_t completed_segment_pulses);

/**
* 单相逐脉冲运行态(ISR 热路径,每来一个 UPDATE 调 Step 一次)
* 直线:±1Hz 逼近 sqrt(f0^2±2an)
* 直线:每次按目标平方值逐步逼近下一频率
* S/正弦:查预计算频率表(开相 BeginAcc/BeginDec 时填好)
*/
typedef struct {
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 t_us; /* 建表用:累计时间 us */
uint32_t tj_us; /* 建表用:S jerk 段 us */
uint32_t ta_us; /* 建表用:S 恒加速段 us */
uint32_t tramp_us; /* 建表用:斜坡总时长 us */
uint32_t tbl_stride; /* 查表:每 stride 个脉冲对应 1 个表项 */
uint32_t tbl_len; /* 表有效长度 */
uint8_t rising; /* 1=频率升高,0=降低 */
uint8_t active; /* 1=本相仍在步进;0=已到 f1 */
uint8_t use_tbl; /* 1=S/正弦走查表 */
PlsrAccelMode_e mode;
} PlsrAccelPulseRt_t;
uint32_t current_frequency_hz; /* 当前输出频率 */
uint32_t start_frequency_hz; /* 当前相起点频率 */
uint32_t end_frequency_hz; /* 当前相终点频率 */
uint32_t acceleration_hz_per_s; /* 当前相使用的变化率 */
uint32_t completed_pulses; /* 当前相已经步进的脉冲数 */
uint32_t total_pulses; /* 当前相计划步进的脉冲数 */
uint32_t elapsed_time_us; /* 时间曲线已经经过的时间 */
uint32_t jerk_time_us; /* S 曲线单个 jerk 阶段时间 */
uint32_t constant_accel_time_us; /* S 曲线恒加速阶段时间 */
uint32_t ramp_time_us; /* 整个斜坡时间 */
uint32_t table_stride; /* 每几个脉冲读取一个表项 */
uint32_t table_length; /* 表中有效项数 */
uint8_t frequency_rising; /* 1=频率升高,0=降低 */
uint8_t is_active; /* 1=当前相仍在运行 */
uint8_t frequency_table_id; /* 0=不用表,1=加速表,2=减速表 */
PlsrAccelMode_e curve_mode;
} PlsrAccelRuntime_t;

/* 当前段唯一的曲线规划和逐拍状态,run_control 直接使用,不再另存副本。 */
extern PlsrAccelPlan_t g_plsr_accel_plan;
extern PlsrAccelRuntime_t g_plsr_accel_runtime;

/** PlanSeg 后调用:预建 S/正弦加减速频率表(勿在 ISR) */
void PlsrAccelPulseRtPrebuild(const PlsrAccelPlan_t *plan);
void PlsrAccelPrebuildFrequencyTables(const PlsrAccelPlan_t *plan);

void PlsrAccelPulseRtBeginAcc(PlsrAccelPulseRt_t *pulse_rt,
const PlsrAccelPlan_t *plan);
void PlsrAccelPulseRtBeginDec(PlsrAccelPulseRt_t *pulse_rt,
const PlsrAccelPlan_t *plan);
void PlsrAccelPulseRtBeginConst(PlsrAccelPulseRt_t *pulse_rt,
void PlsrAccelBeginAcceleration(PlsrAccelRuntime_t *runtime,
const PlsrAccelPlan_t *plan);
void PlsrAccelBeginDeceleration(PlsrAccelRuntime_t *runtime,
const PlsrAccelPlan_t *plan);
void PlsrAccelBeginConstantSpeed(PlsrAccelRuntime_t *runtime,
const PlsrAccelPlan_t *plan);

/** 本相前进 1 脉冲,返回下一拍命令频率 */
uint32_t PlsrAccelPulseRtStep(PlsrAccelPulseRt_t *pulse_rt);
uint32_t PlsrAccelNextFrequency(PlsrAccelRuntime_t *runtime);

#endif

+ 11
- 15
plsr/command/plsr_command.c Datei anzeigen

@@ -19,6 +19,7 @@
#include "plsr_command.h"
#include "plsr_param.h"
#include "plsr_run_control.h"
#include "plsr_pulse_driver.h"
#include "plsr.h"
#include "plsr_persist.h"
#include "modbus_rtu.h"
@@ -52,14 +53,14 @@ static void PlsrCommandWriteDWord(uint16_t addr, uint32_t value)
*/
static void PlsrCommandPublishMonitor(void)
{
PlsrCommandWriteDWord(PLSR_MON_ACC_PULSE_L, (uint32_t)PlsrGetAccPulse());
PlsrCommandWriteDWord(PLSR_MON_ACC_PULSE_L, (uint32_t)g_plsr_accumulated_pulses);
WriteHoldReg(PLSR_MON_ERR, s_fault);

if (PlsrIsBusy() != 0U)
if (g_plsr_busy != 0U)
{
PlsrCommandWriteDWord(PLSR_MON_FREQ_L, PlsrRunControlGetCurFreq());
PlsrCommandWriteDWord(PLSR_MON_FREQ_L, g_plsr_output_frequency_hz);
WriteHoldReg(PLSR_MON_RUN_STATUS, 1U);
WriteHoldReg(PLSR_MON_CUR_SEG, PlsrRunControlGetCurSeg());
WriteHoldReg(PLSR_MON_CUR_SEG, (uint16_t)(g_plsr_current_segment_index + 1U));
}
else
{
@@ -122,13 +123,12 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity)
uint16_t seg0;
uint16_t off;
uint32_t f;
PlsrSeg_t *seg;

if (quantity != 2U)
{
return;
}
if (PlsrIsBusy() == 0U)
if (g_plsr_busy == 0U)
{
return;
}
@@ -148,7 +148,7 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity)
return;
}

cur1 = PlsrRunControlGetCurSeg();
cur1 = (uint16_t)(g_plsr_current_segment_index + 1U);
if ((cur1 < 1U) || ((uint16_t)(cur1 - 1U) != seg0))
{
return;
@@ -163,13 +163,9 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity)
return;
}

seg = PlsrParamGetSeg(seg0);
if (seg != (PlsrSeg_t *)0)
{
seg->freq_hz = (int32_t)f;
/* 运行中改频:立即只把该段频率双字写入 BKP,不等整包参数落盘 */
PlsrPersistSaveSegFreqToBkp(seg0, f);
}
g_plsr_segments[seg0].freq_hz = (int32_t)f;
/* 运行中改频:立即只把该段频率双字写入 BKP,不等整包参数落盘 */
PlsrPersistSaveSegFreqToBkp(seg0, f);

s_live_freq_hz = f;
s_live_freq_req = 1U;
@@ -209,7 +205,7 @@ void PlsrCommandPoll(void)
if (((ctrl & PLSR_CTRL_BIT_START) != 0U) &&
((ctrl & PLSR_CTRL_BIT_STOP) == 0U))
{
if (PlsrIsBusy() != 0U)
if (g_plsr_busy != 0U)
{
/* 0x0C:运行中重复 START,启动无效 */
PlsrCommandSetFault(PLSR_ERR_DUP_START);


+ 89
- 155
plsr/param/plsr_param.c Datei anzeigen

@@ -3,11 +3,13 @@
* @brief 参数块管理实现:运行映像 + 分帧导入/导出 + ParamBlockTask
*
* @details 模块职责
* 维护静态映像 s_cfg / s_seg[],实现寄存器 ↔ 结构体双向转换,以及
* 维护公共参数 g_plsr_config 和段参数 g_plsr_segments[],实现寄存器与
* 运行参数之间的双向转换,以及
* 「5+N 分帧」收齐后的异步 Apply(信号量唤醒本任务)。
*
* 静态状态说明
* s_cfg / s_seg[] — 运行唯一参数源
* g_plsr_config — 公共运行参数
* g_plsr_segments[] — 10 段运行参数
* s_param_sem — 收齐分帧后 Post,Task Pend 后 Apply
* s_expect_frames — 期望总帧 = 5 + 公共帧1 中的段数
* s_recv_frames — 已收帧计数(公共帧1 起算)
@@ -23,91 +25,27 @@
#include "plsr_command.h"
#include "modbus_rtu.h"
#include "ucos_ii.h"
#include <string.h>

static PlsrCfg_t s_cfg; /* 公共运行参数映像 */
static PlsrSeg_t s_seg[PLSR_SEG_MAX]; /* 段表映像(最多 PLSR_SEG_MAX 段) */
PlsrCfg_t g_plsr_config;
PlsrSeg_t g_plsr_segments[PLSR_SEG_MAX];

static OS_EVENT *s_param_sem; /* 分帧收齐后唤醒 ParamBlockTask */
static volatile uint16_t s_expect_frames; /* 本批期望总帧数 = 5 + 段数 */
static volatile uint16_t s_recv_frames; /* 已收帧计数 */
static volatile uint8_t s_xfer_active; /* 1=正在接收一批分帧 */

/*============================================================================*/
/* 运行映像 */
/*============================================================================*/

/**
* @brief 出厂默认参数(见 plsr_param.h)
* @note 与上位机 initCommonDefaults 约定一致,便于未写 PLC 时行为可预期
*/
//void PlsrParamInitDefault(void)
//{
// uint16_t i;
//
// memset(&s_cfg, 0, sizeof(s_cfg));
// memset(s_seg, 0, sizeof(s_seg));
//
// /* 与上位机默认约定一致;真正运行参数由 ApplyFromHold / PersistLoad 覆盖 */
// s_cfg.pulse_y = 0U;
// s_cfg.dir_y = 3U;
// s_cfg.wait_x_sel = 0U;
// s_cfg.ext_x_sel = 0U;
// s_cfg.send_mode = PLSR_SEND_COMPLETE;
// s_cfg.dir_delay_ms = 10U;
// s_cfg.dir_logic = PLSR_DIR_LOGIC_POS;
// s_cfg.accel_mode = PLSR_ACCEL_LINEAR;
// s_cfg.run_mode = PLSR_POS_RELATIVE;
// s_cfg.seg_count = 0U;
// s_cfg.start_seg = 1U;
// s_cfg.default_speed = 1000UL;
// s_cfg.start_speed = 0UL;
// s_cfg.end_speed = 0UL;
// s_cfg.accel_ms = 100U;
// s_cfg.decel_ms = 100U;
//
// for (i = 0U; i < PLSR_SEG_MAX; i++)
// {
// s_seg[i].freq_hz = 0;
// s_seg[i].pulse_cnt = 0;
// s_seg[i].wait_type = PLSR_WAIT_TIME;
// s_seg[i].wait_ms = 0U;
// s_seg[i].act_ms = 0U;
// s_seg[i].jump_seg = 0U;
// }
//}

/** @return 公共参数映像指针(见 plsr_param.h) */
PlsrCfg_t *PlsrParamGetCfg(void)
{
return &s_cfg;
}

/**
* @brief 取段参数指针(见 plsr_param.h)
* @note 越界钳到段 0,避免空指针
*/
PlsrSeg_t *PlsrParamGetSeg(uint16_t seg_0based)
{
if (seg_0based >= PLSR_SEG_MAX)
{
seg_0based = 0U;
}
return &s_seg[seg_0based];
}

/** @return 有效段数,钳制到 [0, PLSR_SEG_MAX](见 plsr_param.h) */
uint16_t PlsrParamGetSegCount(void)
{
if (s_cfg.seg_count < 1U)
if (g_plsr_config.seg_count < 1U)
{
return 0U;
}
if (s_cfg.seg_count > PLSR_SEG_MAX)
if (g_plsr_config.seg_count > PLSR_SEG_MAX)
{
return (uint16_t)PLSR_SEG_MAX;
}
return s_cfg.seg_count;
return g_plsr_config.seg_count;
}

/**
@@ -122,16 +60,16 @@ uint16_t PlsrParamGetStartSeg(void)
{
return 1U;
}
if (s_cfg.start_seg < 1U)
if (g_plsr_config.start_seg < 1U)
{
return 1U;
}
/* TODO:起始段非法时输出故障码 */
if (s_cfg.start_seg > n)
if (g_plsr_config.start_seg > n)
{
return n;
}
return s_cfg.start_seg;
return g_plsr_config.start_seg;
}

/*============================================================================*/
@@ -153,12 +91,6 @@ static void PlsrParamWriteDWord(uint16_t addr, uint32_t value)
WriteHoldReg((uint16_t)(addr + 1U), (uint16_t)((value >> 16) & 0xFFFFU));
}

/** 按有符号 32 位解释双字(频率/脉冲补码) */
static int32_t PlsrParamReadSignedDWord(uint16_t addr)
{
return (int32_t)PlsrParamReadDWord(addr);
}

/**
* @brief 判断本次 FC10 是否为公共帧 2~5(双字速度/加减速)
* @return 1=是;0=否
@@ -222,12 +154,6 @@ static void PlsrParamSetXferStatus(uint16_t st, uint16_t fail_frame)
#define PLSR_PARAM_SPD_MIN_HZ 1U
#define PLSR_PARAM_SPD_MAX_HZ 100000U

/** 记录「最后一个」故障码(校验顺序决定多错时保留末项) */
static void PlsrParamFaultLast(uint16_t *fault, uint16_t code)
{
*fault = code;
}

static uint8_t PlsrParamIsSpeedHzValid(uint32_t hz)
{
return ((hz >= PLSR_PARAM_SPD_MIN_HZ) && (hz <= PLSR_PARAM_SPD_MAX_HZ)) ? 1U : 0U;
@@ -288,93 +214,97 @@ uint8_t PlsrParamBlockApplyFromHold(void)
uint16_t n;
uint16_t base;
uint16_t fault = PLSR_ERR_NONE;
PlsrSeg_t *seg;

s_cfg.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y);
if (s_cfg.pulse_y > 2U)
g_plsr_config.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y);
if (g_plsr_config.pulse_y > 2U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_PULSE_Y);
fault = PLSR_ERR_PULSE_Y;
}

s_cfg.dir_y = ReadHoldReg(PLSR_REG_DIR_Y);
if (s_cfg.dir_y != 3U)
g_plsr_config.dir_y = ReadHoldReg(PLSR_REG_DIR_Y);
if (g_plsr_config.dir_y != 3U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_DIR_Y);
fault = PLSR_ERR_DIR_Y;
}

s_cfg.wait_x_sel = ReadHoldReg(PLSR_REG_WAIT_X) ? 1U : 0U;
s_cfg.ext_x_sel = ReadHoldReg(PLSR_REG_EXT_X) ? 1U : 0U;
s_cfg.send_mode = (PlsrSendMode_e)(ReadHoldReg(PLSR_REG_SEND_MODE) ? 1U : 0U);
s_cfg.dir_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY);
s_cfg.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ?
g_plsr_config.wait_x_sel = ReadHoldReg(PLSR_REG_WAIT_X) ? 1U : 0U;
g_plsr_config.ext_x_sel = ReadHoldReg(PLSR_REG_EXT_X) ? 1U : 0U;
g_plsr_config.send_mode = (PlsrSendMode_e)(ReadHoldReg(PLSR_REG_SEND_MODE) ? 1U : 0U);
g_plsr_config.dir_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY);
g_plsr_config.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ?
PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS;
s_cfg.accel_mode = (PlsrAccelMode_e)ReadHoldReg(PLSR_REG_ACCEL_MODE);
s_cfg.run_mode = (ReadHoldReg(PLSR_REG_RUN_MODE) != 0U) ?
g_plsr_config.accel_mode = (PlsrAccelMode_e)ReadHoldReg(PLSR_REG_ACCEL_MODE);
g_plsr_config.run_mode = (ReadHoldReg(PLSR_REG_RUN_MODE) != 0U) ?
PLSR_POS_ABSOLUTE : PLSR_POS_RELATIVE;

n = ReadHoldReg(PLSR_REG_SEG_COUNT);
s_cfg.seg_count = n;
g_plsr_config.seg_count = n;
if ((n < 1U) || (n > PLSR_SEG_MAX))
{
PlsrParamFaultLast(&fault, PLSR_ERR_SEG_COUNT);
fault = PLSR_ERR_SEG_COUNT;
}

s_cfg.start_seg = ReadHoldReg(PLSR_REG_START_SEG);
g_plsr_config.start_seg = ReadHoldReg(PLSR_REG_START_SEG);
if ((n >= 1U) && (n <= PLSR_SEG_MAX))
{
if ((s_cfg.start_seg < 1U) || (s_cfg.start_seg > n))
if ((g_plsr_config.start_seg < 1U) || (g_plsr_config.start_seg > n))
{
PlsrParamFaultLast(&fault, PLSR_ERR_START_SEG);
fault = PLSR_ERR_START_SEG;
}
}
else if (s_cfg.start_seg < 1U)
else if (g_plsr_config.start_seg < 1U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_START_SEG);
fault = PLSR_ERR_START_SEG;
}

s_cfg.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L);
if (PlsrParamIsSpeedHzValid(s_cfg.default_speed) == 0U)
g_plsr_config.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L);
if (PlsrParamIsSpeedHzValid(g_plsr_config.default_speed) == 0U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_DEFAULT_SPD);
fault = PLSR_ERR_DEFAULT_SPD;
}

s_cfg.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L);
if (PlsrParamIsStartEndSpeedValid(s_cfg.start_speed) == 0U)
g_plsr_config.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L);
if (PlsrParamIsStartEndSpeedValid(g_plsr_config.start_speed) == 0U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_START_END_SPD);
fault = PLSR_ERR_START_END_SPD;
}

s_cfg.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L);
if (PlsrParamIsStartEndSpeedValid(s_cfg.end_speed) == 0U)
g_plsr_config.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L);
if (PlsrParamIsStartEndSpeedValid(g_plsr_config.end_speed) == 0U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_START_END_SPD);
fault = PLSR_ERR_START_END_SPD;
}

s_cfg.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS);
s_cfg.decel_ms = ReadHoldReg(PLSR_REG_DECEL_MS);
g_plsr_config.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS);
g_plsr_config.decel_ms = ReadHoldReg(PLSR_REG_DECEL_MS);

if ((n >= 1U) && (n <= PLSR_SEG_MAX))
{
for (i = 0U; i < n; i++)
{
seg = &s_seg[i];
base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE);
seg->freq_hz = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L));
if (PlsrParamIsSegFreqValid(seg->freq_hz) == 0U)
g_plsr_segments[i].freq_hz =
(int32_t)PlsrParamReadDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L));
if (PlsrParamIsSegFreqValid(g_plsr_segments[i].freq_hz) == 0U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_FREQ_ILLEGAL);
fault = PLSR_ERR_FREQ_ILLEGAL;
}

seg->pulse_cnt = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L));
if (PlsrParamIsPulseCntValid(seg->pulse_cnt) == 0U)
g_plsr_segments[i].pulse_cnt =
(int32_t)PlsrParamReadDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L));
if (PlsrParamIsPulseCntValid(g_plsr_segments[i].pulse_cnt) == 0U)
{
PlsrParamFaultLast(&fault, PLSR_ERR_PULSE_RANGE);
fault = PLSR_ERR_PULSE_RANGE;
}

seg->wait_type = (PlsrWaitType_e)ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT));
seg->wait_ms = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS));
seg->act_ms = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS));
seg->jump_seg = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP));
g_plsr_segments[i].wait_type =
(PlsrWaitType_e)ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT));
g_plsr_segments[i].wait_ms =
ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS));
g_plsr_segments[i].act_ms =
ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS));
g_plsr_segments[i].jump_seg =
ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP));
}
}

@@ -392,7 +322,7 @@ uint8_t PlsrParamBlockApplyFromHold(void)
*
* @details 用途(与落 BKP 无关)
* 1. Apply 钳位后摊回 hold,使「读 PLC」与运行结构体一致;
* 2. ModbusDataInit() memset 清 hold 后,把已 Apply 的 s_cfg/s_seg 填回,
* 2. ModbusDataInit() 清 hold 后,把已 Apply 的运行参数填回,
* 否则上位机读公共/段参数全 0,START 也会用空 hold 再次 Apply 冲掉结构体。
*
* @note 0x100F 保留字写 0;段表写满 PLSR_SEG_MAX 槽,避免读回脏数据
@@ -402,36 +332,40 @@ void PlsrParamBlockExportToHold(void)
{
uint16_t i;
uint16_t base;
PlsrSeg_t *seg;

WriteHoldReg(PLSR_REG_PULSE_Y, s_cfg.pulse_y);
WriteHoldReg(PLSR_REG_DIR_Y, s_cfg.dir_y);
WriteHoldReg(PLSR_REG_WAIT_X, s_cfg.wait_x_sel);
WriteHoldReg(PLSR_REG_EXT_X, s_cfg.ext_x_sel);
WriteHoldReg(PLSR_REG_SEND_MODE, (uint16_t)s_cfg.send_mode);
WriteHoldReg(PLSR_REG_DIR_DELAY, s_cfg.dir_delay_ms);
WriteHoldReg(PLSR_REG_DIR_LOGIC, (uint16_t)s_cfg.dir_logic);
WriteHoldReg(PLSR_REG_ACCEL_MODE, (uint16_t)s_cfg.accel_mode);
WriteHoldReg(PLSR_REG_RUN_MODE, (uint16_t)s_cfg.run_mode);
WriteHoldReg(PLSR_REG_SEG_COUNT, s_cfg.seg_count);
WriteHoldReg(PLSR_REG_START_SEG, s_cfg.start_seg);
PlsrParamWriteDWord(PLSR_REG_DEFAULT_SPD_L, s_cfg.default_speed);
PlsrParamWriteDWord(PLSR_REG_START_SPD_L, s_cfg.start_speed);

WriteHoldReg(PLSR_REG_PULSE_Y, g_plsr_config.pulse_y);
WriteHoldReg(PLSR_REG_DIR_Y, g_plsr_config.dir_y);
WriteHoldReg(PLSR_REG_WAIT_X, g_plsr_config.wait_x_sel);
WriteHoldReg(PLSR_REG_EXT_X, g_plsr_config.ext_x_sel);
WriteHoldReg(PLSR_REG_SEND_MODE, (uint16_t)g_plsr_config.send_mode);
WriteHoldReg(PLSR_REG_DIR_DELAY, g_plsr_config.dir_delay_ms);
WriteHoldReg(PLSR_REG_DIR_LOGIC, (uint16_t)g_plsr_config.dir_logic);
WriteHoldReg(PLSR_REG_ACCEL_MODE, (uint16_t)g_plsr_config.accel_mode);
WriteHoldReg(PLSR_REG_RUN_MODE, (uint16_t)g_plsr_config.run_mode);
WriteHoldReg(PLSR_REG_SEG_COUNT, g_plsr_config.seg_count);
WriteHoldReg(PLSR_REG_START_SEG, g_plsr_config.start_seg);
PlsrParamWriteDWord(PLSR_REG_DEFAULT_SPD_L, g_plsr_config.default_speed);
PlsrParamWriteDWord(PLSR_REG_START_SPD_L, g_plsr_config.start_speed);
WriteHoldReg(0x100FU, 0U);
PlsrParamWriteDWord(PLSR_REG_END_SPD_L, s_cfg.end_speed);
WriteHoldReg(PLSR_REG_ACCEL_MS, s_cfg.accel_ms);
WriteHoldReg(PLSR_REG_DECEL_MS, s_cfg.decel_ms);
PlsrParamWriteDWord(PLSR_REG_END_SPD_L, g_plsr_config.end_speed);
WriteHoldReg(PLSR_REG_ACCEL_MS, g_plsr_config.accel_ms);
WriteHoldReg(PLSR_REG_DECEL_MS, g_plsr_config.decel_ms);

for (i = 0U; i < PLSR_SEG_MAX; i++)
{
seg = &s_seg[i];
base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L), (uint32_t)seg->freq_hz);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L), (uint32_t)seg->pulse_cnt);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT), (uint16_t)seg->wait_type);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS), seg->wait_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS), seg->act_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP), seg->jump_seg);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L),
(uint32_t)g_plsr_segments[i].freq_hz);
PlsrParamWriteDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L),
(uint32_t)g_plsr_segments[i].pulse_cnt);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT),
(uint16_t)g_plsr_segments[i].wait_type);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS),
g_plsr_segments[i].wait_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS),
g_plsr_segments[i].act_ms);
WriteHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP),
g_plsr_segments[i].jump_seg);
}
}



+ 14
- 19
plsr/param/plsr_param.h Datei anzeigen

@@ -4,13 +4,13 @@
*
* @details 模块职责
* 1. 定义公共参数区 0x1000~0x1013、段表区 0x1100+i*0x10 的地址与字段语义;
* 2. 维护运行侧唯一映像 s_cfg / s_seg[](运动规划只读此映像,不直接啃寄存器);
* 2. 维护 g_plsr_config / g_plsr_segments[](运动规划只读,不直接读寄存器);
* 3. 支持上位机「5 公共帧 + N 段帧」分帧 FC10 写入:收齐后 ApplyFromHold;
* 4. 上电由 PersistLoad 填 hold;ExportToHold 仅保留作调试接口。
*
* 与其它模块关系
* - modbus_rtu / modbus_data :ReadHoldReg / WriteHoldReg
* - plsr_command :启动前 ApplyFromHold;运行中写频改 s_seg[].freq_hz
* - plsr_command :启动前 ApplyFromHold;运行中修改当前段 freq_hz
* - plsr_run_control :GetCfg / GetSeg / GetSegCount / GetStartSeg
* - plsr_persist :Apply 成功后 SaveParamsFromHold;动态改频 SaveSegFreqToBkp
* - PlsrParamBlockTask :独立 uC/OS 任务,等信号量后 Apply + 整包落 BKP
@@ -177,22 +177,18 @@ typedef struct {
uint16_t jump_seg; /* 0=顺序/末段结束;1~N=跳转 */
} PlsrSeg_t;

/* ---------- 运行映像访问 ---------- */
/* ---------- 运行参数 ---------- */

/**
* @brief 填出厂默认 s_cfg / s_seg(随后可被 PersistLoad / Apply 覆盖)
* @note 调用时机:PlsrInit 最先调用
*/
void PlsrParamInitDefault(void);

/** @return 公共参数映像指针(非空) */
PlsrCfg_t *PlsrParamGetCfg(void);

/**
* @param seg_0based 段索引 0-based;越界钳到 0
* @return 段参数指针
/*
* 所有 PLSR 模块都直接读取这两份数据。
* g_plsr_config :公共参数,只有一份。
* g_plsr_segments[i] :第 i+1 段参数,i 的范围是 0~9。
*
* 参数任务负责从保持寄存器更新它们;运动过程中其它模块只读取,
* 只有“运行中改频”会修改当前段的 freq_hz。
*/
PlsrSeg_t *PlsrParamGetSeg(uint16_t seg_0based);
extern PlsrCfg_t g_plsr_config;
extern PlsrSeg_t g_plsr_segments[PLSR_SEG_MAX];

/** @return 有效段数,钳制到 [0, PLSR_SEG_MAX] */
uint16_t PlsrParamGetSegCount(void);
@@ -211,7 +207,6 @@ uint16_t PlsrParamGetStartSeg(void);
*/
void PlsrParamBlockInit(void);

/**
/**
* @brief 参数块任务:阻塞等信号量 → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold
* @param pArg 未使用
@@ -228,13 +223,13 @@ void PlsrParamBlockTask(void *pArg);
void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity);

/**
* @brief 保持寄存器 → s_cfg / s_seg,边拷贝边校验
* @brief 保持寄存器 → g_plsr_config / g_plsr_segments,边拷贝边校验
* @return 1=全部通过(写 0x2006=0);0=存在门禁故障(写对应码,不落 BKP)
*/
uint8_t PlsrParamBlockApplyFromHold(void);

/**
* @brief s_cfg / s_seg → 保持寄存器(调试/特殊用途;正常运行路径不再调用
* @brief g_plsr_config / g_plsr_segments → 保持寄存器(调试/特殊用途)
* @note 上电参数由 BKP→hold(PersistLoad);非法参数保留 hold 原值供上位机读回修正
*/
void PlsrParamBlockExportToHold(void);


+ 4
- 53
plsr/plsr.c Datei anzeigen

@@ -1,10 +1,10 @@
/**
* @file plsr.c
* @brief PLSR 门面实现:初始化编排与周期任务
* @brief PLSR 顶层初始化和周期任务
*
* @details 模块职责
* 按固定顺序调用各子模块 Init,并在 PlsrTask 中串联指令轮询、持久化与
* 运行控制节拍。对外 API 均为薄转发,不含业务逻辑
* 本文件只做两件事:按顺序初始化各模块,以及运行 PlsrTask 周期任务。
* 启停和中断函数直接实现在 run_control.c,这里不再做重复转发
*
* 与其它模块关系
* 本文件仅依赖 plsr.h 及各子模块头文件,不被其它 plsr 模块反向引用。
@@ -32,10 +32,9 @@
*/
void PlsrInit(void)
{
//PlsrParamInitDefault();
PlsrPersistInit(); /* 使能 BKPSRAM 写访问,否则落盘无效 */
PlsrPersistLoad(); /* BKPSRAM → g_hold_reg */
(void)PlsrParamBlockApplyFromHold(); /* hold → s_cfg / s_seg[] + 校验 */
(void)PlsrParamBlockApplyFromHold(); /* hold → 运行参数 + 校验 */
PlsrPulseDriverInit();
PlsrSignalIoInit();
PlsrRunControlInit();
@@ -45,54 +44,6 @@ void PlsrInit(void)
PlsrPersistTickMonitor(); /* 监控区 hold+BKP 与运行态对齐 */
}

/**
* @brief 启动多段脉冲(见 plsr.h)
*/
uint8_t PlsrStart(uint16_t start_seg)
{
if (start_seg == 0U)
{
start_seg = PlsrParamGetStartSeg();
}
return PlsrRunControlStart(start_seg);
}

/** @brief 急停(见 plsr.h) */
void PlsrStop(void)
{
PlsrRunControlStop();
}

/** @brief 运行中改频(见 plsr.h) */
uint8_t PlsrChangeFreq(uint32_t new_tgt_hz)
{
return PlsrRunControlChangeFreq(new_tgt_hz);
}

/** @brief 忙闲查询(见 plsr.h) */
uint8_t PlsrIsBusy(void)
{
return PlsrRunControlIsBusy();
}

/** @brief 累计脉冲(见 plsr.h) */
int32_t PlsrGetAccPulse(void)
{
return PlsrRunControlGetAccPulse();
}

/** @brief 清零累计(见 plsr.h) */
void PlsrClearAccPulse(void)
{
PlsrRunControlClearAccPulse();
}

/** @brief 脉冲 ISR 入口(见 plsr.h) */
void PlsrOnPulseIsr(void)
{
PlsrRunControlOnPulseIsr();
}

/**
* @brief PLSR 主周期任务
* @param pArg 未使用


+ 10
- 18
plsr/plsr.h Datei anzeigen

@@ -1,11 +1,10 @@
/**
* @file plsr.h
* @brief PLSR 多段脉冲输出子系统对外门面
* @brief PLSR 多段脉冲输出子系统公共接口
*
* @details 模块职责
* 本文件定义上位机/应用层唯一可见的 PLSR 入口,封装初始化、周期任务、
* 启停控制、运行状态查询与脉冲中断回调。内部实现全部委托给子模块,
* 本层不做运动规划与硬件寄存器操作。
* 本文件只声明公共入口与共享运行状态。初始化和周期任务实现在 plsr.c;
* 启停和脉冲中断直接实现在 run_control.c,不再经过 plsr.c 转发。
*
* 与其它模块关系
* - plsr_param :运行参数映像(公共 + 段表),Modbus 保持寄存器 ↔ 结构体
@@ -24,6 +23,11 @@
#define PLSR_H

#include <stdint.h>

/* run_control 拥有的共享运行状态;段下标从 0 开始。 */
extern volatile uint8_t g_plsr_busy;
extern volatile uint16_t g_plsr_current_segment_index;
extern volatile int32_t g_plsr_accumulated_pulses;
#include "plsr_param.h"

/*
@@ -75,29 +79,17 @@ void PlsrStop(void);
*/
uint8_t PlsrChangeFreq(uint32_t new_tgt_hz);

/**
* @brief 查询是否运动中
* @return 1=busy(段执行或等待中);0=空闲
*/
uint8_t PlsrIsBusy(void);

/**
* @brief 读取累计脉冲计数(有符号,正=正向累计)
* @return 自系统运行以来的净脉冲数,供绝对坐标与监控 0x2000 使用
*/
int32_t PlsrGetAccPulse(void);

/**
* @brief 清零累计脉冲与绝对坐标原点
* @note 调用时机:CommandPoll 收到 CLR 控制位
* @note 副作用:s_acc_pulse=0、s_abs_origin=0,下次 Start 重新锁定原点
* @note 同时清除累计脉冲和绝对模式原点,下次 Start 重新锁定原点
*/
void PlsrClearAccPulse(void);

/**
* @brief 脉冲 TIM UPDATE 中断入口
* @note 调用时机:每发出一个脉冲边沿,由 stm32f4xx_it 中对应 TIM 回调转发
* @note 副作用:s_done++/累计坐标更新、逐拍加减速改频、段末/ACT 边界切段
* @note 副作用:累计本段脉冲和坐标、逐拍改频、处理段末/ACT 边界
*/
void PlsrOnPulseIsr(void);



+ 79
- 24
plsr/pulse_driver/plsr_pulse_driver.c Datei anzeigen

@@ -11,7 +11,7 @@
*
* 关键数据流
* PlanSeg → LockPscRange → DIR_WAIT → Start → ISR SetFreqIsr(只改 ARR)
* 段末 ArmOnePulseStop → AfterSegDone Stop / FOLLOW ClearOnePulseStop
* 段末 ArmSegEndStop → AfterSegDone Stop / FOLLOW ClearOnePulseStop
*/
#include "plsr_pulse_driver.h"
#include "plsr_param.h"
@@ -35,12 +35,15 @@ TIM_HandleTypeDef htim13;

/*
* ---------- 模块静态状态 ----------
* s_last_freq/psc/arr — 上次量化结果;匀速热路径比较后可跳过写寄存器
* g_plsr_output_frequency_hz/psc/arr — 上次量化结果;匀速热路径比较后可跳过写寄存器
* s_active_htim — 当前选中脉冲路 HAL 句柄
* s_psc_locked / locked_psc — 段内 PSC 锁;跨度过大时 locked=0
* s_req_freq / pending — ISR 无法换 PSC 时的挂起改频
*/
static uint32_t s_last_freq;
volatile uint32_t g_plsr_output_frequency_hz;
volatile uint8_t g_plsr_pwm_running;
uint8_t g_plsr_direction_forward;
uint8_t g_plsr_direction_valid;
static uint32_t s_last_psc = 0xFFFFFFFFUL;
static uint32_t s_last_arr = 0xFFFFFFFFUL;
static uint32_t s_last_ccr;
@@ -89,8 +92,7 @@ static TIM_HandleTypeDef *PlsrPulseDriverHtimByY(uint16_t pulse_y)
/** @brief 从公共参数取脉冲端子,越界钳到 Y0 */
static uint8_t PlsrPulseDriverSelectY(void)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
uint16_t y = cfg->pulse_y;
uint16_t y = g_plsr_config.pulse_y;

if (y > 2U)
{
@@ -271,7 +273,10 @@ static void PlsrPulseDriverTimInitOne(TIM_HandleTypeDef *htim, TIM_TypeDef *inst
/** @brief 驱动初始化(见 plsr_pulse_driver.h) */
void PlsrPulseDriverInit(void)
{
s_last_freq = 0U;
g_plsr_output_frequency_hz = 0U;
g_plsr_pwm_running = 0U;
g_plsr_direction_forward = 0U;
g_plsr_direction_valid = 0U;
s_last_psc = 0xFFFFFFFFUL;
s_last_arr = 0xFFFFFFFFUL;
s_last_ccr = 0U;
@@ -293,10 +298,9 @@ void PlsrPulseDriverInit(void)
/** @brief 写方向脚(见 plsr_pulse_driver.h) */
void PlsrPulseDriverSetDir(uint8_t forward)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
uint8_t level;

if (cfg->dir_logic == PLSR_DIR_LOGIC_POS)
if (g_plsr_config.dir_logic == PLSR_DIR_LOGIC_POS)
{
level = (forward != 0U) ? 1U : 0U;
}
@@ -306,12 +310,16 @@ void PlsrPulseDriverSetDir(uint8_t forward)
}
HAL_GPIO_WritePin(PLSR_Y3_PORT, PLSR_Y3_PIN,
level ? GPIO_PIN_SET : GPIO_PIN_RESET);
g_plsr_direction_forward = (forward != 0U) ? 1U : 0U;
g_plsr_direction_valid = 1U;
}

/** @brief 方向脚拉低(见 plsr_pulse_driver.h) */
void PlsrPulseDriverClearDir(void)
{
HAL_GPIO_WritePin(PLSR_Y3_PORT, PLSR_Y3_PIN, GPIO_PIN_RESET);
g_plsr_direction_forward = 0U;
g_plsr_direction_valid = 0U;
}

/** @brief 是否脉冲 TIM(见 plsr_pulse_driver.h) */
@@ -547,7 +555,8 @@ static void PlsrPulseDriverLoadRegs(uint32_t freq_hz)
if (freq_hz == 0U)
{
PlsrPulseDriverStopHtim(htim);
s_last_freq = 0U;
g_plsr_output_frequency_hz = 0U;
g_plsr_pwm_running = 0U;
s_last_psc = 0xFFFFFFFFUL;
s_last_arr = 0xFFFFFFFFUL;
s_last_ccr = 0U;
@@ -570,7 +579,7 @@ static void PlsrPulseDriverLoadRegs(uint32_t freq_hz)
ccr = 1U;
}

s_last_freq = freq_hz;
g_plsr_output_frequency_hz = freq_hz;
s_last_psc = psc;
s_last_arr = arr;
s_last_ccr = ccr;
@@ -623,6 +632,17 @@ static void PlsrPulseDriverCommitOutput(void)
htim->Instance->ARR = s_last_arr;
htim->Instance->CCR1 = ccr;

/*
* 先把 CNT 放到周期末端,再切回 PWM 模式。
*
* ACT 在周期中间打断时,CNT 可能仍停在旧周期的任意位置。
* 如果先切 PWM1,输出比较器会立刻按这个旧 CNT 判断电平,
* 随后再写 CNT=ARR 就会形成一个几微秒的假脉冲。
* 先写 CNT=ARR 并保持 Forced inactive,再切 PWM1,模式切换期间
* 输出始终为低;真正的首个上升沿只会由后续 UPDATE 产生。
*/
htim->Instance->CNT = s_last_arr;

ccmr = htim->Instance->CCMR1;
ccmr &= (uint32_t)(~TIM_CCMR1_OC1M);
ccmr |= TIM_OCMODE_PWM1;
@@ -638,11 +658,12 @@ static void PlsrPulseDriverCommitOutput(void)
htim->Instance->ARR = s_last_arr;
htim->Instance->CCR1 = ccr;

htim->Instance->CNT = s_last_arr;
/* CNT 已在切换 PWM 模式前归位,下面仅清状态并开表。 */
__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);
g_plsr_pwm_running = 1U;

/* 记录起跳工作寄存器,供首拍 ISR 保护当前周期 */
s_protect_arr = s_last_arr;
@@ -657,7 +678,7 @@ static void PlsrPulseDriverCommitOutput(void)
*/
static void PlsrPulseDriverStartCommon(uint32_t freq_hz)
{
if ((s_regs_prepared == 0U) || (freq_hz != s_last_freq) || (freq_hz == 0U))
if ((s_regs_prepared == 0U) || (freq_hz != g_plsr_output_frequency_hz) || (freq_hz == 0U))
{
PlsrPulseDriverLoadRegs(freq_hz);
}
@@ -682,7 +703,7 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz)
uint32_t clk_hz;
TIM_HandleTypeDef *htim;

if (freq_hz == s_last_freq)
if (freq_hz == g_plsr_output_frequency_hz)
{
return;
}
@@ -695,7 +716,8 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz)
if (freq_hz == 0U)
{
PlsrPulseDriverStopHtim(htim);
s_last_freq = 0U;
g_plsr_output_frequency_hz = 0U;
g_plsr_pwm_running = 0U;
s_last_psc = 0xFFFFFFFFUL;
s_last_arr = 0xFFFFFFFFUL;
return;
@@ -713,7 +735,7 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz)

if ((psc == s_last_psc) && (arr == s_last_arr))
{
s_last_freq = freq_hz;
g_plsr_output_frequency_hz = freq_hz;
return;
}

@@ -739,7 +761,7 @@ void PlsrPulseDriverSetFreq(uint32_t freq_hz)
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
}

s_last_freq = freq_hz;
g_plsr_output_frequency_hz = freq_hz;
s_last_psc = psc;
s_last_arr = arr;
}
@@ -792,7 +814,7 @@ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz)
}

/* 匀速热路径:频率未变 → 不算量化、不写寄存器 */
if (freq_hz == s_last_freq)
if (freq_hz == g_plsr_output_frequency_hz)
{
return;
}
@@ -825,7 +847,7 @@ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz)

if ((psc == s_last_psc) && (arr == s_last_arr))
{
s_last_freq = freq_hz;
g_plsr_output_frequency_hz = freq_hz;
return;
}

@@ -838,7 +860,7 @@ void PlsrPulseDriverSetFreqIsr(uint32_t freq_hz)
__HAL_TIM_SET_AUTORELOAD(htim, arr);
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, ccr);

s_last_freq = freq_hz;
g_plsr_output_frequency_hz = freq_hz;
s_last_psc = psc;
s_last_arr = arr;
s_last_ccr = ccr;
@@ -890,24 +912,56 @@ void PlsrPulseDriverStop(void)
PlsrPulseDriverStopHtim(&htim10);
PlsrPulseDriverStopHtim(&htim11);
PlsrPulseDriverStopHtim(&htim13);
s_last_freq = 0U;
g_plsr_output_frequency_hz = 0U;
g_plsr_pwm_running = 0U;
s_last_psc = 0xFFFFFFFFUL;
s_last_arr = 0xFFFFFFFFUL;
}

/**
* @brief 武装 OPM 段末停表(见 plsr_pulse_driver.h)
* @brief ACT 到期:收完当前高电平后锁低,并在周期末停表
*/
void PlsrPulseDriverArmOnePulseStop(void)
void PlsrPulseDriverArmActStop(void)
{
TIM_HandleTypeDef *htim = s_active_htim;
uint32_t cnt;
uint32_t ccr;

if (htim == (TIM_HandleTypeDef *)0)
if ((htim == (TIM_HandleTypeDef *)0) || (htim->Instance == (TIM_TypeDef *)0))
{
return;
}
/* 下一 UPDATE 后计数器停止;须在「已计入末拍上升沿」时武装(见 run_control ISR) */

/* OPM 先武装:当前周期结束的 UPDATE 到来时自动关 CEN。 */
htim->Instance->CR1 |= TIM_CR1_OPM;

cnt = htim->Instance->CNT;
ccr = htim->Instance->CCR1;
if (((htim->Instance->CR1 & TIM_CR1_CEN) != 0U) &&
(cnt < ccr))
{
/* 当前仍为高电平:等本拍 CC1 下降沿后再锁低。 */
s_seg_end_fall_armed = 1U;
__HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_CC1);
__HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);

/*
* 读取到高电平后,CC1 可能恰好在开中断前已经过去。
* 再检查一次;若已进入低电平或 OPM 已停表,立即锁低。
*/
cnt = htim->Instance->CNT;
if (((htim->Instance->CR1 & TIM_CR1_CEN) == 0U) || (cnt >= ccr))
{
PlsrPulseDriverClearSegEndFallArm(htim);
PlsrPulseDriverOcForceLowKeepRun(htim);
}
}
else
{
/* ACT 落在低电平阶段(或边界已停表):无需再等 CC1。 */
PlsrPulseDriverClearSegEndFallArm(htim);
PlsrPulseDriverOcForceLowKeepRun(htim);
}
}

/**
@@ -965,4 +1019,5 @@ void PlsrPulseDriverHoldOutputLow(void)
return;
}
PlsrPulseDriverStopHtim(htim);
g_plsr_pwm_running = 0U;
}

+ 10
- 4
plsr/pulse_driver/plsr_pulse_driver.h Datei anzeigen

@@ -34,6 +34,12 @@ extern TIM_HandleTypeDef htim10;
extern TIM_HandleTypeDef htim11;
extern TIM_HandleTypeDef htim13;

/* 驱动拥有的真实输出状态,run_control 直接读取,不再保存第二份。 */
extern volatile uint32_t g_plsr_output_frequency_hz;
extern volatile uint8_t g_plsr_pwm_running;
extern uint8_t g_plsr_direction_forward;
extern uint8_t g_plsr_direction_valid;

/**
* @brief 初始化三路脉冲 TIM + 方向 GPIO
* @note 调用时机:PlsrInit,在 RunControl 之前
@@ -106,14 +112,14 @@ void PlsrPulseDriverClearStartPeriodProtect(void);
void PlsrPulseDriverStop(void);

/**
* @brief 当前拍结束后停止计数(CR1.OPM),避免段末多出一个上升沿
* @note 调用时机:已计入末拍上升沿的 ISR;本周期结束后 OPM 停表
* @brief ACT 到期后收完当前脉冲、锁低输出,并在本周期末停止计数
* @note 可在周期任意位置调用:高电平等下降沿,低电平立即 Forced inactive
*/
void PlsrPulseDriverArmOnePulseStop(void);
void PlsrPulseDriverArmActStop(void);

/**
* @brief 段末停表:OPM 等周期结束 + CC1 下降沿立刻 Forced 拉低
* @note 仅非 FOLLOW-keep 段末使用;ACT 切段仍用 ArmOnePulseStop
* @note 在已计入末拍上升沿的 UPDATE ISR 中调用
*/
void PlsrPulseDriverArmSegEndStop(void);



+ 392
- 585
plsr/run_control/plsr_run_control.c
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 1
- 12
plsr/run_control/plsr_run_control.h Datei anzeigen

@@ -6,7 +6,7 @@
* pulse_driver / signal_io / param。状态:IDLE / DIR_WAIT / RUN / WAIT_COND。
*
* 与 accel_curve 对齐的符号:
* f_tgt — 段表目标频率 Hz;s_done/s_target — 本段已发/目标脉冲数
* f_tgt — 段表目标频率 Hz;segment_pulses_done/target — 已发/目标脉冲数
*/
#ifndef PLSR_RUN_CONTROL_H
#define PLSR_RUN_CONTROL_H
@@ -14,22 +14,11 @@
#include <stdint.h>

void PlsrRunControlInit(void);
uint8_t PlsrRunControlStart(uint16_t start_seg_1based);
void PlsrRunControlStop(void);
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);
/** @return 主状态机 RC_IDLE/RC_DIR_WAIT/RC_RUN/RC_WAIT_COND(供监控/BKP) */
uint16_t PlsrRunControlGetState(void);
void PlsrRunControlTickMs(void);
/** @brief TIM5 单次延时到期(换向 / WAIT / ACT 剩余) */
void PlsrRunControlOnDelayTimer(void);
void PlsrRunControlOnPulseIsr(void);
uint8_t PlsrRunControlChangeFreq(uint32_t freq_hz);

/**
* @brief 创建任务唤醒信号量(须在 OSInit 之后、首次 Pend/Post 之前)


+ 22
- 16
plsr/signal_io/plsr_signal_io.c Datei anzeigen

@@ -293,20 +293,20 @@ void PlsrSignalIoClearPending(void)
s_db_x5.stamp_ok = 0U;
}

/** wait_x_sel / ext_x_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 PlsrSignalIoTakeWaitFalling(void)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
PlsrEdge_t *db = PlsrSignalIoDbBySel(cfg->wait_x_sel);

if (db->fell != 0U)
/* wait_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */
if (g_plsr_config.wait_x_sel == 0U)
{
db->fell = 0U;
if (s_db_x4.fell != 0U)
{
s_db_x4.fell = 0U;
return 1U;
}
}
else if (s_db_x5.fell != 0U)
{
s_db_x5.fell = 0U;
return 1U;
}
return 0U;
@@ -314,12 +314,18 @@ uint8_t PlsrSignalIoTakeWaitFalling(void)

uint8_t PlsrSignalIoTakeExtFalling(void)
{
PlsrCfg_t *cfg = PlsrParamGetCfg();
PlsrEdge_t *db = PlsrSignalIoDbBySel(cfg->ext_x_sel);

if (db->fell != 0U)
/* ext_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */
if (g_plsr_config.ext_x_sel == 0U)
{
if (s_db_x4.fell != 0U)
{
s_db_x4.fell = 0U;
return 1U;
}
}
else if (s_db_x5.fell != 0U)
{
db->fell = 0U;
s_db_x5.fell = 0U;
return 1U;
}
return 0U;


Laden…
Abbrechen
Speichern