| @@ -21,3 +21,6 @@ build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/ | |||||
| plsr/test/ | plsr/test/ | ||||
| tools/ | tools/ | ||||
| .cursor/ | .cursor/ | ||||
| PLSR测试大纲.html | |||||
| _gen_outline_p1.py | |||||
| run_control_full.txt | |||||
| @@ -97,7 +97,7 @@ int main(void) | |||||
| OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); | OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); | ||||
| /* | /* | ||||
| * 参数块任务:Pend → ApplyFromHold → SaveParamsFromHold → ExportToHold。 | |||||
| * 参数块任务:Pend → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold。 | |||||
| * 分帧写完后由 OnHoldWrite Post;Save 落 BKP,Export 仅供读回钳位结果。 | * 分帧写完后由 OnHoldWrite Post;Save 落 BKP,Export 仅供读回钳位结果。 | ||||
| */ | */ | ||||
| OSTaskCreateExt(PlsrParamBlockTask, | OSTaskCreateExt(PlsrParamBlockTask, | ||||
| @@ -118,7 +118,7 @@ int main(void) | |||||
| } | } | ||||
| /** | /** | ||||
| * @brief 系统时钟:HSE(12MHz) → PLL → 168MHz SYSCLK(APM32F407 / STM32F4 类) | |||||
| * @brief 系统时钟:HSE(12MHz) → PLL → 168MHz SYSCLK | |||||
| * @note 须与 OS_CPU_SysTickInit、pulse_driver 使用的主频常量一致 | * @note 须与 OS_CPU_SysTickInit、pulse_driver 使用的主频常量一致 | ||||
| * @note PLLM 须等于晶振 MHz:12MHz 用 12;误用 8 会使 SYSCLK=252MHz,脉冲频率偏 1.5 倍 | * @note PLLM 须等于晶振 MHz:12MHz 用 12;误用 8 会使 SYSCLK=252MHz,脉冲频率偏 1.5 倍 | ||||
| */ | */ | ||||
| @@ -14,6 +14,7 @@ | |||||
| #include "gpio.h" | #include "gpio.h" | ||||
| #include "plsr_param.h" | #include "plsr_param.h" | ||||
| #include "plsr_command.h" | #include "plsr_command.h" | ||||
| #include "plsr_persist.h" | |||||
| static uint8_t g_modbus_proc_buf[MODBUS_RX_BUF_LEN]; | static uint8_t g_modbus_proc_buf[MODBUS_RX_BUF_LEN]; | ||||
| @@ -68,12 +69,11 @@ void ModbusSlaveTask(void *pArg) | |||||
| (void)pArg; | (void)pArg; | ||||
| /* | /* | ||||
| * ModbusDataInit 会 memset 整个 g_hold_reg,冲掉 PlsrInit 里 PersistLoad/Export 的结果。 | |||||
| * 须立刻 Export:用已 Apply 的 s_cfg/s_seg 填回公共+段参数,供 FC03 读与后续 START。 | |||||
| * (监控区另由 PlsrPersistTickMonitor 每节拍从运行态回填。) | |||||
| * ModbusDataInit 会清零 g_hold_reg;上电后仅从 BKP 恢复参数/监控到 hold, | |||||
| * 不再把运行结构体 Export 回寄存器(非法参数保留在上位机写入值 + 0x2006)。 | |||||
| */ | */ | ||||
| ModbusDataInit(); | ModbusDataInit(); | ||||
| PlsrParamBlockExportToHold(); | |||||
| PlsrPersistLoad(); | |||||
| ModbusPortInit(); | ModbusPortInit(); | ||||
| while (1) | while (1) | ||||
| @@ -79,6 +79,17 @@ uint16_t PlsrCommandGetFault(void) | |||||
| return s_fault; | return s_fault; | ||||
| } | } | ||||
| /** @brief 参数/重复启动类故障是否禁止 START(见 plsr_command.h) */ | |||||
| uint8_t PlsrCommandIsStartBlocked(void) | |||||
| { | |||||
| if ((s_fault >= PLSR_ERR_PARAM_BLOCK_MIN) && | |||||
| (s_fault <= PLSR_ERR_PARAM_BLOCK_MAX)) | |||||
| { | |||||
| return 1U; | |||||
| } | |||||
| return 0U; | |||||
| } | |||||
| /** @brief 指令模块初始化(见 plsr_command.h) */ | /** @brief 指令模块初始化(见 plsr_command.h) */ | ||||
| void PlsrCommandInit(void) | void PlsrCommandInit(void) | ||||
| { | { | ||||
| @@ -160,7 +171,7 @@ void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity) | |||||
| * 1. 读 ctrl 后立即写 0(避免重复触发) | * 1. 读 ctrl 后立即写 0(避免重复触发) | ||||
| * 2. STOP — 急停 | * 2. STOP — 急停 | ||||
| * 3. CLR — 清累计 + ClearFault | * 3. CLR — 清累计 + ClearFault | ||||
| * 4. START — 仅当无 STOP 位:Apply → ClearFault → Stop → Start | |||||
| * 4. START — 忙则 0x0C;否则 Apply 校验 → 0x04~0x0C 门禁则拒启 → Stop+Start | |||||
| * 动态改频在控制位之后、PublishMonitor 之前执行。 | * 动态改频在控制位之后、PublishMonitor 之前执行。 | ||||
| */ | */ | ||||
| void PlsrCommandPoll(void) | void PlsrCommandPoll(void) | ||||
| @@ -187,15 +198,27 @@ void PlsrCommandPoll(void) | |||||
| if (((ctrl & PLSR_CTRL_BIT_START) != 0U) && | if (((ctrl & PLSR_CTRL_BIT_START) != 0U) && | ||||
| ((ctrl & PLSR_CTRL_BIT_STOP) == 0U)) | ((ctrl & PLSR_CTRL_BIT_STOP) == 0U)) | ||||
| { | { | ||||
| (void)PlsrParamBlockApplyFromHold(); | |||||
| /* 启动视为正常入口:先写故障码 0,再 Stop+Start;非法频率由 Start 改写为 4 */ | |||||
| PlsrCommandClearFault(); | |||||
| PlsrStop(); | |||||
| (void)PlsrStart(PlsrParamGetStartSeg()); | |||||
| /* 已进换向/WAIT 精确等待:本周期勿再 PublishMonitor,尽快 Pend */ | |||||
| if (PlsrRunControlIsPreciseWait() != 0U) | |||||
| if (PlsrIsBusy() != 0U) | |||||
| { | |||||
| /* 0x0C:运行中重复 START,启动无效 */ | |||||
| PlsrCommandSetFault(PLSR_ERR_DUP_START); | |||||
| } | |||||
| else if (PlsrParamBlockApplyFromHold() == 0U) | |||||
| { | |||||
| /* Apply 失败:故障码已由参数块写入 0x2006,不落 BKP */ | |||||
| } | |||||
| else if (PlsrCommandIsStartBlocked() != 0U) | |||||
| { | |||||
| /* 0x04~0x0B 等门禁故障:禁止启动 */ | |||||
| } | |||||
| else | |||||
| { | { | ||||
| skip_monitor = 1U; | |||||
| PlsrStop(); | |||||
| (void)PlsrStart(PlsrParamGetStartSeg()); | |||||
| if (PlsrRunControlIsPreciseWait() != 0U) | |||||
| { | |||||
| skip_monitor = 1U; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -51,13 +51,34 @@ | |||||
| * 1 | PLSR_ERR_PULSE_CNT | 实际脉冲数与段目标不一致 | 告警 | 继续跑,不中断 | * 1 | PLSR_ERR_PULSE_CNT | 实际脉冲数与段目标不一致 | 告警 | 继续跑,不中断 | ||||
| * 2 | PLSR_ERR_FREQ_UNREACH | 规划峰值达不到设定目标(三角) | 告警 | 按压后规划继续 | * 2 | PLSR_ERR_FREQ_UNREACH | 规划峰值达不到设定目标(三角) | 告警 | 按压后规划继续 | ||||
| * 3 | PLSR_ERR_CURVE_MISMATCH | 加减速相内脉冲与规划预算不符 | 告警 | 继续跑 | * 3 | PLSR_ERR_CURVE_MISMATCH | 加减速相内脉冲与规划预算不符 | 告警 | 继续跑 | ||||
| * 4 | PLSR_ERR_FREQ_ILLEGAL | 频率 ∉ [1, 100000] Hz | 门禁 | 禁止启动/改频 | |||||
| * 4 | PLSR_ERR_FREQ_ILLEGAL | 段频率 ∉ [1, 100000] Hz(非 0) | 门禁 | 禁止启动/改频 | |||||
| * 5 | PLSR_ERR_PULSE_RANGE | 脉冲个数 ∉ int32 合法范围 | 门禁 | 禁止启动 | |||||
| * 6 | PLSR_ERR_SEG_COUNT | 总段数 ∉ [1, 10] | 门禁 | 禁止启动 | |||||
| * 7 | PLSR_ERR_START_SEG | 起始段 ∉ [1, 总段数] | 门禁 | 禁止启动 | |||||
| * 8 | PLSR_ERR_START_END_SPD | 起/止速非0且 ∉ [1, 100000] Hz | 门禁 | 禁止启动(0=合法) | |||||
| * 9 | PLSR_ERR_PULSE_Y | 脉冲端子 ∉ {Y0,Y1,Y2} | 门禁 | 禁止启动 | |||||
| * 10 | PLSR_ERR_DIR_Y | 方向端子 ≠ Y3 | 门禁 | 禁止启动 | |||||
| * 11 | PLSR_ERR_DEFAULT_SPD | 默认速度 ∉ [1, 100000] Hz | 门禁 | 禁止启动 | |||||
| * 12 | PLSR_ERR_DUP_START | 运行中重复 START | 告警 | 启动无效 | |||||
| */ | */ | ||||
| #define PLSR_ERR_NONE 0x00U | #define PLSR_ERR_NONE 0x00U | ||||
| #define PLSR_ERR_PULSE_CNT 0x01U | #define PLSR_ERR_PULSE_CNT 0x01U | ||||
| #define PLSR_ERR_FREQ_UNREACH 0x02U | #define PLSR_ERR_FREQ_UNREACH 0x02U | ||||
| #define PLSR_ERR_CURVE_MISMATCH 0x03U | #define PLSR_ERR_CURVE_MISMATCH 0x03U | ||||
| #define PLSR_ERR_FREQ_ILLEGAL 0x04U | #define PLSR_ERR_FREQ_ILLEGAL 0x04U | ||||
| #define PLSR_ERR_PULSE_RANGE 0x05U | |||||
| #define PLSR_ERR_SEG_COUNT 0x06U | |||||
| #define PLSR_ERR_START_SEG 0x07U | |||||
| #define PLSR_ERR_START_END_SPD 0x08U | |||||
| #define PLSR_ERR_PULSE_Y 0x09U | |||||
| #define PLSR_ERR_DIR_Y 0x0AU | |||||
| #define PLSR_ERR_DEFAULT_SPD 0x0BU | |||||
| #define PLSR_ERR_DUP_START 0x0CU | |||||
| /** 参数类门禁故障码下限(含) */ | |||||
| #define PLSR_ERR_PARAM_BLOCK_MIN PLSR_ERR_FREQ_ILLEGAL | |||||
| /** 参数类门禁故障码上限(含 0x0C 重复启动) */ | |||||
| #define PLSR_ERR_PARAM_BLOCK_MAX PLSR_ERR_DUP_START | |||||
| /** | /** | ||||
| * @brief 指令模块初始化 | * @brief 指令模块初始化 | ||||
| @@ -90,6 +111,12 @@ void PlsrCommandClearFault(void); | |||||
| /** @return 当前故障码镜像(与 0x2006 一致) */ | /** @return 当前故障码镜像(与 0x2006 一致) */ | ||||
| uint16_t PlsrCommandGetFault(void); | uint16_t PlsrCommandGetFault(void); | ||||
| /** | |||||
| * @brief 是否因参数/重复启动类故障禁止 START | |||||
| * @return 1=0x2006 为 0x04~0x0C(门禁/重复启动);0=可尝试启动 | |||||
| */ | |||||
| uint8_t PlsrCommandIsStartBlocked(void); | |||||
| /** | /** | ||||
| * @brief FC10 写段频率双字后的钩子 | * @brief FC10 写段频率双字后的钩子 | ||||
| * @param start_addr 写入起始保持寄存器地址 | * @param start_addr 写入起始保持寄存器地址 | ||||
| @@ -15,11 +15,12 @@ | |||||
| * | * | ||||
| * 与其它模块关系 | * 与其它模块关系 | ||||
| * Modbus FC10 应答路径调用 OnHoldWrite;Command START 也会同步 ApplyFromHold; | * Modbus FC10 应答路径调用 OnHoldWrite;Command START 也会同步 ApplyFromHold; | ||||
| * Persist:Apply 成功后 SaveParamsFromHold 整包落 BKP;运行中改频单独落频率双字; | |||||
| * ExportToHold 只把钳位后的结构体摊回 hold(读回 / 抗 ModbusDataInit 清零),不负责写 BKP。 | |||||
| * Persist:校验全部通过才 SaveParamsFromHold;失败只写故障码,不落 BKP; | |||||
| * 非上电不再 ExportToHold(hold 保留上位机写入值,由故障码提示修正)。 | |||||
| */ | */ | ||||
| #include "plsr_param.h" | #include "plsr_param.h" | ||||
| #include "plsr_persist.h" | #include "plsr_persist.h" | ||||
| #include "plsr_command.h" | |||||
| #include "modbus_rtu.h" | #include "modbus_rtu.h" | ||||
| #include "ucos_ii.h" | #include "ucos_ii.h" | ||||
| #include <string.h> | #include <string.h> | ||||
| @@ -194,37 +195,95 @@ static void PlsrParamSetXferStatus(uint16_t st, uint16_t fail_frame) | |||||
| WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames); | WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames); | ||||
| } | } | ||||
| /*============================================================================*/ | |||||
| /* 参数校验辅助 */ | |||||
| /*============================================================================*/ | |||||
| #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; | |||||
| } | |||||
| /** | |||||
| * 起速/止速:0 合法(运行层 ResolveStartHz / ResolveEndHz 处理); | |||||
| * 非 0 须在 1~100kHz。 | |||||
| */ | |||||
| static uint8_t PlsrParamIsStartEndSpeedValid(uint32_t hz) | |||||
| { | |||||
| if (hz == 0U) | |||||
| { | |||||
| return 1U; | |||||
| } | |||||
| return PlsrParamIsSpeedHzValid(hz); | |||||
| } | |||||
| /** 段频率:0=用默认速度;非 0 须在 1~100kHz */ | |||||
| static uint8_t PlsrParamIsSegFreqValid(int32_t freq_hz) | |||||
| { | |||||
| if (freq_hz == 0) | |||||
| { | |||||
| return 1U; | |||||
| } | |||||
| if (freq_hz < 0) | |||||
| { | |||||
| return 0U; | |||||
| } | |||||
| return PlsrParamIsSpeedHzValid((uint32_t)freq_hz); | |||||
| } | |||||
| /** 脉冲个数 SDW:须在 int32 有符号范围内 */ | |||||
| static uint8_t PlsrParamIsPulseCntValid(int32_t pulse_cnt) | |||||
| { | |||||
| if (pulse_cnt < (int32_t)(-2147483647L - 1L)) | |||||
| { | |||||
| return 0U; | |||||
| } | |||||
| if (pulse_cnt > (int32_t)2147483647L) | |||||
| { | |||||
| return 0U; | |||||
| } | |||||
| return 1U; | |||||
| } | |||||
| /*============================================================================*/ | /*============================================================================*/ | ||||
| /* 分帧导入 / 导出 */ | /* 分帧导入 / 导出 */ | ||||
| /*============================================================================*/ | /*============================================================================*/ | ||||
| /** | /** | ||||
| * @brief 保持寄存器 → 运行映像(见 plsr_param.h) | |||||
| * @brief 保持寄存器 → 运行映像,边拷贝边校验(见 plsr_param.h) | |||||
| * | * | ||||
| * 字段裁剪策略(当前不做故障码,仅钳制到合法枚举/范围): | |||||
| * pulse_y>2 → 0;dir 强制 3;accel_mode>2 → LINEAR;seg_count 上限 10; | |||||
| * start_seg 钳到 [1,n];wait_type>4 → TIME。 | |||||
| * 频率合法性由 run_control 启动/开段时查 0x04,此处不拒绝写入。 | |||||
| * @details 校验失败时仍完成拷贝(保留上位机原始值),故障码写入 0x2006; | |||||
| * 多项非法时仅保留校验过程中「最后一个」故障码。 | |||||
| * @return 1=全部通过;0=存在门禁故障(不落 BKP) | |||||
| */ | */ | ||||
| uint8_t PlsrParamBlockApplyFromHold(void) | uint8_t PlsrParamBlockApplyFromHold(void) | ||||
| { | { | ||||
| uint16_t i; | uint16_t i; | ||||
| uint16_t n; | uint16_t n; | ||||
| uint16_t base; | uint16_t base; | ||||
| uint16_t m; | |||||
| uint16_t fault = PLSR_ERR_NONE; | |||||
| PlsrSeg_t *seg; | PlsrSeg_t *seg; | ||||
| /* | |||||
| * 从保持寄存器组装运行映像。 | |||||
| * TODO:段数/频率/脉冲/端子冲突/跳转非法 → 故障码(当前仅裁剪) | |||||
| */ | |||||
| s_cfg.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y); | s_cfg.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y); | ||||
| if (s_cfg.pulse_y > 2U) | if (s_cfg.pulse_y > 2U) | ||||
| { | { | ||||
| s_cfg.pulse_y = 0U; | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_PULSE_Y); | |||||
| } | |||||
| s_cfg.dir_y = ReadHoldReg(PLSR_REG_DIR_Y); | |||||
| if (s_cfg.dir_y != 3U) | |||||
| { | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_DIR_Y); | |||||
| } | } | ||||
| s_cfg.dir_y = 3U; /* 方向固定 Y3,忽略上位机其它值 */ | |||||
| WriteHoldReg(PLSR_REG_DIR_Y, 3U); | |||||
| s_cfg.wait_x_sel = ReadHoldReg(PLSR_REG_WAIT_X) ? 1U : 0U; | 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.ext_x_sel = ReadHoldReg(PLSR_REG_EXT_X) ? 1U : 0U; | ||||
| @@ -232,59 +291,83 @@ uint8_t PlsrParamBlockApplyFromHold(void) | |||||
| s_cfg.dir_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY); | s_cfg.dir_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY); | ||||
| s_cfg.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ? | s_cfg.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ? | ||||
| PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS; | PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS; | ||||
| m = ReadHoldReg(PLSR_REG_ACCEL_MODE); | |||||
| if (m > (uint16_t)PLSR_ACCEL_SINE) | |||||
| { | |||||
| m = (uint16_t)PLSR_ACCEL_LINEAR; | |||||
| } | |||||
| s_cfg.accel_mode = (PlsrAccelMode_e)m; | |||||
| s_cfg.accel_mode = (PlsrAccelMode_e)ReadHoldReg(PLSR_REG_ACCEL_MODE); | |||||
| s_cfg.run_mode = (ReadHoldReg(PLSR_REG_RUN_MODE) != 0U) ? | s_cfg.run_mode = (ReadHoldReg(PLSR_REG_RUN_MODE) != 0U) ? | ||||
| PLSR_POS_ABSOLUTE : PLSR_POS_RELATIVE; | PLSR_POS_ABSOLUTE : PLSR_POS_RELATIVE; | ||||
| n = ReadHoldReg(PLSR_REG_SEG_COUNT); | n = ReadHoldReg(PLSR_REG_SEG_COUNT); | ||||
| if (n > PLSR_SEG_MAX) | |||||
| s_cfg.seg_count = n; | |||||
| if ((n < 1U) || (n > PLSR_SEG_MAX)) | |||||
| { | { | ||||
| n = (uint16_t)PLSR_SEG_MAX; | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_SEG_COUNT); | |||||
| } | } | ||||
| s_cfg.seg_count = n; | |||||
| s_cfg.start_seg = ReadHoldReg(PLSR_REG_START_SEG); | s_cfg.start_seg = ReadHoldReg(PLSR_REG_START_SEG); | ||||
| if (s_cfg.start_seg < 1U) | |||||
| if ((n >= 1U) && (n <= PLSR_SEG_MAX)) | |||||
| { | { | ||||
| s_cfg.start_seg = 1U; | |||||
| if ((s_cfg.start_seg < 1U) || (s_cfg.start_seg > n)) | |||||
| { | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_START_SEG); | |||||
| } | |||||
| } | } | ||||
| if ((n >= 1U) && (s_cfg.start_seg > n)) | |||||
| else if (s_cfg.start_seg < 1U) | |||||
| { | { | ||||
| s_cfg.start_seg = n; | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_START_SEG); | |||||
| } | } | ||||
| s_cfg.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L); | s_cfg.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L); | ||||
| if (PlsrParamIsSpeedHzValid(s_cfg.default_speed) == 0U) | |||||
| { | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_DEFAULT_SPD); | |||||
| } | |||||
| s_cfg.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L); | s_cfg.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L); | ||||
| if (PlsrParamIsStartEndSpeedValid(s_cfg.start_speed) == 0U) | |||||
| { | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_START_END_SPD); | |||||
| } | |||||
| s_cfg.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L); | s_cfg.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L); | ||||
| if (PlsrParamIsStartEndSpeedValid(s_cfg.end_speed) == 0U) | |||||
| { | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_START_END_SPD); | |||||
| } | |||||
| s_cfg.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS); | s_cfg.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS); | ||||
| s_cfg.decel_ms = ReadHoldReg(PLSR_REG_DECEL_MS); | s_cfg.decel_ms = ReadHoldReg(PLSR_REG_DECEL_MS); | ||||
| for (i = 0U; i < n; i++) | |||||
| if ((n >= 1U) && (n <= PLSR_SEG_MAX)) | |||||
| { | { | ||||
| 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)); | |||||
| seg->pulse_cnt = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L)); | |||||
| for (i = 0U; i < n; i++) | |||||
| { | { | ||||
| uint16_t w = ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT)); | |||||
| if (w > 4U) | |||||
| 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) | |||||
| { | { | ||||
| w = 0U; | |||||
| PlsrParamFaultLast(&fault, PLSR_ERR_FREQ_ILLEGAL); | |||||
| } | } | ||||
| seg->wait_type = (PlsrWaitType_e)w; | |||||
| seg->pulse_cnt = PlsrParamReadSignedDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L)); | |||||
| if (PlsrParamIsPulseCntValid(seg->pulse_cnt) == 0U) | |||||
| { | |||||
| PlsrParamFaultLast(&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)); | |||||
| } | } | ||||
| 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)); | |||||
| } | } | ||||
| return 1U; | |||||
| PlsrCommandSetFault(fault); | |||||
| if (fault == PLSR_ERR_NONE) | |||||
| { | |||||
| return 1U; | |||||
| } | |||||
| return 0U; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -412,10 +495,8 @@ void PlsrParamBlockInit(void) | |||||
| /** | /** | ||||
| * @brief 参数块任务(见 plsr_param.h) | * @brief 参数块任务(见 plsr_param.h) | ||||
| * | * | ||||
| * for(;;) 为 uC/OS 任务标准死循环: | |||||
| * OSSemPend → ApplyFromHold → SaveParamsFromHold → ExportToHold | |||||
| * Save 用 hold 中上位机刚写入的值落 BKP;Export 仅把钳位结果摊回 hold 供读回。 | |||||
| * Apply 失败则写 XFER_FAIL 与当前 recv 帧号。 | |||||
| * for(;;):OSSemPend → ApplyFromHold(校验+写 0x2006)→ 通过则 SaveParamsFromHold。 | |||||
| * 校验失败:不落 BKP,hold 保留上位机原值,由故障码提示修正。 | |||||
| */ | */ | ||||
| void PlsrParamBlockTask(void *pArg) | void PlsrParamBlockTask(void *pArg) | ||||
| { | { | ||||
| @@ -434,8 +515,7 @@ void PlsrParamBlockTask(void *pArg) | |||||
| if (PlsrParamBlockApplyFromHold() != 0U) | if (PlsrParamBlockApplyFromHold() != 0U) | ||||
| { | { | ||||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U); | PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U); | ||||
| PlsrPersistSaveParamsFromHold(); /* hold 原值 → BKP(不依赖 Export) */ | |||||
| PlsrParamBlockExportToHold(); /* 钳位后映像 → hold,供 Modbus 读回 */ | |||||
| PlsrPersistSaveParamsFromHold(); /* 仅全部校验通过才落 BKP */ | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -6,7 +6,7 @@ | |||||
| * 1. 定义公共参数区 0x1000~0x1013、段表区 0x1100+i*0x10 的地址与字段语义; | * 1. 定义公共参数区 0x1000~0x1013、段表区 0x1100+i*0x10 的地址与字段语义; | ||||
| * 2. 维护运行侧唯一映像 s_cfg / s_seg[](运动规划只读此映像,不直接啃寄存器); | * 2. 维护运行侧唯一映像 s_cfg / s_seg[](运动规划只读此映像,不直接啃寄存器); | ||||
| * 3. 支持上位机「1 公共帧 + N 段帧」分帧 FC10 写入:收齐后 ApplyFromHold; | * 3. 支持上位机「1 公共帧 + N 段帧」分帧 FC10 写入:收齐后 ApplyFromHold; | ||||
| * 4. 支持 ExportToHold:上电默认或读回时把映像摊到保持寄存器。 | |||||
| * 4. 上电由 PersistLoad 填 hold;ExportToHold 仅保留作调试接口。 | |||||
| * | * | ||||
| * 与其它模块关系 | * 与其它模块关系 | ||||
| * - modbus_rtu / modbus_data :ReadHoldReg / WriteHoldReg | * - modbus_rtu / modbus_data :ReadHoldReg / WriteHoldReg | ||||
| @@ -18,9 +18,8 @@ | |||||
| * 关键数据流 | * 关键数据流 | ||||
| * 上位机写公共帧@0x1000 → OnHoldWrite 开传输 expect=1+N | * 上位机写公共帧@0x1000 → OnHoldWrite 开传输 expect=1+N | ||||
| * 上位机写段帧@0x1100+ → recv++;收齐 → OSSemPost | * 上位机写段帧@0x1100+ → recv++;收齐 → OSSemPost | ||||
| * ParamBlockTask → ApplyFromHold → SaveParamsFromHold → ExportToHold → 状态 OK | |||||
| * 上电:PersistLoad → ApplyFromHold → … ModbusDataInit 清 hold … | |||||
| * → ExportToHold(填回参数)+ TickMonitor(填回监控) | |||||
| * ParamBlockTask → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold → 状态 OK/FAIL | |||||
| * 上电:PersistLoad(BKP→hold) → ApplyFromHold(hold→结构体) | |||||
| * | * | ||||
| * 协议概要 | * 协议概要 | ||||
| * - 公共帧:0x1000~0x1013(20 字),0x1009=段数 N,预期总帧=1+N | * - 公共帧:0x1000~0x1013(20 字),0x1009=段数 N,预期总帧=1+N | ||||
| @@ -195,7 +194,8 @@ uint16_t PlsrParamGetStartSeg(void); | |||||
| void PlsrParamBlockInit(void); | void PlsrParamBlockInit(void); | ||||
| /** | /** | ||||
| * @brief 参数块任务:阻塞等信号量 → ApplyFromHold → SaveParamsFromHold → ExportToHold | |||||
| /** | |||||
| * @brief 参数块任务:阻塞等信号量 → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold | |||||
| * @param pArg 未使用 | * @param pArg 未使用 | ||||
| * @note for(;;) 死循环为 RTOS 任务常态;空循环体是错觉(花括号内有 Pend/Apply) | * @note for(;;) 死循环为 RTOS 任务常态;空循环体是错觉(花括号内有 Pend/Apply) | ||||
| */ | */ | ||||
| @@ -210,19 +210,14 @@ void PlsrParamBlockTask(void *pArg); | |||||
| void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity); | void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity); | ||||
| /** | /** | ||||
| * @brief 保持寄存器 → s_cfg / s_seg | |||||
| * @return 1=成功(当前恒为 1;完整校验/故障码待扩展) | |||||
| * @note 成功后由 ParamBlockTask 调用 SaveParamsFromHold 整包落 BKP | |||||
| * @brief 保持寄存器 → s_cfg / s_seg,边拷贝边校验 | |||||
| * @return 1=全部通过(写 0x2006=0);0=存在门禁故障(写对应码,不落 BKP) | |||||
| */ | */ | ||||
| uint8_t PlsrParamBlockApplyFromHold(void); | uint8_t PlsrParamBlockApplyFromHold(void); | ||||
| /** | /** | ||||
| * @brief s_cfg / s_seg → 保持寄存器(供 FC03 读回 / START 前 hold 有效) | |||||
| * @note 调用时机: | |||||
| * - ParamBlockTask:Apply+Save 之后,把钳位结果摊回 hold | |||||
| * - ModbusDataInit 清 hold 之后立刻 Export | |||||
| * - PlsrTask 首次延时后再 Export(双保险) | |||||
| * @note 本函数不写 BKP;落盘用 SaveParamsFromHold | |||||
| * @brief s_cfg / s_seg → 保持寄存器(调试/特殊用途;正常运行路径不再调用) | |||||
| * @note 上电参数由 BKP→hold(PersistLoad);非法参数保留 hold 原值供上位机读回修正 | |||||
| */ | */ | ||||
| void PlsrParamBlockExportToHold(void); | void PlsrParamBlockExportToHold(void); | ||||
| @@ -10,10 +10,8 @@ | |||||
| * 本文件仅依赖 plsr.h 及各子模块头文件,不被其它 plsr 模块反向引用。 | * 本文件仅依赖 plsr.h 及各子模块头文件,不被其它 plsr 模块反向引用。 | ||||
| * | * | ||||
| * 关键数据流 | * 关键数据流 | ||||
| * PlsrInit 顺序:PersistLoad(BKP→hold) → ApplyFromHold → | |||||
| * PulseDriver → RunControl → RestoreRuntime → ExportToHold → | |||||
| * CommandInit → RestoreMonitor → TickMonitor | |||||
| * (随后 ModbusDataInit 可能清 hold,须再 Export + TickMonitor) | |||||
| * PlsrInit 顺序:PersistLoad(BKP→hold) → ApplyFromHold(hold→结构体) → … | |||||
| * ModbusDataInit 清 hold 后:再次 PersistLoad(BKP→hold),不再 ExportToHold | |||||
| * PlsrTask 循环:TickMs → DebouncePoll → CommandPoll → TickMonitor → WakePend | * PlsrTask 循环:TickMs → DebouncePoll → CommandPoll → TickMonitor → WakePend | ||||
| */ | */ | ||||
| #include "plsr.h" | #include "plsr.h" | ||||
| @@ -29,20 +27,18 @@ | |||||
| * @brief 子系统初始化(见 plsr.h) | * @brief 子系统初始化(见 plsr.h) | ||||
| * | * | ||||
| * 顺序要点 | * 顺序要点 | ||||
| * PersistLoad → Apply:BKP 进结构体; | |||||
| * Export:先填 hold(随后 ModbusDataInit 仍可能清零,故任务里再 Export); | |||||
| * RestoreRuntime / TickMonitor:累计脉冲进运行态并与 BKP 对齐。 | |||||
| * PersistLoad → Apply:BKP → hold → 结构体(BKP 内参数应已合法); | |||||
| * 非上电不再把结构体 Export 回 hold;Modbus 清零后由 BKP 再填 hold。 | |||||
| */ | */ | ||||
| void PlsrInit(void) | void PlsrInit(void) | ||||
| { | { | ||||
| //PlsrParamInitDefault(); | //PlsrParamInitDefault(); | ||||
| PlsrPersistLoad(); /* BKPSRAM 稀疏映像 → g_hold_reg */ | |||||
| (void)PlsrParamBlockApplyFromHold(); /* hold → s_cfg / s_seg[] */ | |||||
| PlsrPersistLoad(); /* BKPSRAM → g_hold_reg */ | |||||
| (void)PlsrParamBlockApplyFromHold(); /* hold → s_cfg / s_seg[] + 校验 */ | |||||
| PlsrPulseDriverInit(); | PlsrPulseDriverInit(); | ||||
| PlsrSignalIoInit(); | PlsrSignalIoInit(); | ||||
| PlsrRunControlInit(); | PlsrRunControlInit(); | ||||
| PlsrPersistRestoreRuntime(); /* hold 0x2000 累计 → run_control(须在 DataInit 清 hold 前) */ | |||||
| PlsrParamBlockExportToHold(); /* 结构体 → hold;Modbus 清零后须再 Export */ | |||||
| PlsrPersistRestoreRuntime(); /* hold 0x2000 累计 → run_control */ | |||||
| PlsrCommandInit(); | PlsrCommandInit(); | ||||
| PlsrPersistRestoreMonitor(); /* 恢复故障码;0x3000/运行态强制空闲 */ | PlsrPersistRestoreMonitor(); /* 恢复故障码;0x3000/运行态强制空闲 */ | ||||
| PlsrPersistTickMonitor(); /* 监控区 hold+BKP 与运行态对齐 */ | PlsrPersistTickMonitor(); /* 监控区 hold+BKP 与运行态对齐 */ | ||||
| @@ -116,18 +112,15 @@ void PlsrTask(void *pArg) | |||||
| while(1) | while(1) | ||||
| { | { | ||||
| /* | /* | ||||
| * 首次进入:ModbusSlaveTask 启动时 ModbusDataInit 会 memset(g_hold_reg)。 | |||||
| * 延迟让出 CPU 后再次 Export:把已从 BKP Apply 的运行映像摊回 hold, | |||||
| * 否则读 PLC 公共/段为 0,START 的 ApplyFromHold 也会用空 hold 覆盖结构体。 | |||||
| * TickMonitor:清 hold 后立刻把累计等监控量刷回 hold+BKP。 | |||||
| * 首次进入:ModbusSlaveTask 已 DataInit 清 hold 并 PersistLoad 填回 BKP 参数。 | |||||
| * 此处仅恢复监控/指令侧,不再 Export 结构体到 hold。 | |||||
| */ | */ | ||||
| if (s_booted == 0U) | if (s_booted == 0U) | ||||
| { | { | ||||
| OSTimeDly(3U); /* ~30ms @ 100Hz tick;让出 CPU 给 Modbus 完成 DataInit */ | |||||
| PlsrParamBlockExportToHold(); /* 参数区 hold ← s_cfg/s_seg */ | |||||
| OSTimeDly(3U); /* ~30ms @ 100Hz tick;让出 CPU 给 Modbus 完成初始化 */ | |||||
| PlsrCommandInit(); | PlsrCommandInit(); | ||||
| PlsrPersistRestoreMonitor(); | PlsrPersistRestoreMonitor(); | ||||
| PlsrPersistTickMonitor(); /* 监控区 hold+BKP ← 运行态 */ | |||||
| PlsrPersistTickMonitor(); | |||||
| s_booted = 1U; | s_booted = 1U; | ||||
| } | } | ||||
| @@ -16,13 +16,9 @@ | |||||
| * - plsr_signal_io :WAIT/EXT 外部输入、TIM5 延时 | * - plsr_signal_io :WAIT/EXT 外部输入、TIM5 延时 | ||||
| * | * | ||||
| * 关键数据流 | * 关键数据流 | ||||
| * 上电:PlsrInit → PersistLoad(BKP→hold) → ApplyFromHold → 驱动/IO/RunControl | |||||
| * → RestoreRuntime → ExportToHold → CommandInit → RestoreMonitor → TickMonitor | |||||
| * →(ModbusDataInit 清 hold 后再次 Export + TickMonitor) | |||||
| * 周期:PlsrTask → TickMs → Debounce → CommandPoll → TickMonitor → WakePend | |||||
| * 脉冲:TIM UPDATE 中断 → PlsrOnPulseIsr → RunControl 计数 + 逐拍改频 | |||||
| * 启动:PlsrStart → RunControlStart → 段序/方向 + 曲线规划 + PWM 输出 | |||||
| * 写参:分帧收齐 → ParamBlockTask Apply → SaveParamsFromHold → ExportToHold | |||||
| * 上电:PlsrInit → PersistLoad(BKP→hold) → ApplyFromHold → … | |||||
| * →(ModbusDataInit 后再次 PersistLoad 填 hold) | |||||
| * 写参:分帧收齐 → ParamBlockTask Apply(校验)→ 通过则 SaveParamsFromHold | |||||
| */ | */ | ||||
| #ifndef PLSR_H | #ifndef PLSR_H | ||||
| #define PLSR_H | #define PLSR_H | ||||