| @@ -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 | |||
| @@ -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); | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||
| @@ -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 未使用 | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| @@ -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 之前) | |||
| @@ -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; | |||