9 Revīzijas

Autors SHA1 Ziņojums Datums
  hanyongwei dd37a5345c 清理旧查表残留 pirms 2 nedēļām
  hanyongwei 62b49681a3 修改为定点数Q24,采用查形状表+线性插值 pirms 2 nedēļām
  hanyongwei 92742fffc1 增加了注释 pirms 2 nedēļām
  hanyongwei 5bd7cb9c53 删掉二分降峰后重复判断逻辑 pirms 2 nedēļām
  hanyongwei 1d799e89d5 补充了部分注释 pirms 2 nedēļām
  hanyongwei 27140c2ddb 修改部分过长的变量命名 pirms 2 nedēļām
  hanyongwei b86e343c92 清理冗余设计3 pirms 2 nedēļām
  hanyongwei ad2a6c7d76 清理部分冗余设计2 pirms 2 nedēļām
  hanyongwei 5deb2ab693 清理部分冗余设计 pirms 2 nedēļām
18 mainītis faili ar 4803 papildinājumiem un 4567 dzēšanām
Dalītais skats
  1. +1
    -5
      .gitignore
  2. +10
    -9
      app/main.c
  3. +1673
    -966
      iar/plsr.dep
  4. +10
    -8
      modbus/modbus_rtu.c
  5. +1207
    -1346
      plsr/accel_curve/plsr_accel_curve.c
  6. +36
    -47
      plsr/accel_curve/plsr_accel_curve.h
  7. +52
    -61
      plsr/command/plsr_command.c
  8. +17
    -35
      plsr/command/plsr_command.h
  9. +70
    -177
      plsr/param/plsr_param.c
  10. +12
    -39
      plsr/param/plsr_param.h
  11. +7
    -12
      plsr/plsr.c
  12. +5
    -18
      plsr/plsr.h
  13. +317
    -308
      plsr/pulse_driver/plsr_pulse_driver.c
  14. +4
    -4
      plsr/pulse_driver/plsr_pulse_driver.h
  15. +1282
    -1423
      plsr/run_control/plsr_run_control.c
  16. +0
    -6
      plsr/run_control/plsr_run_control.h
  17. +97
    -83
      plsr/signal_io/plsr_signal_io.c
  18. +3
    -20
      plsr/signal_io/plsr_signal_io.h

+ 1
- 5
.gitignore Parādīt failu

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

+ 10
- 9
app/main.c Parādīt failu

@@ -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 仅供读回钳位结果。


+ 1673
- 966
iar/plsr.dep
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 10
- 8
modbus/modbus_rtu.c Parādīt failu

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



+ 1207
- 1346
plsr/accel_curve/plsr_accel_curve.c
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 36
- 47
plsr/accel_curve/plsr_accel_curve.h Parādīt failu

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

+ 52
- 61
plsr/command/plsr_command.c Parādīt failu

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

+ 17
- 35
plsr/command/plsr_command.h Parādīt failu

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



+ 70
- 177
plsr/param/plsr_param.c Parādīt failu

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

+ 12
- 39
plsr/param/plsr_param.h Parādīt failu

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

+ 7
- 12
plsr/plsr.c Parādīt failu

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

/*


+ 5
- 18
plsr/plsr.h Parādīt failu

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



+ 317
- 308
plsr/pulse_driver/plsr_pulse_driver.c
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 4
- 4
plsr/pulse_driver/plsr_pulse_driver.h Parādīt failu

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


+ 1282
- 1423
plsr/run_control/plsr_run_control.c
Failā izmaiņas netiks attēlotas, jo tās ir par lielu
Parādīt failu


+ 0
- 6
plsr/run_control/plsr_run_control.h Parādīt failu

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


+ 97
- 83
plsr/signal_io/plsr_signal_io.c Parādīt failu

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

+ 3
- 20
plsr/signal_io/plsr_signal_io.h Parādīt failu

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


Notiek ielāde…
Atcelt
Saglabāt