| Autors | SHA1 | Ziņojums | Datums |
|---|---|---|---|
|
|
dd37a5345c |
清理旧查表残留
Signed-off-by: hanyongwei <2043702190@qq.com> |
pirms 2 nedēļām |
|
|
62b49681a3 |
修改为定点数Q24,采用查形状表+线性插值
Signed-off-by: hanyongwei <2043702190@qq.com> |
pirms 2 nedēļām |
|
|
92742fffc1 |
增加了注释
Signed-off-by: hanyongwei <2043702190@qq.com> |
pirms 2 nedēļām |
|
|
5bd7cb9c53 |
删掉二分降峰后重复判断逻辑
Signed-off-by: hanyongwei <2043702190@qq.com> |
pirms 2 nedēļām |
|
|
1d799e89d5 |
补充了部分注释
Signed-off-by: hanyongwei <2043702190@qq.com> |
pirms 2 nedēļām |
|
|
27140c2ddb | 修改部分过长的变量命名 | pirms 2 nedēļām |
|
|
b86e343c92 | 清理冗余设计3 | pirms 2 nedēļām |
|
|
ad2a6c7d76 | 清理部分冗余设计2 | pirms 2 nedēļām |
|
|
5deb2ab693 | 清理部分冗余设计 | pirms 2 nedēļām |
| @@ -19,8 +19,4 @@ iar/settings/modbus.reggroups | |||
| build-plsr_host-Desktop_Qt_5_6_3_MinGW_32bit-Debug/ | |||
| plsr/test/ | |||
| tools/ | |||
| .cursor/ | |||
| PLSR测试大纲.html | |||
| _gen_outline_p1.py | |||
| run_control_full.txt | |||
| plsr/README.md | |||
| .cursor/ | |||
| @@ -75,15 +75,16 @@ int main(void) | |||
| PlsrParamBlockInit(); | |||
| /* Modbus 从站任务:解析 FC03/06/10,维护保持寄存器映像 */ | |||
| OSTaskCreateExt(ModbusSlaveTask, | |||
| NULL, | |||
| &ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1], | |||
| MODBUS_TASK_PRIO, | |||
| MODBUS_TASK_PRIO, | |||
| &ModbusTaskStk[0], | |||
| MODBUS_TASK_STACK_SIZE, | |||
| NULL, | |||
| OSTaskCreateExt(ModbusSlaveTask,//任务函数入口,任务实体函数 | |||
| NULL,//传递给任务函数的入口参数 | |||
| &ModbusTaskStk[MODBUS_TASK_STACK_SIZE - 1],//栈向下增长,栈从高地址往低地址生长 | |||
| MODBUS_TASK_PRIO,//任务优先级 | |||
| MODBUS_TASK_PRIO,//任务id,一般直接等于优先级;可以不等于优先级,但不能冲突 | |||
| &ModbusTaskStk[0],//栈数组的起始地址,数组下标 0 的地址 | |||
| MODBUS_TASK_STACK_SIZE,//栈的元素数量,不是字节数 | |||
| NULL,//TCB 扩展指针 | |||
| OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); | |||
| //开启栈用量统计;任务创建的时候,把整个栈内存全部清零;保存 FPU 浮点寄存器 | |||
| /* PLSR 主任务:TickMs / CommandPoll / 监控发布 */ | |||
| OSTaskCreateExt(PlsrTask, | |||
| @@ -95,7 +96,7 @@ int main(void) | |||
| PLSR_TASK_STACK_SIZE, | |||
| NULL, | |||
| OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP); | |||
| //开启栈用量统计;任务创建的时候,把整个栈内存全部清零;保存 FPU 浮点寄存器 | |||
| /* | |||
| * 参数块任务:Pend → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold。 | |||
| * 分帧写完后由 OnHoldWrite Post;Save 落 BKP,Export 仅供读回钳位结果。 | |||
| @@ -47,7 +47,7 @@ static void FlushUartRx(void); | |||
| static void RestartReceive(void); | |||
| static uint16_t ModbusCrc16(uint8_t *data, uint16_t len); | |||
| static void SendData(uint8_t *buf, uint16_t len); | |||
| static void ModbusDataInit(void); | |||
| //static void ModbusDataInit(void); | |||
| static uint16_t ModbusMapRegAddr(uint16_t addr); | |||
| static uint8_t ModbusIsRegRangeValid(uint16_t addr, uint16_t qty); | |||
| static uint16_t ReadU16Be(const uint8_t *data); | |||
| @@ -70,10 +70,10 @@ void ModbusSlaveTask(void *pArg) | |||
| /* | |||
| * ModbusDataInit 会清零 g_hold_reg;上电后仅从 BKP 恢复参数/监控到 hold, | |||
| * 不再把运行结构体 Export 回寄存器(非法参数保留在上位机写入值 + 0x2006)。 | |||
| * 不有点多此一举,直接不初始化g_hold_reg。 | |||
| */ | |||
| ModbusDataInit(); | |||
| PlsrPersistLoad(); | |||
| // ModbusDataInit(); | |||
| // PlsrPersistLoad(); | |||
| ModbusPortInit(); | |||
| while (1) | |||
| @@ -129,10 +129,10 @@ void ModbusSlaveTask(void *pArg) | |||
| * @brief 初始化保持寄存器映像 | |||
| * @retval None | |||
| */ | |||
| void ModbusDataInit(void) | |||
| { | |||
| memset(g_hold_reg, 0, sizeof(g_hold_reg)); | |||
| } | |||
| //void ModbusDataInit(void) | |||
| //{ | |||
| // memset(g_hold_reg, 0, sizeof(g_hold_reg)); | |||
| //} | |||
| /*============================================================================*/ | |||
| /* 地址映射与区间校验 */ | |||
| @@ -441,7 +441,9 @@ static void WriteMultiRegs(const uint8_t *frame, uint16_t len) | |||
| g_modbus_tx_buf[5] = frame[5]; | |||
| AppendCrcAndSend(g_modbus_tx_buf, 6U); | |||
| /*统计接受帧数,收满释放信号量*/ | |||
| PlsrParamBlockOnHoldWrite(startAddr, quantity); | |||
| /* 动态改频率 */ | |||
| PlsrCommandOnSegFreqHoldWrite(startAddr, quantity); | |||
| } | |||
| @@ -4,14 +4,6 @@ | |||
| * | |||
| * @details 按起/峰/止频率、斜率时间与总脉冲规划三相脉冲预算,并在脉冲序号上取频。 | |||
| * 不读 wait_type,不碰 TIM/GPIO。 | |||
| * | |||
| * 直线:下一频率由起点频率、变化率和已完成脉冲数计算(ISR 增量逼近)。 | |||
| * S/正弦:开相时预计算频率表;ISR 只按脉冲序号查表(匀速相不改频)。 | |||
| * 规划脉冲数与建表使用同一套离散逐拍仿真。 | |||
| * | |||
| * 术语(全模块统一): | |||
| * 字段使用完整名称和单位,例如 accel_pulses、start_frequency_hz, | |||
| * 避免阅读代码时反复猜测 acc_n、f_cur、a_acc 等数学缩写。 | |||
| */ | |||
| #ifndef PLSR_ACCEL_CURVE_H | |||
| #define PLSR_ACCEL_CURVE_H | |||
| @@ -25,29 +17,29 @@ | |||
| */ | |||
| typedef struct { | |||
| uint32_t accel_pulses; /* 入口到目标频率需要的脉冲数 */ | |||
| uint32_t constant_pulses; /* 保持目标频率的脉冲数 */ | |||
| uint32_t const_pulses; /* 保持目标频率的脉冲数 */ | |||
| uint32_t decel_pulses; /* 目标到出口频率需要的脉冲数 */ | |||
| uint32_t start_frequency_hz; /* 本段实际入口频率 */ | |||
| uint32_t target_frequency_hz; /* 实际目标频率,短段可能被降低 */ | |||
| uint32_t end_frequency_hz; /* 本段出口频率 */ | |||
| uint32_t start_freq_hz; /* 本段实际入口频率 */ | |||
| uint32_t target_freq_hz; /* 实际目标频率,短段可能被降低 */ | |||
| uint32_t end_freq_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 configured_frequency_hz, | |||
| uint32_t target_frequency_hz, | |||
| uint32_t PlsrAccelCurveResolveStartHz(uint32_t configured_freq_hz, | |||
| uint32_t target_freq_hz, | |||
| uint32_t default_speed_hz, | |||
| uint32_t acceleration_time_ms, | |||
| uint32_t deceleration_time_ms); | |||
| uint32_t accel_time_ms, | |||
| uint32_t decel_time_ms); | |||
| /** 配置止速 → 规划出口:止速>目标用止速;否则目标与起跳比较取其一(不默认到 0) */ | |||
| uint32_t PlsrAccelCurveResolveEndHz(uint32_t configured_frequency_hz, | |||
| uint32_t target_frequency_hz, | |||
| uint32_t PlsrAccelCurveResolveEndHz(uint32_t configured_freq_hz, | |||
| uint32_t target_freq_hz, | |||
| uint32_t default_speed_hz, | |||
| uint32_t acceleration_time_ms, | |||
| uint32_t deceleration_time_ms); | |||
| uint32_t accel_time_ms, | |||
| uint32_t decel_time_ms); | |||
| /** | |||
| * 规划一整段脉冲域三相预算与实际峰值 | |||
| @@ -55,57 +47,54 @@ uint32_t PlsrAccelCurveResolveEndHz(uint32_t configured_frequency_hz, | |||
| */ | |||
| void PlsrAccelCurvePlan(PlsrAccelPlan_t *plan, | |||
| uint32_t total_pulses, | |||
| uint32_t start_frequency_hz, | |||
| uint32_t target_frequency_hz, | |||
| uint32_t end_frequency_hz, | |||
| uint32_t start_freq_hz, | |||
| uint32_t target_freq_hz, | |||
| uint32_t end_freq_hz, | |||
| uint32_t default_speed_hz, | |||
| uint32_t acceleration_time_ms, | |||
| uint32_t deceleration_time_ms, | |||
| uint32_t accel_time_ms, | |||
| uint32_t decel_time_ms, | |||
| PlsrAccelMode_e curve_mode); | |||
| /** 按已完成脉冲数取频(任务/预览;ISR 热路径用 PlsrAccelNextFrequency) */ | |||
| /** 按已完成脉冲数取频(任务/预览;ISR 热路径用 PlsrAccelNextFreq) */ | |||
| uint32_t PlsrAccelCurveFreqAtPulse(const PlsrAccelPlan_t *plan, | |||
| uint32_t completed_segment_pulses); | |||
| uint32_t completed_seg_pulses); | |||
| /** | |||
| * 单相逐脉冲运行态(ISR 热路径,每来一个 UPDATE 调 Step 一次) | |||
| * 直线:每次按目标平方值逐步逼近下一频率 | |||
| * S/正弦:查预计算频率表(开相 BeginAcc/BeginDec 时填好) | |||
| * S/正弦:时间累计 + 257 点 Q24 形状表线性插值 | |||
| */ | |||
| typedef struct { | |||
| uint32_t current_frequency_hz; /* 当前输出频率 */ | |||
| uint32_t start_frequency_hz; /* 当前相起点频率 */ | |||
| uint32_t end_frequency_hz; /* 当前相终点频率 */ | |||
| uint32_t acceleration_hz_per_s; /* 当前相使用的变化率 */ | |||
| uint32_t cur_freq_hz; /* 当前输出频率 */ | |||
| uint32_t start_freq_hz; /* 当前相起点频率 */ | |||
| uint32_t end_freq_hz; /* 当前相终点频率 */ | |||
| uint32_t accel_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 const_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 freq_rising; /* 1=频率升高,0=降低 */ | |||
| uint8_t is_active; /* 1=当前相仍在运行 */ | |||
| uint8_t frequency_table_id; /* 0=不用表,1=加速表,2=减速表 */ | |||
| PlsrAccelMode_e curve_mode; | |||
| PlsrAccelMode_e curve_mode; /* 直线 / S / 正弦 */ | |||
| } PlsrAccelRuntime_t; | |||
| /* 当前段唯一的曲线规划和逐拍状态,run_control 直接使用,不再另存副本。 */ | |||
| extern PlsrAccelPlan_t g_plsr_accel_plan; | |||
| extern PlsrAccelRuntime_t g_plsr_accel_runtime; | |||
| /** PlanSeg 后调用:预建 S/正弦加减速频率表(勿在 ISR) */ | |||
| void PlsrAccelPrebuildFrequencyTables(const PlsrAccelPlan_t *plan); | |||
| extern PlsrAccelPlan_t g_plsr_accel_plan; /* 本段三相规划结果 */ | |||
| extern PlsrAccelRuntime_t g_plsr_accel_runtime; /* 当前相 ISR 逐拍状态 */ | |||
| void PlsrAccelBeginAcceleration(PlsrAccelRuntime_t *runtime, | |||
| /** 进入加速相:起→峰;S/正弦在 ISR 内形状表取频 */ | |||
| void PlsrAccelBeginAccel(PlsrAccelRuntime_t *runtime, | |||
| const PlsrAccelPlan_t *plan); | |||
| void PlsrAccelBeginDeceleration(PlsrAccelRuntime_t *runtime, | |||
| /** 进入减速相:峰→止;S/正弦在 ISR 内形状表取频 */ | |||
| void PlsrAccelBeginDecel(PlsrAccelRuntime_t *runtime, | |||
| const PlsrAccelPlan_t *plan); | |||
| void PlsrAccelBeginConstantSpeed(PlsrAccelRuntime_t *runtime, | |||
| /** 进入匀速相:钉峰值,ISR 不再改频 */ | |||
| void PlsrAccelBeginConstSpeed(PlsrAccelRuntime_t *runtime, | |||
| const PlsrAccelPlan_t *plan); | |||
| /** 本相前进 1 脉冲,返回下一拍命令频率 */ | |||
| uint32_t PlsrAccelNextFrequency(PlsrAccelRuntime_t *runtime); | |||
| uint32_t PlsrAccelNextFreq(PlsrAccelRuntime_t *runtime); | |||
| #endif | |||
| @@ -30,15 +30,14 @@ | |||
| * s_live_freq_req — 运行中写频请求标志,Poll 消费后清 0 | |||
| * s_live_freq_hz — 待应用的 new_tgt_hz | |||
| */ | |||
| static uint16_t s_fault = PLSR_ERR_NONE; | |||
| static volatile uint8_t s_live_freq_req; | |||
| static volatile uint32_t s_live_freq_hz; | |||
| static uint16_t s_fault = PLSR_ERR_NONE; /* 故障码镜像,与 0x2006 同步 */ | |||
| static volatile uint8_t s_live_freq_req; /* 1=运行中改频待 Poll 消费 */ | |||
| static volatile uint32_t s_live_freq_hz; /* 待应用的新目标频率 Hz */ | |||
| /** | |||
| * @brief 写 32 位值到相邻两个保持寄存器(低字在前) | |||
| * @brief 写 32 位值到相邻两个modbus保持寄存器 | |||
| * @param addr 低字寄存器地址 | |||
| * @param value 32 位值 | |||
| * @note 副作用:WriteHoldReg 两次 | |||
| */ | |||
| static void PlsrCommandWriteDWord(uint16_t addr, uint32_t value) | |||
| { | |||
| @@ -47,9 +46,9 @@ static void PlsrCommandWriteDWord(uint16_t addr, uint32_t value) | |||
| } | |||
| /** | |||
| * @brief 刷新全部监控寄存器 0x2000~0x2006 | |||
| * @brief 刷新全部监控寄存器 0x2000~0x2006到modbus保持寄存器 | |||
| * @note 调用时机:Init 末、Poll 每周期末 | |||
| * @note 数据源:RunControl / Plsr 门面 API + s_fault 镜像 | |||
| * @note 数据源:RunControl / Plsr 任务 + s_fault 镜像 | |||
| */ | |||
| static void PlsrCommandPublishMonitor(void) | |||
| { | |||
| @@ -58,9 +57,9 @@ static void PlsrCommandPublishMonitor(void) | |||
| if (g_plsr_busy != 0U) | |||
| { | |||
| PlsrCommandWriteDWord(PLSR_MON_FREQ_L, g_plsr_output_frequency_hz); | |||
| WriteHoldReg(PLSR_MON_RUN_STATUS, 1U); | |||
| WriteHoldReg(PLSR_MON_CUR_SEG, (uint16_t)(g_plsr_current_segment_index + 1U)); | |||
| PlsrCommandWriteDWord(PLSR_MON_FREQ_L, g_plsr_output_freq_hz); /* 0x2002/03 当前频 */ | |||
| WriteHoldReg(PLSR_MON_RUN_STATUS, 1U); /* 0x2004 运行中 */ | |||
| WriteHoldReg(PLSR_MON_CUR_SEG, (uint16_t)(g_plsr_cur_seg_idx + 1U)); /* 0x2005 1-based */ | |||
| } | |||
| else | |||
| { | |||
| @@ -71,27 +70,27 @@ static void PlsrCommandPublishMonitor(void) | |||
| } | |||
| } | |||
| /** @brief 置故障(见 plsr_command.h) */ | |||
| /** @brief 置故障码 */ | |||
| void PlsrCommandSetFault(uint16_t code) | |||
| { | |||
| s_fault = code; | |||
| WriteHoldReg(PLSR_MON_ERR, code); | |||
| } | |||
| /** @brief 清故障(见 plsr_command.h) */ | |||
| /** @brief 清故障码 */ | |||
| void PlsrCommandClearFault(void) | |||
| { | |||
| s_fault = PLSR_ERR_NONE; | |||
| WriteHoldReg(PLSR_MON_ERR, PLSR_ERR_NONE); | |||
| } | |||
| /** @brief 读故障镜像(见 plsr_command.h) */ | |||
| /** @brief persist模块读故障码 */ | |||
| uint16_t PlsrCommandGetFault(void) | |||
| { | |||
| return s_fault; | |||
| } | |||
| /** @brief 参数/重复启动类故障是否禁止 START(见 plsr_command.h) */ | |||
| /** @brief 参数/重复启动类故障是否禁止 START */ | |||
| uint8_t PlsrCommandIsStartBlocked(void) | |||
| { | |||
| if ((s_fault >= PLSR_ERR_PARAM_BLOCK_MIN) && | |||
| @@ -102,7 +101,7 @@ uint8_t PlsrCommandIsStartBlocked(void) | |||
| return 0U; | |||
| } | |||
| /** @brief 指令模块初始化(见 plsr_command.h) */ | |||
| /** @brief 指令模块初始化 */ | |||
| void PlsrCommandInit(void) | |||
| { | |||
| WriteHoldReg(PLSR_CTRL_REG, 0U); | |||
| @@ -113,133 +112,125 @@ void PlsrCommandInit(void) | |||
| } | |||
| /** | |||
| * @brief 运行中 FC10 写段频率钩子(见 plsr_command.h) | |||
| * @note 门禁:quantity==2、Busy、地址对齐 FREQ_L、seg0==当前段 | |||
| * @brief 运行中 FC10 写段频率 | |||
| * @note 限制:quantity==2、Busy、地址对齐 FREQ_L、当前改频段,当前运行段 | |||
| * @note 频率非法写 0x04 且不更新段表、不请求 ChangeFreq | |||
| */ | |||
| void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity) | |||
| { | |||
| uint16_t cur1; | |||
| uint16_t seg0; | |||
| uint16_t off; | |||
| uint32_t f; | |||
| uint16_t cur_seg; /* 当前运行段(1-based) */ | |||
| uint16_t seg; /* 本次写入对应的段下标(0-based) */ | |||
| uint16_t offset; | |||
| uint32_t freqency; | |||
| if (quantity != 2U) | |||
| { | |||
| return; | |||
| return; /* 须为频率双字 */ | |||
| } | |||
| if (g_plsr_busy == 0U) | |||
| { | |||
| return; | |||
| return; /* 空闲改频走参数块,不走本路径 */ | |||
| } | |||
| if (start_addr < PLSR_REG_SEG1_BASE) | |||
| { | |||
| return; | |||
| } | |||
| off = (uint16_t)(start_addr - PLSR_REG_SEG1_BASE); | |||
| if ((off % PLSR_SEG_STRIDE) != PLSR_SEG_OFF_FREQ_L) | |||
| offset = (uint16_t)(start_addr - PLSR_REG_SEG1_BASE); | |||
| if ((offset % PLSR_SEG_STRIDE) != PLSR_SEG_OFF_FREQ_L) | |||
| { | |||
| return; | |||
| return; /* 地址须对齐该段 FREQ_L */ | |||
| } | |||
| seg0 = (uint16_t)(off / PLSR_SEG_STRIDE); | |||
| if (seg0 >= PLSR_SEG_MAX) | |||
| seg = (uint16_t)(offset / PLSR_SEG_STRIDE); | |||
| if (seg >= PLSR_SEG_MAX) | |||
| { | |||
| return; | |||
| } | |||
| cur1 = (uint16_t)(g_plsr_current_segment_index + 1U); | |||
| if ((cur1 < 1U) || ((uint16_t)(cur1 - 1U) != seg0)) | |||
| cur_seg = (uint16_t)(g_plsr_cur_seg_idx + 1U); | |||
| if ((cur_seg < 1U) || ((uint16_t)(cur_seg - 1U) != seg)) | |||
| { | |||
| return; | |||
| return; /* 仅允许改当前运行段 */ | |||
| } | |||
| f = (uint32_t)ReadHoldReg(start_addr) | | |||
| freqency = (uint32_t)ReadHoldReg(start_addr) | | |||
| ((uint32_t)ReadHoldReg((uint16_t)(start_addr + 1U)) << 16); | |||
| /* 运行中写频也做 0x04 门禁:非法不改段表、不请求 ChangeFreq */ | |||
| if ((f < 1U) || (f > 100000U)) | |||
| if ((freqency < 1U) || (freqency > 100000U)) | |||
| { | |||
| PlsrCommandSetFault(PLSR_ERR_FREQ_ILLEGAL); | |||
| PlsrCommandSetFault(PLSR_ERR_FREQ_ILLEGAL); /* 非法:不改段表、不请求 */ | |||
| return; | |||
| } | |||
| g_plsr_segments[seg0].freq_hz = (int32_t)f; | |||
| /* 运行中改频:立即只把该段频率双字写入 BKP,不等整包参数落盘 */ | |||
| PlsrPersistSaveSegFreqToBkp(seg0, f); | |||
| g_plsr_segs[seg].freq_hz = (int32_t)freqency; /* 更新运行段表 */ | |||
| PlsrPersistSaveSegFreqToBkp(seg, freqency); /* 仅该段频率落 BKP */ | |||
| s_live_freq_hz = f; | |||
| s_live_freq_req = 1U; | |||
| s_live_freq_hz = freqency; | |||
| s_live_freq_req = 1U; /* 交 Poll → ChangeFreq */ | |||
| } | |||
| /** | |||
| * @brief 周期轮询(见 plsr_command.h) | |||
| * @brief 周期轮询 | |||
| * | |||
| * 控制位处理顺序(同周期多 bit 时): | |||
| * 1. 读 ctrl 后立即写 0(避免重复触发) | |||
| * 2. STOP — 急停 | |||
| * 3. CLR — 清累计 + ClearFault | |||
| * 4. START — 忙则 0x0C;否则 Apply 校验 → 0x04~0x0C 门禁则拒启 → Stop+Start | |||
| * 4. START — 忙则 0x0C;否则 Apply 校验 → 0x04~0x0C 故障码则拒启 → Stop+Start | |||
| * 动态改频在控制位之后、PublishMonitor 之前执行。 | |||
| */ | |||
| void PlsrCommandPoll(void) | |||
| { | |||
| uint16_t ctrl = ReadHoldReg(PLSR_CTRL_REG); | |||
| uint8_t skip_monitor = 0U; | |||
| uint8_t skip_monitor = 0U; /* 精确等待时跳过本拍监控刷新 */ | |||
| if (ctrl != 0U) | |||
| { | |||
| WriteHoldReg(PLSR_CTRL_REG, 0U); /* 从站「读-改-清」协议:处理后清零 */ | |||
| WriteHoldReg(PLSR_CTRL_REG, 0U); /* 立即清,避免重复触发 */ | |||
| if ((ctrl & PLSR_CTRL_BIT_STOP) != 0U) | |||
| { | |||
| PlsrStop(); | |||
| PlsrStop(); /* 急停 */ | |||
| } | |||
| if ((ctrl & PLSR_CTRL_BIT_CLR) != 0U) | |||
| { | |||
| PlsrClearAccPulse(); | |||
| PlsrCommandClearFault(); /* 清零累计时同步清 0x2006 */ | |||
| PlsrClearAccPulse(); /* 清累计 + 绝对原点 */ | |||
| PlsrCommandClearFault(); /* 同步清故障码 */ | |||
| } | |||
| /* 同时有停止位时不启动 */ | |||
| /* START:同拍有 STOP 则不启 */ | |||
| if (((ctrl & PLSR_CTRL_BIT_START) != 0U) && | |||
| ((ctrl & PLSR_CTRL_BIT_STOP) == 0U)) | |||
| { | |||
| if (g_plsr_busy != 0U) | |||
| { | |||
| /* 0x0C:运行中重复 START,启动无效 */ | |||
| PlsrCommandSetFault(PLSR_ERR_DUP_START); | |||
| } | |||
| else if (PlsrParamBlockApplyFromHold() == 0U) | |||
| { | |||
| /* Apply 失败:故障码已由参数块写入 0x2006,不落 BKP */ | |||
| PlsrCommandSetFault(PLSR_ERR_DUP_START); /* 0x0C 重复启动 */ | |||
| } | |||
| else if (PlsrCommandIsStartBlocked() != 0U) | |||
| { | |||
| /* 0x04~0x0B 等门禁故障:禁止启动 */ | |||
| PlsrStop(); /* 0x04~0x0B:拒启,仅停 */ | |||
| } | |||
| else | |||
| { | |||
| PlsrStop(); | |||
| PlsrStop(); /* 先清残留再启 */ | |||
| (void)PlsrStart(PlsrParamGetStartSeg()); | |||
| if (PlsrRunControlIsPreciseWait() != 0U) | |||
| { | |||
| skip_monitor = 1U; | |||
| skip_monitor = 1U; /* 换向/WAIT 精确等 TIM5 */ | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /* 动态改频:标志置位后按新目标重规划剩余脉冲 */ | |||
| if (s_live_freq_req != 0U) | |||
| { | |||
| uint32_t f = s_live_freq_hz; | |||
| uint32_t freq = s_live_freq_hz; | |||
| s_live_freq_req = 0U; | |||
| (void)PlsrChangeFreq(f); | |||
| (void)PlsrChangeFreq(freq); /* 重规划剩余脉冲 */ | |||
| } | |||
| if (skip_monitor == 0U) | |||
| { | |||
| PlsrCommandPublishMonitor(); | |||
| PlsrCommandPublishMonitor(); /* 回写 0x2000~0x2006 */ | |||
| } | |||
| } | |||
| @@ -44,40 +44,24 @@ | |||
| /* | |||
| * 故障码定义(写入 0x2006,须经 PlsrCommandSetFault,勿直接只写寄存器) | |||
| * | |||
| * 码 | 宏名 | 含义 | 等级 | 处理策略 | |||
| * ----|-------------------------|--------------------------------|------|------------------ | |||
| * 0 | PLSR_ERR_NONE | 无故障 | — | 启动/CLR 时写入 | |||
| * 1 | PLSR_ERR_PULSE_CNT | 实际脉冲数与段目标不一致 | 告警 | 继续跑,不中断 | |||
| * 2 | PLSR_ERR_FREQ_UNREACH | 规划峰值达不到设定目标(三角) | 告警 | 按压后规划继续 | |||
| * 3 | PLSR_ERR_CURVE_MISMATCH | 加减速相内脉冲与规划预算不符 | 告警 | 继续跑 | |||
| * 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_PULSE_CNT 0x01U | |||
| #define PLSR_ERR_FREQ_UNREACH 0x02U | |||
| #define PLSR_ERR_CURVE_MISMATCH 0x03U | |||
| #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_NONE 0x00U //无故障 | |||
| #define PLSR_ERR_PULSE_CNT 0x01U //实际脉冲数与段目标不一致 | |||
| #define PLSR_ERR_FREQ_UNREACH 0x02U //规划峰值达不到设定目标(三角) | |||
| #define PLSR_ERR_CURVE_MISMATCH 0x03U //加减速相内脉冲与规划预算不符 | |||
| #define PLSR_ERR_FREQ_ILLEGAL 0x04U //段频率 ∉ [1, 100000] Hz(非 0) | |||
| #define PLSR_ERR_PULSE_RANGE 0x05U //脉冲个数 ∉ int32 合法范围 | |||
| #define PLSR_ERR_SEG_COUNT 0x06U //总段数 ∉ [1, 10] | |||
| #define PLSR_ERR_START_SEG 0x07U //起始段 ∉ [1, 10] | |||
| #define PLSR_ERR_START_END_SPD 0x08U //起/止速非0且 ∉ [1, 100000] Hz | |||
| #define PLSR_ERR_PULSE_Y 0x09U //脉冲端子 ∉ {Y0,Y1,Y2} | |||
| #define PLSR_ERR_DIR_Y 0x0AU //方向端子 ≠ Y3 | |||
| #define PLSR_ERR_DEFAULT_SPD 0x0BU //默认速度 ∉ [1, 100000] Hz | |||
| #define PLSR_ERR_DUP_START 0x0CU //运行中重复 START | |||
| /** 参数类门禁故障码下限(含) */ | |||
| /** 参数类门禁故障码下限 */ | |||
| #define PLSR_ERR_PARAM_BLOCK_MIN PLSR_ERR_FREQ_ILLEGAL | |||
| /** 参数类门禁故障码上限(含 0x0C 重复启动) */ | |||
| /** 参数类门禁故障码上限 */ | |||
| #define PLSR_ERR_PARAM_BLOCK_MAX PLSR_ERR_DUP_START | |||
| /** | |||
| @@ -98,7 +82,7 @@ void PlsrCommandPoll(void); | |||
| * @brief 置故障码并立即写 0x2006 | |||
| * @param code PLSR_ERR_* 之一 | |||
| * @note 调用时机:run_control 诊断点;PublishMonitor 也会重发 s_fault | |||
| * @note 副作用:更新 s_fault 镜像,告警码不自动清除(须 START 或 CLR) | |||
| * @note 注意:更新 s_fault 镜像,告警码不自动清除(须 START 或 CLR) | |||
| */ | |||
| void PlsrCommandSetFault(uint16_t code); | |||
| @@ -113,7 +97,7 @@ uint16_t PlsrCommandGetFault(void); | |||
| /** | |||
| * @brief 是否因参数/重复启动类故障禁止 START | |||
| * @return 1=0x2006 为 0x04~0x0C(门禁/重复启动);0=可尝试启动 | |||
| * @return 1=故障码为 0x04~0x0C;0=可尝试启动 | |||
| */ | |||
| uint8_t PlsrCommandIsStartBlocked(void); | |||
| @@ -121,8 +105,6 @@ uint8_t PlsrCommandIsStartBlocked(void); | |||
| * @brief FC10 写段频率双字后的钩子 | |||
| * @param start_addr 写入起始保持寄存器地址 | |||
| * @param quantity 写入字数,须为 2 | |||
| * @note 调用时机:Modbus 从站 FC10 应答路径;仅运行中且地址为「当前段 FREQ_L」有效 | |||
| * @note 副作用:更新段表 freq_hz,置 s_live_freq_req 由 Poll 调 ChangeFreq | |||
| */ | |||
| void PlsrCommandOnSegFreqHoldWrite(uint16_t start_addr, uint16_t quantity); | |||
| @@ -3,21 +3,12 @@ | |||
| * @brief 参数块管理实现:运行映像 + 分帧导入/导出 + ParamBlockTask | |||
| * | |||
| * @details 模块职责 | |||
| * 维护公共参数 g_plsr_config 和段参数 g_plsr_segments[],实现寄存器与 | |||
| * 运行参数之间的双向转换,以及 | |||
| * 「5+N 分帧」收齐后的异步 Apply(信号量唤醒本任务)。 | |||
| * | |||
| * 静态状态说明 | |||
| * g_plsr_config — 公共运行参数 | |||
| * g_plsr_segments[] — 10 段运行参数 | |||
| * s_param_sem — 收齐分帧后 Post,Task Pend 后 Apply | |||
| * s_expect_frames — 期望总帧 = 5 + 公共帧1 中的段数 | |||
| * s_recv_frames — 已收帧计数(公共帧1 起算) | |||
| * s_xfer_active — 正在接收一批分帧传输 | |||
| * 维护公共参数 g_plsr_config 和段参数 g_plsr_segs[],实现寄存器与 | |||
| * 运行参数之间的双向转换,以及「5+N 分帧」收齐后的异步导入 | |||
| * | |||
| * 与其它模块关系 | |||
| * Modbus FC10 应答路径调用 OnHoldWrite;Command START 也会同步 ApplyFromHold; | |||
| * Persist:校验全部通过才 SaveParamsFromHold;失败只写故障码,不落 BKP; | |||
| * Modbus FC10 应答路径调用 OnHoldWrite;START 也会同步 ApplyFromHold; | |||
| * Persist:校验全部通过才 SaveParamsFromHold;失败只写故障码,不存BKP; | |||
| * 非上电不再 ExportToHold(hold 保留上位机写入值,由故障码提示修正)。 | |||
| */ | |||
| #include "plsr_param.h" | |||
| @@ -26,31 +17,30 @@ | |||
| #include "modbus_rtu.h" | |||
| #include "ucos_ii.h" | |||
| PlsrCfg_t g_plsr_config; | |||
| PlsrSeg_t g_plsr_segments[PLSR_SEG_MAX]; | |||
| PlsrCfg_t g_plsr_config; /* 公共参数运行映像 */ | |||
| PlsrSeg_t g_plsr_segs[PLSR_SEG_MAX]; /* 段表运行映像(最多 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=正在接收一批分帧 */ | |||
| /** @return 有效段数,钳制到 [0, PLSR_SEG_MAX](见 plsr_param.h) */ | |||
| /** @return 有效段数,软钳到 [0, PLSR_SEG_MAX] */ | |||
| uint16_t PlsrParamGetSegCount(void) | |||
| { | |||
| if (g_plsr_config.seg_count < 1U) | |||
| { | |||
| return 0U; | |||
| return 0U; /* 无有效段 */ | |||
| } | |||
| if (g_plsr_config.seg_count > PLSR_SEG_MAX) | |||
| { | |||
| return (uint16_t)PLSR_SEG_MAX; | |||
| return (uint16_t)PLSR_SEG_MAX; /* 超上限钳住 */ | |||
| } | |||
| return g_plsr_config.seg_count; | |||
| } | |||
| /** | |||
| * @brief 启动段 1-based(见 plsr_param.h) | |||
| * @note 非法起始段当前仅裁剪,TODO 可报故障码 | |||
| * @brief 启动段 1-based,软钳到 [1, seg_count] | |||
| */ | |||
| uint16_t PlsrParamGetStartSeg(void) | |||
| { | |||
| @@ -58,24 +48,19 @@ uint16_t PlsrParamGetStartSeg(void) | |||
| if (n < 1U) | |||
| { | |||
| return 1U; | |||
| return 1U; /* 无段时仍返回 1 */ | |||
| } | |||
| if (g_plsr_config.start_seg < 1U) | |||
| { | |||
| return 1U; | |||
| } | |||
| /* TODO:起始段非法时输出故障码 */ | |||
| if (g_plsr_config.start_seg > n) | |||
| { | |||
| return n; | |||
| return n; /* 超出则钳到末段 */ | |||
| } | |||
| return g_plsr_config.start_seg; | |||
| } | |||
| /*============================================================================*/ | |||
| /* 寄存器双字读写辅助 */ | |||
| /*============================================================================*/ | |||
| /** 读相邻两寄存器拼成无符号 32 位(低地址=低 16 位) */ | |||
| static uint32_t PlsrParamReadDWord(uint16_t addr) | |||
| { | |||
| @@ -84,15 +69,8 @@ static uint32_t PlsrParamReadDWord(uint16_t addr) | |||
| return ((uint32_t)high << 16) | (uint32_t)low; | |||
| } | |||
| /** 写无符号 32 位到相邻两寄存器 */ | |||
| static void PlsrParamWriteDWord(uint16_t addr, uint32_t value) | |||
| { | |||
| WriteHoldReg(addr, (uint16_t)(value & 0xFFFFU)); | |||
| WriteHoldReg((uint16_t)(addr + 1U), (uint16_t)((value >> 16) & 0xFFFFU)); | |||
| } | |||
| /** | |||
| * @brief 判断本次 FC10 是否为公共帧 2~5(双字速度/加减速) | |||
| * @brief 判断本次 FC10 是否为公共帧 2~5 | |||
| * @return 1=是;0=否 | |||
| */ | |||
| static uint8_t PlsrParamIsCommonTailFrameWrite(uint16_t start_addr, uint16_t quantity) | |||
| @@ -112,7 +90,7 @@ static uint8_t PlsrParamIsCommonTailFrameWrite(uint16_t start_addr, uint16_t qua | |||
| } | |||
| /** | |||
| * @brief 判断本次 FC10 是否为「整段 8 字写到某段基址」 | |||
| * @brief 判断本次 FC10 是否为段参数 | |||
| * @return 1=是段帧;0=否 | |||
| */ | |||
| static uint8_t PlsrParamIsSegFrameWrite(uint16_t start_addr, uint16_t quantity) | |||
| @@ -135,32 +113,21 @@ static uint8_t PlsrParamIsSegFrameWrite(uint16_t start_addr, uint16_t quantity) | |||
| return 0U; | |||
| } | |||
| /** | |||
| * @brief 更新分帧状态三寄存器,供上位机诊断 | |||
| * @param st PLSR_PARAM_XFER_* | |||
| * @param fail_frame 失败帧序号(成功时写 0) | |||
| */ | |||
| static void PlsrParamSetXferStatus(uint16_t st, uint16_t fail_frame) | |||
| { | |||
| WriteHoldReg(PLSR_PARAM_XFER_STATUS_REG, st); | |||
| WriteHoldReg(PLSR_PARAM_XFER_FAIL_FRAME_REG, fail_frame); | |||
| WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames); | |||
| } | |||
| /*============================================================================*/ | |||
| /* 参数校验辅助 */ | |||
| /* 参数校验辅助 */ | |||
| /*============================================================================*/ | |||
| #define PLSR_PARAM_SPD_MIN_HZ 1U | |||
| #define PLSR_PARAM_SPD_MAX_HZ 100000U | |||
| /** 速度 Hz 是否落在 [1, 100k](默认速度/段频非 0) */ | |||
| 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 合法; | |||
| * 非 0 须在 1~100kHz。 | |||
| */ | |||
| static uint8_t PlsrParamIsStartEndSpeedValid(uint32_t hz) | |||
| @@ -202,11 +169,11 @@ static uint8_t PlsrParamIsPulseCntValid(int32_t pulse_cnt) | |||
| /*============================================================================*/ | |||
| /** | |||
| * @brief 保持寄存器 → 运行映像,边拷贝边校验(见 plsr_param.h) | |||
| * @brief 保持寄存器 → 运行映像,边拷贝边校验 | |||
| * | |||
| * @details 校验失败时仍完成拷贝(保留上位机原始值),故障码写入 0x2006; | |||
| * 多项非法时仅保留校验过程中「最后一个」故障码。 | |||
| * @return 1=全部通过;0=存在门禁故障(不落 BKP) | |||
| * @details 校验失败时仍完成拷贝,故障码写入 0x2006; | |||
| * 多项非法时仅保留校验过程中最后一个故障码。 | |||
| * @return 1=全部通过;0=存在门禁故障(不落存BKP) | |||
| */ | |||
| uint8_t PlsrParamBlockApplyFromHold(void) | |||
| { | |||
| @@ -215,41 +182,41 @@ uint8_t PlsrParamBlockApplyFromHold(void) | |||
| uint16_t base; | |||
| uint16_t fault = PLSR_ERR_NONE; | |||
| g_plsr_config.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y); | |||
| g_plsr_config.pulse_y = ReadHoldReg(PLSR_REG_PULSE_Y); /* 脉冲端子 Y0~Y2 */ | |||
| if (g_plsr_config.pulse_y > 2U) | |||
| { | |||
| fault = PLSR_ERR_PULSE_Y; | |||
| fault = PLSR_ERR_PULSE_Y; /* 0x07:端子非法 */ | |||
| } | |||
| g_plsr_config.dir_y = ReadHoldReg(PLSR_REG_DIR_Y); | |||
| g_plsr_config.dir_y = ReadHoldReg(PLSR_REG_DIR_Y); /* 方向端子须为 Y3 */ | |||
| if (g_plsr_config.dir_y != 3U) | |||
| { | |||
| fault = PLSR_ERR_DIR_Y; | |||
| fault = PLSR_ERR_DIR_Y; /* 0x08 */ | |||
| } | |||
| 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.wait_x_sel = ReadHoldReg(PLSR_REG_WAIT_X) ? 1U : 0U; /* 0=X4,1=X5 */ | |||
| g_plsr_config.ext_x_sel = ReadHoldReg(PLSR_REG_EXT_X) ? 1U : 0U; /* 0=X4,1=X5 */ | |||
| 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_delay_ms = ReadHoldReg(PLSR_REG_DIR_DELAY); /* 换向延时 ms */ | |||
| g_plsr_config.dir_logic = (ReadHoldReg(PLSR_REG_DIR_LOGIC) != 0U) ? | |||
| PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS; | |||
| PLSR_DIR_LOGIC_NEG : PLSR_DIR_LOGIC_POS; /* 方向电平逻辑 */ | |||
| 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; | |||
| PLSR_POS_ABSOLUTE : PLSR_POS_RELATIVE; /* 绝对/相对 */ | |||
| n = ReadHoldReg(PLSR_REG_SEG_COUNT); | |||
| g_plsr_config.seg_count = n; | |||
| if ((n < 1U) || (n > PLSR_SEG_MAX)) | |||
| { | |||
| fault = PLSR_ERR_SEG_COUNT; | |||
| fault = PLSR_ERR_SEG_COUNT; /* 0x09:段数越界 */ | |||
| } | |||
| g_plsr_config.start_seg = ReadHoldReg(PLSR_REG_START_SEG); | |||
| g_plsr_config.start_seg = ReadHoldReg(PLSR_REG_START_SEG); /* 1-based 启动段 */ | |||
| if ((n >= 1U) && (n <= PLSR_SEG_MAX)) | |||
| { | |||
| if ((g_plsr_config.start_seg < 1U) || (g_plsr_config.start_seg > n)) | |||
| { | |||
| fault = PLSR_ERR_START_SEG; | |||
| fault = PLSR_ERR_START_SEG; /* 0x0A:启动段越界 */ | |||
| } | |||
| } | |||
| else if (g_plsr_config.start_seg < 1U) | |||
| @@ -260,123 +227,70 @@ uint8_t PlsrParamBlockApplyFromHold(void) | |||
| g_plsr_config.default_speed = PlsrParamReadDWord(PLSR_REG_DEFAULT_SPD_L); | |||
| if (PlsrParamIsSpeedHzValid(g_plsr_config.default_speed) == 0U) | |||
| { | |||
| fault = PLSR_ERR_DEFAULT_SPD; | |||
| fault = PLSR_ERR_DEFAULT_SPD; /* 0x0B:默认速度非法 */ | |||
| } | |||
| g_plsr_config.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L); | |||
| g_plsr_config.start_speed = PlsrParamReadDWord(PLSR_REG_START_SPD_L); /* 起跳频,0 合法 */ | |||
| if (PlsrParamIsStartEndSpeedValid(g_plsr_config.start_speed) == 0U) | |||
| { | |||
| fault = PLSR_ERR_START_END_SPD; | |||
| } | |||
| g_plsr_config.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L); | |||
| g_plsr_config.end_speed = PlsrParamReadDWord(PLSR_REG_END_SPD_L); /* 止速,0 合法 */ | |||
| if (PlsrParamIsStartEndSpeedValid(g_plsr_config.end_speed) == 0U) | |||
| { | |||
| fault = PLSR_ERR_START_END_SPD; | |||
| } | |||
| g_plsr_config.accel_ms = ReadHoldReg(PLSR_REG_ACCEL_MS); | |||
| g_plsr_config.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++) | |||
| { | |||
| base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE); | |||
| g_plsr_segments[i].freq_hz = | |||
| g_plsr_segs[i].freq_hz = | |||
| (int32_t)PlsrParamReadDWord((uint16_t)(base + PLSR_SEG_OFF_FREQ_L)); | |||
| if (PlsrParamIsSegFreqValid(g_plsr_segments[i].freq_hz) == 0U) | |||
| if (PlsrParamIsSegFreqValid(g_plsr_segs[i].freq_hz) == 0U) | |||
| { | |||
| fault = PLSR_ERR_FREQ_ILLEGAL; | |||
| fault = PLSR_ERR_FREQ_ILLEGAL; /* 0x04:段频率非法 */ | |||
| } | |||
| g_plsr_segments[i].pulse_cnt = | |||
| g_plsr_segs[i].pulse_cnt = | |||
| (int32_t)PlsrParamReadDWord((uint16_t)(base + PLSR_SEG_OFF_PULSE_L)); | |||
| if (PlsrParamIsPulseCntValid(g_plsr_segments[i].pulse_cnt) == 0U) | |||
| if (PlsrParamIsPulseCntValid(g_plsr_segs[i].pulse_cnt) == 0U) | |||
| { | |||
| fault = PLSR_ERR_PULSE_RANGE; | |||
| fault = PLSR_ERR_PULSE_RANGE; /* 0x05:脉冲数 INT_MIN */ | |||
| } | |||
| g_plsr_segments[i].wait_type = | |||
| g_plsr_segs[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)); | |||
| g_plsr_segs[i].wait_ms = | |||
| ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_WAIT_MS)); /* 段后等待 ms */ | |||
| g_plsr_segs[i].act_ms = | |||
| ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_ACT_MS)); /* ACT 定时 ms */ | |||
| g_plsr_segs[i].jump_seg = | |||
| ReadHoldReg((uint16_t)(base + PLSR_SEG_OFF_JUMP)); /* 跳转目标段 */ | |||
| } | |||
| } | |||
| PlsrCommandSetFault(fault); | |||
| PlsrCommandSetFault(fault); /* 写 0x2006 镜像 */ | |||
| if (fault == PLSR_ERR_NONE) | |||
| { | |||
| return 1U; | |||
| return 1U; /* 可落存 BKP */ | |||
| } | |||
| return 0U; | |||
| } | |||
| /** | |||
| * @brief 运行映像 → 全部相关保持寄存器(含未使用段槽位) | |||
| * @brief 统计接受帧数,收满释放信号量 | |||
| * | |||
| * @details 用途(与落 BKP 无关) | |||
| * 1. Apply 钳位后摊回 hold,使「读 PLC」与运行结构体一致; | |||
| * 2. ModbusDataInit() 清 hold 后,把已 Apply 的运行参数填回, | |||
| * 否则上位机读公共/段参数全 0,START 也会用空 hold 再次 Apply 冲掉结构体。 | |||
| * | |||
| * @note 0x100F 保留字写 0;段表写满 PLSR_SEG_MAX 槽,避免读回脏数据 | |||
| * @note 落 BKP 请用 PlsrPersistSaveParamsFromHold(读 hold 原值),不依赖本函数 | |||
| */ | |||
| void PlsrParamBlockExportToHold(void) | |||
| { | |||
| uint16_t i; | |||
| uint16_t base; | |||
| 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, 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++) | |||
| { | |||
| base = (uint16_t)(PLSR_REG_SEG1_BASE + i * PLSR_SEG_STRIDE); | |||
| 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); | |||
| } | |||
| } | |||
| /** | |||
| * @brief FC10 写后分帧状态机(见 plsr_param.h) | |||
| * | |||
| * 状态转移: | |||
| * 公共帧1@0x1000~0x100A → BUSY,expect=5+N,recv=1 | |||
| * 公共帧1@0x1000~0x100A,expect=5+N,recv=1 | |||
| * 传输中公共帧2~5 / 段帧 → recv++ | |||
| * recv>=expect → 清 active,Post 信号量(或同步 Apply) | |||
| * 其它写地址忽略(例如运行中只写频率双字不走本路径的分帧逻辑) | |||
| * recv>=expect → 清 active,Post 信号量 | |||
| * 运行中只写频率双字不走本路径 | |||
| */ | |||
| void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity) | |||
| { | |||
| @@ -385,70 +299,54 @@ void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity) | |||
| return; | |||
| } | |||
| /* | |||
| * 公共帧1:0x1000~0x100A(含段数)→ 开启传输,期望帧=5+段数。 | |||
| * 公共帧2~5 / 段帧:传输中且地址匹配 → 帧计数+1。 | |||
| * 收齐后 post 信号量,由 ParamBlockTask 执行 Apply。 | |||
| */ | |||
| if ((start_addr == PLSR_REG_COMMON_F1_BASE) && | |||
| (quantity >= PLSR_PARAM_COMMON_FRAME1_WORDS)) | |||
| { | |||
| /* 公共帧 1:开新一批,期望 5+段数 */ | |||
| uint16_t seg_n = ReadHoldReg(PLSR_REG_SEG_COUNT); | |||
| if (seg_n > PLSR_SEG_MAX) | |||
| { | |||
| seg_n = (uint16_t)PLSR_SEG_MAX; | |||
| seg_n = (uint16_t)PLSR_SEG_MAX; /* 计数软钳,避免 expect 溢出 */ | |||
| } | |||
| s_expect_frames = (uint16_t)(PLSR_PARAM_COMMON_FRAMES + seg_n); | |||
| s_recv_frames = 1U; | |||
| s_xfer_active = 1U; | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_BUSY, 0U); | |||
| s_recv_frames = 1U; /* 帧 1 已计入 */ | |||
| s_xfer_active = 1U; /* 开始收批 */ | |||
| } | |||
| else if ((s_xfer_active != 0U) && | |||
| ((PlsrParamIsCommonTailFrameWrite(start_addr, quantity) != 0U) || | |||
| (PlsrParamIsSegFrameWrite(start_addr, quantity) != 0U))) | |||
| { | |||
| s_recv_frames++; | |||
| WriteHoldReg(PLSR_PARAM_XFER_RECV_REG, s_recv_frames); | |||
| s_recv_frames++; /* 公共帧 2~5 或段帧 +1 */ | |||
| } | |||
| else | |||
| { | |||
| return; | |||
| return; /* 非本批帧 / 未开批 → 忽略 */ | |||
| } | |||
| if ((s_xfer_active != 0U) && (s_recv_frames >= s_expect_frames)) | |||
| { | |||
| s_xfer_active = 0U; | |||
| s_xfer_active = 0U; /* 收齐 */ | |||
| if (s_param_sem != (OS_EVENT *)0) | |||
| { | |||
| (void)OSSemPost(s_param_sem); | |||
| } | |||
| else if (PlsrParamBlockApplyFromHold() != 0U) | |||
| { | |||
| /* 信号量尚未创建时的兜底:同步 Apply */ | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U); | |||
| } | |||
| else | |||
| { | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_FAIL, s_recv_frames); | |||
| (void)OSSemPost(s_param_sem); /* 唤醒 ParamBlockTask */ | |||
| } | |||
| } | |||
| } | |||
| /** @brief 创建信号量并置传输空闲(见 plsr_param.h) */ | |||
| /** @brief 创建信号量并置清空接收计数 */ | |||
| void PlsrParamBlockInit(void) | |||
| { | |||
| s_param_sem = OSSemCreate(0U); | |||
| s_expect_frames = 0U; | |||
| s_recv_frames = 0U; | |||
| s_xfer_active = 0U; | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_IDLE, 0U); | |||
| } | |||
| /** | |||
| * @brief 参数块任务(见 plsr_param.h) | |||
| * @brief 参数块任务 | |||
| * | |||
| * for(;;):OSSemPend → ApplyFromHold(校验+写 0x2006)→ 通过则 SaveParamsFromHold。 | |||
| * 校验失败:不落 BKP,hold 保留上位机原值,由故障码提示修正。 | |||
| * OSSemPend → ApplyFromHold(校验+写 0x2006)→ 通过则 SaveParamsFromHold。 | |||
| * 校验失败:不存 BKP,hold 保留上位机原值,由故障码提示修正。 | |||
| */ | |||
| void PlsrParamBlockTask(void *pArg) | |||
| { | |||
| @@ -466,12 +364,7 @@ void PlsrParamBlockTask(void *pArg) | |||
| if (PlsrParamBlockApplyFromHold() != 0U) | |||
| { | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_OK, 0U); | |||
| PlsrPersistSaveParamsFromHold(); /* 仅全部校验通过才落 BKP */ | |||
| } | |||
| else | |||
| { | |||
| PlsrParamSetXferStatus(PLSR_PARAM_XFER_FAIL, s_recv_frames); | |||
| PlsrPersistSaveParamsFromHold(); /* 仅全部校验通过才存 BKP */ | |||
| } | |||
| } | |||
| } | |||
| @@ -4,7 +4,7 @@ | |||
| * | |||
| * @details 模块职责 | |||
| * 1. 定义公共参数区 0x1000~0x1013、段表区 0x1100+i*0x10 的地址与字段语义; | |||
| * 2. 维护 g_plsr_config / g_plsr_segments[](运动规划只读,不直接读寄存器); | |||
| * 2. 维护 g_plsr_config / g_plsr_segs[]; | |||
| * 3. 支持上位机「5 公共帧 + N 段帧」分帧 FC10 写入:收齐后 ApplyFromHold; | |||
| * 4. 上电由 PersistLoad 填 hold;ExportToHold 仅保留作调试接口。 | |||
| * | |||
| @@ -12,8 +12,8 @@ | |||
| * - modbus_rtu / modbus_data :ReadHoldReg / WriteHoldReg | |||
| * - plsr_command :启动前 ApplyFromHold;运行中修改当前段 freq_hz | |||
| * - plsr_run_control :GetCfg / GetSeg / GetSegCount / GetStartSeg | |||
| * - plsr_persist :Apply 成功后 SaveParamsFromHold;动态改频 SaveSegFreqToBkp | |||
| * - PlsrParamBlockTask :独立 uC/OS 任务,等信号量后 Apply + 整包落 BKP | |||
| * - plsr_persist :拷贝成功后存入BKP;动态改频存频率进BKP | |||
| * - PlsrParamBlockTask :独立 uC/OS 任务,等信号量后 Apply + 整包存 BKP | |||
| * | |||
| * 关键数据流 | |||
| * 上位机写公共帧1@0x1000 → OnHoldWrite 开传输 expect=5+N,recv=1 | |||
| @@ -23,7 +23,7 @@ | |||
| * | |||
| * 协议概要 | |||
| * - 公共 5 帧: | |||
| * 1: 0x1000~0x100A(11 字,含段数 N@0x1009) | |||
| * 1: 0x1000~0x100A(11 字) | |||
| * 2: 0x100B~0x100C(默认速度) | |||
| * 3: 0x100D~0x100E(起速) | |||
| * 4: 0x1010~0x1011(止速;跳过 0x100F) | |||
| @@ -59,7 +59,7 @@ | |||
| /* 0x100F 保留,写帧填 0 */ | |||
| #define PLSR_REG_END_SPD_L 0x1010U /* 止速 Hz */ | |||
| #define PLSR_REG_END_SPD_H 0x1011U | |||
| #define PLSR_REG_ACCEL_MS 0x1012U /* 默认速度加速时间 ms(定斜率) */ | |||
| #define PLSR_REG_ACCEL_MS 0x1012U /* 默认速度加速时间 ms */ | |||
| #define PLSR_REG_DECEL_MS 0x1013U /* 默认速度减速时间 ms */ | |||
| /* ---------- 公共参数分帧(5 帧 + N 段帧) ---------- */ | |||
| @@ -87,16 +87,6 @@ | |||
| #define PLSR_SEG_OFF_ACT_MS 6U /* ACT 时间 ms */ | |||
| #define PLSR_SEG_OFF_JUMP 7U /* 0=顺序/末段停;1~N=跳转目标段号 */ | |||
| /* ---------- 分帧传输状态(上位机可选读) ---------- */ | |||
| #define PLSR_PARAM_XFER_RECV_REG 0x0FFCU /* 已接收帧计数 */ | |||
| #define PLSR_PARAM_XFER_FAIL_FRAME_REG 0x0FFDU /* 失败时的帧序号 */ | |||
| #define PLSR_PARAM_XFER_STATUS_REG 0x0FFEU /* IDLE/BUSY/OK/FAIL */ | |||
| #define PLSR_PARAM_XFER_IDLE 0U | |||
| #define PLSR_PARAM_XFER_BUSY 1U | |||
| #define PLSR_PARAM_XFER_OK 2U | |||
| #define PLSR_PARAM_XFER_FAIL 3U | |||
| /** 方向逻辑:正转时方向脚电平 */ | |||
| typedef enum { | |||
| PLSR_DIR_LOGIC_POS = 0, /* 正转→方向高,反转→低 */ | |||
| @@ -113,7 +103,7 @@ typedef enum { | |||
| PLSR_SEND_FOLLOW = 1 | |||
| } PlsrSendMode_e; | |||
| /** 加减速曲线形状(脉冲域规划) */ | |||
| /** 加减速曲线形状 */ | |||
| typedef enum { | |||
| PLSR_ACCEL_LINEAR = 0, /* f^2 = f0^2 ± 2an */ | |||
| PLSR_ACCEL_S = 1, /* 时间域七段 S:加速度梯形 1:2:1 */ | |||
| @@ -131,7 +121,7 @@ typedef enum { | |||
| } PlsrPosMode_e; | |||
| /** | |||
| * 段末/段内等待类型(由 run_control + signal_io 执行) | |||
| * 段末/段内等待类型 | |||
| * TIME/SIGNAL/ACT/EXT:完成/后续均规划到止速;并阻止 FOLLOW 不停表衔接 | |||
| * EXT_OR_DONE:与下一段同向时后续可接目标/完成匀速跑完并可 FOLLOW 衔接; | |||
| * 与下一段反向时规划同其它等待(减速到止速,下一段从起速再爬) | |||
| @@ -145,8 +135,7 @@ typedef enum { | |||
| } PlsrWaitType_e; | |||
| /** | |||
| * 公共运行参数映像(运动侧只读) | |||
| * 斜率:a ≈ default_speed * 1000 / accel_ms(Hz/s) | |||
| * 公共运行参数映像 | |||
| */ | |||
| typedef struct { | |||
| uint16_t pulse_y; /* 0=Y0/TIM10 1=Y1/TIM13 2=Y2/TIM11 */ | |||
| @@ -178,17 +167,8 @@ typedef struct { | |||
| } PlsrSeg_t; | |||
| /* ---------- 运行参数 ---------- */ | |||
| /* | |||
| * 所有 PLSR 模块都直接读取这两份数据。 | |||
| * g_plsr_config :公共参数,只有一份。 | |||
| * g_plsr_segments[i] :第 i+1 段参数,i 的范围是 0~9。 | |||
| * | |||
| * 参数任务负责从保持寄存器更新它们;运动过程中其它模块只读取, | |||
| * 只有“运行中改频”会修改当前段的 freq_hz。 | |||
| */ | |||
| extern PlsrCfg_t g_plsr_config; | |||
| extern PlsrSeg_t g_plsr_segments[PLSR_SEG_MAX]; | |||
| extern PlsrSeg_t g_plsr_segs[PLSR_SEG_MAX]; | |||
| /** @return 有效段数,钳制到 [0, PLSR_SEG_MAX] */ | |||
| uint16_t PlsrParamGetSegCount(void); | |||
| @@ -210,7 +190,6 @@ void PlsrParamBlockInit(void); | |||
| /** | |||
| * @brief 参数块任务:阻塞等信号量 → ApplyFromHold(校验)→ 通过则 SaveParamsFromHold | |||
| * @param pArg 未使用 | |||
| * @note for(;;) 死循环为 RTOS 任务常态;空循环体是错觉(花括号内有 Pend/Apply) | |||
| */ | |||
| void PlsrParamBlockTask(void *pArg); | |||
| @@ -218,20 +197,14 @@ void PlsrParamBlockTask(void *pArg); | |||
| * @brief FC10 写保持寄存器后由 Modbus 回调 | |||
| * @param start_addr 起始地址 | |||
| * @param quantity 字数 | |||
| * @note 公共帧开传输;段帧累加;收齐 post 信号量(或无信号量时同步 Apply) | |||
| * @note 公共帧开传输;段帧累加;收齐 post 信号量 | |||
| */ | |||
| void PlsrParamBlockOnHoldWrite(uint16_t start_addr, uint16_t quantity); | |||
| /** | |||
| * @brief 保持寄存器 → g_plsr_config / g_plsr_segments,边拷贝边校验 | |||
| * @return 1=全部通过(写 0x2006=0);0=存在门禁故障(写对应码,不落 BKP) | |||
| * @brief 保持寄存器 → g_plsr_config / g_plsr_segs,边拷贝边校验 | |||
| * @return 1=全部通过;0=存在门禁故障 | |||
| */ | |||
| uint8_t PlsrParamBlockApplyFromHold(void); | |||
| /** | |||
| * @brief g_plsr_config / g_plsr_segments → 保持寄存器(调试/特殊用途) | |||
| * @note 上电参数由 BKP→hold(PersistLoad);非法参数保留 hold 原值供上位机读回修正 | |||
| */ | |||
| void PlsrParamBlockExportToHold(void); | |||
| #endif | |||
| @@ -2,12 +2,7 @@ | |||
| * @file plsr.c | |||
| * @brief PLSR 顶层初始化和周期任务 | |||
| * | |||
| * @details 模块职责 | |||
| * 本文件只做两件事:按顺序初始化各模块,以及运行 PlsrTask 周期任务。 | |||
| * 启停和中断函数直接实现在 run_control.c,这里不再做重复转发。 | |||
| * | |||
| * 与其它模块关系 | |||
| * 本文件仅依赖 plsr.h 及各子模块头文件,不被其它 plsr 模块反向引用。 | |||
| * @details 按顺序初始化各模块,以及运行 PlsrTask 周期任务。 | |||
| * | |||
| * 关键数据流 | |||
| * PlsrInit 顺序:PersistLoad(BKP→hold) → ApplyFromHold(hold→结构体) → … | |||
| @@ -24,15 +19,15 @@ | |||
| #include "ucos_ii.h" | |||
| /** | |||
| * @brief 子系统初始化(见 plsr.h) | |||
| * @brief 子系统初始化 | |||
| * | |||
| * 顺序要点 | |||
| * PersistLoad → Apply:BKP → hold → 结构体(BKP 内参数应已合法); | |||
| * PersistLoad → Apply:BKP → hold → 结构体(BKP 内参数已合法); | |||
| * 非上电不再把结构体 Export 回 hold;Modbus 清零后由 BKP 再填 hold。 | |||
| */ | |||
| void PlsrInit(void) | |||
| { | |||
| PlsrPersistInit(); /* 使能 BKPSRAM 写访问,否则落盘无效 */ | |||
| PlsrPersistInit(); /* 使能 BKPSRAM 写访问,否则存BKP无效 */ | |||
| PlsrPersistLoad(); /* BKPSRAM → g_hold_reg */ | |||
| (void)PlsrParamBlockApplyFromHold(); /* hold → 运行参数 + 校验 */ | |||
| PlsrPulseDriverInit(); | |||
| @@ -55,7 +50,7 @@ void PlsrInit(void) | |||
| */ | |||
| void PlsrTask(void *pArg) | |||
| { | |||
| static uint8_t s_booted = 0U; | |||
| static uint8_t s_booted = 0U; /* 1=已完成首次延迟对齐(等 Modbus 填 hold) */ | |||
| (void)pArg; | |||
| @@ -76,9 +71,9 @@ void PlsrTask(void *pArg) | |||
| s_booted = 1U; | |||
| } | |||
| PlsrRunControlTickMs(); /* 优先:WAIT/换向到期立刻开表 */ | |||
| PlsrRunControlTickMs(); /* 处理延时/外部信号输入,优先:WAIT/换向到期立刻开表 */ | |||
| PlsrSignalIoDebouncePoll(); /* OS 节拍消抖确认 */ | |||
| PlsrCommandPoll(); /* 收 START/STOP/CLR,回写 0x2000~0x2006 监控 */ | |||
| PlsrCommandPoll(); /* 收 START/STOP/CLR,回写 0x2000~0x2006 监控 ,决定是否开pwm输出*/ | |||
| PlsrPersistTickMonitor(); /* 监控区:运行态→hold→BKP(每 OS 节拍) */ | |||
| /* | |||
| @@ -25,32 +25,19 @@ | |||
| #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; | |||
| extern volatile uint8_t g_plsr_busy; /* 运行忙标志(上位机监控) */ | |||
| extern volatile uint16_t g_plsr_cur_seg_idx; /* 当前段下标(0-based) */ | |||
| extern volatile int32_t g_plsr_accumulated_pulses; /* 全局累计脉冲(有符号) */ | |||
| #include "plsr_param.h" | |||
| /* | |||
| * PLSR_LOOP_TEST_EN:调试宏。 | |||
| * 1 = 第 1 段跑完后空闲约 1s 自动再启(仅联调);正式产品保持 0。 | |||
| */ | |||
| #ifndef PLSR_LOOP_TEST_EN | |||
| #define PLSR_LOOP_TEST_EN 0 | |||
| #endif | |||
| /** | |||
| * @brief 子系统一次性初始化 | |||
| * @note 调用时机:OS 启动后、Modbus 从站就绪前,由 main 或启动任务调用一次 | |||
| * @note 副作用:加载 Flash 公共参数、初始化 TIM/GPIO、导出默认映像到保持寄存器 | |||
| * @brief plsr任务初始化 | |||
| */ | |||
| void PlsrInit(void); | |||
| /** | |||
| * @brief uC/OS-II 周期任务入口 | |||
| * @param pArg 任务参数(未使用,传 NULL) | |||
| * @note 调用时机:创建后永久循环;WAIT/ACT 到期由信号量立刻唤醒,否则最多 1 个 OS tick | |||
| * @note 副作用:轮询 Modbus 控制位、刷新监控寄存器、推进运行控制状态机、 | |||
| * 空闲时触发 Flash 持久化;首次进入延迟 ~300ms 再导出参数(避免 Modbus 清零冲掉) | |||
| * @param pArg 任务参数 | |||
| */ | |||
| void PlsrTask(void *pArg); | |||
| @@ -35,10 +35,10 @@ 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; | |||
| extern volatile uint32_t g_plsr_output_freq_hz; /* 当前输出频率 Hz */ | |||
| extern volatile uint8_t g_plsr_pwm_running; /* 1=PWM 正在跑 */ | |||
| extern uint8_t g_plsr_direction_forward; /* 1=正转方向语义 */ | |||
| extern uint8_t g_plsr_direction_valid; /* 1=方向脚已有效写出 */ | |||
| /** | |||
| * @brief 初始化三路脉冲 TIM + 方向 GPIO | |||
| @@ -1,12 +1,6 @@ | |||
| /** | |||
| * @file plsr_run_control.h | |||
| * @brief 运行控制:多段脉冲状态机(开段 → 发脉冲 → 等待/跳转 → 收尾) | |||
| * | |||
| * @details 解析发送模式、等待类型与段间频率衔接;协调 accel_curve / | |||
| * pulse_driver / signal_io / param。状态:IDLE / DIR_WAIT / RUN / WAIT_COND。 | |||
| * | |||
| * 与 accel_curve 对齐的符号: | |||
| * f_tgt — 段表目标频率 Hz;segment_pulses_done/target — 已发/目标脉冲数 | |||
| */ | |||
| #ifndef PLSR_RUN_CONTROL_H | |||
| #define PLSR_RUN_CONTROL_H | |||
| @@ -8,7 +8,7 @@ | |||
| * 下降沿触发 OnFalling:仅 pending=1,不在 ISR 里判有效(避免抖动/毛刺)。 | |||
| * | |||
| * 【消抖】 | |||
| * DebouncePoll(任务上下文,约 1ms 一次): | |||
| * DebouncePoll(任务上下文,约 10ms 一次): | |||
| * pending 且首次 → 记 arm_tick; | |||
| * 满 PLSR_SIGNAL_DEBOUNCE_MS(20ms)且 PinIsLow → fell=1。 | |||
| * 上升沿恢复高电平则 Confirm 末尾不置 fell(pending 被清掉)。 | |||
| @@ -32,7 +32,7 @@ | |||
| #include "main.h" | |||
| #include "ucos_ii.h" | |||
| /* ---------- 硬件引脚(与 CubeMX / 方案书一致) ---------- */ | |||
| /* ---------- 硬件引脚 ---------- */ | |||
| #define PLSR_X4_PORT GPIOB | |||
| #define PLSR_X4_PIN GPIO_PIN_5 /* X4:WAIT/EXT 可选 */ | |||
| #define PLSR_X5_PORT GPIOG | |||
| @@ -40,7 +40,7 @@ | |||
| /* | |||
| * TIM5 时基:84MHz / (8399+1) = 10kHz → 100µs/格。 | |||
| * PSC=8399 落在 16 位内;ms 级延时用 100µs 分辨率足够,不必 1µs 时基。 | |||
| * PSC=8399 落在 16 位内 | |||
| */ | |||
| #define PLSR_TIM5_PSC_100US 8399U | |||
| #define PLSR_TIM5_TICKS_PER_MS 10U /* 10×100µs = 1ms */ | |||
| @@ -59,8 +59,8 @@ typedef struct { | |||
| uint16_t pin; | |||
| } PlsrEdge_t; | |||
| static PlsrEdge_t s_db_x4; /* X4 消抖状态 */ | |||
| static PlsrEdge_t s_db_x5; /* X5 消抖状态 */ | |||
| static PlsrEdge_t s_debounce_x4; /* X4 消抖状态 */ | |||
| static PlsrEdge_t s_debounce_x5; /* X5 消抖状态 */ | |||
| static volatile uint8_t s_delay_armed; /* 1=TIM5 单次延时正在计时 */ | |||
| /** 毫秒 → OS tick 数(向上取整,至少 1 tick) */ | |||
| @@ -81,49 +81,49 @@ static INT32U PlsrSignalIoMsToTicks(uint32_t ms) | |||
| } | |||
| /** @return 1=脚当前为低(有效按下/接地) */ | |||
| static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *db) | |||
| static uint8_t PlsrSignalIoPinIsLow(const PlsrEdge_t *debounce) | |||
| { | |||
| return (HAL_GPIO_ReadPin(db->port, db->pin) == GPIO_PIN_RESET) ? 1U : 0U; | |||
| return (HAL_GPIO_ReadPin(debounce->port, debounce->pin) == GPIO_PIN_RESET) ? 1U : 0U; | |||
| } | |||
| /** EXTI 下降沿:仅置 pending,消抖在 DebouncePoll 完成 */ | |||
| static void PlsrSignalIoOnFalling(PlsrEdge_t *db) | |||
| /** EXTI 下降沿:置 pending,重置计时标志消抖在 DebouncePoll 完成 */ | |||
| static void PlsrSignalIoOnFalling(PlsrEdge_t *debounce) | |||
| { | |||
| db->pending = 1U; | |||
| db->stamp_ok = 0U; | |||
| debounce->pending = 1U; | |||
| debounce->stamp_ok = 0U; | |||
| } | |||
| /** | |||
| * @brief 单路消抖确认(DebouncePoll 每 tick 调用) | |||
| * @note 满 debounce_ms 仍低 → fell;否则丢弃 pending | |||
| */ | |||
| static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *db) | |||
| static void PlsrSignalIoDebounceConfirm(PlsrEdge_t *debounce) | |||
| { | |||
| INT32U need; | |||
| if (db->pending == 0U) | |||
| if (debounce->pending == 0U) | |||
| { | |||
| return; | |||
| } | |||
| if (db->stamp_ok == 0U) | |||
| if (debounce->stamp_ok == 0U) | |||
| { | |||
| db->arm_tick = OSTimeGet(); | |||
| db->stamp_ok = 1U; | |||
| debounce->arm_tick = OSTimeGet(); | |||
| debounce->stamp_ok = 1U; | |||
| return; | |||
| } | |||
| need = PlsrSignalIoMsToTicks(PLSR_SIGNAL_DEBOUNCE_MS); | |||
| if ((OSTimeGet() - db->arm_tick) < need) | |||
| if ((OSTimeGet() - debounce->arm_tick) < need) | |||
| { | |||
| return; | |||
| } | |||
| db->pending = 0U; | |||
| db->stamp_ok = 0U; | |||
| if (PlsrSignalIoPinIsLow(db) != 0U) | |||
| debounce->pending = 0U; | |||
| debounce->stamp_ok = 0U; | |||
| if (PlsrSignalIoPinIsLow(debounce) != 0U) | |||
| { | |||
| db->fell = 1U; | |||
| debounce->fell = 1U; | |||
| } | |||
| } | |||
| @@ -164,48 +164,50 @@ static void PlsrSignalIoTim5Init(void) | |||
| { | |||
| __HAL_RCC_TIM5_CLK_ENABLE(); | |||
| TIM5->CR1 = 0U; | |||
| TIM5->DIER = 0U; | |||
| TIM5->SR = 0U; | |||
| TIM5->PSC = PLSR_TIM5_PSC_100US; | |||
| TIM5->ARR = 0xFFFFFFFFUL; | |||
| TIM5->CR1 = 0U; /* 停表:向上计、关 OPM */ | |||
| TIM5->DIER = 0U; /* 关 UPDATE 中断 */ | |||
| TIM5->SR = 0U; /* 清状态标志 */ | |||
| TIM5->PSC = PLSR_TIM5_PSC_100US; /* PSC=8399 → 10kHz(100µs/格),非 1MHz */ | |||
| TIM5->ARR = 0xFFFFFFFFUL; /* 预装最大 ARR,真正延时在 Schedule 重写 */ | |||
| TIM5->CNT = 0U; | |||
| TIM5->EGR = TIM_EGR_UG; | |||
| TIM5->SR = 0U; | |||
| TIM5->EGR = TIM_EGR_UG; /* UG:PSC 立刻生效(会置 UIF) */ | |||
| TIM5->SR = 0U; /* 清 UG 带来的 UIF */ | |||
| HAL_NVIC_SetPriority(TIM5_IRQn, 7, 0); | |||
| HAL_NVIC_EnableIRQ(TIM5_IRQn); | |||
| } | |||
| /** @brief 信号 IO 初始化:消抖状态 + EXTI + TIM5 */ | |||
| void PlsrSignalIoInit(void) | |||
| { | |||
| s_delay_armed = 0U; | |||
| s_db_x4.port = PLSR_X4_PORT; | |||
| s_db_x4.pin = PLSR_X4_PIN; | |||
| s_db_x4.pending = 0U; | |||
| s_db_x4.stamp_ok = 0U; | |||
| s_db_x4.arm_tick = 0U; | |||
| s_db_x4.fell = 0U; | |||
| s_debounce_x4.port = PLSR_X4_PORT; | |||
| s_debounce_x4.pin = PLSR_X4_PIN; | |||
| s_debounce_x4.pending = 0U; | |||
| s_debounce_x4.stamp_ok = 0U; | |||
| s_debounce_x4.arm_tick = 0U; | |||
| s_debounce_x4.fell = 0U; | |||
| s_db_x5.port = PLSR_X5_PORT; | |||
| s_db_x5.pin = PLSR_X5_PIN; | |||
| s_db_x5.pending = 0U; | |||
| s_db_x5.stamp_ok = 0U; | |||
| s_db_x5.arm_tick = 0U; | |||
| s_db_x5.fell = 0U; | |||
| s_debounce_x5.port = PLSR_X5_PORT; | |||
| s_debounce_x5.pin = PLSR_X5_PIN; | |||
| s_debounce_x5.pending = 0U; | |||
| s_debounce_x5.stamp_ok = 0U; | |||
| s_debounce_x5.arm_tick = 0U; | |||
| s_debounce_x5.fell = 0U; | |||
| PlsrSignalIoGpioExtiInit(); | |||
| PlsrSignalIoTim5Init(); | |||
| /* 配完 NVIC 后再清一次,避免配置期误沿 */ | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X4_PIN); | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X5_PIN); | |||
| s_db_x4.fell = 0U; | |||
| s_db_x4.pending = 0U; | |||
| s_db_x5.fell = 0U; | |||
| s_db_x5.pending = 0U; | |||
| s_debounce_x4.fell = 0U; | |||
| s_debounce_x4.pending = 0U; | |||
| s_debounce_x5.fell = 0U; | |||
| s_debounce_x5.pending = 0U; | |||
| } | |||
| /** @brief 装 TIM5 单次延时(ms),OPM 到期进 OnTim5Irq */ | |||
| void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) | |||
| { | |||
| uint32_t ticks; | |||
| @@ -220,22 +222,23 @@ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms) | |||
| } | |||
| /* N ms → N×10 个 100µs 格;OPM 计满一次 UPDATE 即到期 */ | |||
| ticks = delay_ms * PLSR_TIM5_TICKS_PER_MS;//乘10得到us | |||
| TIM5->CR1 = 0U;//控制寄存器 1 清零 | |||
| TIM5->DIER = 0U;//DMA / 中断使能寄存器清零 | |||
| TIM5->SR = 0U;//状态寄存器清零 | |||
| TIM5->PSC = PLSR_TIM5_PSC_100US;//8399得到10KHZ即100us | |||
| TIM5->ARR = ticks - 1U;//自动重装载寄存器 | |||
| TIM5->CR1 = TIM_CR1_URS;//仅更新事件产生更新中断 | |||
| TIM5->EGR = TIM_EGR_UG;//软件生成更新事件写 UG=1:立刻把CNT寄存器加载 ARR 影子寄存器的值;PSC 预分频值生效 | |||
| TIM5->SR = 0U;//再次清除状态寄存器,清除 UG 带来的各种标志。 | |||
| TIM5->CNT = 0U;//计数器清零,从 0 开始计数。 | |||
| TIM5->DIER = TIM_DIER_UIE;//使能更新中断。 | |||
| ticks = delay_ms * PLSR_TIM5_TICKS_PER_MS; | |||
| TIM5->CR1 = 0U; | |||
| TIM5->DIER = 0U; | |||
| TIM5->SR = 0U; | |||
| TIM5->PSC = PLSR_TIM5_PSC_100US; | |||
| TIM5->ARR = ticks - 1U; | |||
| TIM5->CR1 = TIM_CR1_URS; /* 仅溢出产生 UPDATE */ | |||
| TIM5->EGR = TIM_EGR_UG; /* PSC/ARR 立刻生效 */ | |||
| TIM5->SR = 0U; /* 清 UG 带来的 UIF */ | |||
| TIM5->CNT = 0U; | |||
| TIM5->DIER = TIM_DIER_UIE; | |||
| s_delay_armed = 1U; | |||
| TIM5->CR1 = (uint32_t)(TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS); | |||
| TIM5->CR1 = (uint32_t)(TIM_CR1_CEN | TIM_CR1_OPM | TIM_CR1_URS); /* 开表单次 */ | |||
| } | |||
| /** @brief 取消 TIM5 延时并清武装 */ | |||
| void PlsrSignalIoCancelDelay(void) | |||
| { | |||
| TIM5->CR1 = 0U; | |||
| @@ -244,6 +247,7 @@ void PlsrSignalIoCancelDelay(void) | |||
| s_delay_armed = 0U; | |||
| } | |||
| /** @brief 查询 TIM5 剩余延时 ms(未武装或已停返回 0) */ | |||
| uint32_t PlsrSignalIoDelayRemainMs(void) | |||
| { | |||
| uint32_t arr; | |||
| @@ -266,71 +270,79 @@ uint32_t PlsrSignalIoDelayRemainMs(void) | |||
| return 0U; | |||
| } | |||
| left_ticks = (arr + 1U) - cnt; | |||
| /*会有个小bug。假定计数剩余个数655个,则要计数65.5ms, | |||
| 该函数会向下取整,导致返回65ms。再次进来计数时就会误差0.5ms。理论最大误差1ms。*/ | |||
| return left_ticks / PLSR_TIM5_TICKS_PER_MS; | |||
| } | |||
| /** @brief OS 节拍消抖:确认 X4/X5 有效下降沿 */ | |||
| void PlsrSignalIoDebouncePoll(void) | |||
| { | |||
| PlsrSignalIoDebounceConfirm(&s_db_x4); | |||
| PlsrSignalIoDebounceConfirm(&s_db_x5); | |||
| PlsrSignalIoDebounceConfirm(&s_debounce_x4); | |||
| PlsrSignalIoDebounceConfirm(&s_debounce_x5); | |||
| } | |||
| /** @brief 丢弃两侧沿与 pending(EXT 段开跑前) */ | |||
| void PlsrSignalIoClearEdges(void) | |||
| { | |||
| s_db_x4.fell = 0U; | |||
| s_db_x4.pending = 0U; | |||
| s_db_x4.stamp_ok = 0U; | |||
| s_db_x5.fell = 0U; | |||
| s_db_x5.pending = 0U; | |||
| s_db_x5.stamp_ok = 0U; | |||
| s_debounce_x4.fell = 0U; | |||
| s_debounce_x4.pending = 0U; | |||
| s_debounce_x4.stamp_ok = 0U; | |||
| s_debounce_x5.fell = 0U; | |||
| s_debounce_x5.pending = 0U; | |||
| s_debounce_x5.stamp_ok = 0U; | |||
| } | |||
| /** @brief 仅清 pending,保留已确认 fell(纯 EXT 段末) */ | |||
| void PlsrSignalIoClearPending(void) | |||
| { | |||
| s_db_x4.pending = 0U; | |||
| s_db_x4.stamp_ok = 0U; | |||
| s_db_x5.pending = 0U; | |||
| s_db_x5.stamp_ok = 0U; | |||
| s_debounce_x4.pending = 0U; | |||
| s_debounce_x4.stamp_ok = 0U; | |||
| s_debounce_x5.pending = 0U; | |||
| s_debounce_x5.stamp_ok = 0U; | |||
| } | |||
| /** @brief 取 WAIT 口有效下降沿(读后清) */ | |||
| uint8_t PlsrSignalIoTakeWaitFalling(void) | |||
| { | |||
| /* wait_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */ | |||
| /* wait_x_sel:0=X4,1=X5 */ | |||
| if (g_plsr_config.wait_x_sel == 0U) | |||
| { | |||
| if (s_db_x4.fell != 0U) | |||
| if (s_debounce_x4.fell != 0U) | |||
| { | |||
| s_db_x4.fell = 0U; | |||
| s_debounce_x4.fell = 0U; | |||
| return 1U; | |||
| } | |||
| } | |||
| else if (s_db_x5.fell != 0U) | |||
| else if (s_debounce_x5.fell != 0U) | |||
| { | |||
| s_db_x5.fell = 0U; | |||
| s_debounce_x5.fell = 0U; | |||
| return 1U; | |||
| } | |||
| return 0U; | |||
| } | |||
| /** @brief 取 EXT 口有效下降沿(读后清) */ | |||
| uint8_t PlsrSignalIoTakeExtFalling(void) | |||
| { | |||
| /* ext_x_sel:0 代表 X4,1 代表 X5。读取后立即清掉该下降沿。 */ | |||
| /* ext_x_sel:0=X4,1=X5 */ | |||
| if (g_plsr_config.ext_x_sel == 0U) | |||
| { | |||
| if (s_db_x4.fell != 0U) | |||
| if (s_debounce_x4.fell != 0U) | |||
| { | |||
| s_db_x4.fell = 0U; | |||
| s_debounce_x4.fell = 0U; | |||
| return 1U; | |||
| } | |||
| } | |||
| else if (s_db_x5.fell != 0U) | |||
| else if (s_debounce_x5.fell != 0U) | |||
| { | |||
| s_db_x5.fell = 0U; | |||
| s_debounce_x5.fell = 0U; | |||
| return 1U; | |||
| } | |||
| return 0U; | |||
| } | |||
| /** @brief TIM5 UPDATE:到期回调 run_control */ | |||
| void PlsrSignalIoOnTim5Irq(void) | |||
| { | |||
| if ((TIM5->SR & TIM_SR_UIF) != 0U) | |||
| @@ -341,25 +353,27 @@ void PlsrSignalIoOnTim5Irq(void) | |||
| if (s_delay_armed != 0U) | |||
| { | |||
| s_delay_armed = 0U; | |||
| PlsrRunControlOnDelayTimer(); | |||
| PlsrRunControlOnDelayTimer(); /* 方向/WAIT/ACT 到期 */ | |||
| } | |||
| } | |||
| } | |||
| /** @brief EXTI9_5:X4 下降沿 → pending */ | |||
| void PlsrSignalIoOnExti9_5(void) | |||
| { | |||
| if (__HAL_GPIO_EXTI_GET_IT(PLSR_X4_PIN) != 0U) | |||
| { | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X4_PIN); | |||
| PlsrSignalIoOnFalling(&s_db_x4); | |||
| PlsrSignalIoOnFalling(&s_debounce_x4); | |||
| } | |||
| } | |||
| /** @brief EXTI15_10:X5 下降沿 → pending */ | |||
| void PlsrSignalIoOnExti15_10(void) | |||
| { | |||
| if (__HAL_GPIO_EXTI_GET_IT(PLSR_X5_PIN) != 0U) | |||
| { | |||
| __HAL_GPIO_EXTI_CLEAR_IT(PLSR_X5_PIN); | |||
| PlsrSignalIoOnFalling(&s_db_x5); | |||
| PlsrSignalIoOnFalling(&s_debounce_x5); | |||
| } | |||
| } | |||
| @@ -14,26 +14,11 @@ | |||
| * ext_x_sel (0x1003) → 0=X4,1=X5,供 EXT 切段(PLSR_WAIT_EXT / EXT_OR_DONE) | |||
| * WAIT 与 EXT 可独立选口,共用同一套消抖状态机。 | |||
| * | |||
| * 消抖状态机(每路 PlsrEdge_t) | |||
| * pending=1 ← EXTI 下降沿 | |||
| * DebouncePoll:首次见 pending 记 arm_tick;满 20ms 且脚仍低 → fell=1 | |||
| * Take*Falling 消费 fell;ClearPending 只清 pending/stamp,保留已确认 fell | |||
| * | |||
| * TIM5 单次延时用途(run_control 调用) | |||
| * TIM5 单次延时用途 | |||
| * - 方向切换延时 dir_delay_ms(RC_DIR_WAIT → Start) | |||
| * - WAIT 时间 PLSR_WAIT_TIME 段后等待 | |||
| * - ACT 时间 PLSR_WAIT_ACT 段内定时(到期 OPM 收当前拍再切段) | |||
| * - ACT 脉冲先完但定时未到:DelayRemainMs 续装剩余 ms | |||
| * | |||
| * 与其它模块关系 | |||
| * - plsr_param :wait_x_sel / ext_x_sel | |||
| * - plsr_run_control :TakeWait/ExtFalling、Schedule/CancelDelay、OnDelayTimer | |||
| * - plsr.c :PlsrTask 每 OS 节拍 DebouncePoll | |||
| * - stm32f4xx_it :TIM5 / EXTI9_5 / EXTI15_10 转发至本模块 IRQ 入口 | |||
| * | |||
| * 线程/中断约定 | |||
| * fell/pending 由 ISR 写、任务读;Take* 仅在任务上下文消费。 | |||
| * ScheduleDelayMs / CancelDelay 由任务调用;OnTim5Irq 在 TIM5 中断内回调 run_control。 | |||
| */ | |||
| #ifndef PLSR_SIGNAL_IO_H | |||
| #define PLSR_SIGNAL_IO_H | |||
| @@ -42,13 +27,11 @@ | |||
| /** | |||
| * @brief 初始化 X4/X5 EXTI 消抖状态与 TIM5(不启动延时) | |||
| * @note 调用时机:PlsrInit,在 RunControl 之前 | |||
| * @note 副作用:清 pending/fell;TIM5 停表、OPM 单次模式预配置 | |||
| */ | |||
| void PlsrSignalIoInit(void); | |||
| /** | |||
| * @brief 武装 TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer | |||
| * @brief TIM5 单次延时(ms),到期 OnTim5Irq → PlsrRunControlOnDelayTimer | |||
| * @param delay_ms 0 按 1ms;上限 65535ms | |||
| * @note 重入会先 Cancel 再重装;OPM 计满自动停表 | |||
| */ | |||
| @@ -58,7 +41,7 @@ void PlsrSignalIoScheduleDelayMs(uint32_t delay_ms); | |||
| void PlsrSignalIoCancelDelay(void); | |||
| /** | |||
| * @brief 读 TIM5 单次延时剩余 ms(未武装或已停表返回 0;向下取整) | |||
| * @brief 读 TIM5 单次延时剩余 ms | |||
| * @note ACT 脉冲先完、定时仍走时,用于续装剩余 ACT 时间 | |||
| */ | |||
| uint32_t PlsrSignalIoDelayRemainMs(void); | |||