嵌入式功率控制体系:加热 PWM、DC 电机驱动与统一限幅框架
关键词:STM32F103、IR2104 H-Bridge、NMOS 继电器、软件 PWM、packer_power_limit、FreeRTOS、功率管理框架
flowchart TB subgraph TASK[packer_heater_task_once] T1[读取目标温度] T2{PID 计算} T2 -->|需加热| T3[翻转 GPIO] T2 -->|关闭| T4[GPIO 低] end subgraph PWM[软件 PWM] P1[100ms 周期] P2[占空比调节] end TASK --> PWM -->|TIM5 CH1 已禁用| GPIO[加热继电器] style TASK fill:transparent,stroke:#8dc7ff,color:#eaf4ff style PWM fill:transparent,stroke:#8dc7ff,color:#eaf4ff
1. 背景:为什么需要统一功率控制?
在 DEVICE_CO 的 INDL_CONTROLLER 系列工业控制器中,一块控制板上同时存在多种功率负载:大功率阻性加热器、两台有刷直流电机、散热风扇、料斗输送电机等。早期版本中,每个负载的启动和关闭是分散处理的——上层应用直接调用 GPIO 操作或继电器驱动。这种架构在功能简单时足够,但一旦需要引入过流保护、功率统计和动态限流时,问题立刻暴露:没有一个统一的入口点可以拦截和控制所有功率操作。
加热器使用电磁继电器(非固态继电器,无硬件 PWM 能力),最初尝试用 TIM5 CH1 硬件 PWM 直接驱动,结果继电器机械吸合/释放时间远超 PWM 载波周期,导致触点抖动、电弧、寿命急剧缩短,并产生严重的 EMI 干扰。
DC 电机方面,系统使用 IR2104 半桥驱动芯片驱动 4 个 NMOS 构成全 H 桥,需要在 BSP 层精确控制启动时序、停止时序、方向切换。每类负载有自己的硬件特性和安全约束,但又共享同一电源总线——因此需要一个统一的功率管理框架来协调。
这就是本文要阐述的三层体系:加热器软件 PWM、DC 电机 BSP 驱动、统一功率限幅框架 packer_power_limit。
2. 硬件基础
2.1 负载总览
整个系统包含 6 类功率负载,每类通过独立硬件通道控制:
| 负载类型 | 数量 | 驱动方式 | GPIO / 定时器 |
|---|---|---|---|
| DC 电机 1 (MOTOR_1) | 1 | IR2104 H-Bridge (TIM1 CH1/CH1N) | INH/INL/SD + 互补 PWM |
| DC 电机 2 (MOTOR_2) | 1 | IR2104 H-Bridge (TIM8 CH1/CH1N) | INH/INL/SD + 互补 PWM |
| 加热器 (Heater) | 1 | NMOS 继电器 (PA0) | GPIO 电平翻转 |
| 散热风扇 (Fan) | 1 | NMOS 继电器 (PA1) | GPIO 电平翻转 |
| 料斗输送 (Hopper Conveyor) | 1 | NMOS 继电器 (PA6) | GPIO 电平翻转 |
| 备用继电器 (Spare) | 1 | NMOS 继电器 (PB1) | GPIO 电平翻转 |
2.2 GPIO 映射
所有 GPIO 来自 STM32CubeMX 生成的 main.h 宏定义,BSP 层只引用宏、不硬编码引脚值:
/* 继电器 GPIO 资源表(静态表驱动) */
static const relay_gpio_t s_relay_gpios[RELAY_COUNT] = {
[RELAY_HEATER] = { .port = GPIOA, .pin = GPIO_PIN_0 },
[RELAY_FAN] = { .port = GPIOA, .pin = GPIO_PIN_1 },
[RELAY_HOPPER] = { .port = GPIOA, .pin = GPIO_PIN_6 },
[RELAY_SPARE] = { .port = GPIOB, .pin = GPIO_PIN_1 },
};
2.3 DC 电机 IR2104 H-Bridge 硬件拓扑
每路电机采用 IR2104 半桥驱动 + 4 个 NMOS 构成全 H 桥。IR2104 使用 3 线控制:INH(高侧输入)、INL(低侧输入)、SD(关断/使能)。STM32 输出经 IR2104 转换为高侧/低侧栅极信号。TIM1/TIM8 输出互补 PWM(含死区插入)。
| 信号 | 电机 1 | 电机 2 | 定时器 |
|---|---|---|---|
| PWM (CH1/CH1N) | TIM1 CH1 / CH1N | TIM8 CH1 / CH1N | 互补输出 + 死区 |
| INH (GPIO) | GPIO 输出 | GPIO 输出 | 高侧逻辑输入 |
| INL (GPIO) | GPIO 输出 | GPIO 输出 | 低侧逻辑输入 |
| SD (GPIO) | GPIO 输出 | GPIO 输出 | 关断信号,低电平有效 |
3. 加热器软件 PWM —— 继电器 + 100ms 窗口翻转
3.1 为什么不用硬件 PWM
加热器执行机构为一枚普通电磁继电器。早期版本错误地使用 TIM5 CH1 硬件 PWM 输出模式直接驱动——继电器触点的机械动作时间约为 5~10ms,远高于 PWM 载波周期(~1ms),高频开关只会导致触点抖动、电蚀、电磁干扰,最终烧毁继电器和保温层。
正确方案:废除 TIM5 的 PWM 配置,改为 GPIO 电平翻转 + 软件窗口计时,以 100ms 为基准窗口做 ON/OFF 分配。这才是本文所谓"软件 PWM"的本质。
3.2 基本参数
| 参数 | 值 | 说明 |
|---|---|---|
| 基准周期 | 100ms(可运行时配置) | 软件窗口总时长 |
| 最小 ON 宽度 | 1ms | 零占空比保护 |
| 占空比上限 | 200‰(20.0%) | 硬编码安全限幅——不可逾越 |
| 任务周期 | 10ms | HeaterTask 专用任务 |
3.3 HeaterTask 独立任务
加热器控制由一个独立的 FreeRTOS 任务 HeaterTask 负责,运行周期固定为 10ms。该任务不与执行机构(actuator)混合在同一循环中,保证了加热控制的实时性不受其他逻辑拖累。
同期在同一任务中还处理了 散热风扇控制,通过跨任务标志位 indl_controller_heater_fan_request() 驱动。
3.4 窗口化 PWM 计算算法
核心思想:将一个完整的 PWM 周期(例如 100ms)等分为 10ms 窗口,在每个窗口决定是否输出 ON。使用累加器进行滚动判断,无浮点运算,适合嵌入式环境:
static uint8_t compute_target_output(uint16_t duty_per_mille,
uint16_t period_ms)
{
uint16_t on_window_ms;
static uint16_t acc_window_ms = 0;
/* 限幅:不超过 200‰ */
if (duty_per_mille > HEATER_MAX_DUTY_PER_MILLE) {
duty_per_mille = HEATER_MAX_DUTY_PER_MILLE;
}
/* 计算 ON 时间(ms),最小 1ms 保护 */
on_window_ms = (period_ms * duty_per_mille) / 1000;
if (on_window_ms == 0 && duty_per_mille > 0) {
on_window_ms = 1;
}
/* 窗口滚动判断 */
if (acc_window_ms < on_window_ms) {
acc_window_ms += HEATER_TASK_PERIOD_MS; /* 10ms */
return 1; /* ON */
} else {
acc_window_ms += HEATER_TASK_PERIOD_MS;
if (acc_window_ms >= period_ms) {
acc_window_ms = 0; /* 周期复位 */
}
return 0; /* OFF */
}
}
3.5 20% 安全限幅的来历
RCFG_ID_HEATER_DUTY_PER_MILLE 的取值范围为 0~200,超过 200 的值会被 硬编码限幅 到 200(即 20.0%)。该限幅来自一次真实现场事故——因配置错误导致 100% 占空比持续加热,继电器粘连、保温层烧穿、机台起火。此后所有产品强制上限为 20.0%。
安全限幅必须硬编码,不可由运行时参数覆盖。一次真实起火事故让整个产品线加上了
HEATER_MAX_DUTY_PER_MILLE这个不可逾越的上限。
3.6 跨任务标志与临界区
风扇请求、强制加热等信号来自其他任务(如通信任务、状态机任务),通过 volatile 全局标志 + 临界区保护 传入 HeaterTask:
/* 跨任务标志定义 */
static volatile uint8_t heater_fan_request;
static volatile uint8_t heater_force_on;
/* 临界区访问 */
void indl_controller_heater_fan_request(void)
{
taskENTER_CRITICAL();
heater_fan_request = 1;
taskEXIT_CRITICAL();
}
uint8_t indl_controller_heater_fan_consume(void)
{
uint8_t ret;
taskENTER_CRITICAL();
ret = heater_fan_request;
heater_fan_request = 0;
taskEXIT_CRITICAL();
return ret;
}
3.7 强制加热与预热逻辑
工业流程中某些步骤需要在开始前将温度快速提升到目标值(预热),此时 PWM 算法的渐近调功不适用。为此设计了跨任务强制加热机制,可在任意任务中调用 indl_controller_heater_force_on_now(),立即全功率输出,无需等待 PWM 周期对齐。
3.8 功率限制门控
继电器输出受 功率限制门控 控制。当系统处于功率限制模式(如电源过载、紧急降功率),即使 PWM 计算结果为 ON,实际也不允许吸合继电器:
static void write_relay(uint8_t target_on)
{
/* 功率限制门控 */
if (indl_controller_power_limit_request_relay_read()) {
GPIO_ResetBits(HEATER_RELAY_GPIO_PORT, HEATER_RELAY_PIN);
return;
}
/* 系统状态检查:ERROR / RESET 时自动关闭 */
if (sys_state == SYS_STATE_ERROR ||
sys_state == SYS_STATE_RESET) {
GPIO_ResetBits(HEATER_RELAY_GPIO_PORT, HEATER_RELAY_PIN);
return;
}
if (target_on) {
GPIO_SetBits(HEATER_RELAY_GPIO_PORT, HEATER_RELAY_PIN);
} else {
GPIO_ResetBits(HEATER_RELAY_GPIO_PORT, HEATER_RELAY_PIN);
}
}
4. DC 电机 BSP 驱动 —— IR2104 H-Bridge + NMOS Relay
4.1 IR2104 前级反相特性(最容易踩坑的地方)
IR2104 的输入逻辑是 反相的:MCU 输出 HIGH 到 IR2104 的 INH/INL 引脚时,IR2104 实际输出 LOW 给 MOS 管栅极(即关断)。反之,MCU 输出 LOW 时,IR2104 才输出 HIGH 开启 MOS 管。
关键推论:当 MCU 将 INH 和 INL 都置为 HIGH 时,IR2104 两侧均输出 LOW → H 桥上下管全部关闭 → 安全状态。不仅如此,这种状态还允许自举电容充电,为下次 PWM 启动做准备。这就是 "Pre-stage HIGH is safe + bootstrap charge" 的设计原则。
4.2 停止时序 (Stop Sequence)
正确的停止顺序分为三步:
1. 停止 PWM 输出 → TIM_Cmd(TIMx, DISABLE)
2. 拉低 SD 引脚 → GPIO_ResetBits(SD_PORT, SD_PIN)
3. 将 INH 和 INL 均置 HIGH → 进入安全状态 + 自举充电
为什么先停 PWM 再拉 SD? 如果先拉 SD 再停 PWM,在 SD 拉低到 PWM 停止的间隙内,IR2104 可能处于不确定状态,导致 MOS 管直通(shoot-through)。严格时序:PWM 停止 → SD 拉低 → 强制 INH/INL HIGH。
4.3 启动时序 (Start Sequence)
正转 (Forward):
1. INL 强制 HIGH(MCU) = IR2104 INL LOW → 低侧 MOS 关断
2. INH 输出 PWM 波形 → IR2104 INH 跟随 PWM
3. SD 置 HIGH(使能) → 启动 TIM CH1N 互补通道
→ 电流路径:电源 → 高侧 MOS → 电机 → 低侧 MOS (通过另一侧半桥)
反转 (Reverse):
1. INH 强制 HIGH(MCU) = IR2104 INH LOW → 高侧 MOS 关断
2. INL 输出 PWM 波形 → IR2104 INL 跟随 PWM
3. SD 置 HIGH(使能) → 启动 TIM CH1 主通道
→ 电流路径反向
4.4 PWM 占空比限制 —— 96% 上限
IR2104 使用自举电容为上管驱动供电。当占空比接近 100% 时,自举电容没有足够的充电时间,导致上管驱动电压不足而关断。实际限制:PWM 占空比上限为 96%,下限取决于开关频率和死区。在驱动层通过 __HAL_TIM_SET_COMPARE() 之前对占空比进行钳位。
任何时候都不应输出 0% 或 100% 的占空比。0% 意味着长期无 PWM 翻转,自举电容放电殆尽;100% 意味着无低侧导通时间,无法充电。
4.5 方向切换 —— Stop-Then-Start
方向切换 不允许直接反转。必须执行完整的 Stop Sequence → 等待安全状态 → 再执行新方向的 Start Sequence:
bsp_dc_motor_stop(motor_id);
delay_ms(5); /* 等待 MOS 完全关断 + 自举充电 */
bsp_dc_motor_start(motor_id, direction, speed);
这 5ms 的延迟窗口确保了 H 桥不会出现瞬间直通。
4.6 继电器重复写入抑制
继电器写入函数实现了 重复写入抑制 机制,使用 8-bit 位掩码 s_relay_active_mask 实时记录各继电器当前状态,重复写入时直接返回 OK:
static int relay_write_checked(uint8_t relay_id, uint8_t state)
{
/* 重复抑制:检查当前状态是否已经是目标状态 */
uint8_t current = (s_relay_active_mask >> relay_id) & 0x01U;
if (current == state)
return BSP_DC_MOTOR_OK; /* 跳过 GPIO 写入 */
/* 更新屏蔽字并写入 GPIO */
if (state) {
s_relay_active_mask |= (1U << relay_id);
GPIO_SetBits(s_relay_gpios[relay_id].port, s_relay_gpios[relay_id].pin);
} else {
s_relay_active_mask &= ~(1U << relay_id);
GPIO_ResetBits(s_relay_gpios[relay_id].port, s_relay_gpios[relay_id].pin);
}
return BSP_DC_MOTOR_OK;
}
4.7 停止策略:两种 Stop API
| API | 停止内容 | 继电器状态 | 使用场景 |
|---|---|---|---|
stop_all() |
所有电机 + 所有继电器 | 全部关闭 | 紧急停止、系统复位 |
stop_all_motors() |
仅停止电机 | 保持当前状态 | 工艺暂停、轻故障恢复 |
5. 统一功率限幅框架 —— packer_power_limit
5.1 框架定位
packer_power_limit 框架的核心思路:给所有功率操作加一个统一的前置评估层。所有电机启动和继电器写入都必须经过 packer_power_limit_request_* 系列函数,没有任何旁路路径。
框架定义了 6 种负载类型,每种独立跟踪功率:
typedef enum {
LOAD_DC_MOTOR1 = 0,
LOAD_DC_MOTOR2 = 1,
LOAD_HEATER = 2,
LOAD_FAN = 3,
LOAD_HOPPER_CONVEYOR = 4,
LOAD_RELAY4 = 5,
LOAD_TYPE_COUNT = 6
} load_type_t;
5.2 核心数据流:measure → predict → latch → limit
框架采用四段式流水线架构:
ADC 采样 → 滑动平均滤波 → 实测功率更新
│
预测功率 = 当前值 + 新负载增量
(饱和加法)
│
┌──────────────────────────┐
│ 是否超过动态限值? │
│ (measured > dynamic_limit)│
└─────────────┬────────────┘
│
是 ────┴──── 否
┌─────────┐ ┌─────────┐
│ 触发锁存 │ │ 正常通过 │
│ latched=1│ │ latch不变│
└─────────┘ └─────────┘
5.3 测量链路
- ADC 通道:ADC1_IN4(PA4 引脚),分流电阻上的压降 → 差分放大 → ADC 采样
- 4 点滑动平均滤波器:ADC 读数经过 4 点滑动平均滤波去噪。4 点的选择是典型的实时性与平滑度的折中——太长会延迟过流响应,太短则噪声抑制不足
- 饱和安全加法:功率预计算使用饱和加法,避免意外溢出导致错误判断:
static uint32_t sat_add(uint32_t a, uint32_t b) { uint32_t sum = a + b; if (sum < a) sum = UINT32_MAX; return sum; }
5.4 锁存机制与迟滞
锁存(latch)一旦触发,不会在功率回落到阈值以下时立即释放。它使用迟滞(hysteresis):
- 触发阈值:
dynamic_limit_mw(动态限值) - 释放阈值:
dynamic_limit_mw × 0.8(80% 回退) - 锁定标记:
load_power_state_t.latched - 只有滤波后的功率低于释放阈值时,锁存才会清除
这种设计防止了在阈值边界处反复触发/释放的"抖动"问题,是工业控制中标准做法。
5.5 动态限值:按流程类型切换
不同工作流程有不同的功率预算:
typedef enum {
FLOW_TYPE_BAG = 0, /* 灌装流程 */
FLOW_TYPE_SELF_CHECK = 1, /* 自检流程 */
FLOW_TYPE_SEAL = 2, /* 封口流程 */
FLOW_TYPE_RESET = 3, /* 复位流程 */
FLOW_TYPE_COUNT = 4
} flow_type_t;
static uint32_t get_flow_limit_mw(flow_type_t flow) {
static const uint32_t limits[FLOW_TYPE_COUNT] = {
[FLOW_TYPE_BAG] = 500000, /* 500W */
[FLOW_TYPE_SELF_CHECK] = 500000, /* 500W */
[FLOW_TYPE_SEAL] = 500000, /* 500W */
[FLOW_TYPE_RESET] = 500000, /* 500W */
};
return limits[flow];
}
5.6 "阈值全部设为最大值,当前表现为直通模式"——先搭骨架,再校准
这是整个框架最令人玩味的地方。当前配置中:
| 参数 | 当前值 | 备注 |
|---|---|---|
| 各流程动态限值 | 500W | 远超实际负载功率(直通模式) |
| 各负载 profile 值 | 100W | 仅为占位符 |
| 告警触发 | 已定义但从不触发 | APP_ALARM_POWER_OVER_LIMIT (0x0E) |
这种"先上线、后校准"的策略在工业项目中很常见,原因如下:
- 功能完整性优先:先确保评估引擎、锁存机制、日志记录等所有功能链路完整跑通
- 避免回归风险:阈值设得太小会意外触发保护,干扰功能测试
- 数据驱动的校准:先用直通模式跑一段时间,收集实际功率数据,再据此设定合理阈值
- 渐进式部署:第一阶段做计量(accounting),第二阶段做警告(warning),第三阶段做硬限流(hard limit)
5.7 决策快照 —— 为审计做准备
框架为每次请求生成了完整的决策快照(decision snapshot):
typedef struct {
load_type_t load;
flow_type_t flow;
uint32_t predicted_power_mw;
uint32_t current_limit_mw;
bool would_exceed;
bool latched;
uint32_t timestamp_ms;
} power_decision_snapshot_t;
这意味着即使现在是直通,未来某天阈值调小后,可以回查历史记录,分析哪些操作"本应被阻止"——设计上已经做好了审计准备。
6. 集成:加热器、DC 电机、继电器如何统一经过 power_limit
6.1 分层架构
整个功率控制体系分为三个清晰的层次:
┌─────────────────────────────────────────────────────┐
│ Application Layer │
│ (bag filling / self-check / seal / reset) │
└────────────┬───────────────────────────────┬─────────┘
│ start_motor_X() │ relay_write()
▼ ▼
┌──────────────────────────────────────────────────────┐
│ packer_power_limit_request_*() │ ← 统一入口
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ Measurement │→ │ Limit │→ │ Policy │ │
│ │ (ADC filter) │ │ Eval │ │ Latch │ │
│ └──────────────┘ └─────────────┘ └───────────┘ │
└──────────────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────────────────────────┐
│ BSP Layer (bsp_dc_motor) │
│ GPIO / PWM / Relay / Motor Driver Operations │
└──────────────────────────────────────────────────────┘
关键设计点:所有电机启动和继电器写入都必须经过 packer_power_limit_request_* 系列函数,BSP 层只负责"能否执行",不关心"应该执行多少功率"。
6.2 集成流程示例
以加热器 PWM 驱动为例,完整的调用链:
/* HeaterTask 每 10ms 执行一次 */
void heater_task_once(void)
{
/* 1. 强制加热? */
if (indl_controller_heater_force_on_consume()) {
/* 通过 power_limit 检查 */
if (packer_power_limit_request_relay_write(
LOAD_HEATER, current_flow, HEATER_PROFILE_MW) == POWER_LIMIT_OK) {
write_relay(1);
}
return;
}
/* 2. 读取运行时参数 → 窗口 PWM 计算 */
uint16_t duty = rcfg_read_u16(RCFG_ID_HEATER_DUTY_PER_MILLE);
uint16_t period = rcfg_read_u16(RCFG_ID_HEATER_PWM_PERIOD_MS);
uint8_t target = compute_target_output(duty, period);
/* 3. 通过 power_limit 检查后写继电器 */
if (target) {
if (packer_power_limit_request_relay_write(
LOAD_HEATER, current_flow, HEATER_PROFILE_MW) == POWER_LIMIT_OK) {
write_relay(1);
} else {
write_relay(0);
}
} else {
write_relay(0);
}
}
DC 电机同理:
/* 启动电机前必须先经过 power_limit */
packer_power_limit_result_t result;
result = packer_power_limit_request_start_motor(
LOAD_DC_MOTOR1, current_flow, MOTOR1_PROFILE_MW);
if (result == POWER_LIMIT_OK) {
bsp_dc_motor_start(MOTOR_1, DIR_FORWARD, speed);
} else {
/* 被限流阻止,记录告警 */
app_alarm_trigger(APP_ALARM_POWER_OVER_LIMIT);
}
6.3 BSP 层的纯正性
功率限制逻辑在 BSP 层之外实现,位于上层的 packer_actuator / packer_heater 模块。这种设计保持了 BSP 层的纯正性——BSP 只提供原子化的硬件控制原语(start / stop / set_duty),上层策略层组合这些原语实现功率管理、安全联锁、工艺时序。
7. 设计经验与教训
| 决策 | 方案 | 理由 |
|---|---|---|
| 加热器控制方式 | GPIO 翻转(非硬件 PWM) | 继电器机械特性不适用高频 PWM |
| 加热 PWM 实现 | 窗口累加算法 | 简单可靠,无浮点运算 |
| 最大占空比(加热器) | 硬编码 200‰ | 防止配置错误导致的起火事故 |
| DC 电机方向切换 | Stop-Then-Start | 防止 H 桥直通(shoot-through) |
| PWM 占空比(电机) | 96% 上限 | IR2104 自举电容充电需求 |
| 功率评估策略 | 先直通、后校准 | 功能完整性优先,避免回归风险 |
| 锁存释放 | 80% 回退迟滞 | 防止阈值边界抖动 |
| 跨任务通信 | volatile + 临界区 | 轻量级,适用于标志类信号 |
| 功率门控 | 独立函数层 | 统一处理所有输出抑制场景 |
| 最小 ON 时间 | 1ms | 避免零占空比导致的除零异常 |
| 继电器写入 | 重复写入抑制 | 减少 GPIO 总线访问,延长继电器寿命 |
7.1 教训一:不要因为 MCU 有硬件 PWM 就想当然地用它驱动继电器
继电器是机电元件,不是功率 MOSFET。100ms 级别的软件翻转才是正确的打开方式。
7.2 教训二:安全限幅必须硬编码
安全限幅必须硬编码,不可由运行时参数覆盖。一次真实起火事故让整个产品线加上了 HEATER_MAX_DUTY_PER_MILLE 这个不可逾越的上限。
7.3 教训三:IR2104 的前级反相特性必须在 BSP 层显式建模
"Pre-stage HIGH is safe + bootstrap charge" 是设计原则。初始化、停止、方向切换前的过渡状态,均保持 INH = INL = HIGH。在任何代码审查中,只要看到 INH 或 INL 直接接地而没有经过逻辑转换,就需要打上红色标记。
7.4 教训四:框架的价值在于为未来铺好轨道
packer_power_limit 框架的真正价值不在于它现在保护了什么,而在于它为未来的保护铺好了所有轨道。当实际负载数据收集完成、阈值校准后,只需修改几个 #define 就能从"计量"切换到"保护"模式——而代价是零代码重构。
7.5 预热阶段的价值
预热阶段的强制加热 API 是流程控制的关键接口。在批量生产中,预热时间每优化 1 秒都有商业价值——但前提是安全限幅不能被 bypass。
8. 结语
本文介绍的三层功率控制体系已在 DEVICE_CO 的 INDL_CONTROLLER 系列产品上稳定运行多个版本。从加热器用 100ms 窗口做正确的事(继电器 ON/OFF),到 DC 电机用 IR2104 严格时序确保 H 桥安全,再到 packer_power_limit 用"先搭骨架、再校准"的架构给所有功率操作加上统一的前置评估层——这套体系经过了真实现场故障的检验:
- 硬件 PWM 误用 → 起火事故 → 限幅硬编码 → 跨任务预热接口
- IR2104 前级反相踩坑 → Pre-stage HIGH 安全原则 → Stop-Then-Start 方向切换
- 功率管理分散 → packer_power_limit 统一入口 → 直通模式收集数据 → 未来校准
核心思路可以概括为一句话:用正确的时机做正确的事,用硬编码的安全上限兜住失控的配置,用分层架构隔离关注点。
—— DEVICE_CO 嵌入式团队,技术博客系列 · 功率控制专题
Comments NOTHING