引言:无人机电调的 “无感” 革命与 AT32 的适配性
在无人机系统中,电子调速器(ESC)是连接飞控与无刷直流电机(BLDC)的 “桥梁”,其核心任务是精准控制电机转速,直接影响无人机的飞行稳定性、续航与操控响应。传统有感 BLDC 方案需额外安装霍尔传感器或编码器,不仅增加成本与重量,还降低了无人机在恶劣环境(如沙尘、振动)下的可靠性 —— 这也是 “无感 BLDC 方案” 成为主流的核心原因。
无感 BLDC 方案的关键在于无需外部传感器,通过电机自身的电气信号反推转子位置与转速,而 “反电动势过零检测法” 是最成熟、成本最低的无感方案。本文的核心主角 ——AT32F421G8U7(雅特力国产化 32 位 MCU),凭借内置高性价比的比较器模块(文档 3.17 节)、120MHz 主频(文档 2. 规格说明)、宽温工作范围(-40~+105℃,文档 4.4 节),完美适配无人机电调的 “低成本、高实时性、高可靠性” 需求。
本文将从原理到实践,全方位解析如何基于 AT32F421G8U7 的比较器模块实现 BLDC 无感转速测量,涵盖硬件设计、软件实现、工程调试与无人机电调落地,所有内容严格基于DS文档,确保方案的可行性与国产化适配性。
一、原理篇:读懂 BLDC 无感转速测量的 “核心密码”
要掌握比较器方案,需先理清 BLDC 电机的工作逻辑、反电动势的产生机制,以及过零检测与转速计算的内在联系 —— 这是后续硬件 / 软件设计的 “理论基石”。
1.1 BLDC 电机基础:从结构到换相的 “底层逻辑”
BLDC 电机(无刷直流电机)本质是 “电子换向的同步电机”,其核心是通过定子绕组的电流切换,驱动永磁转子旋转。理解以下 3 个核心点,即可掌握 BLDC 的工作原理:
| 核心概念 | 定义与作用 | 与转速测量的关联 |
|---|---|---|
| 电机结构 | 定子(3 组对称绕组,星形 / 三角形连接)+ 转子(永磁体,N/S 极交替) | 绕组是反电动势的 “产生源”,转子位置直接决定反电动势的波形相位 |
| 换相逻辑 | 需按 “6 步换相法” 切换定子绕组电流(如 U→V→W→U…),每步对应 60° 电角度 | 换相点与反电动势过零点存在固定 30° 电角度差 —— 过零检测的核心就是 “捕捉这个差值,间接定位换相点” |
| 电角度与机械角度 | 电角度 = 机械角度 × 极对数(P);例:4 极电机(P=2),机械转 1 圈 = 720° 电角度 | 转速计算需结合极对数,过零间隔对应的是电角度变化,需转换为机械转速(rpm) |
关键结论:BLDC 电机的转子位置与定子绕组的反电动势波形强相关 —— 只要能检测反电动势的 “特征点”(过零点),就能反推转子位置与转速,无需外部传感器。
1.2 反电动势:BLDC 的 “自带位置信号”
当 BLDC 电机旋转时,转子永磁体切割定子绕组,会在绕组两端产生 “反向电压”,即反电动势(Back EMF)。其特性完全匹配无感检测的需求,具体参数如下表:
| 反电动势特性 | 具体描述 | 对无感检测的意义 |
|---|---|---|
| 波形特性 | 近似正弦波 / 梯形波,与转子转速成正比(转速越高,反电动势峰值越大) | 过零点位置固定(与转子磁极轴线对齐),可作为 “位置基准” |
| 过零点定义 | 反电动势电压从 “低于零点” 跳变为 “高于零点”(上升沿)或反之(下降沿)的时刻 | 过零点对应转子磁极经过定子绕组轴线的瞬间,是转速计算的 “时间基准点” |
| 与供电电压的关系 | 反电动势峰值 < 电机供电电压(否则电机无法加速) | 需通过分压电路将反电动势降至 AT32 的安全输入范围(2.4~3.6V,文档 2. 规格说明) |
| 未通电绕组的特性 | 电机运行时,未通电的绕组(悬空绕组)反电动势波形最完整,无电流干扰 | 过零检测需选择 “悬空绕组” 的反电动势 —— 这也是 “6 步换相法” 中过零检测的核心逻辑 |
通俗比喻:反电动势就像 BLDC 电机的 “呼吸”—— 转子每转一圈,3 相绕组的反电动势就会产生固定次数的 “呼吸起伏”(过零点),通过计数 “起伏间隔”,就能知道电机转得有多快。
1.3 过零检测:从 “电压对比” 到 “转速计算” 的桥梁
无感转速测量的核心是 “检测反电动势过零点”,而实现这一目标的关键工具,就是 AT32F421G8U7 的比较器模块。我们先理清 “过零检测” 的完整逻辑链:
1.3.1 过零检测的核心逻辑:“基准” 与 “信号” 的对比
反电动势本身是 “相对电压”,需一个 “零点基准” 才能判断 “过零”—— 这个基准就是电机中点电压(Vmid)。
- 若电机绕组为星形连接,可直接引出中点,Vmid = 电机供电电压(Vbat)/ 2(如 12V 供电,Vmid=6V);
- 若电机无中点引出,需通过 “电源分压” 模拟 Vmid(如 2 个等值电阻对 Vbat 分压,再滤波)。
过零检测的本质的是:用比较器将 “反电动势(Vemf)” 与 “零点基准(Vmid)” 进行实时对比,当 Vemf 跨越 Vmid 时,即判定为 “过零时刻”。具体逻辑如下表:
| 比较器输入 | 输入信号来源 | 比较逻辑 | 过零判定结果 |
|---|---|---|---|
| 正极输入(INP) | BLDC 绕组反电动势(如 A 相) | Vemf(INP) > Vmid(INM) → 比较器输出高电平 | 上升沿过零(转子从 N 极→S 极) |
| 负极输入(INM) | 零点基准电压(Vmid) | Vemf(INP) < Vmid(INM) → 比较器输出低电平 | 下降沿过零(转子从 S 极→N 极) |
1.3.2 转速计算:从 “时间间隔” 到 “rpm” 的转换
捕捉到过零时刻后,需通过 “时间间隔” 反推转速。核心公式基于 “电机转速与过零间隔成反比” 的关系,具体推导如下:

