STM32 硬件过流保护实战:比较器直连 BKIN,纳秒级关断不靠软件

烧掉第三对 MOS 管的那天下午,我终于承认软件保护是靠不住的。

当时的保护方案写得挺完整:ADC 定时采母线电流,超过阈值就在中断里调 HAL_TIM_PWM_Stop() 关掉六路输出。逻辑没问题,测试也过了。但真正短路的时候,从电流开始飙升到 MOS 管击穿,整个过程只有几微秒,而我的保护链路是:定时器触发 ADC,转换完成产生中断,中断里读值比较,比较超限调 HAL 函数关 PWM。这一圈跑下来至少十几微秒,等软件反应过来,管子已经冒烟了。

后来才知道 STM32 上有一条完全不经过 CPU 的硬件保护通路:比较器 COMP 的输出可以在芯片内部直连高级定时器的刹车输入 BKIN,一旦触发,PWM 输出在纳秒级被硬件强制关断,不需要时钟、不需要中断、不需要代码执行。

硬件保护通路长什么样

采样电阻上的电压反映了母线电流。这个电压送到 COMP 的正输入端,COMP 的负输入端接一个阈值电压,两者一比较,超限就翻转输出。

关键在于 COMP 的输出去哪儿。传统做法是引到一个 GPIO,再从 GPIO 绕回来接 TIM 的 BKIN 引脚。这样能用,但多占两个引脚而且外部走线容易引入干扰。STM32 提供了内部直连:COMP 的输出在芯片内部就接到了 TIM1/TIM8 的 BKIN 上,不出芯片,不占引脚。

整条链路是:

采样电阻 → COMP 正输入 → COMP 内部比较 → COMP 输出 → 内部连线 → TIM BKIN → BDTR 逻辑 → 六路 PWM 全部关断

这条链路里没有任何一环需要 CPU 参与。哪怕程序卡死在死循环里、哪怕时钟停了,只要 COMP 供电正常,保护依然生效。实测从采样电阻电压越限到 PWM 引脚变成安全电平,延迟在 100ns 以内,主要是 COMP 本身的传播延迟。

对比一下软件保护:ADC 采样周期加转换时间,几微秒;中断响应和函数调用,几微秒;累计十几微秒。硬件保护快了两个数量级。

阈值怎么定

COMP 的负输入(反相输入)可以选好几种源:

  • VREFINT(内部参考电压,约 1.2V)或它的 1/4、1/2、3/4 分压
  • DAC 输出,这个最灵活,阈值可以软件动态调整
  • 外部引脚,用电阻分压给一个固定阈值

用 DAC 做阈值是我最推荐的方案。因为不同工况下允许的电流上限不一样:启动时允许大电流冲击,稳态运行时阈值可以收紧。用 DAC 就能在运行中改阈值,改一个寄存器的事。

阈值电压的计算要从电流反推。假设采样电阻 5mΩ,运放放大 20 倍,允许峰值电流 30A:

复制代码
采样电阻上的电压 = 30A × 5mΩ = 150mV
运放输出 = 150mV × 20 = 3.0V

所以 DAC 要输出 3.0V 作为阈值。12 位 DAC,3.3V 满量程:

c 复制代码
/* 阈值 3.0V -> DAC 码值 */
uint16_t dac_code = (uint16_t)(3.0f / 3.3f * 4095);   /* = 3723 */
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_code);
HAL_DAC_Start(&hdac, DAC_CHANNEL_1);

留出余量很重要。阈值卡在正常工作电流的边缘,稍微有点纹波就误触发,电机根本转不起来。我一般把阈值设在稳态峰值电流的 1.5 到 2 倍,既能挡住真正的短路,也不会被正常的换相尖峰打断。

COMP 配置

c 复制代码
COMP_HandleTypeDef hcomp1;

void comp_init(void)
{
    __HAL_RCC_COMP_CLK_ENABLE();

    hcomp1.Instance = COMP1;
    hcomp1.Init.InputPlus     = COMP_INPUT_PLUS_IO1;      /* 采样信号引脚 */
    hcomp1.Init.InputMinus    = COMP_INPUT_MINUS_DAC1_CH1; /* DAC 做阈值 */
    hcomp1.Init.OutputPol     = COMP_OUTPUTPOL_NONINVERTED;
    hcomp1.Init.Hysteresis    = COMP_HYSTERESIS_MEDIUM;   /* 滞回,防抖 */
    hcomp1.Init.BlankingSrce  = COMP_BLANKINGSRC_TIM1_OC5; /* 消隐窗口 */
    hcomp1.Init.Mode          = COMP_POWERMODE_HIGHSPEED;  /* 高速模式 */
    hcomp1.Init.TriggerMode   = COMP_TRIGGERMODE_NONE;     /* 不要中断 */

    HAL_COMP_Init(&hcomp1);
    HAL_COMP_Start(&hcomp1);
}

几个参数值得说清楚。

Hysteresis 滞回必须开。采样信号上叠着开关噪声,如果没有滞回,信号在阈值附近抖动会让 COMP 输出疯狂翻转,一次真实过流可能被解读成几十次触发。滞回给比较器加一个死区:向上越过阈值才翻转,要落回到阈值以下一定幅度才翻回来。中等档位一般够用。

Blanking 消隐是这套方案的关键细节。MOS 管开通瞬间会有一个很大的电流尖峰(反向恢复电流加寄生电容充电),这个尖峰是正常的,但幅度可能超过保护阈值。如果不做处理,每次 PWM 上升沿都会误触发保护,电机完全转不动。

消隐的做法是:用定时器的一个比较通道(比如 TIM1_OC5)生成一个窗口信号,在这个窗口内 COMP 的输出被屏蔽。窗口时间设成开关尖峰的持续时间,一般几百纳秒到一两微秒。这样开通瞬间的尖峰被忽略,之后的真实过流照样能触发。

Mode 功耗/速度模式选高速。低功耗模式下 COMP 的传播延迟能到几百纳秒甚至微秒级,保护速度就打折扣了。这个外设本身耗电很少,没必要在这里省。

TriggerMode 不要开中断。硬件保护通路根本不需要中断,COMP 触发后 BKIN 会直接关 PWM,同时 TIM 会产生 Break 中断(如果你使能了),在那个中断里做善后处理(记录故障、点故障灯、通知上层)就行。COMP 自己的中断纯属多余。

定时器侧的 BDTR 配置

TIM1/TIM8 的 BDTR 寄存器控制刹车功能,几个位必须配对:

c 复制代码
TIM_BreakDeadTimeConfigTypeDef bdt = {0};

bdt.OffStateRunMode  = TIM_OSSR_ENABLE;
bdt.OffStateIDLEMode = TIM_OSSI_ENABLE;
bdt.LockLevel        = TIM_LOCKLEVEL_1;
bdt.DeadTime         = 100;                    /* 死区时间 */
bdt.BreakState       = TIM_BREAK_ENABLE;       /* BKE: 使能刹车 */
bdt.BreakPolarity    = TIM_BREAKPOLARITY_HIGH; /* BKP: 高电平有效 */
bdt.BreakFilter      = 0x0F;                   /* 数字滤波 */
bdt.AutomaticOutput  = TIM_AUTOMATICOUTPUT_DISABLE;  /* AOE: 关键 */

HAL_TIMEx_ConfigBreakDeadTime(&htim1, &bdt);

/* 使能 Break 中断做善后 */
__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_BREAK);

AOE(Automatic Output Enable)必须关掉 。这个位如果置 1,刹车条件消失后 PWM 输出会在下一个更新事件自动恢复。听起来很方便,实际上非常危险:短路可能是间歇性的,自动恢复会让保护和短路交替发生,管子在反复冲击下更容易炸。正确做法是保护触发后锁死,必须软件确认故障已排除、显式调 HAL_TIM_MspPostInit 重新使能 MOE 才恢复输出。

BreakPolarity 极性要和 COMP 输出极性对上。COMP 正常时输出低、过流时输出高,那 BKP 就选高电平有效。搞反了的结果是:正常运行时一直处于刹车状态,PWM 根本输出不来,你会以为定时器坏了。

BreakFilter 数字滤波是 BKIN 侧的第二道防抖,采样若干个时钟周期都确认有效才算触发。和 COMP 的滞回配合用,误触发概率大幅降低。但滤波器会增加响应延迟(每级大约几个定时器时钟周期),所以别开满,0x0F 那档大概几百纳秒,可以接受。

LockLevel 锁定级别是防止运行中被误改配置。Level 1 会锁住 BDTR 里的死区和刹车相关位,写入被忽略。这个锁一旦设置只能靠复位解除,量产固件建议开上,开发阶段可以先不开。

Break 中断里做什么

硬件已经把 PWM 关了,中断里只需要做善后:

c 复制代码
void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim)
{
    /* PWM 输出此时已被硬件关断,MOE 位被清零 */

    g_fault_flag = FAULT_OVERCURRENT;
    g_fault_count++;

    /* 记录故障时刻的状态,方便事后分析 */
    g_fault_snapshot.timestamp = HAL_GetTick();
    g_fault_snapshot.duty      = __HAL_TIM_GET_COMPARE(htim, TIM_CHANNEL_1);
    g_fault_snapshot.speed     = g_current_speed;

    /* 关掉其他相关外设,进入安全状态 */
    HAL_GPIO_WritePin(RELAY_GPIO_Port, RELAY_Pin, GPIO_PIN_RESET);
    led_fault_on();

    /* 不要在这里自动重启,等上层确认 */
}

这个快照很有用。过流故障往往是间歇性的,现场偶发一次事后没法复现。把触发时刻的占空比、转速、时间戳记下来,多攒几次就能看出规律,是启动瞬间炸的还是加速阶段炸的,是同一个占空比附近还是随机。

恢复输出要显式做,而且要有条件:

c 复制代码
int try_recover_from_fault(void)
{
    if (g_fault_count > 3) {
        return -1;    /* 连续故障太多次,拒绝恢复,需要人工介入 */
    }

    /* 确认采样值已回到安全范围 */
    if (read_current_adc() > SAFE_CURRENT_THRESHOLD) {
        return -1;
    }

    /* 清故障标志,重新使能主输出 */
    __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK);
    __HAL_TIM_MOE_ENABLE(&htim1);
    g_fault_flag = FAULT_NONE;
    return 0;
}

软硬件双层保护才完整

硬件保护快,但它只能做一件事:超过某个瞬时阈值就关断。判断不了"电流没超峰值但持续偏高"这种慢性过载。

所以合理的方案是两层:

硬件层(COMP + BKIN):阈值设在绝对不能超的位置(比如 MOS 管额定电流的 80%),处理短路、堵转、驱动电路故障这类灾难性事件,纳秒级响应。

软件层(ADC + 定时判断):阈值设得低一些,加上时间维度判断。比如"电流超过额定值 1.2 倍持续 500ms"就降额或停机,这属于热保护范畴,慢一点没关系。

两层的阈值要留出足够间隔,不然软件层还没来得及动作硬件层就跳了,等于软件层白写。我一般硬件阈值设在软件阈值的 1.5 倍以上。

十个坑

一、BKP 极性配反。 正常运行时就处于刹车状态,PWM 一点输出都没有。查 BDTR 的 BKP 位和 COMP 输出极性是否匹配。

二、AOE 开着导致反复冲击。 刹车条件消失后自动恢复,短路间歇发生就变成反复冲击 MOS 管,比不保护更危险。量产固件必须关 AOE。

三、没配 Blanking,开关尖峰误触发。 每个 PWM 上升沿都触发保护,电机完全转不起来。用定时器 OC 通道生成消隐窗口,屏蔽开通瞬间几百纳秒。

四、没开滞回,一次过流触发几十次。 COMP 输出在阈值附近疯狂翻转。开 Hysteresis,中等档位起步。

五、COMP 用了低功耗模式。 传播延迟从纳秒变微秒,保护速度打折扣。功率场合固定用高速模式。

六、阈值算错,把运放增益漏了。 直接用采样电阻上的电压当阈值,忘了后面还有 20 倍放大,结果阈值差 20 倍,等于没保护。

