4. 软件过流保护策略:ADC采样、数字滤波、阈值判断、故障标志位处理

过流保护这件事,说简单也简单——电流大了就跳闸。但在电机驱动这种实时系统里,怎么跳、什么时候跳、跳了之后怎么恢复,这里面的门道可不少。我做了这么多年驱动器,见过太多因为过流保护没处理好而炸管子的案例。

这一章,我们就来聊聊软件过流保护的完整链路:从ADC采样到数字滤波,再到阈值判断和故障标志位处理。每一步都有坑,我会把我在项目中踩过的坑都告诉你。

4.1 过流保护的整体架构

先看一张整体流程图,让你对软件过流保护有个全局认识。

软件过流保护流程 电流采样(ADC) 数字滤波 阈值判断 正常 过流 继续运行 故障标志位置位 说明:整个流程在PWM中断中执行,每个开关周期完成一次采样-滤波-判断循环

这个流程看起来简单,但每个环节都有讲究。我们一个一个来看。

4.2 ADC采样:时机比精度更重要

ADC采样,说白了就是把模拟电流信号变成数字量。但什么时候采,比用什么分辨率采更重要。

我见过不少新手,上来就选16位ADC,觉得精度越高越好。结果呢?采样时刻不对,采到的全是开关噪声,再高的精度也白搭。

4.2.1 采样时机的选择

在SVPWM调制中,电流采样通常有两种方式:

  • 单电阻采样:在PWM周期的中点采样,此时电流纹波最小
  • 双电阻/三电阻采样:在PWM周期的开始和结束时刻采样

我个人习惯用三电阻采样,配合PWM中心对齐模式。采样点选在PWM计数器的峰值和谷值时刻,这时候开关管已经稳定,噪声最小。

我的经验:采样时刻偏移超过1μs,采到的电流值可能偏差5%以上。尤其是在大电流工况下,这个偏差足以让过流保护误动作或失效。

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之间。

注意:滤波系数不能太小。我曾经在一个项目中把ALPHA设成0.05,结果过流信号被滤掉了,管子烧了才发现。后来我改成0.3,既滤掉了噪声,又保证了响应速度。

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;
}
关键点:防抖计数要设计成「快升慢降」。上升时加1,下降时减1,这样能避免临界状态下的频繁触发。

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次后,进入永久锁存状态
  • 故障记录:每次故障都要记录时间、电流值、故障类型,方便事后分析
我的习惯:在故障恢复前,先让PWM输出一个很短的测试脉冲(占空比5%),确认电流正常后再全速运行。这个「试探性恢复」帮我避免了好几次二次故障。

4.6 实战中的避坑指南

最后,分享几个我在项目中踩过的坑:

  1. 采样和滤波的时序冲突:ADC转换完成中断里做滤波,滤波还没算完,下一次采样又来了。解决方案是用DMA,让滤波在后台完成。
  2. 阈值设置太紧:我曾经把过流阈值设在额定电流的1.1倍,结果电机一启动就保护。后来放宽到1.5倍,配合防抖,问题解决。
  3. 故障标志位被意外清除:某个中断服务程序里不小心把故障标志位清零了,导致保护失效。建议用volatile修饰,并且只允许在状态机中修改。
  4. 忽略ADC参考电压的稳定性:参考电压波动会导致采样值漂移,过流阈值也跟着漂。我后来加了参考电压监测,漂移超过2%就报警。