电机驱动开发学习4. 直流无刷电机的控制实现
- 一、直流无刷电机速度控制原理
-
- [1.1 速度控制的基本概念](#1.1 速度控制的基本概念)
- [1.2 影响电机转速的因素](#1.2 影响电机转速的因素)
- [1.3 速度控制方法](#1.3 速度控制方法)
-
- [1.3.1 PWM调压控制](#1.3.1 PWM调压控制)
- [1.3.2 闭环速度控制](#1.3.2 闭环速度控制)
- [1.3.3 野火BLDC开发板霍尔传感器的应用](#1.3.3 野火BLDC开发板霍尔传感器的应用)
- [1.4 PWM控制策略](#1.4 PWM控制策略)
-
- [1.4.1 ON-PWM型(上桥臂恒通,下桥臂PWM)](#1.4.1 ON-PWM型(上桥臂恒通,下桥臂PWM))
- [1.4.2 PWM-ON型(上桥臂PWM,下桥臂恒通)](#1.4.2 PWM-ON型(上桥臂PWM,下桥臂恒通))
- [1.4.3 H_PWM-L_ON型(上下桥臂混合调制)](#1.4.3 H_PWM-L_ON型(上下桥臂混合调制))
- [1.4.4 双PWM调制(上下桥臂都使用PWM)](#1.4.4 双PWM调制(上下桥臂都使用PWM))
- [1.4.5 野火开发板的PWM策略选择](#1.4.5 野火开发板的PWM策略选择)
- 二、硬件接线
-
- [1. 开发板接线](#1. 开发板接线)
- [2. 驱动板](#2. 驱动板)
- 三、软件设计
-
- [1. 软件整体架构](#1. 软件整体架构)
- [2. 核心模块说明](#2. 核心模块说明)
-
- [3.2.1 **主控制层main.c**](#3.2.1 主控制层main.c)
- [3.2.2 **电机控制层bsp_bldcm_control.c**](#3.2.2 电机控制层bsp_bldcm_control.c)
- [3.2.3 速度调节](#3.2.3 速度调节)
- [3.2.4 **定时器硬件层bsp_motor_tim.c**](#3.2.4 定时器硬件层bsp_motor_tim.c)
-
- [**TIM8 - PWM生成**:](#TIM8 - PWM生成:)
- [**TIM5 - Hall传感器**:](#TIM5 - Hall传感器:)
- [**关键算法 - 六步换向**:](#关键算法 - 六步换向:)
-
- [case1 步6](#case1 步6)
- [case 2 步骤4](#case 2 步骤4)
- [3. 安全机制](#3. 安全机制)
本系列博文使用的驱动板是野火无刷电机驱动板(20220514版本)。
本系列博文使用的是野火F407骄阳开发板。
本博文部分资源摘自野火官方《电机应用开发指南》。

一、直流无刷电机速度控制原理
1.1 速度控制的基本概念
直流无刷电机(BLDC)的速度控制是指通过调节输入电机的电参数,使电机达到并维持在期望的转速。速度控制的核心原理是:电机的转速与输入电压(或等效电压)成正比。
1.2 影响电机转速的因素
直流无刷电机的转速主要受以下因素影响:
- 电枢电压:电压越高,转速越快
- 负载转矩:负载越大,转速越低
- 励磁磁通:由永磁体决定,对于给定电机为固定值
理想情况下的转速公式为:
n = U a − I a R a C E Φ n = \frac{U_a - I_a R_a}{C_E \Phi} n=CEΦUa−IaRa
其中:
- n n n:电机转速(RPM)
- U a U_a Ua:电枢电压(V),即施加到电机绕组上的电压
- I a I_a Ia:电枢电流(A),流过绕组的电流
- R a R_a Ra:电枢绕组电阻(Ω)
- C E C_E CE:电动势常数,由电机结构决定
- Φ \Phi Φ:每极磁通量(Wb),对于永磁BLDC电机由永磁体决定
从公式可以看出,调节电枢电压 U a U_a Ua 即可改变电机转速,这也是PWM调速的理论基础。
1.3 速度控制方法
1.3.1 PWM调压控制
最常用的速度控制方法是PWM(脉冲宽度调制)控制。通过调节PWM占空比来改变施加到电机上的平均电压,从而实现调速。
工作原理:
- PWM信号是一个周期性的方波
- 占空比(Duty Cycle)表示高电平时间占整个周期的比例
- 占空比越大,平均电压越高,电机转速越快
- 占空比越小,平均电压越低,电机转速越慢
例如:
- 50%占空比 → 平均电压为电源电压的一半 → 中等转速
- 80%占空比 → 平均电压为电源电压的80% → 较高转速
- 20%占空比 → 平均电压为电源电压的20% → 较低转速
1.3.2 闭环速度控制
为了提高速度控制精度,通常采用闭环控制系统:
- 速度检测:使用霍尔传感器、编码器或反电动势检测获取实际转速
- 误差计算:将目标转速与实际转速比较,得到速度误差
- PID调节:通过PID算法计算控制量,调整PWM占空比
- 输出调整:根据PID输出更新PWM信号,使电机转速趋近目标值
闭环控制的优势:
- 能够抵抗负载变化带来的速度波动
- 提高稳态精度
- 加快动态响应
1.3.3 野火BLDC开发板霍尔传感器的应用
下面是来自官方文档的野火 57BL75S10-230TF9 型号直流无刷电机规格:

注意这里的电气规格表:
- 极对数(Rotor poles):4对极
- 额定转速(RATED SPEED):3000 RPM
- 空载转速(NOLOAD SPEED):4500RPM
- 电压(VOLTAGE):24VDC
野火BLDC开发板使用3个霍尔传感器(H1、H2、H3)来检测转子位置,实现速度反馈。
霍尔传感器工作原理:
- 3个霍尔传感器在空间上相隔120°电角度安装
- 当永磁转子旋转时,霍尔传感器检测到磁场变化,输出高低电平
- 3个霍尔信号组合成6种状态(001、010、011、100、101、110),对应转子的6个位置
- 每经过一个霍尔状态,转子转过60°电角度
速度计算方法:
n = 60 × f p × 6 n = \frac{60 \times f}{p \times 6} n=p×660×f
其中:
- n n n:电机转速(RPM)
- f f f:霍尔信号变化频率(Hz)
- p p p:电机极对数
- 6 6 6:每个电周期包含6个霍尔状态
STM32捕获霍尔信号的实现:
- 使用定时器输入捕获功能(TIM2/TIM3等)捕获霍尔信号跳变
- 记录相邻两次霍尔跳变的时间间隔 Δ t \Delta t Δt
- 根据时间间隔计算转速: n = 60 p × 6 × Δ t n = \frac{60}{p \times 6 \times \Delta t} n=p×6×Δt60
- 或者统计单位时间内的霍尔跳变次数来计算频率
来自官方的接线图,本试验使用的是F407-骄阳开发板:

1.4 PWM控制策略
在BLDC六步换相控制中,PWM信号可以施加在不同的开关管上,形成不同的调制方式。常见的PWM控制策略有以下几种:
1.4.1 ON-PWM型(上桥臂恒通,下桥臂PWM)

工作原理:
- 在每个60°电角度区间内,上桥臂的一个开关管保持导通(ON)
- 下桥臂的对应开关管使用PWM信号进行斩波控制
- 每60°切换一次导通的开关管组合
优点:
- 控制简单,只需一路PWM信号
- 电流检测方便,可以在下桥臂串联采样电阻
- 适合低成本应用
缺点:
- 上桥臂持续导通,损耗较大
- 动态响应相对较慢
野火开发板实现:
- 使用TIM1高级定时器的互补PWM输出功能
- 配置三路互补PWM,其中一路占空比可调,另两路根据换相状态固定导通或关闭
- 例如:在第一个60°区间,U相上桥臂恒通,V相下桥臂PWM调制,W相关闭
1.4.2 PWM-ON型(上桥臂PWM,下桥臂恒通)

工作原理:
- 与ON-PWM相反,上桥臂使用PWM调制
- 下桥臂的对应开关管保持导通
优点:
- 可以利用上桥臂MOS管的体二极管进行续流
- 在某些工况下效率更高
缺点:
- 电流检测较复杂,需要隔离采样或母线电流采样
- 上桥臂驱动需要自举电路或隔离电源
1.4.3 H_PWM-L_ON型(上下桥臂混合调制)

工作原理:
- 在不同扇区交替使用ON-PWM和PWM-ON模式
- 或者在某些扇区使用双PWM调制
优点:
- 可以优化不同工况下的效率
- 减少开关损耗
缺点:
- 控制逻辑复杂
- 需要更精细的时序控制
1.4.4 双PWM调制(上下桥臂都使用PWM)

工作原理:
- 上桥臂和下桥臂同时使用PWM信号控制
- 两个PWM信号互补且带有死区时间
优点:
- 可以实现四象限运行(正反转、电动/发电)
- 动态响应快,控制灵活
- 适合高性能应用
缺点:
- 控制复杂度高
- 开关损耗较大
- 对MCU性能要求高
1.4.5 野火开发板的PWM策略选择
野火BLDC开发板默认采用ON-PWM调制方式,原因如下:
-
硬件设计适配:
- 下桥臂有电流采样电阻,方便电流检测
- TIM1支持互补PWM输出,带死区插入功能
-
软件实现简单:
- 只需要控制一个通道的PWM占空比
- 换相逻辑清晰,易于理解和调试
-
性能满足需求:
- 对于大多数应用场景,ON-PWM已经足够
- 效率高,发热小
PWM频率选择:
- 推荐范围:10kHz ~ 20kHz
- 野火示例代码使用:16kHz
- 频率太低:电机噪音大,转矩脉动明显
- 频率太高:开关损耗增加,MCU负担重
二、硬件接线
1. 开发板接线

与电机通过直流有/无刷电机驱动接口2连接:

2. 驱动板

三、软件设计
1. 软件整体架构
main.c
├── bsp_bldcm_control.c (核心控制逻辑)
│ ├── bsp_motor_tim.c (PWM+Hall定时器)
│ ├── bsp_key.c (按键扫描)
│ └── bsp_debug_usart.c (串口通信)
├── bsp_led.c (状态指示)
└── system_stm32f4xx.c (时钟配置)
控制流程
#mermaid-svg-fqw0VX495ddRVRin{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fqw0VX495ddRVRin .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fqw0VX495ddRVRin .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fqw0VX495ddRVRin .error-icon{fill:#552222;}#mermaid-svg-fqw0VX495ddRVRin .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fqw0VX495ddRVRin .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fqw0VX495ddRVRin .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fqw0VX495ddRVRin .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fqw0VX495ddRVRin .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fqw0VX495ddRVRin .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fqw0VX495ddRVRin .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fqw0VX495ddRVRin .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fqw0VX495ddRVRin .marker.cross{stroke:#333333;}#mermaid-svg-fqw0VX495ddRVRin svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fqw0VX495ddRVRin p{margin:0;}#mermaid-svg-fqw0VX495ddRVRin .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fqw0VX495ddRVRin .cluster-label text{fill:#333;}#mermaid-svg-fqw0VX495ddRVRin .cluster-label span{color:#333;}#mermaid-svg-fqw0VX495ddRVRin .cluster-label span p{background-color:transparent;}#mermaid-svg-fqw0VX495ddRVRin .label text,#mermaid-svg-fqw0VX495ddRVRin span{fill:#333;color:#333;}#mermaid-svg-fqw0VX495ddRVRin .node rect,#mermaid-svg-fqw0VX495ddRVRin .node circle,#mermaid-svg-fqw0VX495ddRVRin .node ellipse,#mermaid-svg-fqw0VX495ddRVRin .node polygon,#mermaid-svg-fqw0VX495ddRVRin .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fqw0VX495ddRVRin .rough-node .label text,#mermaid-svg-fqw0VX495ddRVRin .node .label text,#mermaid-svg-fqw0VX495ddRVRin .image-shape .label,#mermaid-svg-fqw0VX495ddRVRin .icon-shape .label{text-anchor:middle;}#mermaid-svg-fqw0VX495ddRVRin .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fqw0VX495ddRVRin .rough-node .label,#mermaid-svg-fqw0VX495ddRVRin .node .label,#mermaid-svg-fqw0VX495ddRVRin .image-shape .label,#mermaid-svg-fqw0VX495ddRVRin .icon-shape .label{text-align:center;}#mermaid-svg-fqw0VX495ddRVRin .node.clickable{cursor:pointer;}#mermaid-svg-fqw0VX495ddRVRin .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fqw0VX495ddRVRin .arrowheadPath{fill:#333333;}#mermaid-svg-fqw0VX495ddRVRin .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fqw0VX495ddRVRin .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fqw0VX495ddRVRin .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fqw0VX495ddRVRin .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fqw0VX495ddRVRin .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fqw0VX495ddRVRin .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fqw0VX495ddRVRin .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fqw0VX495ddRVRin .cluster text{fill:#333;}#mermaid-svg-fqw0VX495ddRVRin .cluster span{color:#333;}#mermaid-svg-fqw0VX495ddRVRin div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fqw0VX495ddRVRin .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fqw0VX495ddRVRin rect.text{fill:none;stroke-width:0;}#mermaid-svg-fqw0VX495ddRVRin .icon-shape,#mermaid-svg-fqw0VX495ddRVRin .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fqw0VX495ddRVRin .icon-shape p,#mermaid-svg-fqw0VX495ddRVRin .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fqw0VX495ddRVRin .icon-shape .label rect,#mermaid-svg-fqw0VX495ddRVRin .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fqw0VX495ddRVRin .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fqw0VX495ddRVRin .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fqw0VX495ddRVRin :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} K1/K2
K3/K4
v命令
d命令
速度>0
速度=0
超时
系统启动
初始化外设
禁用电机输出
主循环
检测按键
检测串口
修改速度
修改方向
设置速度
设置方向
应用状态
使能电机
禁用电机
启动Hall传感器
输出PWM
Hall中断
读取Hall状态
六步换向
更新PWM通道
堵转检测
停止电机
2. 核心模块说明
3.2.1 主控制层main.c
- 系统初始化:时钟配置(168MHz)、外设初始化
- 主循环 :轮询处理按键输入(
deal_key_input)和串口命令(deal_serial_data) - 启动流程:LED→按键→串口→电机初始化→禁用输出(安全状态)
3.2.2 电机控制层bsp_bldcm_control.c
数据结构:
c
typedef struct {
motor_dir_t direction; // 正转/反转
uint16_t dutyfactor; // PWM占空比
uint8_t is_enable; // 使能状态
uint32_t lock_timeout; // 堵转超时
} bldcm_data_t;
核心功能:
- 速度控制 :
set_bldcm_speed()→ 设置PWM脉冲值 - 方向控制 :
set_bldcm_direction()→ 正转/反转切换 - 使能管理 :
set_bldcm_enable()→ 使能SD引脚 + 启动Hall传感器set_bldcm_disable()→ 停止PWM + 禁用Hall + 关闭SD
- 人机交互 :
- 串口协议:
v [速度],d [方向],?帮助 - 按键映射: K1加速、K2减速、K3正转、K4反转
- 串口协议:
3.2.3 速度调节
本程序的速度控制是 开环 PWM 占空比调节,没有转速闭环(无 PID、无测速反馈)。用户设定的「速度」本质是 TIM8 比较寄存器的脉冲宽度,在六步换向写入当前导通相的上桥 CCR。
关键参数 (bsp_motor_tim.h / bsp_bldcm_control.h):
| 宏 | 值 | 含义 |
|---|---|---|
MOTOR_PWM_PERIOD_COUNT |
5600 | PWM 周期计数(ARR+1) |
MOTOR_PWM_PRESCALER_COUNT |
2 | TIM8 预分频 |
MOTOR_PWM_MAX_PERIOD_COUNT |
5500 | 允许的最大速度设定值 |
BLDCM_SPEED_STEP |
550 | 按键每次加减的步进(满量程 1/10) |
PWM 频率(TIM8 时钟 168MHz):168MHz / 2 / 5600 ≈ 15kHz
占空比近似为 速度值 / 5600(例如 v 2800 → 约 50%)。
调用链:
串口 v 1000 / 按键 K1·K2
↓
set_bldcm_speed(v) // 保存 dutyfactor,调用 set_pwm_pulse
↓
set_pwm_pulse(v) // 仅更新 static motor_bldcm_pulse
↓
apply_motor_state() // v>0 → set_bldcm_enable;v=0 → set_bldcm_disable
↓
Hall 换向中断 bldcm_commutate()
↓
__HAL_TIM_SET_COMPARE(..., motor_bldcm_pulse) // 写入当前导通相 CCR
核心代码:
c
void set_bldcm_speed(uint16_t v)
{
motor_data.dutyfactor = v;
set_pwm_pulse(v); // 只改变量,不直接写 TIM 寄存器
}
void set_pwm_pulse(uint16_t pulse)
{
motor_bldcm_pulse = pulse;
}
使能与停转 (apply_motor_state):
dutyfactor > 0:拉高 SD → 启动 Hall → 首次换向输出 PWMdutyfactor == 0:停 Hall、CCR 清零、下桥 GPIO 拉低、SD 关断
串口 v 0 或 K2 减到 0 均走停转路径,不是「零占空比空转」。
输入方式:
| 来源 | 函数 | 行为 |
|---|---|---|
串口 v [0~5500] |
deal_serial_data() |
atoi 解析,set_bldcm_speed + apply_motor_state |
| K1 加速 | deal_key_input() |
speed += BLDCM_SPEED_STEP,上限 5500 |
| K2 减速 | deal_key_input() |
speed -= BLDCM_SPEED_STEP,下限 0 |
需注意:
- 调速有延迟 :
set_pwm_pulse不立刻改 TIM8 CCR;新占空比要等到 下一次 Hall 换向 写入导通相后才生效。运行中改速度,当前步的 CCR 会保持旧值直到下个 Hall 边沿。 - 仅调制一相上桥 :六步中每一时刻只有一相
motor_bldcm_pulse,其余相 CCR=0,因此速度调节的是该步导通相的母线电压占比,而非三路同时 PWM。 - 速度与方向独立 :
d 0/1或 K3/K4 只改direction换向表;速度仍由dutyfactor决定,改方向后需apply_motor_state重新使能才会按新方向转。
3.2.4 定时器硬件层bsp_motor_tim.c
TIM8 - PWM生成:
- 频率配置:周期5600,预分频2
- 三路 PWM 输出(CH1/CH2/CH3)至 PI5/PI6/PI7;下桥 PH13/14/15 由 GPIO 控制(非 TIM 互补通道)
- 换向事件由软件触发(
TIM_COMMUTATION_SOFTWARE) - 死区与直通风险见下文 §3 死区与直通风险
TIM5 - Hall传感器:
无刷电机转子每转过 60° 电角度,Hall 传感器输出组合会变化一次(共 6 种有效状态)。本工程用 TIM5 的 Hall 传感器接口模式 (HAL_TIMEx_HallSensor_Init)把三路信号接到定时器,任一 Hall 边沿变化时触发换向。
hall_tim初始化代码:
c
static void hall_tim_init(void)
{
TIM_HallSensor_InitTypeDef hall_sensor_cfg;
MOTOR_HALL_TIM_CLK_ENABLE();
motor_htimx_hall.Instance = MOTOR_HALL_TIM;
motor_htimx_hall.Init.Prescaler = MOTOR_HALL_PRESCALER_COUNT - 1;
motor_htimx_hall.Init.CounterMode = TIM_COUNTERMODE_UP;
motor_htimx_hall.Init.Period = MOTOR_HALL_PERIOD_COUNT - 1;
motor_htimx_hall.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
hall_sensor_cfg.IC1Prescaler = TIM_ICPSC_DIV1;
hall_sensor_cfg.IC1Polarity = TIM_ICPOLARITY_BOTHEDGE;
hall_sensor_cfg.IC1Filter = 10;
hall_sensor_cfg.Commutation_Delay = 0U;
HAL_TIMEx_HallSensor_Init(&motor_htimx_hall, &hall_sensor_cfg);
HAL_NVIC_SetPriority(MOTOR_HALL_TIM_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(MOTOR_HALL_TIM_IRQn);
}
硬件连接 (见 bsp_motor_tim.h):
| 信号 | GPIO | TIM5 通道 |
|---|---|---|
| Hall U | PH10 | CH1 |
| Hall V | PH11 | CH2 |
| Hall W | PH12 | CH3 |
三路引脚配置为复用推挽 + 内部上拉;STM32 内部将 U/V/W 做 异或(XOR) 后接到 TI1,因此任意一相电平翻转都会产生捕获/触发事件,无需分别监听三个外部中断。
定时器参数:
- 预分频
128、周期0xFFFF:计数器在相邻 Hall 边沿之间自由计数,可用于测速(本例主要用作换向触发) - 输入捕获:
IC1Polarity = TIM_ICPOLARITY_BOTHEDGE(双边沿),上升沿与下降沿均触发,换向响应更快 - 数字滤波:
IC1Filter = 10,抑制 PWM 开关、长线传输等带来的毛刺 - 换向延迟:
Commutation_Delay = 0(无额外硬件延迟)
中断与换向流程:
- 电机使能时调用
hall_enable():开启TIM_IT_TRIGGER/TIM_IT_UPDATE,启动 Hall 模式,并主动调用一次HAL_TIM_TriggerCallback()完成首次换向 - 任一 Hall 边沿 →
TIM5_IRQHandler→HAL_TIM_IRQHandler→HAL_TIM_TriggerCallback() - 回调中
get_hall_state()读取 PH10/11/12,组合为 3 位状态值(1~6,对应六步换向表) - 按
get_bldcm_direction()选择正/反转换向表,更新 TIM8 各相 PWM 与下桥 GPIO HAL_TIM_GenerateEvent(..., TIM_EVENTSOURCE_COM)软件触发 COM 事件,与 TIM8 换向逻辑同步- 停转时
hall_disable()关闭 Hall 中断并停止 TIM5
关键算法 - 六步换向:
六步换向核心在 bsp_motor_tim.c 里实现,通过中断触发。这里首先要获取霍尔传感器的位置,根据位置判断步骤,以前case为例:
case1 步6
c
// 关闭V相上桥PWM + 关闭V相下桥
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_2, 0);
HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET);
// 关闭W相上桥PWM + 关闭W相下桥
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_3, 0);
HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_RESET); // ⚠️ 代码bug:应为OCNPWM1
// U相上桥输出PWM + W相下桥常通
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_1, motor_bldcm_pulse);
HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_SET);
导通路径: U相 → W相 (电流从U流入,W流出)
| 相位 | 上桥臂(OC) | 下桥臂(OCN) | 状态说明 |
|---|---|---|---|
| U相 | PWM脉冲 | RESET(0) | 上桥PWM调制 |
| V相 | 0(关闭) | RESET(0) | 完全关闭 |
| W相 | 0(关闭) | SET(1) | 下桥常通 |
MOS管开关状态:
- ✅ Q_UH (U上): PWM导通/关断
- ❌ Q_UL (U下): 始终关断
- ❌ Q_VH (V上): 始终关断
- ❌ Q_VL (V下): 始终关断
- ❌ Q_WH (W上): 始终关断
- ✅ Q_WL (W下): 始终导通
case 2 步骤4
c
// 关闭W相上桥PWM + 关闭W相下桥
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_3, 0);
HAL_GPIO_WritePin(MOTOR_OCNPWM3_GPIO_PORT, MOTOR_OCNPWM3_PIN, GPIO_PIN_RESET);
// 关闭U相上桥PWM + 关闭U相下桥
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_1, 0);
HAL_GPIO_WritePin(MOTOR_OCNPWM2_GPIO_PORT, MOTOR_OCNPWM2_PIN, GPIO_PIN_RESET); // ⚠️ 代码bug:应为OCNPWM1
// V相上桥输出PWM + U相下桥常通
__HAL_TIM_SET_COMPARE(&motor_htimx_bldcm, TIM_CHANNEL_2, motor_bldcm_pulse);
HAL_GPIO_WritePin(MOTOR_OCNPWM1_GPIO_PORT, MOTOR_OCNPWM1_PIN, GPIO_PIN_SET); // ⚠️ 代码bug:应为OCNPWM1
导通路径: V相 → U相 (电流从V流入,U流出)
| 相位 | 上桥臂(OC) | 下桥臂(OCN) | 状态说明 |
|---|---|---|---|
| U相 | 0(关闭) | SET(1) | 下桥常通 |
| V相 | PWM脉冲 | RESET(0) | 上桥PWM调制 |
| W相 | 0(关闭) | RESET(0) | 完全关闭 |
MOS管开关状态:
- ❌ Q_UH (U上): 始终关断
- ✅ Q_UL (U下): 始终导通
- ✅ Q_VH (V上): PWM导通/关断
- ❌ Q_VL (V下): 始终关断
- ❌ Q_WH (W上): 始终关断
- ❌ Q_WL (W下): 始终关断
Hall状态分析: 此时Hall传感器检测到UVW=010(U=0,V=1,W=0),转子位置相比步1转过60°电角度
3. 安全机制
3.3.1 SD(Shutdown)引脚硬件关断
驱动芯片的 SD(Shutdown) 引脚接 PE6,由 MCU 直接控制功率级使能:
MOTOR_BLDCM_ENABLE_SD():输出高电平,允许驱动芯片工作MOTOR_BLDCM_DISABLE_SD():输出低电平,硬件关断 MOS 驱动输出
set_bldcm_enable() / set_bldcm_disable() 中与 Hall、PWM 配合使用:使能时先拉高 SD 再启动 Hall;禁用时先停 Hall 与 PWM,最后拉低 SD,确保功率级彻底关闭。
3.3.2 堵转超时检测(TIM8 更新事件计数)
转子卡住时 Hall 状态不再变化,可通过「Hall 多久没更新」判断堵转:
- TIM8 UPDATE 中断 :每个 PWM 周期(约 67us,
motor_update++) - Hall 换向中断 :
HAL_TIM_TriggerCallback中若 Hall 位置 (1~6)发生变化,则motor_update = 0 - 判定 :
motor_update > MOTOR_STALL_PWM_PERIODS(8000 ≈ 533ms)→ 置位stall_pending,主循环关断并串口输出Stall timeout
3.3.3 死区与直通风险
本程序采用 PWM-ON 调制(上桥 TIM8 PWM、下桥 GPIO 常通),未 使用 TIM8 互补输出,也未 调用 HAL_TIMEx_ConfigBreakDeadTime() 配置硬件死区。本 lesson4 代码里不存在 MCU 侧死区时间。但如前章所述,野火开发板在硬件上实现了MOS前级逻辑互锁。
3.3.4 上电默认禁用电机
main() 中完成 bldcm_init() 后立即调用 set_bldcm_disable(),保证上电后 SD 为低、Hall 未启动、PWM 占空比为 0。用户需通过串口或按键将速度设为 > 0 后,apply_motor_state() 才会调用 set_bldcm_enable() 启动电机。
本文源代码位置:
https://gitee.com/xundh/learn-motor-stm32
程序使用方法:
按键:
- reset: 停止
- key1: 加速
- key2: 减速
- key3: 正转
- key4: 反转
串口通讯:
