电机驱动开发学习6. 三相电流采集与过流保护
-
- [1. 为什么需要电流采集与过流保护](#1. 为什么需要电流采集与过流保护)
- [2. 电流采样电路原理](#2. 电流采样电路原理)
-
- [2.1 U 相电流检测原理图](#2.1 U 相电流检测原理图)
- [3. 硬件过流保护电路](#3. 硬件过流保护电路)
-
- [3.1 过流保护原理图](#3.1 过流保护原理图)
- [3.2 工作原理](#3.2 工作原理)
- [3.3 硬件保护与软件保护的分工](#3.3 硬件保护与软件保护的分工)
- [4. 软件实现](#4. 软件实现)
-
- [4.1 关键宏定义](#4.1 关键宏定义)
- [4.2 ADC 多通道 + DMA 配置](#4.2 ADC 多通道 + DMA 配置)
- [4.3 DMA 完成回调:分离各通道并累加](#4.3 DMA 完成回调:分离各通道并累加)
- [4.4 偏置电压校准](#4.4 偏置电压校准)
- [4.5 主函数:电压、温度、三相电流同一帧输出](#4.5 主函数:电压、温度、三相电流同一帧输出)
- [4.6 软件过流保护](#4.6 软件过流保护)
- [5. 实验操作](#5. 实验操作)
-
- [5.1 接线](#5.1 接线)
- [5.2 串口指令](#5.2 串口指令)
- [5.3 现象观察](#5.3 现象观察)
- [5.4 注意事项](#5.4 注意事项)

本系列教程学习资源来自野火官方电机教程,部分代码及博客内容摘自官方教程。
本系列教程使用野火骄阳 F407 开发板及野火有刷/无刷驱动板。
本篇代码按本人实际驱动板配置 :采样电阻 0.02 Ω (原理图 R48 等,
Current_U/V/W),电流 ADC 偏置 1.65 V(上电校准)。野火文档中另有 0.01 Ω 改版,以板上丝印/原理图为准。
在 上一篇 中,我们完成了电源电压 VBUS 与 NTC 温度的 ADC 采集。本篇在此基础上,继续学习驱动板上三相电流的采样原理 、硬件过流保护电路,以及如何在 STM32 中通过 ADC + DMA 读取 U/V/W 三相电流。
1. 为什么需要电流采集与过流保护
电机驱动系统中,电流是最核心的反馈量之一:
- 监测运行状态:空载、带载、堵转时电流差异明显,可用于保护判断。
- 闭环控制基础:为后面FOC作准备,因为FOC 等高级算法需要实时相电流;即使当前用六步换相开环驱动,也应具备电流观测能力。
- 安全保护:短路、堵转、异常负载会导致电流急剧上升,必须在毫秒级内切断功率输出,否则 MOS 管、电机绕组都有烧毁风险,这比通过NTC保护更快。
保护手段通常分两层:
| 方式 | 响应速度 | 实现位置 | 典型用途 |
|---|---|---|---|
| 硬件过流保护 | 极快(μs 级) | 比较器 + 锁存 + SD 关断 | 瞬时过流、短路 |
| 软件过流/堵转保护 | 较慢(ms 级) | ADC 采样 + 程序判断 | 堵转检测、限流、统计 |
野火无刷驱动板两种都做了 :比较器硬件直接拉低 Motor_SD_B 关断驱动;MCU 侧则通过 ADC 周期性读取三相电流,供显示、调试及后续算法使用。本文讲的是软件上的实现。
2. 电流采样电路原理
野火无刷驱动板对控制信号和 ADC 采样电路均做了隔离(光耦 + AMC1200 隔离运放),可有效抑制功率地的开关噪声。
以 U 相为例,三相(U/V/W)电路结构相同,仅采样电阻串接位置不同。
2.1 U 相电流检测原理图


信号链路如下:
相电流 → 采样电阻(0.02Ω) → 隔离运放(×8) → 差分转单端运放(+1.65V偏置) → Motor_IU_ADC → STM32 ADC
① 采样电阻
在 U 相下桥臂回路串入 0.02 Ω、2 W、1% 采样电阻(原理图如 R48,Current_W;U/V 相各有一颗),利用欧姆定律将电流转为毫伏级电压:
V i = I × R s h u n t = I × 0.02 V_i = I \times R_{shunt} = I \times 0.02 Vi=I×Rshunt=I×0.02
② 隔离运放放大
信号经 AMC1200 隔离运放放大 8 倍 后差分输出,再经普通运放转为单端信号,并叠加 1.65 V 偏置电压 (零电流时 ADC 输入约为 1.65 V,而非 0 V,便于测量正负电流;更早期板子为 1.24 V)。

根据原理图与实测,ADC 端电压与相电流的关系为:
V c u r r e n t _ a d c = 8 × V i + 1.65 = 0.16 × I + 1.65 V_{current\_adc} = 8 \times V_i + 1.65 = 0.16 \times I + 1.65 Vcurrent_adc=8×Vi+1.65=0.16×I+1.65
因此软件中需先减去上电校准得到的偏置(零电流时约 1.65 V),再除以 8 倍放大与 0.02 Ω 采样电阻,反算电流。
③ 引脚连接(驱动板接口 2)
| 信号 | 开发板引脚 | ADC 通道 |
|---|---|---|
| Motor_IU_ADC | PF6 | ADC3_IN4 |
| Motor_IV_ADC | PF7 | ADC3_IN5 |
| Motor_IW_ADC | PF8 | ADC3_IN6 |
| VBUS | PF9 | ADC3_IN7 |
| TEMP | PF10 | ADC3_IN8 |
3. 硬件过流保护电路
3.1 过流保护原理图

该电路对 U/V/W 三相共用一套保护逻辑,任意一相过流均可触发关断。
3.2 工作原理
比较器(LMV331)
- 每相 ADC 电压
Motor_IU/V/W_ADC送入比较器反相端 IN-。 - IN+ 接电阻分压基准 2V84_IN (板上硬件过流比较阈值,标称约 10 A)。
- 比较器规则:IN+ > IN- 输出高电平,反之输出低电平。
正常状态
三相 ADC 电压均低于 2V84_IN → 比较器输出高电平 3.3 V → 后级比较器 U33 输出低电平 → Q8 截止 → Motor_SD_B = 3.3 V → 驱动正常工作,过流指示灯不亮。
过流触发
任一相 ADC 电压超过 2V84_IN → 该相比较器输出低电平 → 经 R133/R134/C33 的 RC 网络,U33 反相端电压被拉低 → U33 输出高电平 → Q8 导通 → Motor_SD_B 被迅速拉至 0 V → 驱动芯片 SD 引脚关断 ,功率输出切断,过流指示灯点亮。

恢复过程
电流恢复正常后,比较器重新输出高电平,但保护不会立刻解除:R145/C99 的 RC 充电需达到一定电压后,U34 才重新输出高电平,使 Motor_SD_B 恢复 3.3 V。这段延时避免电流在阈值附近抖动时频繁启停。
迟滞(R144)
R144 为 U33 提供施密特迟滞:触发电压约 1.571 V,恢复电压约 1.729 V,防止临界状态反复触发。
3.3 硬件保护与软件保护的分工
| 项目 | 硬件过流 | 软件(ADC) |
|---|---|---|
| 响应 | 极快,不依赖 CPU | 受采样周期限制(约 ms 级) |
| 阈值 | 约 10 A(2V84_IN) | 8 A(MOTOR_OVERCURRENT_MA,可调) |
| 触发后 | 直接关断 SD | 串口打印三相电流 + 程序关断 |
| 恢复 | 需等 RC 充电 + 重新使能 | 程序可主动判断 |
实验时用手堵转电机或快速加载,可观察到驱动板过流灯亮、Motor_SD_B 变低,电机立即停止------这是硬件保护在起作用,与程序是否运行无关。
4. 软件实现
在 lesson5 的 ADC 框架上,扩展为 5 通道扫描采集:温度、VBUS、IU、IV、IW。
4.1 关键宏定义
c
#define VREF 3.3f
#define ADC_NUM_MAX 320
#define CURR_SHUNT_OHM 0.02f /* 原理图 R48 等,0.02 Ω / 2 W */
#define CURR_ADC_BIAS_V 1.65f /* 零电流时 ADC 端典型偏置(由校准实测) */
#define GET_ADC_VDC_VAL(val) ((float)(val) / 4096.0f * VREF)
#define GET_ADC_CURR_VAL(val) (((float)(val) / 8.0f / CURR_SHUNT_OHM) * 1000.0f) // mA
#define GET_VBUS_VAL(val) (((float)(val) - 1.24f) * 37.0f)
#define MOTOR_OVERCURRENT_MA 8000 // 软件过流阈值(mA)
#define ADC_WAVE_HEADER0 0xAAU
#define ADC_WAVE_HEADER1 0x55U
#define ADC_WAVE_FRAME_LEN 12U
GET_ADC_CURR_VAL 的含义:ADC 电压 val(已去偏置)→ 除以放大倍数 8 → 除以采样电阻 0.02 Ω → 乘 1000 转为 mA。
CURR_ADC_BIAS_V 仅作说明;实际上电后电机未启动时通过 17 次采样 得到各相 adc_offset,运行时减去该偏置,无需在公式里写死 1.65 V。
与 lesson5 相同,采样数据通过串口以二进制帧 输出,而非 printf 文本。lesson6 在 lesson5 的 6 字节帧(VBUS + 温度)基础上扩展为 12 字节同一帧,将 U/V/W 三相电流与电压、温度合并输出。
4.2 ADC 多通道 + DMA 配置
使用 ADC3 + DMA2 Stream0 Channel2,开启扫描模式,连续转换 5 个通道:
- Rank 1:TEMP(PF10)
- Rank 2:VBUS(PF9)
- Rank 3:IU(PF6)
- Rank 4:IV(PF7)
- Rank 5:IW(PF8)
DMA 循环搬运 320 个半字到 adc_buff[]。缓冲区按 [T, VBUS, IU, IV, IW, T, VBUS, ...] 交错排列,步长为 5。
4.3 DMA 完成回调:分离各通道并累加
c
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
HAL_ADC_Stop_DMA(hadc);
/* 温度、VBUS:直接求本轮均值 */
for (uint32_t count = 0; count < ADC_NUM_MAX; count += 5)
adc_mean += adc_buff[count];
// ...
/* 三相电流:累加到 sum,由 get_curr_val_x 再做平均 */
for (uint32_t count = 2; count < ADC_NUM_MAX; count += 5)
adc_mean += adc_buff[count];
adc_mean_sum_u += adc_mean / (ADC_NUM_MAX / 5);
adc_mean_count_u++;
HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t *)&adc_buff, ADC_NUM_MAX);
}
电流通道采用多次 DMA 回调累加再求平均,比单次采样更平滑;可根据实际需求调整累加次数。
4.4 偏置电压校准
硬件零电流时 ADC 输入约 1.65 V (新版),不能当作 0 A。上电后电机未启动时,各相独立采集 17 次 平均值作为 adc_offset(实测偏置会接近 1.65 V):
c
int32_t get_curr_val_u(void)
{
static uint8_t flag = 0;
static uint32_t adc_offset = 0;
curr_adc_mean = adc_mean_sum_u / adc_mean_count_u;
adc_mean_count_u = 0;
adc_mean_sum_u = 0;
if (flag < 17) {
adc_offset = curr_adc_mean;
flag++;
}
if (curr_adc_mean >= adc_offset)
curr_adc_mean -= adc_offset;
else
curr_adc_mean = 0;
float vdc = GET_ADC_VDC_VAL(curr_adc_mean);
return GET_ADC_CURR_VAL(vdc);
}
V、W 相逻辑相同(get_curr_val_v、get_curr_val_w)。
4.5 主函数:电压、温度、三相电流同一帧输出
在 lesson5 的 AA 55 帧头上,把 U/V/W 三相电流与 VBUS、温度打包进同一帧 (12 字节),每 50 ms 发送一次。串口助手需切换到 HEX 显示:
c
if (HAL_GetTick() % 50 == 0 && print_flag == 0) {
print_flag = 1;
adc_send_waveform_frame(); /* 一帧内含 VBUS + 温度 + IU + IV + IW */
}
同一帧格式(12 字节,小端 uint16):
| 偏移 | 长度 | 内容 | 换算 |
|---|---|---|---|
| 0~1 | 2 | 帧头 AA 55 |
固定 |
| 2~3 | 2 | VBUS | 实际电压(V) × 100 |
| 4~5 | 2 | 温度 | 实际温度(℃) × 10 |
| 6~7 | 2 | U 相电流 | mA |
| 8~9 | 2 | V 相电流 | mA |
| 10~11 | 2 | W 相电流 | mA |
示例:24.5 V、25.3 ℃、U=120 mA、V=130 mA、W=110 mA →
AA 55 92 09 FD 00 78 00 82 00 6E 00
adc_send_waveform_frame() 先调用 adc_fill_wave_snapshot() 在同一时刻读取全部 5 路量,再一次性组帧发送:
c
static void adc_fill_wave_snapshot(uint16_t *vbus_x100, uint16_t *temp_x10,
int32_t *iu_ma, int32_t *iv_ma, int32_t *iw_ma)
{
*vbus_x100 = float_to_u16_scaled(get_vbus_val(), 100.0f);
*temp_x10 = float_to_u16_scaled(get_ntc_t_val(), 10.0f);
*iu_ma = get_curr_val_u();
*iv_ma = get_curr_val_v();
*iw_ma = get_curr_val_w();
}
void adc_send_waveform_frame(void)
{
uint8_t frame[ADC_WAVE_FRAME_LEN];
/* 快照后写入 frame[0..11],一次 Usart_SendBytes 发出 */
}
按键/串口控制电机的方式与 lesson4、lesson5 相同。
4.6 软件过流保护
除驱动板硬件比较器(约 10 A)外,程序在 motor_overcurrent_poll() 中周期性检测三相电流;任一相超过 MOTOR_OVERCURRENT_MA(8 A)时关断电机并打印提示:
c
static void motor_overcurrent_poll(void)
{
int32_t iu, iv, iw;
if (!motor_hall_is_active())
return;
if (!adc_is_overcurrent(&iu, &iv, &iw))
return;
printf("Overcurrent: U=%ldmA V=%ldmA W=%ldmA\r\n", (long)iu, (long)iv, (long)iw);
set_bldcm_disable();
}
主循环中同时保留 lesson4 的 Hall 堵转检测(motor_stall_poll()),形成堵转 + 软件过流 + 硬件过流三层保护。
5. 实验操作
5.1 接线
与 lesson5 相同,驱动板排线接开发板直流有/无刷电机驱动接口 2:
| 驱动板 | 开发板 |
|---|---|
| 5V_IN / GND | 5V / GND |
| U+ / V+ / W+ | PI5 / PI6 / PI7 |
| U- / V- / W- | PH13 / PH14 / PH15 |
| SD / GND | PE6 / GND |
| HU / HV / HW / GND | PH10 / PH11 / PH12 / GND |
| TEMP / VBUS | PF10 / PF9 |
| IU / IV / IW | PF6 / PF7 / PF8 |
三相绕组线序(以电机说明为准):黄 U、绿 V、蓝 W。
编码器:黄 HU、绿 HV、蓝 HW、黑 H-、红 H+。
5.2 串口指令
d 0 \n 电机正转
d 1 \n 电机反转
v 1000 \n 速度设为 1000
v 0 \n 停转
5.3 现象观察
- 空载运行 :串口每 50 ms 输出一帧
AA 55开头的 12 字节 HEX 数据,三相电流字段(偏移 6~11)数值较小。

- 三相峰值 错开(红/蓝/绿不同时到顶)
- 尖峰之间 经常掉到 0
- 幅值在 几百 mA
-
手捏堵转 :电流字段明显增大;超过软件阈值(4 A)时串口打印
Overcurrent: ...并关断;若超过硬件阈值(约 10 A),驱动板过流灯亮,电机立刻停止。
-
恢复:等待 RC 充电延时后,重新发送使能指令方可再次启动。
5.4 注意事项
- 测量精度受
VREF影响,可用万用表实测 VDDA 后修正bsp_adc.h中的VREF宏。 - 软件过流阈值可在
bsp_adc.h中修改MOTOR_OVERCURRENT_MA(默认 4000 mA)。 - 电机运行中不要直接按 RESET 复位,应先停转再复位,避免反电动势损坏硬件。
- 采样电阻以原理图/板上丝印 为准(本板 R48 = 0.02 Ω );
bsp_adc.h中CURR_SHUNT_OHM须与之一致。野火另有 0.01 Ω 改版,勿照搬文档。 - 偏置电压若实测接近 1.24 V 而非 1.65 V,说明是更早期板型,仍可依赖上电 17 次
adc_offset校准,一般无需改代码。