夜雨聆风学习资料网

ARTICLE · 1248558

TOP10开源BLDC源码自取;AM32 源码解构低速轮询、高速中断

TOP10开源BLDC源码自取;AM32 源码解构低速轮询、高速中断

接合集基于AI,手把手教你入门无刷电机控制算法。

TOP10源码下载地址:

https://spacecodding.com/

这是百万老师作为技术顾问的一站式学习智能自动化全栈技术的AI时代新型技术论坛。AI+嵌入式提高+无刷电机控制算法+ESP32+MCU&NPU小模型训练开发部署

同时提供全自动化开发嵌入式软件智能体开源部署和基于智能体的嵌入式提高,无刷电机控制算法学习班,有需求加微信:shujieshuyan

上一篇拆 ESCape32,它的过零检测从头到尾只有一条路:

比较器进 TIM2 输入捕获,硬件锁时间,过零中断算延迟。

AM32 不一样。它保留了两条路:低速轮询,高速中断。

这不是历史包袱,是刻意的速度取舍。看懂这个取舍,你就看懂了 AM32 的核心。

· · ·

一、AM32 和 ESCape32 最大的不同

先把结论放这。

低速时,AM32 不用中断,用 10/20kHz 的定时器节拍去轮询比较器输出,连续采到足够多次才认过零。

高速时,轮询来不及了,切到比较器中断,过零一到就进中断。

两条路的切换点,是换向间隔 commutation_interval。低于阈值切中断,高于阈值回轮询。失步、超时,也回轮询。

所以 AM32 的代码里有两套过零逻辑:

getBemfState() 加 zcfoundroutine(),是轮询路径。

interruptRoutine() 加 PeriodElapsedCallback(),是中断路径。

两条路最后都汇到 commutate()。

· · ·

二、六个问题,AM32 只改了答案

六个问题本身上一篇已经拆过,这里不再重复。只列 AM32 给出的不同答案:

问题
AM32 的答案
怎么从静止转起来
startMotor()
 先开环推一步,再进轮询
没有霍尔,怎么知道转子在哪
比较器 + bemfcounter 连续计数
过零之后什么时候换向
waitTime = interval/2 - advance
怎么判断测得对不对
bad_count
、filter_level、失步判据
出了异常怎么不死机
失步、超时后回轮询;其余保护与上一篇同构
油门怎么变成 PWM
tenKhzRoutine()
 里做斜坡、限幅、变频

先记住一句话:AM32 不是把无感做复杂了,是把低速和高速分成了两种工况,各用各的检测手段。

· · ·

三、AM32 的双路径流程图(带函数名)

两条路径的状态切换,单独画出来更清楚。

· · ·

四、两条路径,为什么这么分

低速时,BEMF 幅值小,比较器输出容易被 PWM 噪声、续流、死区干扰。你用中断,可能一次噪声就误触发。

AM32 的做法是轮询加计数。

getBemfState() 每个节拍读一次比较器。如果电平和预期极性 rising 一致,bemfcounter 加一;不一致,bad_count 加一。bad_count 超过阈值,bemfcounter 清零,重来。

只有 bemfcounter 连续超过 min_bemf_counts_up 或 min_bemf_counts_down,才认这个过零。

这就是一个软件投票器。低速时它比中断稳。

高速时,换向间隔短到轮询周期都覆盖不了。比如 20kHz 轮询,周期 50 微秒。换向间隔掉到 50 微秒以下,轮询就会漏掉过零。这时候必须用中断。

所以切换点 polling_mode_changeover 本质上是一个时间阈值:换向间隔短于它,轮询来不及,切中断。

filter_level 也跟着速度走。启动时 12,高速时降到 2。低速多采几次确认,高速少采几次,避免滤波本身把换向点拖晚。

· · ·

五、沿调试顺序,看 AM32 的六处不同

第 1 层:开环推一步,先让转子动

对应函数:startMotor()、commutate()、comStep()、changeCompInput()。

startMotor() 做的事很直接:

如果还没转,先 commutate() 推一步,把 commutation_interval 设成 10000,把间隔定时器设成 5000,running=1。然后打开比较器中断。

commutate() 负责步进:正转 step++,反转 step--,算出 rising = step % 2,调 comStep(step) 写三相输出,再调 changeCompInput() 把比较器切到新的浮空相。

这一步的调试动作:

先看三相互补 PWM 和死区。再看相序和转向。确认 rising 和比较器极性对得上。

DEAD_TIME 在 Inc/targets.h 里按目标板定义,常见值 30 到 120。死区的取舍上一篇讲过,AM32 的差异是把它和比较器映射一起塞进了 targets.h,换板时在同一个文件里改。

· · ·

第 2 层:轮询怎么确认过零

对应函数:getBemfState()、interruptRoutine()。

getBemfState() 在轮询路径里用。它根据当前步号选浮空相,读比较器电平,和 rising 比较,累加 bemfcounter 或 bad_count。

interruptRoutine() 在中断路径里用。它先做一次软件滤波:

for (i = 0; i < filter_level; i++),每次读比较器,如果还是旧电平,直接返回。

连续 filter_level 次都确认翻转了,才关中断、屏蔽相中断、记录 lastzctime 和 thiszctime、清间隔定时器、启动 COM 定时器。

这一步的调试动作:

先看浮空相 BEMF 和比较器输出,确认翻转沿对齐。再看 filter_level,低速加大,高速减小。再看 bad_count_threshold,太小容易误判,太大反应慢。

bad_count_threshold 默认是 CPU_FREQUENCY_MHZ / 24,也就是两三次坏采样就清零。TARGET_MIN_BEMF_COUNTS 按目标板不同,常见 1 到 6。

· · ·

第 3 层:两条路径怎么算换向延迟

对应函数:PeriodElapsedCallback()、zcfoundroutine()。

两条路径算延迟的公式是一样的:

waitTime = commutation_interval / 2 - advance

30 度的来历上一篇已经推过:过零在浮空窗口中间,换向在窗口末端。这里只看 AM32 的量化方式:

advance = (commutation_interval * temp_advance) >> 6

6 表示 60 度分成 64 份,每份 0.9375 度。默认 temp_advance = 16,对应大约 15 度提前,实际延迟约 15 度。

中断路径里,interruptRoutine() 只负责在过零时启动 COM 定时器;PeriodElapsedCallback() 在定时器到期后调 commutate(),并重新计算下一次的 waitTime。

轮询路径里,zcfoundroutine() 自己盲等:

while (INTERVAL_TIMER_COUNT < waitTime),等到时间到,再 commutate()。

这一步的调试动作:

看浮空相 BEMF 和相电流。换向点应该在过零之后、电流建立之前。调 temp_advance,看电流尖峰和发热。

调早了电流大,调晚了转矩掉。

· · ·

第 4 层:什么时候从轮询切中断

对应函数:startMotor()、zcfoundroutine()。

AM32 的启动默认走轮询。

startMotor() 先推一步,设一个很大的初始间隔,让转子先动。然后 tenKhzRoutine() 每个节拍调 getBemfState(),连续采到过零,调 zcfoundroutine()。

zcfoundroutine() 算完延迟,忙等到换向点,调 commutate(),然后判断要不要切中断:

如果 commutation_interval < polling_mode_changeover,说明转速上来了,轮询来不及,切中断。

如果开了 stall_protection 或 rc_car_reverse,则要等 zero_crosses >= 20 且间隔小于 2000 才切。

AM32 也有正弦启动。配置打开后,advanceincrement() 用 pwmSin 表走 360 个角度,把三相正弦推起来,到 phase_A_position == 0 时切六步,设 commutation_interval=9000、zero_crosses=20、step=changeover_step,再 commutate()。

这一步的调试动作:

看启动成功率、启动电流、切换点。启动电流大,降 startup_power。切闭环太早,BEMF 不够强,容易失步;太晚,启动慢。

· · ·

第 5 层:失步后怎么回轮询

对应函数:主循环里的失步判据、tenKhzRoutine() 里的保护。

失步判据在 main() 的主循环里:

getAbsDif(last_average_interval, average_interval) > average_interval >> 1

变化超过一半,并且 average_interval < 2000,就判失步。然后 zero_crosses=0、desync_happened++、必要时 running=0、old_routine=1,退回轮询重新来。

超时保护看 INTERVAL_TIMER_COUNT > 45000。超过就 bemf_timeout_happened++,切回轮询,清 zero_crosses,调 zcfoundroutine()。如果油门已经回零,直接 running=0。

堵转、过流、低压、温度这四层,骨架和上一篇一样,AM32 的区别是实现方式:stallPid 在低转速补油门,currentPid 做积分限幅,低压按电芯数或绝对电压算,温度按 degrees_celsius 压最大占空比。

这一步的调试动作没变:做堵转、突加负载、低压测试,看保护触发后能不能恢复,恢复时间多长,会不会反复起停。AM32 额外要看的是,保护退出后有没有正确回到轮询。

· · ·

第 6 层:10/20kHz 节拍里怎么给油门

对应函数:tenKhzRoutine()、setInput()。

tenKhzRoutine() 是 10/20kHz 的节拍,负责:

读油门,算 adjusted_input。

做占空比斜坡,启动时用 max_ramp_startup,低速用 max_ramp_low_rpm,高速用 max_ramp_high_rpm。

1kHz 跑电流环、堵转环、速度环。

按 commutation_interval 或 average_interval 改变 PWM 频率。

按温度、低压、低转速限制最大占空比。

这一步的调试动作:

看占空比阶跃响应,看低速启动是否平顺,看高速电流纹波和 MOS 温度。调 startup_power、max_ramp_*、pwm_frequency。

· · ·

六、AM32 怎么移植

移植步骤和上一篇同构:复制同系列 Mcu/ 目录,改 targets.h,改比较器映射和极性,改三相 PWM 和 ADC 通道。

AM32 的差异集中在两点:

一是 changeCompInput() 按步号切比较器通道,COMP_MAP 错了过零全反。

二是 POLLING_MODE_THRESHOLD 和 TARGET_MIN_BEMF_COUNTS 这两个参数,直接决定轮询路径能不能启动、什么时候切中断。

参数
怎么定
错了会怎样
DEAD_TIME
按 MOS 关断时间加驱动延迟,示波器看死区
不够直通炸管,太大低速掉转矩
TARGET_MIN_BEMF_COUNTS
低速过零需要连续采到几次
太小误判,太大启动慢
POLLING_MODE_THRESHOLD
轮询切中断的间隔阈值
切早了高速丢过零,切晚了低速噪声大
COMP_MAP
比较器输入和三相的对应关系
过零全反,换向错位
VOLTAGE_DIVIDER
 / CURRENT_OFFSET
电压/电流采样增益和零偏
保护误触发或失效
LOOP_FREQUENCY_HZ
轮询节拍
太低漏过零,太高占 CPU

AM32 特有的坑只有两个:TARGET_MIN_BEMF_COUNTS 太大,启动时一直等不到过零;POLLING_MODE_THRESHOLD 太小,低速过早切中断,噪声误触发。比较器极性、死区、相序这些通用坑,上一篇已经列过。

· · ·

七、和 ESCape32 的对比

维度
ESCape32
AM32
过零检测
硬件输入捕获
低速轮询 / 高速比较器中断
滤波
定时器输入滤波 + blanking
bemfcounter
 / bad_count / filter_level
换向触发
TIM2 比较 → TIM1 COM
COM_TIMER 中断 → commutate()
30 度公式
(ival - ival*timing/32)/2interval/2 - (interval*temp_advance>>6)
启动
开环第一步 + 可选正弦
startMotor()
 + 轮询 + 可选正弦
代码组织
src
 + mcu
Src
 + Mcu + Inc/targets.h
目标数
7 个 MCU 家族
7+ 个 MCU 家族,targets.h 16 万字节
适合
读核心算法
看工程化、多目标、量产功能

一句话:ESCape32 适合先看懂无感的骨架,AM32 适合看这套骨架怎么长成一个能卖的产品。

· · ·

八、AM32 最值钱的地方

不是六步表,也不是换向公式,是那条切换线。

polling_mode_changeover 定在哪,决定了低速噪声会不会误触发、高速会不会漏过零。它跟你的电机、PWM 频率、比较器响应都有关系,没有标准答案。

AM32 给的是一个可抄的结构:低速轮询,高速中断,失步回轮询。

但阈值不是抄来的,是拿示波器和日志一段段试出来的。

· · ·

九、AM32 的读码顺序

先走轮询路径:tenKhzRoutine() → getBemfState() → zcfoundroutine()。

再走中断路径:interruptRoutine() → PeriodElapsedCallback() → commutate()。

最后看主循环里的失步判据和超时保护,确认异常怎么回轮询。commutate() 和 changeCompInput() 两条路径都要经过,放在中间对照看。

DShot、遥测、启动音是外围,先跳过。

· · ·

最后

AM32 最值得学的,不是它也会六步换向,而是它承认一件事:一种检测手段,覆盖不了全速段。

所以它把“听”分成了两种:低速慢慢听,高速抢着听。

相关学习资料