七、阈值定得太紧被正常电流触发。 启动冲击电流可能是稳态的好几倍,阈值贴着稳态电流设肯定误触发。留 1.5 到 2 倍余量。

八、Break 中断里试图重启 PWM。 中断里立刻恢复输出,如果故障还在就是无限循环触发。恢复逻辑放主循环,加故障计数上限和电流确认。

九、只做硬件保护不做软件保护。 硬件挡不住慢性过载和过温,长时间偏高电流会把管子热死。两层配合才完整。

十、LockLevel 在开发阶段就开满。 锁了之后改不了配置又只能靠复位解除,调试很痛苦。开发用 OFF,量产再开 Level 1。

一个可以照抄的初始化顺序

顺序很重要,配错顺序会出现"保护配好了但没生效"或者"一上电就跳保护"。

c 复制代码
void motor_protect_init(void)
{
    /* 1. 先启 DAC 给出阈值,保证 COMP 有正确的参考 */
    dac_threshold_init(3723);          /* 3.0V, 对应 30A */

    /* 2. 再配 COMP,此时阈值已稳定 */
    comp_init();                        /* 滞回 + 消隐 + 高速 */

    /* 3. 配定时器 PWM(还不使能输出) */
    MX_TIM1_Init();

    /* 4. 配 BDTR:死区 + 刹车 + 关 AOE */
    tim1_bdtr_config();

    /* 5. 清一次可能存在的 Break 标志 */
    __HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK);

    /* 6. 最后使能 PWM 输出和主输出 */
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
    __HAL_TIM_MOE_ENABLE(&htim1);
}

先给阈值再启比较器,是为了避免 COMP 在阈值还是 0V 的时候就判定过流,把 Break 标志置上。第 5 步清标志是补救,万一前面还是抖了一下,这里清掉再使能输出,就不会一上电就跳保护。

小结

从烧管子到搞明白这套硬件保护,我花了大概两周。真正的转折点是意识到一件事:软件永远追不上功率器件的失效速度。

代码执行需要时钟、需要取指、需要中断响应,加起来是微秒级;MOS 管从过流到击穿是几微秒。这两个时间尺度是重叠的,靠软件抢时间本身就是个赌博。COMP 直连 BKIN 这条通路把保护从软件时间尺度拉到了硬件时间尺度,赌局才变成了确定性。

配置这套东西大概两百行代码,一个下午能调通。相比一对 MOS 管的成本和炸管的排查时间,这笔投入太值了。

保护逻辑写在中断里,等于把安全托付给了调度延迟;写在 BDTR 里,才是真的托付给了硬件。

相关推荐
嵌入式阿蔡25 分钟前
存储_14:嵌入式存储测试岗面试——可靠性/兼容/车规面试题与项目话术
单片机·嵌入式硬件·面试·嵌入式·存储
Brilliantwxx2 小时前
【PCB】(入门篇) 电子入门系列|从电路分析到 PCB 设计
stm32·单片机·嵌入式硬件
俊基科技9 小时前
A-59U工业级多模语音处理模块在矿山矿井通信与呼叫报警系统中的应用
嵌入式硬件·嵌入式开发·硬件开发·ai降噪·回音消除·拾音降噪
周周记笔记11 小时前
GD32F450xx MCU Datasheet 解读:硬件测试工程师的测试方法论
单片机·嵌入式硬件
潘潘的嵌入式日记13 小时前
I²C 从机接收总被覆盖?双缓冲要在 STOP 时交接
c语言·开发语言·单片机
AOI小白新手上路15 小时前
江科协51单片机16-1 AD/DA ADC 三通道综合显示(LCD1602 版)改造说明
单片机·嵌入式硬件·51单片机
HUI-47415 小时前
EG1135|SSOP‑16 同步整流降压控制器,大功率外置 MOS 降压优选
单片机·嵌入式硬件·硬件工程
单片机仿真设计16 小时前
【proteus仿真】基于 STM32 单片机恒温箱控制系统设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·毕设
国科安芯16 小时前
星载CAN总线通信网络中抗辐射MCU的通信可靠性设计分析
网络·人工智能·分布式·单片机·嵌入式硬件·架构