资讯详情

资讯详情

STM32有感BLDC驱动开发:从霍尔解码到HRTIM控制

简介面向STM32应用开发与电机控制学习场景这份资料提供了一套完整的有感直流无刷电机BLDC驱动控制C语言工程源码适合自动化、电子信息、计算机等相关专业学生用于毕业设计、课程设计或项目初期演示也适合刚接触电机驱动的开发者对照学习Hall传感器换相与PWM控制逻辑。包内共733个文件以433个C源码和226个头文件为主体涵盖HAL库驱动、控制算法与主程序逻辑同时包含少量启动汇编文件、链接脚本、库文件及说明文档压缩包整体约24.11MB工程结构清晰便于直接在STM32平台导入查看。目前已有680人学习下载代码经验证可运行具备较高参考价值。除基础驱动功能外还可在此基础上修改换相时序、调速策略或添加保护逻辑进一步扩展为完整的电机控制方案。1. 这套STM32有感BLDC工程拆开以后先看哪几个文件压缩包解压出来第一眼看到的不是main.c而是libarm_cortexM4l_math.a、libarm_cortexM4lf_math.a、arm_common_tables.c、stm32f3xx_hal_hrtim.c这一串文件名。光凭这几个文件就能确定工程跑在STM32F3系列上定时器用到了HRTIM文件里还保留了两套ARM DSP数学库源码。对六步换相的有感BLDC来说DSP库不是必需品这套文件结构更像是从ST电机控制例程或CubeMX模板里带出来的。真正决定电机转不转的是霍尔解码、换相表和PWM保护三块。文章按这个顺序拆适合做课程设计、毕业设计、第一次接触电机控制的开发者最后留一个不开示波器也能验证换相表对错的土办法对已经在做驱动的同事也有用。2. 霍尔信号解码与换相表六组导通状态为什么是这个顺序2.1 120°安装的霍尔电平组合与实测precedence先解决最容易绕晕的问题三路霍尔信号只有三个bit凭什么决定六个换相状态答案在霍尔的安装角度。常见电机里霍尔按120°电角度安装三路方波彼此错开120°一个电周期内每60°电角度出现一组新的电平组合六个状态分别是101、100、110、010、011、001反转时顺序倒过来。注意这个映射跟霍尔传感器在端盖上的实际位置、电机相序都有关不同厂商的电机这六个状态对应的导通相不一定相同所以网上抄来的换相表不能直接套必须先实测。实测霍尔编码(ABC)推荐导通相状态索引101A上桥导通、B下桥导通0100A上桥导通、C下桥导通1110B上桥导通、C下桥导通2010B上桥导通、A下桥导通3011C上桥导通、A下桥导通4001C上桥导通、B下桥导通5这张表有个前提霍尔按120°安装且相序为A-B-C。如果手里电机是60°安装或者绕线顺序不同表里的状态索引顺序会整体平移或镜像。所以正确做法是先用自己的电机测一遍测法很简单控制器只接逻辑电源不接功率电源用串口打印读霍尔编码手动缓慢旋转电机记录六个状态出现顺序再填表。低速手转时霍尔信号很干净不会遇到后面要讲的毛刺。2.2 换相表的数据结构查表比在中断里现算快得多#define COMM_INVALID 0xFF static const uint8_t hall_to_comm[8] { COMM_INVALID, /* 0b000: 理论不存在用于错误检测 */ 5, /* 0b001: C B- */ 3, /* 0b010: B A- */ 4, /* 0b011: C A- */ 1, /* 0b100: A C- */ 0, /* 0b101: A B- */ 2, /* 0b110: B C- */ COMM_INVALID /* 0b111: 可当三路霍尔短路故障 */ }; void comm_set(uint8_t idx, uint16_t duty) { /* 先全部输出低电平防止换相瞬间上下桥直通 */ HAL_GPIO_WritePin(AH_GPIO_Port, AH_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(BH_GPIO_Port, BH_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(CH_GPIO_Port, CH_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(AL_GPIO_Port, AL_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(BL_GPIO_Port, BL_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(CL_GPIO_Port, CL_Pin, GPIO_PIN_RESET); /* 三个上桥用PWM占空比调压 */ __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_2, duty); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_3, duty); switch (idx) { case 0: /* A B- */ HAL_GPIO_WritePin(AH_GPIO_Port, AH_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(BL_GPIO_Port, BL_Pin, GPIO_PIN_SET); break; case 1: /* A C- */ HAL_GPIO_WritePin(AH_GPIO_Port, AH_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(CL_GPIO_Port, CL_Pin, GPIO_PIN_SET); break; /* 其余4个状态按表展开 */ } }duty的单位是定时器计数比较值而不是百分比。假如PWM频率16kHz、ARR8999那duty4500对应50%。这套写法用的是H_PWM-L_ON方式上桥PWM调压、下桥GPIO常通只需要三个定时器通道管脚占用少。代价是下桥开通瞬间没有硬件死区依赖“先全关再开对应管”的时序靠软件制造死区。如果追求更可靠六个管子全部走定时器互补输出由HRTIM的DTG寄存器自动插死区这个在第4章展开。2.3 霍尔毛刺边沿中断不能直接信霍尔信号在高速时不比低速干净尤其换相点附近机械振动会让霍尔输出出现几十微秒的毛刺。如果直接把边沿中断当换相触发一个毛刺就让换相状态跳两格电机会高频抖动、母线电流突然走高。常见两种处理定时器输入捕获硬件滤波或者用采样确认法。采样确认法的思路是读到变化后不立刻更新等20~25μs再读一次两次一致才算有效。void hall_change_isr(void) { __HAL_GPIO_EXTI_CLEAR_IT(HALL_A_Pin); delay_us(25); /* 等毛刺窗口过去 */ uint8_t h hall_read(); if (h g_hall_last_read) return; /* 和刚才电平一致判定为毛刺 */ h 0x07; g_hall_last_read h; if (hall_to_comm[h] ! COMM_INVALID) { comm_set(hall_to_comm[h], g_current_duty); } }霍尔三路都接EXTI任意一路变化都会进中断。25μs的延时在中断里不算长但霍尔线如果靠近功率线干扰严重时还是要换成输入捕获硬件滤波。另一个细节g_current_duty是主循环和中断共享的变量要加volatile修饰并且霍尔中断优先级设成最高。否则高速时换相不及时电机超过一定转速后会出现丢步表现就是带载掉速后电流异常大。3. 三段式启动和速度环从预定位到闭环切换3.1 为什么不能直接按霍尔状态启动BLDC自己不会启动。静止时转子可能停在任意位置如果直接按当前霍尔状态给电力矩方向很可能和转子位置不对应结果电机不是抖一下就是嗡嗡响不动。即使霍尔状态恰好对应正确扇区静态转矩也可能不够克服负载。所以工程上几乎都用三段式预定位、强制换相、闭环运行。预定位的做法是随便选一个导通状态比如A B-给一个不大的占空比持续200~400ms让转子转到这个位置。之后从该状态开始按顺序换相电机就开始朝固定方向转。这段强制换相其实不依赖霍尔纯粹按时间表推进。等到转速高到霍尔边沿间隔稳定再切换为由霍尔中断触发换相的闭环模式。3.2 启动状态机代码与阶段参数typedef enum { BLDC_ALIGN 0, BLDC_RAMP, BLDC_RUN } bldc_state_t; bldc_state_t state BLDC_ALIGN; uint32_t phase_start_ms; uint16_t ramp_step_ms; uint16_t ramp_duty; uint16_t hall_evt_cnt; void bldc_fsm_1ms(void) { uint32_t now HAL_GetTick(); switch (state) { case BLDC_ALIGN: comm_set(0, pwm_percent_to_ccr(10)); /* 固定状态, 10%占空比 */ if (now - phase_start_ms 250) /* 转子已到位 */ { state BLDC_RAMP; phase_start_ms now; ramp_step_ms 18; /* 换相步进18ms起步 */ ramp_duty pwm_percent_to_ccr(15); } break; case BLDC_RAMP: if (now - phase_start_ms ramp_step_ms) { comm_idx (comm_idx 1) % 6; comm_set(comm_idx, ramp_duty); phase_start_ms now; if (ramp_step_ms 6) ramp_step_ms - 1; /* 每步缩短1ms, 加速 */ } if (hall_evt_cnt 12 speed_rpm() 500) { state BLDC_RUN; /* 切闭环 */ } break; case BLDC_RUN: /* 10ms周期调用速度环PID */ break; } }预定位250ms是经验值负载惯量大就加到400ms。启动占空比15%是从母线电流实测定的静止状态下电机电流不小占空比太高直接顶到限流。ramp_step_ms从18ms逐步减到6ms对应电机从零升到几百转。强制换相阶段听到“咔咔咔”是正常的只要声音均匀、无卡顿就说明换相顺序对如果某一拍声音明显不匀回到第2章重新核对霍尔表。切入闭环门槛取500rpm加连续12次有效霍尔事件。低于这个转速霍尔边沿间隔大速度误差计算噪声高PID容易把占空比打到饱和表现是低速低频抖动。到500rpm以上霍尔边沿间隔稳定在毫秒级速度环才能正常工作。3.3 速度计算与增量式PID转速可以从霍尔事件间隔直接换算。三路霍尔一个机械周期共产生6个边沿每路上升沿加下降沿相邻边沿间隔是60°电角度电周期等于6倍间隔时间。计算公式rpm 10,000,000 / (间隔微秒 × 极对数)。四对极、间隔2000μs时转速就是10,000,000 / (2000×4) 1250rpm。typedef struct { int32_t kp, ki, kd; int32_t integral; int32_t prev_err; int32_t out; /* 千分比占空比 0~1000 */ int32_t out_limit; /* 例如900, 保留10%余量 */ } pid_inc_t; int32_t pid_inc_run(pid_inc_t *p, int32_t err) { p-integral err; if (p-integral p-out_limit) p-integral p-out_limit; if (p-integral -p-out_limit) p-integral -p-out_limit; int32_t d p-kd * (err - p-prev_err); p-prev_err err; p-out (p-kp * err p-ki * p-integral d) / 100; if (p-out p-out_limit) p-out p-out_limit; if (p-out 0) p-out 0; return p-out; }增量式PID输出是占空比千分比。积分项直接限幅在输出限幅防止转速还没到目标时PID先饱和。Kp100表示100rpm误差产生10%占空比增量这个值已经比较激进我一般Kp从50起步Ki取Kp的十分之一Kd先给0。速度采样周期10msPID执行周期也10ms两个周期不一致会让积分项产生额外积累调试时先确认这两个时间基准。参数起步值调节方向Kp50~100加到临界振荡退回2/3KiKp/10消除稳态误差过大会低频抖动Kd0速度环一般不用噪声大4. HRTIM与ADC注入组配合死区、限流和过流保护4.1 HRTIM的互补PWM和死区配置工程里的stm32f3xx_hal_hrtim.c来自STM32F334这类带HRTIM的芯片。HRTIM定时器时钟可以到144MHz16kHz PWM下有9000个计数点占空比分辨率约0.011%死区时间寄存器能配到几十ns级别。对BLDC六步换相来说普通TIM1也够用但HRTIM的优势在硬件自动处理上下桥互补和死区不用软件一次次插延时。htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 8999; /* 144MHz / 9000 16kHz */ htim1.Init.RepetitionCounter 0; HAL_TIM_PWM_Init(htim1); TIM_BreakDeadTimeConfigTypeDef bdtc {0}; bdtc.OffStateRunMode TIM_OSSR_ENABLE; bdtc.OffStateIDLEMode TIM_OSSI_ENABLE; bdtc.LockLevel TIM_LOCKLEVEL_1; bdtc.DeadTime 72; /* DTS144MHz时, 72/144MHz 0.5us */ bdtc.BreakState TIM_BREAK_ENABLE; bdtc.BreakPolarity TIM_BREAKPOLARITY_HIGH; bdtc.BreakFilter 0; HAL_TIMEx_ConfigBreakDeadTime(htim1, bdtc);DeadTime72是寄存器值实际死区时间等于72除以DTS。DTS配144MHz就是0.5μs。死区不要一味调大它会压缩有效占空比范围并在换相瞬间拉出电流尖峰小功率BLDC上0.5~1μs是安全起点。BreakState打开后BKIN引脚一旦被硬件置位PWM输出立刻进入安全关断状态这个引脚用来接比较器过流输出响应速度比ADC中断快一个数量级。4.2 PWM同步采样ADC注入组怎么触发母线电流每个PWM周期呈锯齿状不同时刻采样数值能差一倍必须在PWM输出中点采集这时候电感电流正好等于平均值。用定时器TRGO事件触发ADC注入组转换可以避免在中断里手工对齐采样时刻也让采样点固定不抖动。ADC_InjectionConfTypeDef inj {0}; inj.InjectedNbr 1; inj.InjectedRank 1; inj.InjectedChannel ADC_CHANNEL_4; /* 母线电流采样引脚 */ inj.InjectedSamplingTime ADC_SAMPLINGTIME_13CYCLES_5; inj.ExternalTrigConv ADC_EXTERNALTRIGINJECCONV_T1_TRGO; inj.ExternalTrigConvEdge ADC_EXTERNALTRIGINJECCONVEDGE_RISING; inj.InjectedOffset 0; HAL_ADCEx_InjectedConfigChannel(hadc1, inj, 1); HAL_ADCEx_InjectedStart(hadc1);F3系列ADC注入组触发事件名和F1不同如果CubeMX生成不出来检查ADC时钟配置和所选定时器的TRGO输出是否使能。注入组最大的特点是转换过程中不打断规则组转换适合和PWM同步做强实时采样。转换结果放在注入组数据寄存器主循环读取后和软件过流阈值比较。4.3 故障分级硬件快关断与软件慢保护故障类型检测方式处理动作恢复方式硬件过流比较器输出→BKIN引脚PWM立即关断故障锁定断电重启软件过流ADC注入组连续3次超阈值占空比清零延时1s允许重新启动过压母线电压分压采样关闭PWM电压回落且稳定后恢复堵转霍尔状态100ms无变化关闭PWM手动重新启动软件过流为什么要连续3次才确认一次ADC结果超阈值可能是采样窗口恰好落在开关噪声上直接停机太敏感现场会频繁误触发。连续3次确认真过流后先清占空比再置故障标志不要在中断里做整套故障处理电流环数据交给DMA故障状态机放主循环低频跑。堵转检测单独开一路霍尔状态超过100ms没有变化判断为机械卡死立刻关PWM否则电机静止状态下一直通流MOS管温度会快速上升。5. 低速手感与换相噪声排查固定占空比逐扇区验证5.1 不开示波器的换相表验证法把换相表扫一遍每个导通状态保持800ms轮流输出六个状态占空比固定10%。用手缓慢转动电机正常时六个位置都有均匀的定位力矩阻力大小接近不会在某一个角度卡死或反向猛拽。如果某个状态对应的转子位置错误电机会在这个角度明显跳动、反向顶住甚至出现连续嗒嗒声。这个方法能查出换相表的整体顺序问题也能顺手确认六个霍尔状态有没有被重复映射。void manual_comm_scan(void) { uint16_t duty pwm_percent_to_ccr(10); for (int i 0; i 6; i) { comm_set(i, duty); HAL_Delay(800); } }5.2 霍尔边沿和真正换相点之间差30°电角度霍尔信号边沿对齐的是反电动势过零点而最佳换相点在过零点之后30°电角度。很多工程图省事让霍尔边沿直接触发换相电机能转但效率低、噪声大、母线电流偏高。转速越高换相滞后带来的损耗越大所以按转速分段加提前角是常见做法。换算公式电周期微秒 60,000,000 / (转速rpm × 极对数)再乘上需要的角度差除以360。uint32_t comm_delay_us(uint16_t rpm, uint8_t pole_pairs, int8_t advance_deg) { float elec_period_us 60000000.0f / ((float)rpm * pole_pairs); float deg 30.0f - (float)advance_deg; /* 正提前角会减少等待 */ return (uint32_t)(elec_period_us * deg / 360.0f); }5.3 在线修正六个扇区的提前角把提前角做成数组六个扇区独立配置通过串口在线修改。比如串口收到ADV 2 15就把扇区2的提前角改成15°。测试时先全部设0°确认能转再把角度按5°一档往上加观察母线电流电流下降说明换相点在靠近最优位置电流反弹说明已经过头。六扇区各调一次存进结构体后低速起步和高速带载的噪声会明显改善最后把数组固化到Flash作为出厂参数。这样逐扇区调出来的提前角表比在代码里反复改死区参数有效得多。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

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

立即咨询 →