FOC(ベクトル制御)の実装プロジェクト、第15日目です。
前回(Day 14)は、SVPWM(空間ベクトル変調)の実装により、STM32から任意の電圧ベクトルを出力することに成功しました。これは人間で言えば、脳からの指令を手足に伝える「運動神経」が繋がった状態です。
今回は、その対となる 「電流センシング」 の実装です。
モーターのコイルに実際にどれだけの電流が流れているかを正確にフィードバックすることは、ロボットが負荷や反力を感じる「触覚」の実装に等しく、FOCの制御ループを閉じるための最重要工程となります。
1. 今回のミッション:マイクロ秒の静寂を狙う
FOCにおける電流計測は、単に HAL_ADC_Start() を呼べば良いというものではありません。インバータ回路特有の過酷なノイズ環境下で、正確なアナログ値を取得する必要があります。
PWM制御されたインバータは、高速でスイッチング(ON/OFF)を繰り返しています。スイッチングの瞬間(エッジ)には強烈なリンギングノイズが発生するため、このタイミングでAD変換を行うとデータは使い物になりません。
したがって、「スイッチングノイズが収まり、かつ電流が安定して流れている一瞬の静寂」 をマイクロ秒単位で狙い撃ちする必要があります。
本日の目標は以下の通りです。
- ローサイド・シャント抵抗方式における「計測可能なタイミング」を理論的に特定する。
- STM32のタイマー設定(PWM Mode 2)を駆使し、そのタイミングで自動的にADCを起動させる。
- オシロスコープと内部カウンタ値を用いて、同期タイミングが完璧であることを証明する。
2. 理論:どこで測るべきか?
ハードウェア構成:ローサイド・シャント方式
今回の回路構成は、インバータの下側アーム(GND側)に電流検出用のシャント抵抗を配置する 「ローサイド・シャント方式」 です。
この方式には物理的な制約があります。シャント抵抗にモータ電流が流れるのは、「下側のFETがONになっている時」 だけです。
インバータの動作原理上、上側FETと下側FETは相補的に動作します(デッドタイムを除く)。
つまり、PWM信号(上側FET駆動信号)が LOW の期間だけ、下側FETがONになり、シャント抵抗に電流が流れます。

