4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CubeIDEでのB-G431B-ESC1の開発方法

Last updated at Posted at 2025-04-22

B-G431B-ESC1はかなりハイスペックなESCです。
マイコンに自分でプログラムを書き込んで使うため、燃えたりするリスクはありますが、扱えるようになればかなり便利です。CubeIDEを用いた開発方法を解説します。

MOSFETの開閉を誤るとESCは燃えます。デッドタイムなどの設定もかなり重要になるので、この記事に書いてあることはしっかりと確認しましょう。

公式サイト

回路図

.iocファイルの設定(CubeMX)

まずプロジェクトを作成する際、ターゲットはSTM32G431CBU6を選びましょう。
image.png

クロック

ボードには最初から外部クロックが載っているので、System CoreのRCCでHSEをCrystalにします。
image.png

クロックは以下のようにHSEを使ってPLLCLKから供給し、100MHzにしました。
image.png

ピン配置

以下のようにピンを仮設定してください。(黄色いピン)
スクリーンショット 2025-04-20 184413.png

出力用PWM

TIM1のPWMのModeを以下のように設定します。(Nは出力反転。ローサイドとハイサイドのMOSFETで開閉を逆にするため。)
image.png

Configurationを以下のように設定します。Counter Periodは2500 - 1ですが、Center Aligned modeを使うので、実質的な周波数は40kHzではなく半分の20kHzになります。ADCをいい感じのタイミングで読む(Low-Side Current Sense)ためにトリガーを使うので、RCRを1にしてTRGOをUpdate Eventにします。MOSFETは開閉に時間がかかるので、ハイサイドとローサイドが同時に開くのを防ぐために、Dead Timeを75(750ns)にします。データシートによると開閉時間は200nsらしく、マージンを設けて750nsにします。 基本的にこれを設定しておけば、低めのDutyで動かせば燃えるリスクはかなり低減します。

逆にこれを設定しないと出力関係なく燃えるので要注意

image.png

各チャンネルのModeは全てPWM mode 2にします。
image.png

電流センサー

OPAMP

シャント抵抗にかかる電圧を取得するためにオペアンプで増幅します。OPAMP1~3を全て以下のように設定してください。Modeは色々ありますが、これが良いです。倍率は16です。
image.png

ADC

ADCはModeを以下のように、VOPAMP1~3をADC1とADC2で選択してください。
image.png
image.png

ADC1のConfigurationは以下のようにします。ADC1とADC2を同期させて、TIM1のトリガーのタイミングで電流を読みます。
image.png

ADC2
image.png

ADCの完了割り込みも有効にしておきましょう。
image.png

コードをジェネレートしましょう。

プログラム

とりあえず強制転流のみのプログラムです。
main.cのユーザーコード内に書いていきましょう。

インクルード

/* USER CODE BEGIN Includes */
#include "math.h"
/* USER CODE END Includes */

変数宣言

/* USER CODE BEGIN PV */
const float UVW_UNIT_VECTOR[3][2] = {
	{1.0f, 0.0f},
	{-0.5f, 0.86602540378f},
	{-0.5f, -0.86602540378f}
};

float feedback_current[3] = {0.0f};;
float offset_current[3] = {0.0f};

float duty_out = 0.1f;
float freq_out = 10.0f;
/* USER CODE END PV */

関数宣言

/* USER CODE BEGIN 0 */
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc){
	feedback_current[0] = -((ADC1->JDR1 - offset_current[0]) / 4095.0f * 3.3f / 16.0f) * (7000.0f / 12.0f);
	feedback_current[1] = -((ADC2->JDR1 - offset_current[1]) / 4095.0f * 3.3f / 16.0f) * (7000.0f / 12.0f);
	feedback_current[2] = -((ADC2->JDR2 - offset_current[2]) / 4095.0f * 3.3f / 16.0f) * (7000.0f / 12.0f);
}

void outputDuty(float alpha, float beta){
	float norm = sqrt(alpha * alpha + beta * beta);
	if(norm > 1.0f) {
		alpha /= norm;
		beta /= norm;
		norm = 1.0f;
	}
	float u = alpha * UVW_UNIT_VECTOR[0][0] + beta * UVW_UNIT_VECTOR[0][1];
	float v = alpha * UVW_UNIT_VECTOR[1][0] + beta * UVW_UNIT_VECTOR[1][1];
	float w = alpha * UVW_UNIT_VECTOR[2][0] + beta * UVW_UNIT_VECTOR[2][1];
	__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1250.0f - 1000.0f * u);
	__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 1250.0f - 1000.0f * v);
	__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, 1250.0f - 1000.0f * w);
}
/* USER CODE END 0 */

Init

    /* USER CODE BEGIN 2 */
    HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
    HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
    HAL_ADC_Start(&hadc1);
    HAL_ADC_Start(&hadc2);
    HAL_ADCEx_InjectedStart_IT(&hadc1);
    
    HAL_OPAMP_Start(&hopamp1);
    HAL_OPAMP_Start(&hopamp2);
    HAL_OPAMP_Start(&hopamp3);
    
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);
    
    HAL_Delay(100);
    
    for(int i = 0; i < 10; i ++){
        offset_current[0] += ADC1->JDR1 / 10.0f;
        offset_current[1] += ADC2->JDR1 / 10.0f;
        offset_current[2] += ADC2->JDR2 / 10.0f;
        HAL_Delay(10);
    }
    /* USER CODE END 2 */

メインループ

	/* USER CODE BEGIN WHILE */
	while (1)
	{
		/* USER CODE END WHILE */

		/* USER CODE BEGIN 3 */
		float angle_out = HAL_GetTick() / 1000.0f * 2.0f * M_PI * freq_out;
		outputDuty(duty_out * cos(angle_out), duty_out * sin(angle_out));
	}
	/* USER CODE END 3 */

duty_outは0.1(10%)にしてあります。
強制転流は電流が流れやすく、発熱もしやすいため、実験する際は電源装置で12Vくらいの低い電圧で、1Aくらいの電流制限で行ってください。

電流値はデバッグしながらLive Expressionsなどからみてください。(三相交流にはなっていますが、電源装置に表示される電流値と結構値が違うので、もしかすると計算式が間違っているかもしれないです。)

エンコーダやホールセンサを用いた角度の取得は、各自でモーターに合わせてコーディングしてください。

モーターによって異なるため、この記事に例を示してしまうと、それをコピペした際に危険を伴うと判断したため、オープンループである強制転流のみの例を示しました。

ベクトル制御について、ある程度の知識を得てから行うようにしてください。
ベクトル制御についても、いつか解説しようと思います。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?