电机驱动开发学习20. 霍尔有感 SVPWM FOC 速度闭环(对齐野火官方)
- 一、本章在系列中的位置
-
- [1.1 与前面章节的衔接](#1.1 与前面章节的衔接)
- [1.2 本章目标](#1.2 本章目标)
- [1.3 本章新增知识点(前面章节未展开的)](#1.3 本章新增知识点(前面章节未展开的))
- 二、官方算法详解(本章核心)
-
- [2.1 ST `MCM_Clarke` / `MCM_Park` / `MCM_Rev_Park`](#2.1 ST
MCM_Clarke/MCM_Park/MCM_Rev_Park) -
- [2.1.1 与 lesson15「经典」公式对照](#2.1.1 与 lesson15「经典」公式对照)
- [2.1.2 为什么不能混用](#2.1.2 为什么不能混用)
- [2.1.3 物理直觉(简记)](#2.1.3 物理直觉(简记))
- [2.2 ST 电压 s16 标度](#2.2 ST 电压 s16 标度)
-
- [2.2.1 官方在算什么](#2.2.1 官方在算什么)
- [2.2.2 与 lesson18 的差异](#2.2.2 与 lesson18 的差异)
- [2.2.3 `SQRT3FACTOR` 从哪来](#2.2.3
SQRT3FACTOR从哪来)
- [2.3 β 取反(发生在 SVPWM,不是 RevPark 之外再翻一次)](#2.3 β 取反(发生在 SVPWM,不是 RevPark 之外再翻一次))
- [2.4 CCR 公式(`PWMC_SetPhaseVoltage`)](#2.4 CCR 公式(
PWMC_SetPhaseVoltage)) -
- [2.4.1 周期约定](#2.4.1 周期约定)
- [2.4.2 中间量](#2.4.2 中间量)
- [2.4.3 和 lesson18 七段式的关系](#2.4.3 和 lesson18 七段式的关系)
- [2.5 霍尔相移 `HALL_PHASE_SHIFT = 240°`](#2.5 霍尔相移
HALL_PHASE_SHIFT = 240°) -
- [2.5.1 定义](#2.5.1 定义)
- [2.5.2 扇区角怎么算(对齐 `HALL_Clear`)](#2.5.2 扇区角怎么算(对齐
HALL_Clear)) - [2.5.3 调相移的正确姿势](#2.5.3 调相移的正确姿势)
- [2.6 `HALL_CalcElAngle`:边沿锚定 + 速度积分](#2.6
HALL_CalcElAngle:边沿锚定 + 速度积分) -
- [2.6.1 官方在做什么](#2.6.1 官方在做什么)
- [2.6.2 本课对应实现](#2.6.2 本课对应实现)
- [2.6.3 和 lesson17/18「TIM5 插值」的对比](#2.6.3 和 lesson17/18「TIM5 插值」的对比)
- [2.7 一条必须成套的链路(排障用)](#2.7 一条必须成套的链路(排障用))
- [2.1 ST `MCM_Clarke` / `MCM_Park` / `MCM_Rev_Park`](#2.1 ST
- 三、移植了官方的哪些功能
- 四、硬件与铭牌
-
- [4.1 引脚(Motor1 / 接口 1)](#4.1 引脚(Motor1 / 接口 1))
- [4.2 电机铭牌(本机)](#4.2 电机铭牌(本机))
- 五、程序设计
-
- [5.1 相对 lesson18/19 的目录变更](#5.1 相对 lesson18/19 的目录变更)
- [5.2 控制链路](#5.2 控制链路)
- [5.3 启动状态机(`bsp_foc_boot`)](#5.3 启动状态机(
bsp_foc_boot)) -
- [5.3.1 本章启动方式](#5.3.1 本章启动方式)
- [5.3.2 默认路径(`FOC_BOOT_USE_ALIGN = 0`)](#5.3.2 默认路径(
FOC_BOOT_USE_ALIGN = 0)) - [5.3.3 Run 内的「软衔接」(不是 Drag,但是启动关键)](#5.3.3 Run 内的「软衔接」(不是 Drag,但是启动关键))
- [5.3.4 可选路径(`FOC_BOOT_USE_ALIGN = 1`,教学/标定)](#5.3.4 可选路径(
FOC_BOOT_USE_ALIGN = 1,教学/标定)) - [5.3.5 与 lesson19 / 官方启动对照](#5.3.5 与 lesson19 / 官方启动对照)
- [5.4 与官方 / lesson19 运行对照](#5.4 与官方 / lesson19 运行对照)
- [5.5 按键功能(骄阳板)](#5.5 按键功能(骄阳板))
- 六、实验步骤
-
- [6.1 先烧官方 hex 验证硬件](#6.1 先烧官方 hex 验证硬件)
- [6.2 本课固件](#6.2 本课固件)
- [6.3 变量曲线解析](#6.3 变量曲线解析)
- 七、小结与下一章

一、本章在系列中的位置
1.1 与前面章节的衔接
| 章节 | 前节已经掌握的 | 本章如何用到 |
|---|---|---|
| 14 FOC 架构 | 双环 + 坐标变换总览 | 控制框图不变 |
| 15 Clark/Park/InvPark | 经典等幅值变换公式 | 本章改为 ST MCSDK 同款公式(见 1.3) |
| 16 DQ 电流环 | Id/Iq PI | 保留;环路搬到 ADC 注入 ISR |
| 17 速度外环 | 速度 PI → Iq* | 保留(约 50 ms 慢环) |
| 18 SVPWM | 自研七段式:扇区→Ta/Tb→CCR | 替换为官方 PWMC_SetPhaseVoltage(关键差异) |
| 19 三段式启动 | Align→Drag→Run,接口 2 六步桥 | 本章换 接口 1 ;默认 无 Align/Drag,对齐官方 Charge→Offset→Run |
一句话:
lesson19 在六步板上教「怎么从静止爬起来」;
lesson20 在三相全桥上教「怎么按野火官方 Hall FOC 把正弦电机真正 SVPWM 闭环转起来」。
1.2 本章目标
在 无刷驱动接口 1 + 正弦反电动势霍尔电机 上,对照官方工程
improve_part/PMSM_FOC_V5.44_Hall_speed_control,跑通:
ChargeCap → OffsetCal → Run(霍尔θ + Id/Iq 双环 + 官方同款 SVPWM + 速度外环)
本机实测:串口 t 500 / w 1 后转速可平滑爬到 约 500 RPM (空载电流数百 mA 量级);运行中可用板载 K3/K4 以 50 RPM 步进加减目标转速。
排线必须用驱动板 「无刷电机驱动接口 1」(TIM1 三相互补),不是 lesson19 的接口 2。
1.3 本章新增知识点(前面章节未展开的)
| # | 新知识点 | lesson15~18 差异 | 本课落点 |
|---|---|---|---|
| 1 | ST MCSDK 坐标变换约定 | lesson15 教的是教科书 Clark/Park;官方 MCM_Clarke/Park/Rev_Park 的 β、Id/Iq 投影不同 |
bsp_foc.c |
| 2 | 电流极性 I = Offset − ADC |
早期课常用 ADC − Offset;与 ST 链混用会整环反号 |
bsp_adc.c |
| 3 | 同 PWM 周期双相采样 + 第三相重构 | lesson6 三相轮询/断续注入时刻不一致 | ADC1 注入 rank1/2 连采 U+V,W=-U-V |
| 4 | 官方 SVPWM:PWMC_SetPhaseVoltage |
lesson18 七段式公式与 ST 的 s16 标度、β 取反、CCR 公式不同;混用必堵转过流 | bsp_svpwm.c |
| 5 | 电压 s16 标度:32767 ↔ Vbus/√3 |
lesson18 用伏特直接算时间;官方先数字化再算 CCR | svpwm_calc() 内转换 |
| 6 | 霍尔相移 HALL_PHASE_SHIFT=240° |
与 ST 变换、扇区表绑定;不能拿经典 Park 去扫 60° 冒充 | MOTOR_HALL_PHASE_SHIFT_DEG |
| 7 | PWM 频率电流环(≈15 kHz) | lesson16/17 主循环 ~25 ms 太慢,跟不上正弦调制 | dq_current_loop_hf() @ ADC JEOS |
| 8 | 霍尔 θ:边沿锚定 + ω·Ts 积分 + 扇区钳位 | lesson17/18 偏 TIM5 插值;官方是 HALL_CalcElAngle 风格 |
foc_theta_hf_* |
| 9 | 启动:Charge 自举 + Offset 零偏(低边 ON) | lesson19 强调 Align/Drag;正弦+霍尔官方可直接 Offset→Run | bsp_foc_boot.c |
下面 第二节 把表中第 1、4、5、6、8 条展开讲透;读代码时请对照
bsp_foc.c/bsp_svpwm.c。
二、官方算法详解(本章核心)
官方源文件:
- 变换:
PMSM_control/Src/mc_math.c→MCM_Clarke/MCM_Park/MCM_Rev_Park - SVPWM:
MCLib/.../pwm_curr_fdbk.c→PWMC_SetPhaseVoltage - 霍尔:
MCLib/.../hall_speed_pos_fdbk.c→HALL_CalcElAngle/HALL_Clear - 参数:
pmsm_motor_parameters.h→HALL_PHASE_SHIFT = 240
2.1 ST MCM_Clarke / MCM_Park / MCM_Rev_Park
2.1.1 与 lesson15「经典」公式对照
lesson15 常用的经典等幅值写法是:
I α = I a I β = I a + 2 I b 3 I d = I α cos θ + I β sin θ I q = − I α sin θ + I β cos θ V α = V d cos θ − V q sin θ V β = V d sin θ + V q cos θ \begin{aligned} I_\alpha &= I_a \\ I_\beta &= \frac{I_a + 2I_b}{\sqrt{3}} \\ I_d &= I_\alpha\cos\theta + I_\beta\sin\theta \\ I_q &= -I_\alpha\sin\theta + I_\beta\cos\theta \\ V_\alpha &= V_d\cos\theta - V_q\sin\theta \\ V_\beta &= V_d\sin\theta + V_q\cos\theta \end{aligned} IαIβIdIqVαVβ=Ia=3 Ia+2Ib=Iαcosθ+Iβsinθ=−Iαsinθ+Iβcosθ=Vdcosθ−Vqsinθ=Vdsinθ+Vqcosθ
官方 ST MCSDK(注释与实现一致)是另一套约定:
I α = I a I β = − I a + 2 I b 3 I d = I α sin θ + I β cos θ I q = I α cos θ − I β sin θ V α = V q cos θ + V d sin θ V β = − V q sin θ + V d cos θ \begin{aligned} I_\alpha &= I_a \\ I_\beta &= -\frac{I_a + 2I_b}{\sqrt{3}} \\ I_d &= I_\alpha\sin\theta + I_\beta\cos\theta \\ I_q &= I_\alpha\cos\theta - I_\beta\sin\theta \\ V_\alpha &= V_q\cos\theta + V_d\sin\theta \\ V_\beta &= -V_q\sin\theta + V_d\cos\theta \end{aligned} IαIβIdIqVαVβ=Ia=−3 Ia+2Ib=Iαsinθ+Iβcosθ=Iαcosθ−Iβsinθ=Vqcosθ+Vdsinθ=−Vqsinθ+Vdcosθ
本课浮点实现(bsp_foc.c):
c
/* Clarke */
Ialpha = Ia;
Ibeta = -(Ia + 2*Ib) / sqrt(3);
/* Park */
Id = Ialpha*sinθ + Ibeta*cosθ;
Iq = Ialpha*cosθ - Ibeta*sinθ;
/* RevPark */
Valpha = Vq*cosθ + Vd*sinθ;
Vbeta = -Vq*sinθ + Vd*cosθ;
2.1.2 为什么不能混用
| 混用方式 | 典型现象 |
|---|---|
| 经典 Park + ST RevPark / 官方 SVPWM | Id、Iq 对调或反号,PI 顶饱和 |
| ST Park + lesson18 旧七段式 SVPWM | 转矩方向错,堵转/过流 |
| 经典变换 + 官方相移 240 | 相当于再偏约 90°,扫相移也救不回来 |
本课从采样极性 → 变换 → SVPWM → 霍尔相移,必须整条链路用 ST 约定;lesson15 公式只作对照,不要抄回本工程。
2.1.3 物理直觉(简记)
- Clarke:ST 的 ( I β ) (I_\beta) (Iβ)相对经典多一个负号(β 轴朝向约定不同)。
- Park:ST 把 (d) 轴投影写成 ( α sin + β cos ) (\alpha\sin+\beta\cos) (αsin+βcos),等价于把电角度零点定义在与经典差约 90° 的参考上。
- RevPark:与 ST Park 互逆,保证 ( V d , V q ) (V_d,V_q) (Vd,Vq) 能还原到同一 ( α β ) (\alpha\beta) (αβ)。
- 官方霍尔角、
HALL_PHASE_SHIFT、SVPWM 都按这套零点标定。
2.2 ST 电压 s16 标度
2.2.1 官方在算什么
MCSDK 内部电流、电压多用 int16(s16):
- 角度:q1.15, ( 32768 ↔ 360 ∘ ) (32768 \leftrightarrow 360^\circ) (32768↔360∘) 电角度;
- 电压:满量程与母线相关------数字量 32767 对应相电压幅值 ( V b u s / 3 ) (V_{bus}/\sqrt{3}) (Vbus/3 )
(SVPWM 六边形内切圆半径,即线性区内最大可用相电压)。
因此伏特 → s16:
u s 16 = s a t ( V v o l t ⋅ 32767 ⋅ 3 V b u s ) u_{s16} = \mathrm{sat}\left(\frac{V_{\mathrm{volt}}\cdot 32767\cdot\sqrt{3}}{V_{bus}}\right) us16=sat(VbusVvolt⋅32767⋅3 )
本课(bsp_svpwm.c):
c
scale = 32767.0f * sqrt(3) / Vbus;
alpha_s16 = Valpha_volt * scale;
beta_s16 = Vbeta_volt * scale;
反算: ( V v o l t = u s 16 ⋅ V b u s / ( 32767 ⋅ 3 ) ) (V_{\mathrm{volt}} = u_{s16}\cdot V_{bus}/(32767\cdot\sqrt{3})) (Vvolt=us16⋅Vbus/(32767⋅3 ))。
2.2.2 与 lesson18 的差异
| lesson18 自研 SVPWM | 官方 / 本课 | |
|---|---|---|
| 输入 | 直接用伏特算 (T_a,T_b) | 先换成 s16,再进 CCR 整数公式 |
| 限幅 | ( V m a x = V b u s × 2 / 3 ) (V_{max}=V_{bus}\times 2/3) (Vmax=Vbus×2/3) | 内切圆 ( V m a x = V b u s / 3 ) (V_{max}=V_{bus}/\sqrt{3}) (Vmax=Vbus/3 ) |
| 宏 | 旧 FOC_SVPWM_VMAX_MUL=2/3 |
现为 1/√3 |
PI 限幅、圆限幅必须与这一标度一致,否则「电压数字很大但实际占空比不对」或提前饱和。
2.2.3 SQRT3FACTOR 从哪来
官方:
c
#define SQRT3FACTOR 0xDDB4 /* = 16384 * √3 * 2 = 56756 */
hT_Sqrt3 = (PWM_PERIOD_CYCLES * SQRT3FACTOR) / 16384;
即 ( h T _ S q r t 3 ≈ P W M _ P E R I O D × 2 3 ) (hT\_Sqrt3 \approx PWM\_PERIOD \times 2\sqrt{3}) (hT_Sqrt3≈PWM_PERIOD×23 )。后面与 alpha_s16 相乘再除以 ( 2 18 ) (2^{18}) (218) 等,把「电压数字 × 周期」压回 CCR 量级。
2.3 β 取反(发生在 SVPWM,不是 RevPark 之外再翻一次)
官方 PWMC_SetPhaseVoltage 核心两行:
c
wUAlpha = Valfa_beta.alpha * hT_Sqrt3;
wUBeta = -(Valfa_beta.beta * PWMperiod) * 2; /* 注意负号 */
本课对应:
c
wUAlpha = alpha_s16 * hT_Sqrt3;
wUBeta = -(beta_s16 * pwm_period) * 2;
含义:
- RevPark 已经给出 ST 约定下的 (V_\alpha,V_\beta);
- 进入占空比几何时,ST 对 β 再取负,与扇区判别 (wX,wY,wZ) 的定义配套;
- 若在调用
svpwm_calc前再手动Ubeta = -Vbeta(旧FOC_SVPWM_NEGATE_BETA=1),等于取反两次,转矩方向再次弄反。
因此本课:FOC_SVPWM_NEGATE_BETA = 0,取反只保留在 svpwm_calc 内部。
2.4 CCR 公式(PWMC_SetPhaseVoltage)
2.4.1 周期约定
- 中心对齐 PWM:
ARR = PeriodArr(本课 5600)。 - 官方
PWM_PERIOD_CYCLES= 上下计数一整周 =2 * ARR。 - 本课:
pwm_period = PeriodArr * 2。
50% 占空附近:CCR ≈ pwm_period/4 = ARR/2。
2.4.2 中间量
text
wX = wUBeta
wY = (wUBeta + wUAlpha) / 2
wZ = (wUBeta - wUAlpha) / 2
按 (wY,wZ,wX) 符号分成 6 个扇区(官方 SECTOR_1...6,下标 0...5),每扇给出 wTimePhA/B/C,例如扇区 1:
text
wTimePhA = pwm_period/4 + (wX - wZ) / 262144
wTimePhB = wTimePhA + wZ / 131072
wTimePhC = wTimePhB - wX / 131072
其中 (262144=2{18})、(131072=2{17}),与前面 hT_Sqrt3、s16 相乘的位宽配套。
最后:
text
CCR_A = clamp(wTimePhA, 0, ARR)
CCR_B = clamp(wTimePhB, 0, ARR)
CCR_C = clamp(wTimePhC, 0, ARR)
写入 TIM1 CH1/2/3。低调制时采样可固定采 U/V(官方亦常落在 SECTOR_4 采 AB),与本课「同拍 U+V、重构 W」一致。
2.4.3 和 lesson18 七段式的关系
lesson18:先判扇区 → 算 ( T a , T b ) (T_a,T_b) (Ta,Tb) → 再映射到中央对齐 CCR。
官方:在 s16 电压空间 里用 ( w X , w Y , w Z ) (wX,wY,wZ) (wX,wY,wZ) 一次算出三相时间。
二者在理想连续域可等价,但定点标度、β 符号、扇区编号任一不一致就会错;本课不再维护两套公式,只保留官方这一套。
2.5 霍尔相移 HALL_PHASE_SHIFT = 240°
2.5.1 定义
官方 pmsm_motor_parameters.h:
c
#define HALL_PHASE_SHIFT 240 /* degrees */
本课:MOTOR_HALL_PHASE_SHIFT_DEG = 240.0f,
FOC_HALL_ANGLE_OFFSET = 240° × π/180。
相移补偿的是:霍尔开关边沿时刻 与 d 轴(或 ST 约定下的电角度零点) 之间的机械/电气安装偏差,必须与 MCM_Park 使用同一角度定义。
2.5.2 扇区角怎么算(对齐 HALL_Clear)
霍尔 6 态映射到扇区号 sec ∈ [0..5](本课 foc_hall_to_sector,与官方正转序一致):
| 霍尔态 | sec | 中心角(PhaseShift=240 时) |
|---|---|---|
| 5 | 0 | 270° |
| 1 | 1 | 330° |
| 3 | 2 | 30° |
| 2 | 3 | 90° |
| 6 | 4 | 150° |
| 4 | 5 | 210° |
公式:
- 扇区起点(边沿锚定) : ( θ b o u n d = P H A S E _ S H I F T + s e c × 60 ∘ ) (\theta_{bound} = PHASE\_SHIFT + sec\times 60^\circ) (θbound=PHASE_SHIFT+sec×60∘)
- 扇区中心(静止/超时) : ( θ c e n t e r = P H A S E _ S H I F T + ( s e c + 0.5 ) × 60 ∘ ) (\theta_{center} = PHASE\_SHIFT + (sec+0.5)\times 60^\circ) (θcenter=PHASE_SHIFT+(sec+0.5)×60∘)
2.5.3 调相移的正确姿势
- 只允许在 整条 ST 链路已对齐 后,按电机/霍尔安装 ±60° 微调;
- 不要用「经典 Park + 扫相移」去凑官方 240------那是在补偿另一套零点,会把问题掩盖成随机最佳角(本课调试中曾出现假象最优 60°)。
2.6 HALL_CalcElAngle:边沿锚定 + 速度积分
2.6.1 官方在做什么
霍尔每 60° 电角度才跳变一次,若 θ 只取扇区中心,Park 会呈 60° 台阶。官方做法:
- 霍尔边沿 IRQ :根据新霍尔态写入
MeasuredElAngle(含PhaseShift),并更新扇区电角速度估计(捕获周期 →hElSpeedDpp,单位:每个速度环周期转过的 s16 角增量)。 - 每个 PWM/FOC 节拍调用
HALL_CalcElAngle:
c
/* 伪代码,对应 hall_speed_pos_fdbk.c */
MeasuredElAngle += hElSpeedDpp;
hElAngle += hElSpeedDpp + CompSpeed;
return hElAngle;
即:在两次边沿之间,用最近测得的电角速度做开环积分,得到近似连续的电角度;下一边沿再拉回真实边界,消除积分漂移。
速度过低或捕获无效时置 HALL_MAX_PSEUDO_SPEED,避免用噪声速度空转角度。
2.6.2 本课对应实现
| 官方 | 本课 |
|---|---|
边沿写 MeasuredElAngle |
foc_theta_hf_on_hall_edge() → foc_hall_boundary_theta() |
捕获估速 → hElSpeedDpp |
捕获周期 → s_hf_omega(rad/s) |
HALL_CalcElAngle 每拍积分 |
foc_theta_hf_step(dt):θ += ω·Ts |
| 扇区内限制 | foc_theta_hf_clamp_sector() |
| 静止/超时 | ω 衰减,回落到 foc_hall_center_theta() |
电流环在 ADC 注入完成 ISR 里跑(≈15 kHz),每拍调用一次 foc_theta_hf_step,效果对齐官方「FOC 节拍更新电角度」。
2.6.3 和 lesson17/18「TIM5 插值」的对比
| TIM5 扇区插值(lesson17/18) | HALL_CalcElAngle 风格(本章) | |
|---|---|---|
| 边沿间 θ | 用本扇区已走时间 / 上扇区周期 × 60° | 用估得的 ω 积分 |
| 与官方 | 不同实现 | 同思路 |
| 堵转风险 | 周期陈旧时可能插到错误位置 | 超时强制 ω→0 并钉中心,避免 θ 空转 |
2.7 一条必须成套的链路(排障用)
text
I = Offset − ADC
→ ST Clarke / Park
→ Id/Iq PI(伏特)
→ ST RevPark
→ s16 标度(32767 ↔ Vbus/√3)
→ PWMC 同款 CCR(内部 β 取反)
→ θ = PhaseShift(240) + 边沿/ω积分
任一环节改回「经典 / lesson18 旧式 / 二次取反 β」,就会回到堵转、过流、Id 饱和。
三、移植了官方的哪些功能
对照路径:PMSM_FOC_V5.44_Hall_speed_control(ST MCSDK 5.4.4 + 野火骄阳)。
| 官方模块 / 文件 | 官方函数或定义 | 本课对应 | 说明 |
|---|---|---|---|
mc_math.c |
MCM_Clarke / MCM_Park / MCM_Rev_Park |
foc_clark / foc_park / foc_inv_park |
公式对齐,浮点实现 |
pwm_curr_fdbk.c |
PWMC_SetPhaseVoltage |
svpwm_calc |
整段 CCR 算法移植(含内部 β 取反) |
pwm_curr_fdbk.h |
SQRT3FACTOR、电压满量程约定 |
SVPWM_SQRT3FACTOR、32767↔Vbus/√3 |
标度必须一致 |
r3_2_*.c(思想) |
低调制固定采 A/B,第三相重构;I=Offset−ADC |
ADC1 同拍 U+V + W=-U-V;Offset−ADC |
未完整搬双 ADC 扇区 JSQR(留 lesson21) |
hall_speed_pos_fdbk.c |
HALL_CalcElAngle / HALL_Clear、扇区表 |
foc_theta_hf_*、foc_hall_to_sector |
边沿+ω积分+钳位;相移 240 |
pmsm_motor_parameters.h |
HALL_PHASE_SHIFT = 240 |
MOTOR_HALL_PHASE_SHIFT_DEG |
同值 |
mc_tasks.c |
Charge / Offset / FOC @ PWM | bsp_foc_boot + ADC ISR FOC |
启停改串口,逻辑同构 |
| 电流环调度 | TSK_HighFrequencyTask @ PWM |
HAL_ADCEx_InjectedConvCpltCallback → dq_current_loop_hf |
同为 PWM 量级 |
刻意未完整移植:
- R3_2 按扇区切换 ADC1/ADC2 JSQR(高调制采样窗)
- MCSDK 整套故障状态机 / UI
四、硬件与铭牌
4.1 引脚(Motor1 / 接口 1)
*本章改用反电动势为正弦的PMSM电机。*型号是 : 42JSF840AS-1000-8,接线:


规格参数:
PWM U/V/W : PA8/PA9/PA10 TIM1 CH1/2/3
PWM UL/VL/WL : PB13/14/15 TIM1 CH1N/2N/3N
ADC 触发 : TIM1 CH4 → ADC1 注入(同拍 U+V)
霍尔 HU/HV/HW : PC6/7/8 TIM3 Hall
电流 IU/IV/IW : PA3/PA4/PA6
母线 / 温度 : PB0 / PB1 ADC2 轮询
SD 使能 : PG12
PWM ≈ 15 kHz 中心对齐(MOTOR_PWM_PERIOD_COUNT=5600)。官方 Demo 约 16 kHz,控制结构一致。
4.2 电机铭牌(本机)
| 项目 | 数值 |
|---|---|
| 额定 | 24 V / 4 A / 3000 rpm(电源建议 24 V--10 A) |
| 极对数 | 4 |
| 反电动势 | 正弦波 |
| 霍尔 | 5 V,120° |
关键宏见 User/foc/bsp_motor_params.h:MOTOR_POLE_PAIRS、MOTOR_HALL_PHASE_SHIFT_DEG=240、MOTOR_STARTUP_IQ_MA。
五、程序设计
5.1 相对 lesson18/19 的目录变更
User/
├── foc/
│ ├── bsp_foc.c/h ★ ST 变换 + foc_theta_hf(官方霍尔角)
│ ├── bsp_svpwm.c/h ★ 官方 PWMC_SetPhaseVoltage 移植
│ ├── bsp_foc_boot.c/h ★ Charge→Offset→Run(默认同官方,无 Align)
│ └── bsp_motor_params.h ★ 相移 240、极对数、起步 Iq
├── adc/
│ └── bsp_adc.c/h ★ 同拍 U+V、Offset−ADC、JEOS 调 HF FOC
├── pid/
│ └── bsp_pid_app.c/h ★ 速度慢环 + 串口;电流环在 ISR
├── tim/
│ └── bsp_motor_tim.c/h ★ 接口1 互补 PWM + 霍尔捕获
├── key/
│ └── bsp_key.c/h ★ 骄阳板按键 GPIO(SW2~SW5)
└── bldcm_control/
└── bsp_bldcm_control.c ★ `deal_key_input()`:启停后按键调速/调 Iq
5.2 控制链路
#mermaid-svg-9jhljOB8PXZixYrp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9jhljOB8PXZixYrp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9jhljOB8PXZixYrp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9jhljOB8PXZixYrp .error-icon{fill:#552222;}#mermaid-svg-9jhljOB8PXZixYrp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9jhljOB8PXZixYrp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9jhljOB8PXZixYrp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9jhljOB8PXZixYrp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9jhljOB8PXZixYrp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9jhljOB8PXZixYrp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9jhljOB8PXZixYrp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9jhljOB8PXZixYrp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9jhljOB8PXZixYrp .marker.cross{stroke:#333333;}#mermaid-svg-9jhljOB8PXZixYrp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9jhljOB8PXZixYrp p{margin:0;}#mermaid-svg-9jhljOB8PXZixYrp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9jhljOB8PXZixYrp .cluster-label text{fill:#333;}#mermaid-svg-9jhljOB8PXZixYrp .cluster-label span{color:#333;}#mermaid-svg-9jhljOB8PXZixYrp .cluster-label span p{background-color:transparent;}#mermaid-svg-9jhljOB8PXZixYrp .label text,#mermaid-svg-9jhljOB8PXZixYrp span{fill:#333;color:#333;}#mermaid-svg-9jhljOB8PXZixYrp .node rect,#mermaid-svg-9jhljOB8PXZixYrp .node circle,#mermaid-svg-9jhljOB8PXZixYrp .node ellipse,#mermaid-svg-9jhljOB8PXZixYrp .node polygon,#mermaid-svg-9jhljOB8PXZixYrp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9jhljOB8PXZixYrp .rough-node .label text,#mermaid-svg-9jhljOB8PXZixYrp .node .label text,#mermaid-svg-9jhljOB8PXZixYrp .image-shape .label,#mermaid-svg-9jhljOB8PXZixYrp .icon-shape .label{text-anchor:middle;}#mermaid-svg-9jhljOB8PXZixYrp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9jhljOB8PXZixYrp .rough-node .label,#mermaid-svg-9jhljOB8PXZixYrp .node .label,#mermaid-svg-9jhljOB8PXZixYrp .image-shape .label,#mermaid-svg-9jhljOB8PXZixYrp .icon-shape .label{text-align:center;}#mermaid-svg-9jhljOB8PXZixYrp .node.clickable{cursor:pointer;}#mermaid-svg-9jhljOB8PXZixYrp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9jhljOB8PXZixYrp .arrowheadPath{fill:#333333;}#mermaid-svg-9jhljOB8PXZixYrp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9jhljOB8PXZixYrp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9jhljOB8PXZixYrp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9jhljOB8PXZixYrp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9jhljOB8PXZixYrp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9jhljOB8PXZixYrp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9jhljOB8PXZixYrp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9jhljOB8PXZixYrp .cluster text{fill:#333;}#mermaid-svg-9jhljOB8PXZixYrp .cluster span{color:#333;}#mermaid-svg-9jhljOB8PXZixYrp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9jhljOB8PXZixYrp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9jhljOB8PXZixYrp rect.text{fill:none;stroke-width:0;}#mermaid-svg-9jhljOB8PXZixYrp .icon-shape,#mermaid-svg-9jhljOB8PXZixYrp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9jhljOB8PXZixYrp .icon-shape p,#mermaid-svg-9jhljOB8PXZixYrp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9jhljOB8PXZixYrp .icon-shape .label rect,#mermaid-svg-9jhljOB8PXZixYrp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9jhljOB8PXZixYrp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9jhljOB8PXZixYrp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9jhljOB8PXZixYrp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 约 50 ms
PWM 量级 · ADC 注入完成
ADC1: U,V 同拍
I = Offset − ADC
W = −U − V
ST Clarke
θ: 边沿 + ω·Ts
ST Park
PI Id / Iq
ST RevPark
官方同款 SVPWM
TIM1 CCR U/V/W
霍尔 RPM
PI 速度
Iq*
注意:β 取反已在 svpwm_calc 内部 完成(对齐 wUBeta = -β·...),FOC_SVPWM_NEGATE_BETA 保持 0,不要二次取反。
5.3 启动状态机(bsp_foc_boot)
实现文件:User/foc/bsp_foc_boot.c,主循环约每 25 ms 调 foc_boot_poll();w 1 触发 foc_boot_request()。
5.3.1 本章启动方式
| 对比 | lesson19(接口 2) | 本课默认(接口 1,对齐官方) |
|---|---|---|
| 路径 | Align → 开环 Drag → Run | Charge → Offset → Run |
| 开环拖转 | 有(六步升频换相) | 无 (FOC_BOOT_USE_ALIGN=0,不进 Align/Drag) |
| 切入闭环时转子 | 已由 Drag 转起来 | 常为 静止或极低速,靠霍尔扇区角 + 起步 Iq 直接闭环拉转 |
本课默认 不是 「开环强拖到一定转速再切闭环」;
而是官方 Hall FOC 一样:硬件准备好电流采样后,立刻用霍尔 θ 进电流环,再软衔接速度环。
代码里仍保留 FOC_BOOT_ALIGN / FOC_BOOT_DRAG 枚举与阶段函数,并不在使用;正常启动不会走进 Drag(文件头注释亦写明)。
5.3.2 默认路径(FOC_BOOT_USE_ALIGN = 0)
IDLE
→ ChargeCap 低边三管 ON,~10 ms,给高边自举电容充电
→ OffsetCal 低边仍 ON + TIM1 继续跑,ADC 采零偏(必须 cal=1)
→ Run 霍尔 θ + Id=0 / Iq* 电流环 + SVPWM;再衔接速度外环
| 阶段 | 做什么 | 为何需要 | 关键注意 |
|---|---|---|---|
| ChargeCap | motor_pwm_turn_on_low_sides() |
半桥自举电容没电,高边驱动起不来 | ~10 ms 后 stop_pwm_output() 再进 Offset |
| OffsetCal | 低边 ON + adc_curr_recalibrate() |
运放零点;闭环用 I = Offset − ADC |
TIM1/CC4 不能停,否则注入采样几乎停、零偏失败/超时进 FAIL |
| Run | foc_boot_enter_run() |
同步霍尔 θ、seed Iq PI、交棒给 bsp_pid_app |
见下节「软衔接」 |
串口日志典型顺序:
text
FOC boot: charge boot cap (low sides ON)
FOC boot: charge done -> offset cal
FOC boot: offset cal (low sides ON, wait settle+samples)
FOC boot: offset done -> hall+Iq run (no align, official path)
FOC boot handover: hall=... rpm=... Iq=... Vq=... th=... (hall+PI)
5.3.3 Run 内的「软衔接」(不是 Drag,但是启动关键)
进入 Run 时转子往往还没转起来,因此有一层 闭环内 的过渡,容易和「开环启动」混淆:
-
交接(handover)
- 用当前霍尔扇区中心角复位
foc_theta_hf_*; - 给定起步
Iq* ≈ MOTOR_STARTUP_IQ_MA(并限幅); pi_seed(Iq, Vq≈2.5 V),避免 PI 从 0 积分导致力矩塌陷;foc_boot_take_handover()把 Iq 交给速度环软衔接。
- 用当前霍尔扇区中心角复位
-
Grace 宽限期
- 刚切入时缩短堵转误判窗口;并尽量保证一段 以电流环为主 的窗口(宏:
FOC_BOOT_GRACE_MIN_MS、FOC_BOOT_GRACE_RPM_EXIT、FOC_BOOT_GRACE_FWD_EDGES)。 - 转速与正向霍尔边沿够了再完全放开后续保护/速度环节奏,减少霍尔抖动导致的失速。
- 刚切入时缩短堵转误判窗口;并尽量保证一段 以电流环为主 的窗口(宏:
-
之后
- ADC ISR 里
dq_current_loop_hf()全速跑 Id/Iq + 官方 SVPWM; - 主循环速度 PI 输出更新
g_fm_target_iq,跟t xxx/ K3·K4 目标转速。
- ADC ISR 里
所以:
没有开环 Drag 升速 ;有的是 「电流环先拉转 → 速度环再接管」 的闭环软启动。
5.3.4 可选路径(FOC_BOOT_USE_ALIGN = 1,教学/标定)
... → Offset → Align(Vd 固定在霍尔扇区中心,~800 ms) → Run
- Align :开环 Vd 电压矢量把转子拉到已知扇区(不是六步 Drag)。
- Drag:状态机仍保留,接口 1 上官方路径不依赖它;强拖逻辑易与霍尔扇区「钉死」冲突,本课默认关闭。
- 串口还有
v/a等开环标定命令(foc_boot_openloop_vq_step/foc_boot_openloop_vd_deg),用于核对 hall↔θ,不属于正常w 1启动流程。
5.3.5 与 lesson19 / 官方启动对照
| 官方 Hall FOC | lesson19 | 本课默认 | |
|---|---|---|---|
| Charge / Offset | 有 | 弱化/不同硬件 | 有,对齐官方 |
| Align / Drag | 一般无 Drag | 核心教学内容 | 默认关;Align 可选 |
| 切入 Run | 霍尔 θ + FOC | 拖转够速再切 | Offset 完直接 Run |
| 启动力矩 | 电流环 Iq | 开环 duty 升频 | 起步 Iq + PI seed |
5.4 与官方 / lesson19 运行对照
| 项 | 官方 Demo | lesson19 | 本课 lesson20 |
|---|---|---|---|
| 接口 | 1 | 2 六步 | 1 互补 |
| 调制输出 | PWMC_SetPhaseVoltage |
|V|→单占空比 | 同官方算法 |
| 坐标变换 | ST MCM_* | 经典 | ST |
| 电流极性 | Offset−ADC | 早期习惯因板而异 | Offset−ADC |
| 电流环 | PWM 任务 | 主循环慢 | ADC ISR ≈15 kHz |
| 启动 | Charge→Offset→Run | Align→Drag→Run | Charge→Offset→Run |
| 启停 / 调速 | KEY2 启停 | 串口 | 串口 w/s/t 启停 ;K3/K4 运行中调速(见 5.5) |
5.5 按键功能(骄阳板)
main 主循环调用 deal_key_input()(bsp_bldcm_control.c),扫描骄阳板 SW2~SW5(按下为高电平):
| 板丝印 | 宏 | 引脚 | 本课默认 FOC 速度环已开(w 1) |
仅电流环开 | 双环都关(开环占空比) |
|---|---|---|---|---|---|
| K1 / SW2 | KEY1 | PA0 | --- | --- | 占空比 + 步进 |
| K2 / SW3 | KEY2 | PG2 | ---(官方 Demo 用此键启停) | --- | 占空比 - 步进 |
| K3 / SW4 | KEY3 | PC13 | 目标 RPM +50(上限 3000) | Iq +200 mA* | 正转 |
| K4 / SW5 | KEY4 | PG3 | 目标 RPM −50(下限 0) | Iq −200 mA* | 反转 |
说明:
- 本课启停走串口(与官方 KEY2 启停不同);速度环跑起来后,用 K3/K4 现场改
g_fm_target_rpm,内部会foc_spd_recalc_now()立刻刷新斜坡与 Iq。 - 步进宏:
FOC_SPD_KEY_STEP=50、CURR_PID_KEY_STEP=200(bsp_pid_app.h)。 - SW7 为硬件复位(NRST),不可软件扫描。
六、实验步骤
6.1 先烧官方 hex 验证硬件
官方DEMO可从官网下载。
...\PMSM_FOC_V5.44_Hall_speed_control\PMSM_control\MDK-ARM\PMSM_control\PMSM_control.hex
烧录后按 KEY2(PG2):应能顺转。
6.2 本课固件
烧录后用串口启动:
s 0 → 等 ~2s(电流重校准)
r
t 500
w 1
电机转起来后,可不用再发串口,直接按板子按键调速:
- K3:目标转速 +50 RPM
- K4:目标转速 −50 RPM
串口会打印类似 Key K3: RPM +50 -> 550, Iq=...。
应该出现实验现象:
- 出现
FOC boot handover后 RPM 爬向目标(如 500); g_fm_theta_deg连续锯齿(非卡死在扇区中心);- 空载相电流约数十~数百 mA,不应持续数安堵转。
停机:w 0 或 s 0。
日志示例:
text
[12:03:25.241]发→◇r
□
[12:03:25.244]收←◆[MOT] PI/fault reset, Iq=1642 mA
[12:03:26.740]发→◇t 500
□
[12:03:26.744]收←◆[MOT] RPM target: 500, Iq=1642 mA, use w 1 to run
[12:03:27.836]发→◇w 1
□
[12:03:27.840]收←◆[MOT] FOC boot start: IF1 sine+Hall poles=4 shift=240 deg
[MOT] FOC boot: charge->offset->hall+Iq (official)
[MOT] Speed loop ON, target 500 RPM (auto boot)
[MOT] [DBG] st=charge hall=6/6 rpm=0 Iu/Iv/Iw=0/-75/75 mA
[MOT] [DBG] Vd=0.00 Vq=0.00 Id=-70/0 Iq=-45/1642 theta=150 CCR=3865/1046/4554 Vbus=23.6
[MOT] [DBG] off=1553/1553/1553 inj=1532799 raw=1535/1552/1553 cal=1 en=1 fault=0
[MOT] FOC boot: charge boot cap (low sides ON)
[12:03:27.903]收←◆[MOT] FOC boot: charge done -> offset cal
[MOT] FOC boot: offset cal (low sides ON, wait settle+samples)
[12:03:28.356]收←◆[MOT] [DBG] st=offset hall=6/6 rpm=0 Iu/Iv/Iw=0/0/0 mA
[MOT] [DBG] Vd=0.00 Vq=0.00 Id=-0/0 Iq=0/1642 theta=150 CCR=3865/1046/4554 Vbus=24.4
[MOT] [DBG] off=0/0/0 inj=1540278 raw=1580/1559/1553 cal=0 en=1 fault=0
[12:03:28.753]收←◆[MOT] ADC off U/V/W=1554/1553/1553 cal=1 inj=1546040 raw=1560/1536/1553
[MOT] FOC boot: offset done -> hall+Iq run (no align, official path)
[MOT] FOC boot handover: hall=6 rpm=0 Iq=1600 Vq=+2.50 th=150 (hall+PI)
[MOT] FOC run: Iq handover RPM=54 Iq=1600 mA (hall+PI)
[12:03:28.854]收←◆[MOT] [DBG] st=run hall=2/2 rpm=375 Iu/Iv/Iw=-20/10/10 mA
[MOT] [DBG] Vd=-3.53 Vq=5.46 Id=35/0 Iq=28/1600 theta=68 CCR=2342/4059/1488 Vbus=23.8
[MOT] [DBG] off=1554/1553/1553 inj=1547803 raw=1555/1563/1553 cal=1 en=1 fault=0
[MOT] [DBG] drag step=2 duty=4071 fwd=95 raw=95 boot_theta=150 blend=0.00
[12:03:29.357]收←◆[MOT] [DBG] st=run hall=2/2 rpm=369 Iu/Iv/Iw=-25/90/-65 mA
[MOT] [DBG] Vd=-3.41 Vq=5.54 Id=-89/0 Iq=-56/746 theta=81 CCR=1930/4015/1585 Vbus=24.0
[MOT] [DBG] off=1554/1553/1553 inj=1555308 raw=1549/1560/1553 cal=1 en=1 fault=0
[MOT] [DBG] drag step=6 duty=4083 fwd=255 raw=255 boot_theta=150 blend=0.00
[12:03:29.854]收←◆[MOT] [DBG] st=run hall=6/6 rpm=389 Iu/Iv/Iw=-312/-115/427 mA
[MOT] [DBG] Vd=-3.32 Vq=5.59 Id=-47/0 Iq=126/1411 theta=188 CCR=1449/2427/4110 Vbus=23.2
[MOT] [DBG] off=1554/1553/1553 inj=1562817 raw=1526/1539/1553 cal=1 en=1 fault=0
[MOT] [DBG] drag step=5 duty=4134 fwd=255 raw=255 boot_theta=150 blend=0.00
[12:03:30.354]收←◆[MOT] [DBG] st=run hall=4/4 rpm=372 Iu/Iv/Iw=-140/-70/210 mA
[MOT] [DBG] Vd=-3.39 Vq=5.55 Id=-54/0 Iq=113/1611 theta=201 CCR=1577/1933/4023 Vbus=23.8
[MOT] [DBG] off=1554/1553/1553 inj=1570318 raw=1535/1552/1553 cal=1 en=1 fault=0
[MOT] [DBG] drag step=5 duty=4085 fwd=255 raw=255 boot_theta=150 blend=0.00
[12:03:30.469]发→◇s 0
□
[12:03:30.473]收←◆[MOT] All loops OFF, recalibrating current (wait ~2s)
6.3 变量曲线解析
6.3.1 电角度

红色是电角度,连续锯齿,0-360-0。
6.3.2 目标转速

蓝色是目标转速,按K3/K4改转速时平衡爬升、无明显持续振荡。
6.3.3 g_fm_target_iq Q轴电流给定 I q ∗ I q ∗ I^*_qIq* Iq∗Iq∗
希望电机产生多大转矩电流。开速度环 w 1时,主要由速度 PI 算出来写进去 ;转速偏低就抬高 I q ∗ I q ∗ I^*_qIq* Iq∗Iq∗ , 偏高就压低。
空载稳态往往很小(几十mA),加速或带载会明显变大。
g_fm_iq_actual 是 Q 轴电流实际反馈 I q I q I_qIq IqIq,意思:三相电流经 Clark→Park 算出来的真实 I q I q I_qIq IqIq(再滤波后×1000 成 mA)。
电流环 PI 跟踪: ( e = g _ f m _ t a r g e t _ i q − g _ f m _ i q _ a c t u a l ) (e = g\_fm\_target\_iq - g\_fm\_iq\_actual) (e=g_fm_target_iq−g_fm_iq_actual),再输出 ( V q ) (V_q) (Vq)。
加速过程:

6.3.4 比较寄存器CCR g_fm_ccr_*
| 变量 | 对应相 | 含义 |
|---|---|---|
g_fm_ccr_u |
U 相 | TIM1 CH1 的 CCR,决定 U 相上下管导通时间 |
g_fm_ccr_v |
V 相 | TIM1 CH2 的 CCR |
g_fm_ccr_w |
W 相 | TIM1 CH3 的 CCR |
来源:电流环算出 ( V d , V q ) (V_d,V_q) (Vd,Vq) → RevPark 得 ( V α , V β ) (V_\alpha,V_\beta) (Vα,Vβ) → 官方同款 SVPWM → 得到三相 CCR,再镜像到这三个 g_fm_* 给 FreeMASTER 看。
数值怎么理解
- 不是"伏特",是定时器计数:越大,该相高边导通时间越长(中心对齐 PWM)。
- 正常运行时三相 互不相等、随电角度轮流高低;。
- 大致范围在约
0 ~ ARR(中心对齐下 50% 占空附近约在ARR/2一带)。
和扇区的关系
g_fm_svpwm_sector(1~6)变时,哪一相最高/最低会跟着换------这就是 SVPWM 在"转矢量"。
看曲线时注意
它们按 FOC 节拍(约 15 kHz)在变;FreeMASTER 采集比较慢,单相 ccr_u 会像乱毛刺。看表里三相瞬时值和扇区即可;要波形就加快采样,并给 CCR 单独 Y 轴。

七、小结与下一章
本章把 lesson15~18 的「能算」升级为「与官方 Hall FOC 同一套约定能转」:
- ST 坐标变换 + Offset−ADC + 相移 240 成套使用;
- 官方
PWMC_SetPhaseVoltage级 SVPWM(s16 标度 + 内部 β 取反 + CCR 公式); - PWM 频率电流环 +
HALL_CalcElAngle风格霍尔 θ; - 启动对齐官方 Charge→Offset→Run。
代码地址: