资讯详情

资讯详情

STM32 BLDC无刷直流电机控制:六步换相与PID调速实战

简介这是一份基于STM32F103RB的无刷直流电机BLDC控制工程实例面向嵌入式初学者和电机控制开发者可帮助理解三相逆变器驱动、PWM调速、反电动势或霍尔位置检测以及PID闭环控制等关键环节。包内共402个文件约9.12MB以C语言源码、头文件、STM32工程配置如uvproj、sct、ini为主同时包含hex、axf、map等编译输出文件便于直接烧录与对照分析少量txt与htm文档可用于查阅说明。整个工程目录完整从电机驱动到底层外设配置均有涉及非常适合通过实际代码掌握STM32定时器、ADC、DMA等资源的综合运用。已有214人学习是一份适合自学与项目参考的BLDC控制实例。1. 为什么 BLDC 无刷直流控制总要落到 STM32 定时器上如果只给一个直流电机通电它只会疯狂加速直到机械极限。BLDC 与有刷直流电机最大的区别不在于没有电刷这个机械特征而在于换相必须由控制器主动完成——转子转到什么位置、哪一相通电、通电多少时间都要在微秒级内同步完成。这个同步点恰好落在 STM32 这类 MCU 的优势区高级定时器输出互补 PWM 并插入死区配合 ADC 和比较器做反电动势检测一套外设就撑起六步换相控制。很多人最终卡住的不是电机本身而是代码写完了电机不转。下面按六步换相时序、TIM1 六路 PWM 生成、霍尔换相中断、PID 调参这个顺序推进适合能烧录程序但没系统性跑通过 BLDC 的开发者。2. 六步换相原理需要先于代码存在从 Hall 信号推导导通相对于方波驱动的 BLDC控制器的工作本质是把三相桥中的功率管按两两导通模式轮流打开。难点在于导通顺序必须与转子位置严格同步而这个同步信号来自于霍尔元件或反电动势过零检测。不了解这一点就直接抄换相表电机要么不转要么反转后过流保护。2.1 梯形波反电动势与两两导通的时间窗口BLDC 的永磁转子旋转时每相绕组中会感应出梯形波反电动势。梯形波的平顶区域意味着该相电势稳定适合在对应时间窗口内对该相通电做功。六步换相Six-Step Commutation正是利用这个特征任意时刻只让两相导通第三相悬空用于检测反电动势或处于非导通状态。通电相的选择必须领先转子磁场位置 90 度电角度才能获得最大转矩输出。如果偏置角度错位电流做的功一部分变成热量表现为电机温升快、效率低、噪音大。2.2 六步换相序列表拿到 Hall 值后如何映射占空比使用 3 个霍尔传感器时转子每转过 60 度电角度三路 Hall 信号中就会有一路发生变化组合出 6 个有效状态。以 120 度电气安装角的霍尔排列为例霍尔值从 101 变为 100对应换相表需要将导通相从 AB- 切换为 AC-。以下是标准的逻辑换相表——这里的核心是 Hall 值与功率管开关的对应关系霍尔值 (H1 H2 H3)电角度范围上桥导通下桥导通电流路径0100°~60°Q1 (A)Q4 (B-)A → B01160°~120°Q1 (A)Q6 (C-)A → C001120°~180°Q3 (B)Q6 (C-)B → C101180°~240°Q3 (B)Q2 (A-)B → A100240°~300°Q5 (C)Q2 (A-)C → A110300°~360°Q5 (C)Q4 (B-)C → B如果电机需要反转通常把这张表倒序使用或者把 PWM 占空比放在上桥、下桥做同步调制。表中的 Hall 值与导通相映射取决于霍尔传感器的安装角度和三相绕组线序。逻辑上正确不代表物理上能转首次上电前应先用示波器测量三路 Hall 信号与反电动势的相对相位关系再决定是否需要对换相表做整体偏移。提示这张表是工程基线。同一颗电机在不同驱动板上因为采样电阻位置、母线电压检测点不同换相时刻会产生微小的电角度偏移表现就是相电流畸变。2.3 Hall 安装角度的两种排布120° 与 60° 的区别120° 安装是指三个霍尔传感器物理上相差 120 度输出信号按顺序跳变换相表对应上面 6 个状态。60° 安装的霍尔器件彼此间隔 60 度信号跳变更多但 6 个有效状态里会出现连续两次同一相切换的边界情况。市面上绝大多数 BLDC 驱动方案都按 120° 电气安装配合 TIM 的编码器模式读取 Hall 信号。对于 60° 安装换相表的地址映射要重新推导不能直接套用 120° 的表格。常见做法是把 Hall 信号接入 STM32 的输入捕获通道利用 TIM 编码器模式直接解码出转子位置。也可以把三路 Hall 接在普通 GPIO 上用 Hall 值查表——这是后文将要采用的实现方式因为它不依赖特定定时器的编码器资源逻辑也直观。Hall 信号本身是数字电平输出形式分为推挽和开漏两种。开漏输出的 Hall 元件需要外部上拉电阻通常 4.7kΩ 到 10kΩ推挽输出可直接接 MCU 引脚但要注意 3.3V 电平容忍度。电机线束中的 UVW 功率线与 Hall 信号线捆扎在一起时会引入共模噪声建议在 PCB 上给三路 Hall 信号加上 100Ω 串联电阻和 10nF 对地电容形成低通滤波边沿抖动严重的场合还能在 GPIO 配置里启用施密特触发器。3. 用 STM32 高级定时器生成 6 路 PWMTIM1 配置与死区参数从上一章的换相表可以看出三相桥 6 个功率管的开关组合由 Hall 值唯一确定。接下来要解决的是如何让 STM32 在微秒级内完成 PWM 占空比切换、死区插入和输出使能。写成裸机代码时这三件事必须在一个中断周期内全部完成任何一个环节延时不均都会造成换相噪音。3.1 三相全桥驱动电路的关键参数不要把 MOSFET 直接接到 MCU 引脚。虽然部分集成驱动芯片内部已经集成了逻辑翻译和电荷泵但仍有相当多项目使用分立元件方案MCU 输出 3.3V PWM → 栅极驱动器如 IR2101、EG2130→ 高压半桥。分立方案成本低、功率级灵活但外围参数必须与外设配置对齐。挑选栅极驱动器时三个参数与 MCU 外设配置直接相关死区时间驱动器本身不会产生死区必须在 MCU 定时器时基里配置。死区太短会造成上下桥直通典型值取 500ns~1μs。输入逻辑电平部分驱动器为 3.3V 兼容另一些需要 5V 电平这决定了是否要在 TIM 输出通道上做电平转换。高端供电方式自举电容靠下桥导通时充电。若 PWM 占空比长时间接近 100%下桥几乎没有导通窗口自举电容电压跌落高边 MOS 进入线性区发热剧增。如果电机额定 48V 或更高还要在低压控制侧和高压母线之间保证地电位的一致性否则 MCU 复位是家常便饭。栅极驱动芯片的 VBS 欠压锁定值通常在 8V 左右母线电压低于 12V 时高边驱动可能无法建立这也是低压 BLDC 板有时电机抖而不转的原因之一。3.2 TIM1 六路 PWM互补输出与中心对齐模式TIM1 和 TIM8 是 STM32 里少有的带互补输出通道的高级定时器。TIM1_CH1/CH1N、CH2/CH2N、CH3/CH3N 可以直接产生三相半桥所需的六路 PWM且能在硬件层面插入死区无需软件干预。下面以标准外设库为例void Bldc_Tim1_PwmInit(uint16_t arr, uint16_t psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能时钟GPIOA用于CH1-CH3GPIOB用于CH1N-CH3N RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // PA8: TIM1_CH1, PA9: TIM1_CH2, PA10: TIM1_CH3 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // PB13: CH1N, PB14: CH2N, PB15: CH3N GPIO_InitStructure.GPIO_Pin GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_Init(GPIOB, GPIO_InitStructure); // 中心对齐模式下 PWM频率 72MHz / (psc1) / (arr1) / 2 TIM_TimeBaseStructure.TIM_Period arr; // arr2249 时 16kHz TIM_TimeBaseStructure.TIM_Prescaler psc; // psc0 不分频 TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_CenterAligned1; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 三路PWM同时输出极性为高有效 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity TIM_OCNPolarity_High; TIM_OC1Init(TIM1, TIM_OCInitStructure); TIM_OC2Init(TIM1, TIM_OCInitStructure); TIM_OC3Init(TIM1, TIM_OCInitStructure); // 死区寄存器值 DTGTdts 1/72MHz ≈ 13.9nsDTG72 → 死区≈1us TIM_BDTRInitStructure.TIM_OSSRState TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState TIM_OSSIState_Disable; TIM_BDTRInitStructure.TIM_LOCKLevel TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime 72; TIM_BDTRInitStructure.TIM_Break TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput TIM_AutomaticOutput_Enable; TIM_BDTRInit(GPIOA, TIM_BDTRInitStructure); TIM_Cmd(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM1, ENABLE); // MOE主输出使能少这行则无输出 }这段代码做完三件事配置 PA 和 PB 引脚为复用推挽把 TIM1 设置为中心对齐模式下的 PWM1通过 BDTR 寄存器插入死区并打开主输出使能。参数上有几个容易出问题的位置。第一中心对齐模式里更新事件同时在上溢和下溢产生换相时刻要以此为同步基准否则 CCR 更新可能被覆盖。第二TIM_CtrlPWMOutputs必须在TIM_Cmd之后调用否则 MOE 位被清零PWM 引脚全部处于无效电平。第三死区时间如果配置过大半桥有效导通时间变短电机在高转速下出现加不上速的现象这不是 PID 问题而是死区吃掉有效占空比。3.3 Hall GPIO 输入与触发源选择Hall 信号是边沿敏感的换相中断必须在 Hall 跳变的瞬间进入。把三路 Hall 接到 PC0/PC1/PC2同时配置为 EXTI 外部中断源。为避免边沿抖动时重复触发中断GPIO 应开启上拉硬件上加 RC 滤波。void Bldc_Hall_GpioInit(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource0); GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource1); GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOC, GPIO_InitStructure); EXTI_InitStructure.EXTI_Line EXTI_Line0 | EXTI_Line1 | EXTI_Line2; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Rising_Falling; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // EXTI1_IRQn 和 EXTI2_IRQn 的NVIC配置同理 }EXTI_Trigger_Rising_Falling是双沿触发不论 Hall 从 0 变 1 还是从 1 变 0 都会进中断。中断服务函数里必须在读取 GPIO 电平后重新组合出完整的 Hall 码不能只依据触发的那一路。每次退出中断前清挂起位否则同一边沿会反复触发换相顺序错乱后电机直接抖动停滞。4. 换相中断与速度闭环从电角度同步到 PID 调速定时器输出 PWM 只是手段真正的 BLDC 控制中枢在 Hall 中断里。每次霍尔状态变化要在几微秒内完成读取新 Hall 码 → 查找换相表 → 更新 CCR 寄存器的全流程同时把上一次换相的时间间隔记录到速度测量单元。4.1 Hall 换相中断里的最小动作集#define HALL_A() GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0) #define HALL_B() GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1) #define HALL_C() GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_2) static volatile uint8_t bldc_comm_step[8]; static volatile uint16_t bldc_duty; void Bldc_SetPwm(uint16_t ch1, uint16_t ch2, uint16_t ch3) { TIM_SetCompare1(TIM1, ch1); TIM_SetCompare2(TIM1, ch2); TIM_SetCompare3(TIM1, ch3); } void Bldc_HallIsr(void) { uint8_t hall_code, step; uint32_t now_tick; static uint32_t last_tick; hall_code ((uint8_t)HALL_C() 2) | ((uint8_t)HALL_B() 1) | (uint8_t)HALL_A(); step bldc_comm_step[hall_code 0x07]; switch (step) { case 0: Bldc_SetPwm(bldc_duty, 0, 0); break; case 1: Bldc_SetPwm(bldc_duty, 0, 0); break; case 2: Bldc_SetPwm(0, bldc_duty, 0); break; case 3: Bldc_SetPwm(0, bldc_duty, 0); break; case 4: Bldc_SetPwm(0, 0, bldc_duty); break; case 5: Bldc_SetPwm(0, 0, bldc_duty); break; default: TIM_CtrlPWMOutputs(TIM1, DISABLE); break; } now_tick TIM_GetCounter(TIM2); speed_period_ticks now_tick - last_tick; last_tick now_tick; EXTI_ClearITPendingBit(EXTI_Line0 | EXTI_Line1 | EXTILine2); }上面 switch 的含义是根据 step 把bldc_duty写入当前导通相所在通道的比较寄存器并把悬空相的比较值清零。实际工程里更常见的写法是把六组 PWM 配置预先存成uint16_t pwm_table[6][3]数组中断里直接做三次赋值避免 switch 块随引脚重映射而散落。数组方式也方便日后把某相上桥常通、下桥斩波的调制方式做成实时切换。4.2 占空比在这里如何生效设定TIM_OCMode_PWM1且极性为高时CCR 值越大有效电平宽度越宽。在中心对齐模式下CCR 等于 ARR 时为 100% 占空比CCR 等于 0 时为 0%。BLDC 两两导通模式里上下桥由硬件按互补关系工作CCR 数值本身只决定上桥脉冲宽度下桥自动反相并插入死区不需要软件干涉。调速阶段改变bldc_duty即改变所有导通相的比较值。如果只调节下桥导通窗口需要把对应通道的TIM_OCPolarity改为低用相反方向改变 CCR——这是上桥常通、下桥斩波的调制方式EMI 表现好但母线利用率略低。4.3 用增量式 PID 把目标速度锁在 Hall 换相频率上BLDC 速度环的反馈可以直接测量 Hall 换相周期。设定目标转速后把换相间隔换算为速度值用增量式 PID 输出占空比。电机电气频率与转速的关系电气频率 转速 × 极对数 / 60而每电气周期发生 6 次换相所以换相频率 电气频率 × 6。typedef struct { float kp, ki, kd; float err_prev1, err_prev2; float out_prev; float out_max, out_min; } PidInc_t; uint16_t Bldc_PidUpdate(PidInc_t *pid, float target, float feedback) { float err target - feedback; float delta pid-kp * (err - pid-err_prev1) pid-ki * err pid-kd * (err - 2.0f * pid-err_prev1 pid-err_prev2); float out pid-out_prev delta; if (out pid-out_max) out pid-out_max; if (out pid-out_min) out pid-out_min; pid-err_prev2 pid-err_prev1; pid-err_prev1 err; pid-out_prev out; return (uint16_t)out; }delta是本次增量ki项直接作用在误差上而不是积分累积上这是增量式 PID 的标准形式。out_cur被限幅到arr / 2以内防止满载时占空比接近 100% 导致自举电容无法充电。调用时机放在速度测量完成后、下一次 Hall 中断到来之前在后台循环里以定时器节拍执行即可。4.4 启动策略开环强拖再切闭环BLDC 不能像有刷电机那样一上电直接给额定电压。静止时霍尔输出只能反映转子停在哪个扇区无法知道确切角度直接按换相表通电可能产生零转矩盲区。常见做法有两种定位法任意一相通电 1 到 2 秒利用定子磁拉力把转子钳到已知位置再按换相表启动。适用于负载转动惯量不大的场合。强拖法以低占空比、缓慢递增的换相频率驱动电机转速上升后才切换到 Hall 闭环。强拖法启动平稳但需要记录定位过程中的换相次数否则转子上电瞬间的加速度不可控。对于风机、泵这类空载启动需求通常用定位法150ms 定位加 300ms 斜坡加速到目标转速的一半然后切到 PID 闭环。惯量较大的负载定位法的停滞感明显可以改为先检测零速状态再快速扫频强拖。5. Hall 相位偏置角度的校准技巧与验证上一章的控制环正常工作之后大概率会碰到两类现象电机能转但电流大、噪音尖或者某些转速段电机振动剧烈像堵转却未停。这两种症状的根源指向同一个问题——Hall 信号与反电动势的相位偏置没有校准到位。5.1 什么情况下必须重新校准偏置角度霍尔传感器安装在电机内部机械上存在个体公差。直接套用电机的出厂换相表上电相电流波形必然存在相位偏移。判断依据是看母线电流给定固定占空比用钳形表或采样电阻观测相电流如果波形高电平宽度与低电平不对称或换相时刻附近出现明显电流尖峰就是相序或偏置角度需要调整。校准思路是重写换相表的偏移值把Hall 值变化物理上对应的电角度与实际导通时刻对齐。若 Hall 安装超前中性点 10 度电角度换相表的索引应整体后移对应角度即按 60 度一个扇区计算约偏移 0.167 个扇区工程上直接在 Hall 中断里对 hall 码做位循环移位#define HALL_PHASE_ADJUST 1 // 单位: 扇区正值代表提前换相 uint8_t hall_code ((uint8_t)HALL_C() 2) | ((uint8_t)HALL_B() 1) | (uint8_t)HALL_A(); #if HALL_PHASE_ADJUST 0 hall_code ((hall_code HALL_PHASE_ADJUST) | (hall_code (3 - HALL_PHASE_ADJUST))) 0x07; #endifHALL_PHASE_ADJUST调整的是扇区级粗校准适用于霍尔安装公差在正负半个扇区内的场景。更细的亚扇区偏差通过在 Hall 边沿后延迟若干微秒再切换 CCR 补偿单位延迟对应的电角度 360 × 延迟时间 × 电气频率。5.2 用反电动势过零做一次软件自校准不需要额外硬件利用 BLDC 悬空相的反电动势过零检测即可完成精确校准。算法原理电机由外部带着匀速旋转时每相反电动势过零点相对 Hall 跳变的位置应当是固定的电角度。把过零信号捕获后与 Hall 事件的时间差换算成角度补偿值存到 Flash 参数区。需要注意反电动势过零检测只能在中高速区使用低速时反电动势幅值太小过零比较器容易误触发校准时的转速应取额定转速的 30% 以上。5.3 换相表调乱后的典型排查顺序排查顺序固定为以下三步先确认三个 Hall 对地波形为标准的 120 度互差方波再用手缓慢旋转转子逐个记录 9 种 Hall 组合中实际出现的 6 种有效码最后用示波器对比换相时刻与某相反电动势从正到负的过零点的相位差差多少度就反推偏移量。如果下载了别人的工程代码但没有改 Hall 引脚映射还有一种低级错误GPIO 的上拉配置不对导致 Hall 悬空值不稳定查表有时命中有时偏一个扇区症状看起来像偏置乱跳。这种情况排除相位校准之前先把三路 Hall 的上拉全部打开并确认 Hall 到 MCU 的排线没有引入串扰。上面给出的六步换相表、TIM1 互补 PWM 初始化和增量式 PID 都是可复现的工程基线你把 Hall 偏置校完再回看整个启动过程BLDC 控制的难点就只剩换相时序和电流限制两件事了。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →