前一篇聊了 AM32 的硬件选型和起步准备这次我们把重点放到源码本身上面把 AM32 的代码架构、状态机流转、核心控制环路逐个拆开看。老实说AM32 的代码量不算大只是一开始接触容易迷失在定义宏和配置项里所以我尽量按模块来讲把每一个关键环节为什么这么设计、改哪里、怎么调都理顺方便你直接对号入座。如果你手里正好有板子建议边读边翻源码体验会好很多。1. 源码架构的整体梳理与设计思路1.1 代码规模与目录结构AM32 的源码量不大全部算下来 C 文件大概二十来个加上头文件也就几十个文件。相比于 BLHeli_S 那套汇编代码AM32 用 C 语言重写的好处非常直观可读性高、移植性好、改功能不需要抱着指令集手册逐行对。对于一个开源社区项目来说这几乎决定了它能不能吸引人持续贡献事实也证明 AM32 目前已经支持了非常多不同厂牌的硬件平台。从目录上看AM32 源码主要分为几个部分Bootloader 相关代码负责固件烧录和引导启动驱动层代码包含了定时器、ADC、PWM 输出、LED 指示等硬件资源的初始化与操作核心控制逻辑主要是状态机、换相控制、PWM 生成、电流环和速度环遥测与通信协议处理包括 DShot、Multishot 等信号解析以及 bidirectional DShot 的处理逻辑配置管理负责参数的读写、保存与恢复。如果你打开源码扫一圈会发现很多文件是强平台相关的比如tcop相关、mcu相关毕竟 AM32 要适配不同厂家、不同主控的硬件设计。真正跨平台通用的部分集中在控制逻辑和协议处理这些模块上这也是 AM32 架构做得比较聪明的地方把硬件差异隔离在底层把核心算法抽象在公共层后续换主控时能省下大量重复工作。1.2 架构分层硬件隔离与模块解耦AM32 的架构分层思想很清晰最下面是硬件抽象层中间是协议解析与状态管理最上面是电机控制相关的核心功能。这样的分层带来的直接好处是当你需要把 AM32 移植到一颗新 MCU 上时不需要去改动闭环控制和状态机的逻辑只需要把底层定时器、ADC、GPIO 这些资源对应的初始化函数和回调补齐即可。实际上这种方式也继承了 STM32 标准外设库时代的习惯对做嵌入式的人来说不难理解。在具体实现上AM32 并没有像操作系统那样引入抽象接口层而是通过宏定义和条件编译做裁剪。以存储配置为例不同主控的 Flash 操作方式不同AM32 通过EE_xxx系列函数对上层提供统一读写接口底层则按平台实现。这样上层逻辑只需要调用存储接口不需要关心当前跑的是哪颗芯片阅读代码的时候也比较容易把握主线。从编译角度看AM32 采用 Makefile 组织构建通过命令行配置板型、主频、编译选项等编译产物包含 bootloader 和固件两部分。对于实际开发来说只要选对板型配置基本可以一条命令完成编译。调试阶段建议打开DEBUG_xxx相关的宏可以输出更多信息帮助定位问题。1.3 状态机设计从启动到稳态的完整闭环AM32 的控制逻辑围绕一个核心状态机运转。电调上电后首先进入初始化状态完成参数加载、硬件自检随后进入待机与信号检测状态等待来自飞控的油门信号收到有效信号后根据油门值进入启动状态启动完成后进入闭环运行状态如果信号丢失或出现异常则回到安全状态或直接停车。这个状态机的设计逻辑和 BLHeli 的思路一脉相承因为无感无刷电机的启动本身就是一个比较复杂的开环到闭环切换过程。状态机的好处是让代码执行路径非常清晰在任何时刻系统都处于一个可预期的状态中异常处理也有明确入口。实际调试中如果你的电机出现启动抖动或者启动不起来绝大多数问题都可以对应到状态机中某个环节没有满足切换条件比如反电动势过零检测的阈值设置不合理或者启动阶段的换相加速曲线太激进。状态机切换并不像是在 PC 上跑 RTOS 那样用任务调度而是在中断和主循环中按顺序扫描。AM32 的实时控制部分大多在定时器中断里完成确保 PWM 周期和 ADC 采样时刻的确定性主循环则负责处理低优先级的任务比如参数处理和通信报文组装。这种软实时设计在电调这种对时序极其敏感的场合非常合适。2. 核心模块原理与代码解析2.1 六步换相与 PWM 调制方式无感无刷电机的控制基于六步换相法简单来说就是按照转子位置依次导通上下桥臂让定子磁场牵着转子跑。AM32 的换相控制完全基于反电动势过零检测来确定换相时机这个机制替代了霍尔传感器的位置反馈。先看 PWM 调制方式。AM32 和 BLHeli_S 类似支持多种调制模式常用的包括互补 PWM、独立 PWM、以及针对低噪声优化的调制方式。代码中通过配置项PWM_MODE来选择具体到定时器的输出通道配置直接影响上下桥臂的开关方式。这里有一个关键的细节换相不是在 PWM 载波周期的任意时刻都能执行的需要在合适的时刻更新占空比和换相状态否则容易产生电流尖峰。AM32 的代码中TIM 更新中断里处理换相和 PWM 更新确保相位准确。换相执行逻辑一般分为几个阶段检测过零事件、预估换相延时、执行换相、等待下一次过零检测。AM32 在这部分有一套比较精细的实现通过 ADC 采样悬浮相电压来判断过零时刻。需要注意的是PWM 开关噪声对过零检测影响很大所以 AM32 在采样时机上做了专门处理比如只在 PWM 开启周期的特定时间段采样规避续流噪声。2.2 反电动势过零检测与移相控制反电动势过零检测是无感无刷控制的核心和难点。当转子转动时未导通相的相反电动势会跨过中性点电压通过比较器或 ADC 采样判断这一过零事件就能间接得知转子位置。AM32 在这方面的处理和硬件平台密切相关有的板子使用比较器中断检测过零有的则使用 ADC 连续采样后软件判断。这两种方式各有优劣比较器方式响应快实时性好但由于比较器阈值固定转速范围宽时容易失效ADC 方式灵活可以通过软件调节阈值和滤波但采样时刻和计算延迟需要仔细处理。AM32 源码里两种方式都有支持具体取决于板型定义。实际使用中高速电机对过零检测的延时要敏感得多所以代码里会有针对高速段的补偿处理。移相角度本质上是对过零事件到换相时刻之间的延时进行预估这个延时和当前转速直接相关。AM32 会通过定时器捕获当前换相周期换算成电角度速度再根据设定的提前角参数计算延时。这里有一个非常影响手感的概念叫“提前角”。如果换相时刻太晚电机会出现电流波动大、效率下降如果太早则可能失步甚至烧毁。AM32 中相关的调节参数不多但影响很大尤其是针对高 KV 电机和低 KV 电机的差异化调校几乎都要动到这里。调试时建议从一个保守的提前角开始逐步增大观察电流波形和温度变化。2.3 电流采样与力矩控制AM32 在高配版本中支持电流闭环通过采样母线电流或相电流来实现力矩的精准控制。电流采样常用的方式是采样电阻加运放放大然后送入 ADC。AM32 中会根据硬件配置选择采样点位置代码中能看到明显的电流采样校准逻辑包括零点偏移校准和增益校准。电流环在 AM32 中不是简单的 PI 调节器挂上去就完了它还需要和速度环、PWM 占空比控制协调工作。实际架构上速度环的输出作为电流环的给定电流环输出再决定 PWM 占空比。这样做的好处非常多对负载变化响应更快堵转保护更可靠而且可以精准限制最大电流保护电机和电调。电流环的调试有一个常见误区一上来就加很大的 P结果电机运转时出现尖锐噪音甚至失控。我建议先把 I 设得小一点P 慢慢往上加同时观察电流波形有没有振荡。对于大多数穿越机电机AM32 的默认参数已经在一个合理的范围真正需要动参数的往往是特制电机或者大载重机型。2.4 通信协议解析与双向 DShotAM32 支持传统的 PWM 油门信号、Multishot、DShot 以及双向 DShot这也是现阶段飞控和电调通信的主流方式。DShot 是数字信号协议通过脉冲宽度编码 0 和 1校验方式为 CRC相比传统 PWM 信号抗干扰能力更强传输速率更稳定。AM32 源码中 DShot 解码部分是通过定时器输入捕获实现的对时序要求非常严格如果信号质量差或者定时器配置不正确很容易出现解码失败或丢包。双向 DShot 是很多飞手关心的功能因为它可以把电调端的转速遥测、温度、电流等数据回传给飞控并直接用于飞控的转速闭环滤波。AM32 对双向 DShot 的支持比较成熟源码中能看到发送和接收状态机的切换逻辑。调试时如果发现回传数据不稳定优先检查信号线质量和飞控端配置其次再考虑电调固件参数。3. 源码修改与移植指南3.1 准备工作与编译环境搭建在动手改代码之前先把编译环境准备好。AM32 使用的是 ARM GCC 工具链和 Makefile 构建系统所以你得先安装arm-none-eabi-gcc然后再安装make。这两样在 Windows、Linux、macOS 下都有对应的安装方式。如果你之前编译过其他 STM32 项目环境基本是现成的。接着需要拉取 AM32 源码。把仓库克隆到本地以后进入Src/config目录可以看到大量以板型命名的头文件例如bgc_g_210.h、hglrc_zeus_f722.h之类的。找到对应你手里电调的板型文件如果找不到完全一致的就选择一个主控型号相同且外围资源如定时器映射、ADC 引脚、PWM 通道相近的板型作为基础然后手动调整引脚映射。编译命令很简单在源码根目录执行make BOARD你的板型名。比如make BOARDbgc_g_210。如果编译通过会在Src/build或类似目录下生成 hex 或 bin 文件。我遇到的大部分编译问题都出在工具链版本上特别是新版本 GCC 对语法检查更严格偶尔会报一些旧代码里的 warning 为 error这时看看报错位置一般都能解决。3.2 引脚映射修改方法引脚映射是移植过程中最容易出错的地方。AM32 底层用到了定时器的多通道输出、ADC 的多个采样通道、比较器如果有、UART 或 SPI 等外设每个外设和引脚的对应关系都要写对。打开板型头文件你会看到类似MOTOR_PIN、CURRENT_PHASE_A、RX_PIN这类宏定义它们最终会被编译进初始化代码。修改引脚映射时最重要的是对照芯片数据手册确认引脚复用功能。STM32 系列的引脚功能映射比较复杂比如同一个定时器的同一个通道可能有多个引脚可选但不同的封装和板子设计决定了只有特定引脚被引出。我通常在改完映射后先编译一遍再下载到板子上测试 DShot 信号是否能正常接收因为这部分如果引脚错了现象往往是没有反应或者信号噪声巨大。为了防止改错我习惯在头文件里把每一个映射的寄存器值都用注释标注出来标明是哪一组复用功能。这样丢一段时间再回来改代码查起来会省很多时间。3.3 关键参数配置说明AM32 的配置项非常多但多数时候只需要关注几个和飞行手感、保护功能直接相关的部分。STARTUP_POWER启动功率决定了电机启动时施加的初始占空比。这个值太低了启动会卡顿或反转太高了会“哐”一下弹出。正常情况下从默认值开始调每次增减不要太大直接听声音最直观。PWM_FREQUENCYPWM 载波频率。升高载频能降低电机噪音但会增加开关损耗和电调发热。对穿越机来说 24kHz 或 48kHz 比较常见大载重机则要慎重电调发热可能会超标。DIRECTION电机旋转方向。AM32 支持通过信号线切换和参数切换两种方式如果你用的是双向 DShot飞控端也能切换方向但电调参数优先级要搞清楚。BEEP_STRENGTH鸣叫音量实际上电调发声是利用电机线圈发声不必太纠结音质但如果你的电调上电不响多看看这个参数和电机连接状态。其他如BRAKE_ON_STOP、DITHER_THRESHOLD等参数在特定使用场景下也很重要。我个人的习惯是每次只改一个参数记录飞行手感的变化而不是一次性把参数全改完避免变量太多无从排查。3.4 编译、烧录与调试技巧AM32 的烧录方式比较灵活可以通过 Betaflight Configurator 的 CLI 接口刷写也可以使用专门的 AM32 Configurator一款图形化配置工具直接烧录还可以通过 SWD 调试器配合 STM32CubeProgrammer 烧录。对于开发者来说SWD 是最推荐的因为你可以直接看到运行日志和寄存器状态。调试方面有一点比较隐蔽如果你启用了调试宏大量的打印输出会占用单片机时间影响实时控制。所以在调完功能后记得把调试开关关掉重新编译再刷写。此外AM32 支持通过串口输出遥测信息你可以外接一个串口模块在调试时观察电调内部的转速、电流和温度数据这比单纯靠耳朵听靠谱得多。在实际调试过程中我建议按照“先验证硬件通路、再验证控制逻辑、最后整机测试”的顺序来做。先用示波器确认 PWM 波形和 ADC 采样是否正常再上电转动电机最后再装上桨叶实测。如果一开始就装桨测试启动瞬间的大电流冲击很可能把驱动部分烧掉这种损失完全可以避免。4. 常见问题与排查方法4.1 电机启动抖动或反转电机启动抖动或者方向不固定通常是启动阶段的开环参数不合适。AM32 启动流程是先施加一个较小的开环占空比然后逐步加速直到反电动势信号足够可靠后切换到闭环。如果启动功率过低电机的初始扭矩不够转子还没转起来就切换到闭环自然就会抖动。遇到这种情况先排除机械问题轴承卡涩、桨叶装歪然后微调启动功率和启动加速度。如果是低 KV 大电机启动功率要适当提高如果是高 KV 小电机则需要更平滑的启动曲线把加速度调低。另外有一个很容易被忽略的原因电池电压太低时启动也会乏力尤其在大电流放电的场景下建议用充满电的电池测试。4.2 高速运转时出现失步高速失步的现象是电机在高油门段突然发出异响、转速急剧掉下来甚至直接堵转停机。这个问题的根因往往在过零检测和换相延时的配合上。随着转速升高反电动势的幅值和频率都在变过零检测的相位延迟影响越来越大如果提前角补偿不到位换相时机就会逐渐偏离最佳点最终失步。先把PWM_FREQUENCY确认一下载频太低会导致电流纹波大反电动势信号毛刺多影响检测。再把提前角参数缓慢调大同时观察电机温度温度上升过快说明提前角可能太大了。另外高速失步也要检查电调供电电压的稳定性电源线太细或者接头松动在急加速瞬间电压跌落同样会造成失步。4.3 电机堵转保护与过热保护触发AM32 内置的堵转保护逻辑并不复杂如果启动后规定时间内没有检测到稳定的反电动势过零事件就判定为堵转并停止输出。这个保护在正常使用中很有效但也容易误触发比如某些启动工况下负载突变比较大。过热保护方面AM32 会在没有温度传感器的情况下通过电流积分估算温升。如果频繁触发过热保护不要一味降低保护阈值先检查散热条件和电流参数是否设置过大。对于大载重机型我把电流限幅关掉或者调大后发热明显下降因为电流环不会反复在限幅点附近振荡了。4.4 双向 DShot 回传数据异常双向 DShot 回传数据不稳定是很多人头疼的问题。先检查信号线是否太长或者线径过细数字信号的边沿会因此变缓造成误码。然后看飞控端的双向 DShot 功能是否开启以及 DShot 速率设置是否匹配。AM32 的双向 DShot 对时序要求较高如果发现回传的转速跳变很厉害可以在飞控端开启滤波同时在电调端把TELEMETRY_RATE调低一点优先保证数据稳定性。如果以上还不能解决用示波器看信号线波形重点检查上升沿和下降沿的振铃。振铃严重时可以串联一个小电阻几十欧姆或者在信号线对地加一个小电容不过这个办法治标不治本最好还是优化布局和线材。5. 实测心得与后续扩展经过这么多次改代码、烧录、调试我最大的感受是 AM32 这套源码的价值不只是“开源免费”这么简单它把无感无刷控制里最核心的思路用非常工程化的方式展现出来了。认真读一遍源码再结合示波器的波形去对很多之前觉得玄乎的概念比如提前角、过零延时、启动闭环切换一下就变得非常具体。后续如果要继续深入我建议往这几个方向走移植 AM32 到一款自己设计的电调板子上这能逼你把硬件原理和软件驱动彻底打通尝试修改控制算法比如加入更高级的观测器来替代反电动势过零检测结合双向 DShot 和飞控的转速数据做一些电机效率优化的实验。我自己的下一步计划是拿 AM32 的双向 DShot 数据配合地面站分析不同桨叶和电机组合下的效率曲线。这个内容后面有机会再单独写一篇。对你来说最有效的做法还是手上拿一块支持 AM32 的电调把源码打开边看边测。实践几次之后你会发现自己对电调的理解完全不一样。