4. 软件过流保护策略:ADC采样、数字滤波、阈值判断、故障标志位处理
过流保护这件事,说简单也简单——电流大了就跳闸。但在电机驱动这种实时系统里,怎么跳、什么时候跳、跳了之后怎么恢复,这里面的门道可不少。我做了这么多年驱动器,见过太多因为过流保护没处理好而炸管子的案例。
这一章,我们就来聊聊软件过流保护的完整链路:从ADC采样到数字滤波,再到阈值判断和故障标志位处理。每一步都有坑,我会把我在项目中踩过的坑都告诉你。
4.1 过流保护的整体架构
先看一张整体流程图,让你对软件过流保护有个全局认识。
这个流程看起来简单,但每个环节都有讲究。我们一个一个来看。
4.2 ADC采样:时机比精度更重要
ADC采样,说白了就是把模拟电流信号变成数字量。但什么时候采,比用什么分辨率采更重要。
我见过不少新手,上来就选16位ADC,觉得精度越高越好。结果呢?采样时刻不对,采到的全是开关噪声,再高的精度也白搭。
4.2.1 采样时机的选择
在SVPWM调制中,电流采样通常有两种方式:
- 单电阻采样:在PWM周期的中点采样,此时电流纹波最小
- 双电阻/三电阻采样:在PWM周期的开始和结束时刻采样
我个人习惯用三电阻采样,配合PWM中心对齐模式。采样点选在PWM计数器的峰值和谷值时刻,这时候开关管已经稳定,噪声最小。
4.2.2 ADC配置要点
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 分辨率 | 12位 | 对于电机驱动,12位足够,更高分辨率反而增加转换时间 |
| 采样时间 | ≥500ns | 保证采样电容充分充电,尤其是大电流时 |
| 触发方式 | 硬件触发(PWM事件) | 避免软件触发带来的抖动 |
| 转换模式 | 连续扫描 | 三相电流同时采样,减少相位差 |
4.3 数字滤波:别让噪声骗了你
ADC采到的原始数据,直接拿去判断过流?千万别这么干。你想想看,电机运行时,电流上叠加的开关噪声、尖峰干扰,随便一个毛刺就能让阈值判断误触发。
数字滤波的作用,就是把真实电流信号从噪声中提取出来。常用的方法有几种:
4.3.1 滑动平均滤波
这是最基础的方法,把最近N次采样值取平均。N一般取4~16。
// 滑动平均滤波示例
#define FILTER_LEN 8
uint16_t filter_buffer[FILTER_LEN];
uint8_t filter_index = 0;
uint32_t filter_sum = 0;
uint16_t moving_average_filter(uint16_t adc_value)
{
filter_sum -= filter_buffer[filter_index];
filter_buffer[filter_index] = adc_value;
filter_sum += adc_value;
filter_index = (filter_index + 1) % FILTER_LEN;
return (uint16_t)(filter_sum / FILTER_LEN);
}
这个方法简单,但有个问题:N越大,延迟越大。对于过流保护,延迟超过几个PWM周期就可能烧管子。
4.3.2 一阶低通滤波
我更喜欢用一阶低通滤波,计算量小,延迟可控。
// 一阶低通滤波
#define ALPHA 0.3f // 滤波系数,范围0~1
float low_pass_filter(float input, float last_output)
{
return ALPHA * input + (1.0f - ALPHA) * last_output;
}
ALPHA值越大,滤波效果越弱,响应越快;ALPHA值越小,滤波效果越强,但延迟越大。我一般取0.2~0.4之间。
4.3.3 中值滤波
对于偶发的尖峰干扰,中值滤波效果最好。取连续3~5次采样的中间值,能有效剔除异常点。
// 3点中值滤波
uint16_t median_filter_3(uint16_t a, uint16_t b, uint16_t c)
{
if (a > b) { uint16_t t = a; a = b; b = t; }
if (b > c) { uint16_t t = b; b = c; c = t; }
if (a > b) { uint16_t t = a; a = b; b = t; }
return b;
}
4.4 阈值判断:分级保护更可靠
阈值判断不是简单的「超过就跳」。实际项目中,我习惯用分级保护策略:
| 保护等级 | 阈值 | 动作 | 恢复方式 |
|---|---|---|---|
| 一级(警告) | 额定电流×1.2 | 记录日志,降低PWM占空比 | 自动恢复 |
| 二级(软过流) | 额定电流×1.5 | 立即封锁PWM输出 | 延时后自动尝试恢复 |
| 三级(硬过流) | 额定电流×2.0 | 立即封锁PWM,触发硬件保护 | 需手动复位 |
为什么要分级?因为有些过流是瞬时的,比如电机启动瞬间的冲击电流。如果一过流就锁死,系统根本没法正常工作。
4.4.1 阈值判断的防抖处理
即使加了滤波,阈值判断时也要做防抖。我的做法是:连续N次超过阈值,才判定为过流。
// 过流判断防抖
#define OVERCURRENT_THRESHOLD 3000 // 12位ADC值
#define DEBOUNCE_COUNT 3 // 连续3次判定
uint8_t overcurrent_debounce(uint16_t filtered_current)
{
static uint8_t count = 0;
if (filtered_current > OVERCURRENT_THRESHOLD)
{
count++;
if (count >= DEBOUNCE_COUNT)
{
count = DEBOUNCE_COUNT; // 防止溢出
return 1; // 确认过流
}
}
else
{
if (count > 0) count--;
}
return 0;
}
4.5 故障标志位处理:状态机思维
故障标志位不是简单的0或1。我建议用状态机来管理故障状态,这样逻辑更清晰。
4.5.1 故障状态机
typedef enum {
FAULT_NONE = 0,
FAULT_WARNING = 1,
FAULT_SOFT_OC = 2,
FAULT_HARD_OC = 3,
FAULT_LATCHED = 4 // 锁存状态,需手动复位
} fault_state_t;
fault_state_t current_fault_state = FAULT_NONE;
void fault_state_machine(void)
{
switch (current_fault_state)
{
case FAULT_NONE:
if (check_hard_oc()) current_fault_state = FAULT_HARD_OC;
else if (check_soft_oc()) current_fault_state = FAULT_SOFT_OC;
else if (check_warning()) current_fault_state = FAULT_WARNING;
break;
case FAULT_WARNING:
// 降低功率,尝试恢复
reduce_pwm_duty();
if (!check_warning()) current_fault_state = FAULT_NONE;
break;
case FAULT_SOFT_OC:
// 封锁PWM,延时后尝试恢复
pwm_disable();
delay_ms(1000);
if (!check_soft_oc()) current_fault_state = FAULT_NONE;
break;
case FAULT_HARD_OC:
// 立即封锁,进入锁存状态
pwm_disable();
current_fault_state = FAULT_LATCHED;
break;
case FAULT_LATCHED:
// 等待手动复位
break;
}
}
4.5.2 故障恢复策略
故障恢复不是简单地清除标志位就完事了。我总结了几条原则:
- 软故障自动恢复:延时1~5秒后尝试恢复,恢复前先检测故障是否消失
- 硬故障锁存:必须手动复位或断电重启,防止反复触发损坏器件
- 恢复次数限制:连续恢复失败3次后,进入永久锁存状态
- 故障记录:每次故障都要记录时间、电流值、故障类型,方便事后分析
4.6 实战中的避坑指南
最后,分享几个我在项目中踩过的坑:
- 采样和滤波的时序冲突:ADC转换完成中断里做滤波,滤波还没算完,下一次采样又来了。解决方案是用DMA,让滤波在后台完成。
- 阈值设置太紧:我曾经把过流阈值设在额定电流的1.1倍,结果电机一启动就保护。后来放宽到1.5倍,配合防抖,问题解决。
- 故障标志位被意外清除:某个中断服务程序里不小心把故障标志位清零了,导致保护失效。建议用volatile修饰,并且只允许在状态机中修改。
- 忽略ADC参考电压的稳定性:参考电压波动会导致采样值漂移,过流阈值也跟着漂。我后来加了参考电压监测,漂移超过2%就报警。