
简介这份资源围绕德州仪器DRV8701电机驱动芯片展开面向智能车竞赛中C车、F车方向的参赛者与嵌入式电机控制初学者帮助解决H桥驱动电路设计、电流检测与闭环调速等实际问题。压缩包共6个文件约462KB包含3个json工程文件、2个zip压缩包和1个txt说明文档json对应原理图与PCB工程数据zip内为驱动电路工程及Gerber制板文件txt提供使用说明便于直接查看电路结构与复现方案。资源涵盖DRV8701的栅极驱动、过流与短路保护、霍尔电流检测、PWM与SPWM控制方式及电源与控制接口设计可帮助读者理解高效低噪声驱动的实现思路并对照原理图和PCB完成硬件搭建与调试。目前已有2263人学习下载适合需要快速获取驱动电路参考设计、缩短智能车硬件开发周期的读者。1. DRV8701 电机驱动为什么它成了智能车圈里的“低调狠货”如果你拆过最近两年的智能车或者小型机器人底盘大概率会在板子上看到一颗丝印为 DRV8701 的芯片。它不像 TB6612 那样被新手教程翻来覆去地讲也不像 L293D 那样古老到几乎成了历史文物但在真正跑起来、要拼响应速度和电流能力的场合DRV8701 出现的频率越来越高。原因很直接它是一颗单通道 H 桥栅极驱动器自己不集成 MOSFET而是靠外置 MOS 管来决定功率上限。这意味着你可以用同一颗芯片驱动一个几安培的小电机也可以换成大功率 MOS 去推几十安培的负载灵活性远超那些把功率管封在里面的模块。我第一次接触它是在一台需要快速启停的循迹小车上当时用 TB6612 总觉得加速肉、刹车软换了 DRV8701 加一对低内阻 MOS 之后同样的电机和电池起步明显更干脆刹车时电机两端能迅速短接那种“说停就停”的手感是集成模块给不了的。这篇文章就围绕这颗芯片把电路怎么搭、参数怎么算、代码怎么写、坑在哪里按我实际调试的顺序讲一遍。适合已经会点单片机、想从模块级驱动往分立方案走一步的人也适合手头有 DRV8701 板子但一直没调通的同行。2. DRV8701 内部结构与外围电路从框图到能焊的板子2.1 它到底集成了什么、没集成什么DRV8701 的内部框图可以拆成三块逻辑输入与保护、电荷泵、栅极驱动。逻辑输入侧兼容 3.3V 和 5V 电平接受 PWM 和方向信号内部带死区控制防止上下管直通。电荷泵负责把栅极驱动电压抬到比电源正极还高这样才能驱动高侧 N 沟道 MOS。栅极驱动输出就是 GATE 引脚分高侧和低侧直接连到外置 MOS 的栅极。它没有集成的是功率 MOSFET、电流采样电阻、续流二极管如果 MOS 体二极管够用也可以不加。所以选型时第一件事就是确定电机的工作电压和堵转电流然后去挑一对耐压足够、内阻够低的 N 沟道 MOS。常见做法是选耐压 30V 以上、内阻几毫欧到十几毫欧的管子比如 IRF3205 这类虽然老但皮实的型号或者更现代的贴片 MOS。电流采样电阻一般取几毫欧到几十毫欧功率要算够不然发热会让你怀疑人生。2.2 最小系统电路怎么连下面这张表是我在面包板验证阶段用的连接清单后来画 PCB 也是按这个逻辑走的。注意 VM 是电机电源VCC 是逻辑电源两者要共地。引脚连接对象说明VM电池正极电机供电范围 5.9V 到 45VVCC3.3V 或 5V逻辑供电必须加 0.1uF 去耦GND电池负极和逻辑地单点共地避免大电流串扰IN1/IN2单片机 GPIO方向或 PWM 输入具体看模式nSLEEP上拉或 GPIO低电平休眠悬空可能不工作GATE_HS高侧 MOS 栅极串一个小电阻典型 10 到 47 欧GATE_LS低侧 MOS 栅极同样串电阻SH高侧 MOS 源极接电机一端和采样电阻SL低侧 MOS 源极接采样电阻和地VREF参考电压设定电流限制阈值SO电流反馈输出接单片机 ADC可做堵转检测实际焊接时电荷泵电容和 VM 去耦电容要尽量靠近芯片引脚栅极走线要短而粗。我见过有人把 MOS 放在板子另一头栅极线拉了五六厘米结果开关波形振铃严重MOS 发热明显。这不是玄学是走线电感在作怪。2.3 电流限制和死区时间怎么设DRV8701 的电流限制是通过 VREF 引脚电压和采样电阻共同决定的。内部比较器会把 SH 引脚上的电压和 VREF 比较一旦超过就关断输出进入衰减模式。计算公式常见形式是I_limit VREF / (R_sense × A)其中 A 是内部电流镜增益具体值查手册。我一般先选 R_sense再反推 VREF。比如想要 5A 限流R_sense 取 0.01 欧假设增益为 10那 VREF 就是 0.5V。这个电压可以用单片机 PWM 加 RC 滤波产生也可以用 DAC甚至两个电阻分压。用 PWM 的好处是可以在运行中动态调整限流值比如起步时放宽堵转时收紧。死区时间方面DRV8701 内部已经固定了不需要外部设置。但外置 MOS 的开关速度会影响实际死区效果栅极电阻太小会导致开关太快、振铃大太大又会让死区相对变长、效率下降。我一般从 22 欧开始试用示波器看 SH 引脚波形振铃能接受、发热不严重就定下来。3. 用单片机驱动 DRV8701PWM 模式与代码落地3.1 两种控制模式PWM 模式和相位模式DRV8701 支持两种输入模式通过引脚配置或者型号后缀区分。一种是 PWM 模式IN1 给 PWMIN2 给方向另一种是相位模式IN1 和 IN2 都给 PWM但相位相反。智能车场景里我更喜欢 PWM 模式因为方向信号和速度信号分开代码逻辑清晰调速时只动一个定时器通道。以 ESP32-C3 为例它没有传统的 LEDC 之外的高级定时器但 LEDC 足够用。下面是一段初始化代码把 GPIO 配置成 PWM 输出和方向输出。// ESP32-C3 PWM 初始化用于 DRV8701 调速 #include driver/ledc.h #define PIN_PWM 4 // 接 DRV8701 IN1 #define PIN_DIR 5 // 接 DRV8701 IN2 #define PIN_SLEEP 6 // 接 nSLEEP高电平使能 void drv8701_init(void) { // 配置 PWM 定时器 ledc_timer_config_t timer_cfg { .speed_mode LEDC_LOW_SPEED_MODE, .duty_resolution LEDC_TIMER_10_BIT, // 0-1023 .timer_num LEDC_TIMER_0, .freq_hz 20000, // 20kHz超出人耳范围 .clk_cfg LEDC_AUTO_CLK }; ledc_timer_config(timer_cfg); // 配置 PWM 通道 ledc_channel_config_t ch_cfg { .gpio_num PIN_PWM, .speed_mode LEDC_LOW_SPEED_MODE, .channel LEDC_CHANNEL_0, .timer_sel LEDC_TIMER_0, .duty 0, .hpoint 0 }; ledc_channel_config(ch_cfg); // 方向引脚和使能引脚 gpio_set_direction(PIN_DIR, GPIO_MODE_OUTPUT); gpio_set_direction(PIN_SLEEP, GPIO_MODE_OUTPUT); gpio_set_level(PIN_SLEEP, 1); // 退出休眠 } void motor_set(int speed) { // speed 范围 -1023 到 1023 if (speed 0) { gpio_set_level(PIN_DIR, 0); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, speed); } else { gpio_set_level(PIN_DIR, 1); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, -speed); } ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); }这段代码里PWM 频率选 20kHz 是为了避开听觉范围同时开关损耗也能接受。占空比分辨率 10 位对应 0 到 1023够精细了。方向引脚在正转时拉低、反转时拉高注意 DRV8701 的 IN1 和 IN2 逻辑要对照手册不同模式可能相反。nSLEEP 必须拉高否则芯片不输出这个坑我后面还会提。3.2 电流反馈怎么读、怎么用SO 引脚输出的是和负载电流成比例的电流一般需要外接一个电阻转成电压再送 ADC。以 ESP32-C3 的 ADC 为例读回来的值可以用来做堵转保护。下面是一个简单的堵转检测逻辑。// 读取 SO 引脚电压判断是否堵转 #define PIN_SO 3 #define ADC_CHANNEL ADC_CHANNEL_3 int read_current_raw(void) { int raw 0; adc_oneshot_read(ADC_UNIT_1, ADC_CHANNEL, raw); return raw; // 12 位0-4095 } // 在主循环里调用超过阈值就停 void check_stall(void) { static int over_count 0; int raw read_current_raw(); // 假设 3.3V 对应 4095电流阈值折算成 raw 约 2500 if (raw 2500) { over_count; if (over_count 10) { // 连续 10 次超限 motor_set(0); gpio_set_level(PIN_SLEEP, 0); // 休眠保护 } } else { over_count 0; } }这里的关键是阈值标定。我一般先用可调电源限流让电机堵转记录 SO 电压对应的 ADC 值再留 20% 余量作为阈值。连续多次超限才动作是为了避开启动瞬间的电流尖峰。这个逻辑不复杂但能救电机和驱动板。3.3 加减速曲线让启停不突兀直接给阶跃 PWM电机会猛冲一下齿轮和轮胎都受罪。我习惯加一个简单的斜坡函数每次调用增加或减少固定步长直到达到目标值。下面是一个 10ms 调用一次的斜坡。// 斜坡控制每 10ms 调用一次 static int current_speed 0; static int target_speed 0; void ramp_update(void) { int step 20; // 每次变化 20约 0.2 秒从 0 到 1023 if (current_speed target_speed) { current_speed step; if (current_speed target_speed) current_speed target_speed; } else if (current_speed target_speed) { current_speed - step; if (current_speed target_speed) current_speed target_speed; } motor_set(current_speed); } void motor_set_target(int speed) { target_speed speed; }步长和调用周期决定了加速快慢。智能车循迹时我一般用 0.2 到 0.5 秒的加速时间太快会打滑太慢跟不上弯道。这个参数没有标准答案得在赛道上试。4. 避坑与排查DRV8701 调试中常见的五个翻车现场4.1 上电后电机不转芯片发烫现象接好线、程序也跑了电机纹丝不动摸芯片有点烫。 原因最常见的是上下管直通。可能是 IN1 和 IN2 同时给了高电平或者死区被外部干扰破坏也可能是 MOS 栅极接错。 解决先断开电机用示波器看 GATE_HS 和 GATE_LS确认没有同时为高的时刻。再检查 IN1/IN2 逻辑是否符合所选模式。如果 MOS 发烫而芯片不烫查 MOS 引脚是否焊反。4.2 nSLEEP 悬空导致时好时坏现象有时候上电正常有时候完全没反应动一下板子又好了。 原因nSLEEP 内部有下拉悬空时可能被拉低进入休眠也可能受干扰。 解决直接上拉 10k 到 VCC或者用单片机 GPIO 控制初始化时拉高。别省这个电阻。4.3 电流限制误触发电机没劲现象电机能转但力量很小一加载就停SO 电压也不高。 原因VREF 设得太低或者采样电阻太大导致限流阈值远低于电机正常工作电流。 解决重新计算 VREF 和 R_sense用示波器看 SH 波形确认正常工作时峰值有没有超过阈值。也可以临时把 VREF 拉到 VCC 排除限流因素。4.4 PWM 频率太低导致啸叫和抖动现象电机低速时嗡嗡响转速不稳。 原因PWM 频率落在人耳敏感区比如 1kHz 到 5kHz同时低频率下电流纹波大。 解决把 PWM 频率提到 15kHz 以上常见 20kHz。注意频率提高后栅极驱动损耗会增加摸一下 MOS 温度必要时减小栅极电阻。4.5 电源地线处理不当单片机复位现象电机一启动单片机就重启或者 ADC 读数乱跳。 原因电机大电流经过地线在地线电阻上产生压降导致单片机供电地浮动。 解决电机电源地和逻辑地单点连接走线尽量短粗。VM 和 VCC 的去耦电容不能省最好再并一个 100uF 以上的电解。ADC 采样线远离电机线。5. 进阶技巧用 SO 反馈做闭环和故障记录5.1 把 SO 变成电流环前面用 SO 只做了堵转保护其实它可以做更细的电流闭环。思路是设定目标电流读 SO 换算实际电流用 PI 控制器调整 PWM 占空比。这样即使电池电压下降电机输出力矩也能保持稳定。下面是一个简化的 PI 实现。// 电流 PI 控制每 1ms 调用一次 float target_current 2.0f; // 目标 2A float kp 0.5f, ki 0.1f; float integral 0.0f; void current_loop(void) { float actual read_current_raw() * 3.3f / 4095.0f / 0.01f / 10.0f; // 折算成安培 float error target_current - actual; integral error * 0.001f; // 1ms 积分 float output kp * error ki * integral; // 限幅到 0-1023 if (output 1023) output 1023; if (output 0) output 0; ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, (int)output); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); }参数 kp 和 ki 需要根据实际电机和采样电路整定。我一般先把 ki 置零加大 kp 直到电流开始振荡再退回一半然后加 ki 消除静差。这个过程有点玄学但用示波器看电流波形会直观很多。5.2 故障记录让每次翻车都有据可查调试阶段最怕的是偶发故障过去了就复现不了。我的习惯是在 RAM 里开一个环形缓冲区记录每次电流超限时的 PWM 值、SO 读数、时间戳。一旦触发保护就把这些数据通过串口打出来。这样即使现场翻车也能回看当时发生了什么。下面是一个简单的记录结构。typedef struct { uint32_t tick; int pwm; int current_raw; } fault_log_t; #define LOG_SIZE 32 static fault_log_t log_buf[LOG_SIZE]; static int log_idx 0; void log_fault(int pwm, int current_raw) { log_buf[log_idx].tick xTaskGetTickCount(); log_buf[log_idx].pwm pwm; log_buf[log_idx].current_raw current_raw; log_idx (log_idx 1) % LOG_SIZE; } void dump_log(void) { for (int i 0; i LOG_SIZE; i) { printf(tick%lu pwm%d cur%d\n, log_buf[i].tick, log_buf[i].pwm, log_buf[i].current_raw); } }这个技巧不限于 DRV8701任何电机驱动调试都用得上。从那以后我每次焊新板子都先把日志和限流保护跑一遍再上电机全速。希望这些经验能帮你少烧几个 MOS早点让轮子转起来。本文还有配套的精品资源点击获取