1.4 AT32F421G8U7 比较器:为何是 “无感方案的最优解”
AT32F421G8U7 内置 1 个轨到轨比较器(COMP)(文档 3.17 节),其特性完美匹配无人机电调的需求 —— 相比 “ADC 采样过零” 方案,比较器方案具有 “实时性高、CPU 占用低、抗干扰强” 的优势。
1.4.1 比较器核心特性(基于文档表 44)
文档 3.17 节明确了比较器的关键参数,这些参数直接决定过零检测的精度与可靠性,需在设计中重点关注:
| 参数名称 | 取值范围(文档表 44) | 推荐配置 | 配置理由(无人机电调场景) |
|---|---|---|---|
| 供电电压(VDDA) | 2.4~3.6V | 3.3V | 与 AT32 主供电(VDD)一致,避免电压差导致的测量误差(文档 458 页:VDD 与 VDDA 最大差 300mV) |
| 传播延迟(tD) | 高速模式:40~100ns | 高速模式 | 电调需快速响应转速变化(≤10ms),高速模式延迟最小,避免过零时刻捕捉偏差 |
| 迟滞电压(Vhys) | 低迟滞:5~17mV | 低迟滞 | 反电动势过零区域电压变化平缓,低迟滞可提高检测灵敏度;同时避免高迟滞导致的相位偏差 |
| 启动时间(tSTART) | 高速模式:1.0~3.5μs | 高速模式 | 电机启动时需快速进入过零检测状态,高速模式启动最快,避免启动阶段转速丢失 |
| 工作电流(IDDA) | 高速模式:40~61μA | 高速模式 | 无人机电调对功耗敏感,高速模式电流仅数十微安,无明显功耗压力 |
1.4.2 比较器 vs ADC:两种过零方案的对比
为何选择比较器而非 ADC?下表从 “无人机电调需求” 出发,对比两种方案的优劣:
| 对比维度 | 比较器方案(本文方案) | ADC 采样方案 | 结论(无人机电调场景) |
|---|---|---|---|
| 实时性 | 硬件比较,延迟 40~100ns(文档表 44) | 软件采样 + 数据处理,延迟≥10μs | 比较器更优,满足电调 “毫秒级响应” 需求 |
| CPU 占用 | 仅中断时占用 CPU(记录时刻) | 需周期性采样 + 滤波 + 过零判断,占用高 | 比较器更优,释放 CPU 处理换相、PID 控制等核心任务 |
| 抗干扰能力 | 支持硬件迟滞(5~70mV),抗噪声强 | 需软件滤波,易受高频噪声干扰 | 比较器更优,适应无人机复杂电磁环境 |
| 硬件复杂度 | 仅需分压 + 滤波,电路简单 | 需多通道 ADC + 复杂软件算法 | 比较器更优,降低硬件成本与故障率 |
关键结论:在无人机电调的 “实时性、低功耗、抗干扰” 需求下,AT32F421G8U7 的比较器方案是 “性价比最高的无感选择”。
二、硬件篇:AT32F421G8U7 的 “硬件适配设计”
硬件是方案落地的基础,需严格遵循 AT32F421G8U7 的电气特性(文档 6.3 节)、引脚定义(文档表 5),设计反电动势采集、零点基准、保护电路,确保信号准确、硬件可靠。
2.1 核心引脚定义:AT32 与 BLDC 的 “信号连接”
根据文档表 5(引脚定义),AT32F421G8U7 的 PA0、PA1、PA4、PA5 引脚支持比较器功能,是无感转速测量的 “核心引脚”。需明确每个引脚的功能与硬件连接:
| 引脚编号 | 引脚名称 | 比较器通道(文档表 5) | 核心功能 | 硬件连接方式 | 文档依据(关键特性) |
|---|---|---|---|---|---|
| PA0 | GPIOA_0 | COMP_INP2(正极输入) | A 相反电动势采集 | 经分压电路连接 BLDC A 相绕组 | 模拟模式下支持 0~3.3V 输入(文档表 5、6.3.13 节) |
| PA1 | GPIOA_1 | COMP_INP1(负极输入) | 零点基准电压(Vmid)采集 | 经分压 + 滤波电路连接电机中点或电源分压点 | 模拟模式下无 5V 容忍性,需严格控制电压≤3.3V(文档 393 页) |
| PA4 | GPIOA_4 | COMP_INM4(正极输入) | B 相反电动势采集 | 经分压电路连接 BLDC B 相绕组 | 同 PA0,支持模拟输入(文档表 5) |
| PA5 | GPIOA_5 | COMP_INP0(正极输入) | C 相反电动势采集 | 经分压电路连接 BLDC C 相绕组 | 同 PA0,支持模拟输入(文档表 5) |
| NVIC 中断 | COMP_IRQn | - | 比较器过零中断 | 软件配置 NVIC 优先级,抢占优先级 1(高于普通外设) | 文档 3.4 节(NVIC 支持 16 级优先级) |
关键注意点:
- PA0/PA1/PA4/PA5 必须配置为 “模拟输入模式”(GPIO_MODE_ANALOG),此时引脚无 5V 电平容忍性(文档 393 页注),输入电压不得超过 VDDA+0.3V(即 3.6V),否则会损坏引脚;
- 比较器的 “正极输入(INP)” 可切换(A→B→C 相),“负极输入(INM)” 固定为 PA1(Vmid),需通过软件实现相位切换(后续软件篇详解)。
2.2 反电动势采集电路:“高压” 转 “安全信号”
BLDC 电机的反电动势峰值通常等于电机供电电压(如 12V、24V),远超 AT32 的 VDDA 范围(2.4~3.6V),需通过分压电路将其降至 0~3.3V,同时保证信号不失真。
2.2.1 分压电路设计原理
分压电路的核心是 “两个串联电阻”,根据欧姆定律,输出电压(Vout)与输入电压(Vin,即反电动势)的关系为:\(Vout = Vin \times \frac{R2}{R1+R2}\) 设计需满足两个核心要求:
- 最大 Vout ≤ 3.3V(AT32 安全输入上限);
- 分压电阻总阻值适中(10kΩ~16kΩ),避免输入阻抗过低导致反电动势信号衰减(文档 6.3.17 节:比较器输入阻抗较高,无需额外匹配)。
2.2.2 分压电路参数计算(以 12V 电机为例)
假设电机供电电压 Vbat=12V(反电动势峰值≤12V),目标 Vout_max=3.3V,计算电阻参数:
- 代入公式:3.3V = 12V × (R2/(R1+R2)) → R1/R2 ≈ 2.63;
- 选择 R2=4.7kΩ(精度 1%),则 R1=12kΩ(12/4.7≈2.55,接近 2.63,误差可接受);
- 总阻值 R1+R2=16.7kΩ,符合 “10kΩ~16kΩ” 范围,无信号衰减风险。
2.2.3 滤波电路设计:抑制 “高频噪声”
无人机飞行时,电机线会产生高频噪声(如 PWM 开关噪声),若直接输入比较器,会导致 “过零误触发”。需在分压后增加RC 滤波电路,滤除高频噪声。
| 滤波参数 | 推荐配置 | 设计依据 |
|---|---|---|
| 电容(C) | 10nF(陶瓷电容) | 滤除 100kHz 以上高频噪声,且体积小、成本低 |
| 电阻(Rf) | 1kΩ(限流电阻) | 避免电容充放电电流过大损坏 AT32 引脚,同时辅助滤波 |
| 滤波截止频率(fc) | ≈15.9kHz | 计算公式:fc=1/(2πRC),15.9kHz 远低于反电动势的基波频率(电机转速 1000rpm 时,基波频率≈50Hz),不影响过零检测 |
2.2.4 反电动势采集电路示意图与器件选型
电路示意图:
plaintext
BLDC A相绕组 → R1(12kΩ,1%) → R2(4.7kΩ,1%) → GND
↓
Rf(1kΩ) → C(10nF) → GND
↓
AT32 PA0(COMP_INP2)
器件选型表:
| 器件类型 | 型号推荐 | 参数要求 | 文档依据 |
|---|---|---|---|
| 分压电阻(R1/R2) | 0805 封装 12kΩ/4.7kΩ | 精度 1%(避免温漂导致分压比偏差),功率≥1/16W(实际功耗仅 0.1mW,远低于额定) | 文档 6.3.13 节(GPIO 输入阻抗要求,避免电阻过小导致电流过大) |
| 滤波电阻(Rf) | 0805 封装 1kΩ | 精度 5%,功率≥1/16W | 无特殊要求,仅需限流、辅助滤波 |
| 滤波电容(C) | 0805 封装 10nF | X7R 材质(温漂 ±15%,适应 - 55~+125℃),耐压≥6.3V | 文档 6.3.12 节(电气敏感性,避免电容失效导致噪声侵入) |
2.3 零点基准电路:“精准的电压标尺”
零点基准电压(Vmid)是过零检测的 “标尺”,其稳定性直接决定过零检测的精度。若电机无中点引出,需通过 “电源分压” 模拟 Vmid,设计如下:
2.3.1 基准电路设计原理
模拟 Vmid 的核心是 “对电机供电电压(Vbat)分压”,确保 Vmid=Vbat/2,且电压稳定、无噪声。电路需满足:
- 分压电阻精度高(1%),避免温漂导致 Vmid 偏移;
- 增加滤波电容,抑制 Vbat 的纹波噪声;
- 串联限流电阻,保护 AT32 PA1 引脚。
2.3.2 基准电路参数计算(以 12V 电机为例)
目标 Vmid=6V(Vbat=12V/2),需分压至 AT32 的安全范围(3.3V),因此需 “两级分压”:
- 第一级:对 12V 分压至 6V(Vmid1),选择 R3=R4=10kΩ(1%),则 Vmid1=12V×(10k/(10k+10k))=6V;
- 第二级:对 6V 分压至 3.3V(Vmid2,输入 AT32 PA1),选择 R5=5.6kΩ、R6=6.8kΩ(1%),则 Vmid2=6V×(6.8k/(5.6k+6.8k))≈3.27V(接近 3.3V,误差≤1%);
- 滤波:在 R6 两端并联 100nF 陶瓷电容 + 1μF 钽电容,滤除高低频噪声。
2.3.3 基准电路示意图与器件选型
电路示意图:
plaintext
Vbat(12V) → R3(10kΩ) → R4(10kΩ) → GND
↓(Vmid1=6V)
R5(5.6kΩ) → R6(6.8kΩ) → GND
↓(Vmid2≈3.27V)
C1(100nF)→ GND
C2(1μF)→ GND
↓
AT32 PA1(COMP_INM1)
器件选型表:
| 器件类型 | 型号推荐 | 参数要求 | 文档依据 |
|---|---|---|---|
| 分压电阻(R3/R4/R5/R6) | 0805 封装 10kΩ/5.6kΩ/6.8kΩ | 精度 1%,功率≥1/16W | 文档 6.3.19 节(温度传感器特性,避免电阻温漂导致 Vmid 偏移) |
| 高频滤波电容(C1) | 0805 封装 100nF | X7R 材质,耐压≥6.3V | 滤除 Vbat 的高频纹波(如 PWM 开关噪声) |
| 低频滤波电容(C2) | 0805 封装 1μF | 钽电容(ESR 低,滤波效果好),耐压≥6.3V | 滤除 Vbat 的低频纹波(如电池放电导致的电压波动) |
2.4 硬件保护电路:“避免意外损坏”
无人机电调工作在高电压、大电流环境,需设计保护电路,避免 AT32 或电机损坏。核心保护措施如下表:
| 保护类型 | 设计方案 | 器件选型 | 文档依据 |
|---|---|---|---|
| 过压保护 | 在反电动势分压电路输入端并联 TVS 管 | SMAJ15CA(15V,双向) | 文档表 6(绝对最大电压:GPIO 输入电压≤6V(FT 引脚),TVS 管可钳位尖峰电压至 15V 以下) |
| 过流保护 | 在 AT32 引脚串联 100Ω 限流电阻 | 0805 封装 100Ω,1% | 文档表 7(绝对最大电流:I/O 引脚灌电流≤25mA,限流电阻可避免过流) |
| 静电保护(ESD) | PCB 布局时增加 ESD 防护环 | 无器件,PCB 设计实现 | 文档表 31(ESD 绝对最大值:人体模型 6000V,防护环可增强 ESD 能力) |
| 电源去耦 | 在 AT32 VDD/VDDA 引脚旁并联电容 | 100nF 陶瓷电容 + 4.7μF 钽电容 | 文档 4.3.6 节(供电方案图,去耦电容可稳定电源,减少噪声) |
2.5 PCB 设计规范:“信号纯净的关键”
PCB 布局不合理会导致噪声侵入、信号衰减,直接影响比较器的过零检测精度。需遵循以下规范,参考文档 6.3.12 节(电气敏感性)、7.7 节(热特性):
| PCB 设计项 | 规范要求 | 原因分析 |
|---|---|---|
| 模拟信号布线 | 1. 反电动势采集线(PA0/PA4/PA5)、基准线(PA1)需短而直,长度≤5cm; 2. 线宽≥0.2mm; 3. 远离功率线(如电机相线、PWM 驱动线)≥2mm | 缩短路径减少信号衰减;远离功率线避免电磁干扰(EMI) |
| 接地设计 | 1. 模拟地(AGND)与数字地(DGND)单点连接; 2. 比较器相关电路的地单独铺铜,再连接至 AGND | 避免数字地噪声(如 CPU、定时器)干扰模拟信号,确保 Vmid 稳定 |
| 电源布线 | 1. VDD/VDDA 线宽≥0.5mm; 2. 去耦电容靠近 AT32 引脚(距离≤3mm) | 保证供电电流充足,去耦电容快速滤除电源噪声 |
| 散热设计 | 1. AT32 芯片下方铺铜(面积≥1cm²); 2. 功率器件(如 MOS 管)单独铺散热铜皮 | 文档 7.7 节(热特性:LQFP48 封装 θJA=87℃/W,铺铜可降低结温,避免高温失效) |
| 防护设计 | 1. 电机接口增加 TVS 管、自恢复保险丝; 2. PCB 边缘增加防护圈 | 避免电机侧的高压、大电流冲击 AT32 电路 |
三、软件篇:基于 AT32 HAL 库的 “代码实现”
软件是方案的 “灵魂”,需基于 AT32 HAL 库,实现比较器、GPIO、定时器的初始化,以及过零中断捕捉、转速计算。所有代码均标注文档依据,确保配置合规、逻辑正确。
3.1 软件开发环境搭建
首先需搭建适配 AT32F421G8U7 的开发环境,确保编译、下载、调试正常:
| 工具 / 软件 | 版本 / 型号 | 配置说明 |
|---|---|---|
| 集成开发环境(IDE) | Keil MDK 5.38 | 安装 AT32F421 系列器件包(可从雅特力官网下载) |
| 编译器 | ARM Compiler 6 | 优化等级选择 “-O1”(平衡代码大小与执行速度) |
| 调试器 | J-Link V11 | 连接 AT32 SWD 接口(PA13=SWDIO,PA14=SWCLK,文档表 5) |
| 固件库 | AT32F421 HAL 库 V1.0.1 | 基于文档功能开发,确保外设驱动与芯片特性匹配 |
3.2 全局变量与宏定义:“代码的基础配置”
定义核心参数(如电机极对数、比较器延迟)、状态变量(如过零时刻、转速),确保代码可维护:
c
运行
#include "at32f421_hal.h"
/* -------------------------- 宏定义(基于文档参数) -------------------------- */
#define MOTOR_POLE_PAIRS 2 // 电机极对数(4极电机,文档3.10节转速计算需用到)
#define COMP_DELAY_US 1 // 比较器传播延迟补偿(高速模式≤100ns,近似1μs,文档表44)
#define PHASE_NUM 3 // 电机相数(A/B/C三相)
#define ZERO_CROSS_BUFF 5 // 过零时刻缓存长度(每相5个点,用于滤波)
#define CALC_INTERVAL_MS 10 // 转速计算间隔(10ms,平衡实时性与CPU占用)
/* -------------------------- 相位枚举(代码可读性) -------------------------- */
typedef enum {
PHASE_A = 0, // PA0/A相(COMP_INP2)
PHASE_B, // PA4/B相(COMP_INM4)
PHASE_C // PA5/C相(COMP_INP0)
} Motor_PhaseTypeDef;
/* -------------------------- 全局变量(状态记录) -------------------------- */
COMP_HandleTypeDef hcomp1; // 比较器句柄(文档3.17节,仅1个比较器)
TIM_HandleTypeDef htim14; // 定时器句柄(TMR14,文档3.10.2节)
Motor_PhaseTypeDef curr_phase = PHASE_A; // 当前检测相位(初始A相)
uint32_t zero_cross_time[PHASE_NUM][ZERO_CROSS_BUFF] = {0}; // 过零时刻缓存(每相5个点)
uint8_t zero_cross_cnt[PHASE_NUM] = {0}; // 每相过零计数器
float motor_speed_rpm = 0.0f; // 最终转速(rpm)
uint32_t prev_calc_time = 0; // 上一次转速计算时刻(ms)
3.3 核心外设初始化:“硬件功能的激活”
需依次初始化 GPIO(模拟输入)、比较器(过零检测)、定时器(时刻记录),每个初始化函数均严格遵循文档配置要求。
3.3.1 GPIO 初始化(模拟输入模式)
功能:将 PA0/PA1/PA4/PA5 配置为模拟输入,为比较器提供纯净的模拟信号,参考文档 3.15 节(GPIO 特性)、表 5(引脚定义)。
c
运行
/**
* @brief GPIO初始化(比较器输入引脚:PA0/A相、PA1/基准、PA4/B相、PA5/C相)
* @文档依据 3.15节(GPIO模式)、表5(引脚模拟功能)、6.3.13节(GPIO电气特性)
* @param 无
* @retval 无
*/
void GPIO_COMP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 1. 使能GPIOA时钟(文档3.15节:GPIO时钟需单独使能) */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 2. 配置PA0/PA1/PA4/PA5为模拟输入模式 */
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // 模拟输入模式(比较器需模拟信号)
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉(避免影响输入电平,文档719页)
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
关键配置说明:
GPIO_MODE_ANALOG:必须配置为模拟模式,才能将引脚信号接入比较器(文档表 5 明确标注这些引脚支持模拟输入);GPIO_NOPULL:模拟输入模式下禁止上下拉,否则会引入额外电流,导致反电动势或基准电压偏移(文档 719 页)。
3.3.2 定时器初始化(TMR14,1μs 计时)
功能:精确记录过零时刻,为转速计算提供时间基准,参考文档 3.10.2 节(TMR14 特性)、表 37(定时器参数)。
c
运行
/**
* @brief TMR14初始化(1μs计时,用于过零时刻记录)
* @文档依据 3.10.2节(TMR14为16位通用定时器)、表37(定时器时钟=2×APB1)、6.3.15节(定时器特性)
* @param 无
* @retval 无
*/
void TIM14_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* 1. 使能TMR14时钟(APB1总线,文档3.10.2节) */
__HAL_RCC_TIM14_CLK_ENABLE();
/* 2. TMR14核心配置:1μs计数,最大计数值65535μs(≈65ms) */
htim14.Instance = TIM14;
// 分频系数:APB1时钟=120MHz,TIM14时钟=2×APB1=240MHz(文档表37)
// 240MHz/(239+1)=1MHz → 计数周期=1μs
htim14.Init.Prescaler = 239;
htim14.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim14.Init.Period = 0xFFFF; // 最大计数值(65535)
htim14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim14) != HAL_OK)
{
Error_Handler(); // 初始化失败,需处理(如复位)
}
/* 3. 定时器主模式配置(无触发输出,仅计时) */
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim14, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* 4. 启动定时器 */
if (HAL_TIM_Base_Start(&htim14) != HAL_OK)
{
Error_Handler();
}
}
关键配置说明:
Prescaler = 239:根据文档表 37,APB1 时钟 = 120MHz 时,TIM14 的时钟 = 2×APB1=240MHz,分频后为 1MHz(1μs 计数),确保过零时刻记录精度;Period = 0xFFFF:最大计数值 65535μs,若电机转速过低(如 < 100rpm),过零间隔可能超过 65ms,需在软件中处理定时器溢出(后续转速计算模块详解)。
3.3.3 比较器初始化(COMP1,过零检测)
功能:配置比较器的输入通道、模式、中断,实现反电动势与基准电压的实时比较,参考文档 3.17 节(比较器特性)、表 44(比较器参数)、3.4 节(NVIC 中断)。
c
运行
/**
* @brief 比较器1初始化(A/B/C相切换+过零中断)
* @文档依据 3.17节(比较器模式、迟滞、中断)、表44(高速模式参数)、表5(比较器通道)、3.4节(NVIC)
* @param 无
* @retval 无
*/
void COMP1_Init(void)
{
COMP_InitTypeDef COMP_InitStruct = {0};
COMP_InputConfigTypeDef COMP_InputConfigStruct = {0};
/* 1. 使能比较器时钟(文档3.17节:COMP时钟需单独使能) */
__HAL_RCC_COMP_CLK_ENABLE();
/* 2. 比较器基础配置:高速模式、低迟滞、边沿中断 */
hcomp1.Instance = COMP1;
// 模式:高速模式(传播延迟40~100ns,文档表44,满足实时性)
COMP_InitStruct.Mode = COMP_MODE_HIGH_SPEED;
// 迟滞:低迟滞(5~17mV,文档表44,提高灵敏度,抑制噪声)
COMP_InitStruct.Hysteresis = COMP_HYSTERESIS_LOW;
// 输出极性:非反相(INP>INM时输出高,符合过零逻辑)
COMP_InitStruct.OutputPolarity = COMP_OUTPUTPOLARITY_NON_INVERTED;
// 无消隐(无需电机换相消隐,简化逻辑)
COMP_InitStruct.BlankingSource = COMP_BLANKINGSOURCE_NONE;
// 关闭窗口模式(仅需单阈值比较)
COMP_InitStruct.WindowMode = COMP_WINDOWMODE_DISABLE;
// 中断触发:上升沿+下降沿(捕捉所有过零点)
COMP_InitStruct.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING;
if (HAL_COMP_Init(&hcomp1, &COMP_InitStruct) != HAL_OK)
{
Error_Handler();
}
/* 3. 初始输入通道配置:A相(PA0=INP2)→ 基准(PA1=INM1) */
COMP_InputConfigStruct.PositiveInput = COMP_POSITIVEINPUT_INP2; // PA0=INP2(文档表5)
COMP_InputConfigStruct.NegativeInput = COMP_NEGATIVEINPUT_INP1; // PA1=INM1(文档表5)
if (HAL_COMP_ConfigInput(&hcomp1, &COMP_InputConfigStruct) != HAL_OK)
{
Error_Handler();
}
/* 4. 使能比较器中断(NVIC配置) */
// 中断优先级:抢占优先级1,子优先级0(高于普通外设,避免中断丢失)
HAL_NVIC_SetPriority(COMP_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(COMP_IRQn);
/* 5. 启动比较器 */
if (HAL_COMP_Start(&hcomp1) != HAL_OK)
{
Error_Handler();
}
}
关键配置说明:
COMP_MODE_HIGH_SPEED:选择高速模式,传播延迟仅 40~100ns(文档表 44),确保过零时刻捕捉无明显延迟;COMP_HYSTERESIS_LOW:低迟滞模式(5~17mV),既能避免噪声导致的误触发,又不会因迟滞过大导致过零相位偏差;COMP_TRIGGERMODE_IT_RISING_FALLING:同时捕捉上升沿和下降沿过零,增加转速计算的采样点,提高精度。
3.4 过零中断服务函数:“捕捉过零时刻的核心”
当比较器检测到反电动势过零时,会触发COMP_IRQHandler中断服务函数。函数需完成 “中断标志清除、过零时刻记录、相位切换” 三大任务,参考文档 3.17 节(比较器中断)、3.4 节(NVIC)。
c
运行
/**
* @brief 比较器中断服务函数(过零时刻捕捉+相位切换)
* @文档依据 3.17节(比较器中断标志)、表5(相位通道切换)、3.10.2节(定时器值读取)
* @param 无
* @retval 无
*/
void COMP_IRQHandler(void)
{
COMP_InputConfigTypeDef COMP_InputConfigStruct = {0};
uint32_t curr_time = 0;
/* 1. 检查并清除比较器边沿中断标志(避免重复触发) */
if (__HAL_COMP_GET_FLAG(&hcomp1, COMP_FLAG_EDGE) != RESET)
{
__HAL_COMP_CLEAR_FLAG(&hcomp1, COMP_FLAG_EDGE);
/* 2. 读取当前定时器值(记录过零时刻,单位:μs) */
curr_time = __HAL_TIM_GET_COUNTER(&htim14);
/* 3. 记录当前相的过零时刻(循环缓存,覆盖旧数据) */
if (zero_cross_cnt[curr_phase] < ZERO_CROSS_BUFF)
{
zero_cross_time[curr_phase][zero_cross_cnt[curr_phase]] = curr_time;
zero_cross_cnt[curr_phase]++;
}
else
{
// 缓存满,循环覆盖(保留最新5个时刻)
for (uint8_t i = 0; i < ZERO_CROSS_BUFF - 1; i++)
{
zero_cross_time[curr_phase][i] = zero_cross_time[curr_phase][i + 1];
}
zero_cross_time[curr_phase][ZERO_CROSS_BUFF - 1] = curr_time;
}
/* 4. 切换至下一相检测(A→B→C→A循环,文档表5通道配置) */
curr_phase = (curr_phase + 1) % PHASE_NUM;
switch (curr_phase)
{
case PHASE_A:
// A相:PA0=COMP_INP2(文档表5)
COMP_InputConfigStruct.PositiveInput = COMP_POSITIVEINPUT_INP2;
break;
case PHASE_B:
// B相:PA4=COMP_INM4(文档表5)
COMP_InputConfigStruct.PositiveInput = COMP_POSITIVEINPUT_INM4;
break;
case PHASE_C:
// C相:PA5=COMP_INP0(文档表5)
COMP_InputConfigStruct.PositiveInput = COMP_POSITIVEINPUT_INP0;
break;
default:
COMP_InputConfigStruct.PositiveInput = COMP_POSITIVEINPUT_INP2;
break;
}
// 基准通道固定:PA1=COMP_INM1(文档表5)
COMP_InputConfigStruct.NegativeInput = COMP_NEGATIVEINPUT_INP1;
HAL_COMP_ConfigInput(&hcomp1, &COMP_InputConfigStruct);
}
/* 5. HAL库中断处理(必要步骤,确保中断正常) */
HAL_COMP_IRQHandler(&hcomp1);
}
关键逻辑说明:
- 相位切换:通过
COMP_InputConfigStruct.PositiveInput切换比较器的正极输入通道,实现 A→B→C 相的轮流检测,确保电机运行时总有一相是 “悬空绕组”,反电动势波形完整; - 循环缓存:保留最新 5 个过零时刻,后续转速计算时可通过 “滑动平均” 滤波,减少单次过零误差的影响。
3.5 转速计算模块:“从时刻到 rpm 的转换”
转速计算的核心是 “处理过零时刻间隔,代入公式计算”,需解决 “定时器溢出、比较器延迟补偿、滤波” 三大问题,参考文档 3.10 节(定时器精度)、3.17 节(比较器延迟)。
c
运行
/**
* @brief 转速计算(基于A相过零间隔,滑动平均滤波)
* @文档依据 3.10节(定时器溢出处理)、3.17节(比较器延迟补偿)、6.3.15节(定时器精度)
* @param 无
* @retval 无
*/
void Motor_Speed_Calc(void)
{
uint32_t curr_time_ms = HAL_GetTick(); // 当前系统时间(ms)
uint32_t delta_t = 0; // 过零间隔(μs)
uint32_t t1 = 0, t2 = 0; // 相邻过零时刻(μs)
float avg_delta_t = 0.0f; // 平均过零间隔(μs)
uint8_t valid_cnt = 0; // 有效过零间隔数量
/* 1. 每CALC_INTERVAL_MS(10ms)计算一次,避免频繁占用CPU */
if (curr_time_ms - prev_calc_time < CALC_INTERVAL_MS)
{
return;
}
prev_calc_time = curr_time_ms;
/* 2. 检查A相过零数据是否足够(至少2个时刻才能计算间隔) */
if (zero_cross_cnt[PHASE_A] < 2)
{
motor_speed_rpm = 0.0f; // 数据不足,转速为0
return;
}
/* 3. 计算A相相邻过零间隔,处理定时器溢出(65535μs) */
avg_delta_t = 0.0f;
valid_cnt = 0;
for (uint8_t i = 1; i < zero_cross_cnt[PHASE_A]; i++)
{
t1 = zero_cross_time[PHASE_A][i - 1];
t2 = zero_cross_time[PHASE_A][i];
// 处理定时器溢出:若t2 < t1,说明溢出,间隔=(65536 - t1) + t2
if (t2 >= t1)
{
delta_t = t2 - t1;
}
else
{
delta_t = (0xFFFF + 1 - t1) + t2;
}
/* 4. 过滤异常间隔(排除误触发,如间隔<100μs或>100ms) */
if (delta_t > 100 && delta_t < 100000)
{
// 补偿比较器延迟(文档表44:高速模式≤100ns,近似1μs)
delta_t += COMP_DELAY_US;
avg_delta_t += delta_t;
valid_cnt++;
}
}
/* 5. 计算平均间隔,代入公式计算转速 */
if (valid_cnt > 0)
{
avg_delta_t /= valid_cnt; // 滑动平均,减少误差
// 转速公式:n = 60×10^6 / (2×P×Δt)
motor_speed_rpm = (60.0f * 1000000.0f) / (2 * MOTOR_POLE_PAIRS * avg_delta_t);
}
else
{
motor_speed_rpm = 0.0f; // 无有效间隔,转速为0
}
/* 6. 调试:打印转速(需配置USART,文档3.12节) */
// printf("Motor Speed: %.1f rpm\r\n", motor_speed_rpm);
}
关键处理说明:
- 定时器溢出处理:当 t2 <t1 时,说明定时器已溢出(从 65535 计数到 0),间隔需计算为 “溢出部分 + 当前部分”,避免转速计算错误;
- 延迟补偿:根据文档表 44,比较器高速模式传播延迟≤100ns,近似补偿 1μs,减少系统误差;
- 异常过滤:排除过短(<100μs,可能是噪声误触发)或过长(>100ms,可能是电机停转)的间隔,确保转速计算可靠。
3.6 主函数与初始化流程:“代码的入口”
主函数负责 “初始化外设、启动任务、主循环处理”,确保软件流程清晰、稳定运行:
c
运行
/**
* @brief 主函数
* @文档依据 1.介绍(需结合HAL库)、3.5节(系统时钟)、各外设初始化依据
* @param 无
* @retval int
*/
int main(void)
{
/* 1. HAL库初始化(初始化定时器、中断控制器等) */
HAL_Init();
/* 2. 系统时钟配置(120MHz,文档3.5节时钟树、表27 PLL特性) */
SystemClock_Config();
/* 3. 外设初始化(按依赖顺序:GPIO→TIM→COMP) */
GPIO_COMP_Init(); // GPIO模拟输入初始化
TIM14_Init(); // 定时器初始化(计时)
COMP1_Init(); // 比较器初始化(过零检测)
/* 4. 主循环(持续处理转速计算,可添加其他任务如PID控制) */
while (1)
{
Motor_Speed_Calc(); // 转速计算(每10ms一次)
// 其他任务:如电机换相控制、转速闭环PID、故障检测等
}
}
/**
* @brief 系统时钟配置(120MHz,文档3.5节、表27 PLL特性)
* @文档依据 3.5节(时钟管理)、表27(PLL输入2~16MHz,输出≤120MHz)、6.3.10节(闪存等待周期)
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 1. 外部高速时钟(HSE=8MHz,文档3.5节) */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
// PLL配置:HSE为输入(8MHz),倍频15倍→120MHz(表27:PLL_OUT≤120MHz)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL15;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 2. 总线时钟配置(文档2.规格说明:APB1/APB2最大120MHz) */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟=PLL时钟(120MHz)
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // HCLK=120MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1=120MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2=120MHz
// 闪存等待周期:>96MHz时需3个等待周期(文档6.3.10节,表28)
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 错误处理函数(初始化失败时调用)
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
while (1)
{
// 错误指示:如点亮LED,方便调试
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2); // 假设PA2接LED
HAL_Delay(500);
}
}
关键配置说明:
- 系统时钟:根据文档表 27(PLL 特性),PLL 输入时钟范围 2~16MHz,输出≤120MHz,选择 HSE=8MHz×15=120MHz,满足 AT32 的最高主频,确保软件实时性;
- 闪存等待周期:文档 6.3.10 节(表 28)明确,当 HCLK>96MHz 时,闪存需 3 个等待周期,否则会导致代码执行错误,需配置
FLASH_LATENCY_3。

1285

被折叠的 条评论
为什么被折叠?