理想の計測ポイント
PWM信号がLOWの期間中ならいつでも良いわけではありません。
LOWに落ちた直後や、HIGHに上がる直前はスイッチングノイズが含まれます。最も電流波形が安定し、かつPWMのDuty比が変化しても確実に確保される時間は、「PWMがLOWになっている期間の、ど真ん中」 です。
STM32のタイマーカウンタ(TIM1)は、Center Aligned Mode(三角波動作)を使用しています。カウンタ値が 0(谷底)と ARR(山頂)を行き来する中で、どちらが「LOWのど真ん中」になるでしょうか?
-
PWM Mode 1 の場合: カウンタ値 < CCR で HIGH となります。つまりカウンタ
0付近は HIGH(上側ON)となり、電流は流れません。 -
PWM Mode 2 の場合: カウンタ値 < CCR で LOW となります。つまりカウンタ
0付近は LOW(下側ON)となります。
結論として、PWM Mode 2 を使用し、カウンタが 0(谷底)になった瞬間にADCのトリガを引く のが正解となります。
3. STM32の実装 (CubeMX & Code)
理論に基づき、STM32の Advanced Control Timer (TIM1) を設定します。手動でタイミングを微調整する必要はありません。ハードウェアの機能を論理的に組み合わせるだけで、完璧な同期が実現できます。
3.1 タイマー設定 (TIM1)
最も重要な変更点は、PWM Generation のモード設定です。
-
Counter Settings
-
Counter Mode:
Center Aligned Mode 1- カウンタがアップ・ダウンカウント(三角波)を繰り返します。
-
Repetition Counter (RCR):
1- Update Event (UEV) の発生頻度を制御します。Center Aligned Mode 1 と組み合わせることで、カウンタがアンダーフロー(0)したタイミングでのみイベントを発生させ、ADCトリガ(TRGO2)として利用します。
-
Counter Mode:
-
PWM Generation Channel 1-3
-
Mode:
PWM mode 2- ここが最大のポイントです。
CNT < CCRの条件で Inactive (LOW) を出力します。 - これにより、カウンタの谷底(0)付近が常に「LOW(電流計測可能区間)」の中心となります。
- ここが最大のポイントです。
-
Mode:
-
Trigger Output (TRGO2)
3.2 コード実装 (main.c)
設定が正しければ、コードは非常にシンプルになります。以下は MX_TIM1_Init の抜粋と、検証用のコールバック関数です。
/* TIM1 Initialization (抜粋) */
static void MX_TIM1_Init(void)
{
// ... (省略) ...
// 三角波モード設定
htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
// アンダーフロー(0)のみでイベントを発生させるための設定
htim1.Init.RepetitionCounter = 1;
// ... (省略) ...
// ★重要: 谷底(CNT=0)でLOWになるモードを選択
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
// ... (省略) ...
}
次に、ADC変換完了時に呼ばれるコールバック関数で、タイミング検証用の処理を記述します。
/* ADC変換完了割り込み (Injected Group) */
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// 1. 割り込みが入った瞬間のカウンタ値を記録 (デバッグ用)
// 理論上、0に近い小さな値になるはず
debug_cnt = TIM1->CNT;
// 2. オシロスコープ確認用にGPIOピンを反転 (赤線)
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
// (本番ではここに電流値の工学単位変換やFOC計算処理が入ります)
}
4. 検証結果:完璧な同期
実装したコードをSTM32G474に書き込み、オシロスコープで波形を確認しました。
- 黄色 (CH1): TIM1 PWM出力波形
- 赤色 (CH2): ADC完了割り込みタイミング (GPIOトグル)
オシロスコープ波形
(※黄色いPWM波形がLOWになっている区間の中心で、赤色の信号がトグルしている様子)
確認結果は明白です。
黄色の波形が LOW(0V) になっている区間の、まさに 「ど真ん中」 で赤色の信号が変化しています。
デジタルオシロスコープを「Single」または「Stop」させて波形を拡大すると、ジッター(揺らぎ)のない正確な同期が見て取れます。
-
PWM波形: Center Aligned Mode + PWM Mode 2 により、カウンタ
0を中心としたLOW区間が生成されている。 - トリガ位置: Update Event (CNT=0) でADCが開始され、変換時間経過後にLOW区間の中央付近でデータ取得が完了している。
数値による裏付け
さらに、デバッガを通して変数 debug_cnt(割り込み発生時のTIM1カウンタ値)を確認しました。
CNT: 470
この数値の意味を検証します。
STM32G4のクロックは 170MHz です。カウンタ値 470 は、時間にして約 $2.76 \mu s$ に相当します。
$$\frac{470}{170 \times 10^6} \approx 2.76 \times 10^{-6} , [s]$$
- ADCサンプリング + 変換時間: 約 $1 \sim 1.5 \mu s$ (設定依存)
- 割り込み遅延 + GPIO操作: 数百 $ns$ 〜 $1 \mu s$
合計すると、カウンタが 0(谷底)でトリガされてから、処理が完了して CNT を読み取るまでの時間として非常に妥当な値です。
もし設定を誤り、カウンタの山頂(ARR = 4250)やエッジでトリガがかかっていれば、この値は 2125 付近や 4000 付近になるはずです。「470」という数値は、システムが 「谷底(0)で正確にトリガをかけ、最短時間でデータを取得した」 ことの数学的な証明です。
5. まとめ
これで、FOC制御における「目(センサ)」と「触覚(電流計測)」が揃いました。
- PWM Mode 2 と Update Event を組み合わせることで、ローサイド・シャント抵抗に電流が流れる 「LOW区間の真ん中」 をハードウェア的に捉えることに成功しました。
- この同期制御に、CCR4等の余計なコンペアマッチ機能は不要です。STM32のタイマーモードの本質を理解し、正しく設定するだけで、理想的なセンシングタイミングは実現できます。
次回(Day 16)は、いよいよ取得した生の電流値(AD値)を物理量(アンペア)に変換し、モーターを回すためのトルク制御計算(クラーク変換・パーク変換)を実装していきます。
ついに、モーターが「力」を持ちます。お楽しみに。

