※この記事は九工大衛星開発プロジェクトADCS新人教育用資料を一般公開しているものです。
はじめに
今回の記事の内容はSTM32の開発環境の構築です。STM32CubeIDEのインストールからLチカまでの解説を行います。
nucleo STM32F446RE1を例に説明していきます。
ちなみに、STM32の開発環境にはmbedやArduinoなどを使うこともできます。これらの環境の方が簡単ではありますが動作が遅かったり機能制限があったりといったデメリットが存在します。
C言語で記述されたCubeHAL2を使うとSTM32の全ての機能を活用することがでます。CubeIDEはCubeHAL用の統合開発環境になります。
CubeIDEでの開発の流れ
- プロジェクトの生成
- GUIでピンやクロックの設定
- 初期コードの自動生成
- C言語の記述
- 書き込み&デバッグ
CubeIDE(CubeMX)最大の特徴は画面上でマウスでポチポチするだけで初期コードが自動生成されることだと思います。
この機能によって面倒な設定を自分で記述しなくてもプログラムを書き始められます。
そしてユーザーがプログラムを書き加えた後からピンを再設定しコードの再生成をしても、ユーザーが書いた部分は消えません。
インストールしよう
まずはCubeIDEをSTMicroelectronics社のHPからダウンロードしてきます。
ダウンロード先はここです→ https://www.st.com/ja/development-tools/stm32cubeide.html
ページの下の方にある「ソフトウェアの入手」というところから自分の環境にあったインストーラーをダウンロードします。
Windowsの場合は一番下の「STM32CubeIDE-Win」になります。
ダウンロードするときにユーザー登録するように言われるので、登録していない人は登録しましょう。登録済みの人はログインするとダウンロードできます。
ダウンロードが完了したらZIPファイルを解凍して出てきた「st-stm32cubeide_****.exe」のようなファイル名のインストーラーを実行します。
※このときインストーラーが日本語を含むパスの下にあると起動に失敗します。
インストーラーが起動したら画面の指示に従って順にインストールを進めていきます。
ワークスペースを設定しよう
インストールが完了したらCubeIDEを起動してみましょう。下のような画面が表示されると思います。
Workspaceというのは今後書くプログラムを保存しておく場所のことです。新しいフォルダを作成してそこを指定するのをおすすめします。
右側の「Browse...」というボタンを押してWorkspaceにする場所を指定します。指定できたら「Launch」を押してCubeIDEを起動します。
2回目以降は直前に開いたWorkspaceの場所が最初から入力された状態になるため、「Launch」を押すだけで起動できるようになります。
(「Use this as default and do not ask again」にチェックを入れると次回以降この画面を省略して、初回で指定したWorkspaceで起動するようにできます。)
プロジェクトを作成しよう
CubeIDEが起動したら下のような画面が表示されると思います。「Start new STM32 project」のボタンを押して新しいプロジェクトを作成しましょう。
右上のメニューから File > New > STM32 project と選択しても意味は同じです。
次に、下のような画面が表示されます。ここでは開発対象のマイコンの型番を指定します。
※初回起動時は設定ファイルのダウンロードが発生し時間がかかるかもしれません。
今回はNUCLEO-F446RE(評価ボード)を使用するので、「MCU/MPU selector」ではなく「Board selector」を選択します。
右下に選択可能なマイコンボードが大量に表示されると思います。このままだと探しにくいので、左上の検索ボックスに「F446」と入力します。
NUCLEO-F446REが右下に表示されたらNUCLEO-F446REを選択し、右下の**「next」を押します**。
次に表示された画面でプロジェクト名を入力して、右下の**「Finish」を押して**プロジェクトを生成します。
そのあと何回かダイアログが表示されて質問されますが、「Yes」を押しましょう。
上の図の②の部分は今回はC言語で書くため何もいじらずにそのまま進みましたが、C++も選択できるようです。
また、「Finish」ではなく「next」を押すとCubeHALのバージョンを選択できたりするようです。
CubeMXでピンの割り当てをしよう
プロジェクトが生成されると次のような画面が表示されます。
ここではマイコンのピンの機能を割り当てることができます。
右の画面でどれか1つのピンをクリックすると、そのピンに割り当てることができる機能が表示されます。
左の列にはこのマイコンが使える機能が表示されており、それを選択するとその機能の設定ができます。(詳しくは次回解説)
「Clock Configuration」のタブを開くと、マイコンの動作速度などを設定することができます。
今回はボードを指定してプロジェクトを生成したので、nucleoに内蔵されたLEDやUARTの設定が既にされています。
この画面を見るとnucleoに内蔵されたLEDはPA5に接続されていることが分かります。
わかりやすくするためにPA5を右クリックしてEnter User Labelを選択し、「LED」と入力しなおしてみましょう3。
設定が終わったら**「Ctrl+S」を押して保存します。保存と同時に次のような画面が表示されます。
ここで「Yes」を押すとコードが自動で生成されます**。
※初回起動時はライブラリのダウンロードが発生し時間がかかるかもしれません。
C言語でプログラムを書いている最中にピンの設定を変える場合は、Project explorerから「{プロジェクト名}.ioc」を開くと先ほどの画面になります。
ピンの設定を変えたら再び「Ctrl+S」を押してコードを再生成しましょう。この時、自分が書き足したプログラムが消されることはありません。設定を変更した箇所に対応するコードのみが書き換えられます。
C言語を書いてみよう
コードの自動生成が終わったらプログラムを書いていきましょう。
まずは**「main.c」を開きます**。
「main.c」が見つからない場合、下の図のような手順で開くことができると思います。
「main.c」を開くとすでにいろいろとプログラムが書かれた状態になっています。これが先ほど自動生成されたコードになります。
例えば、下の方を見ていくと200行目あたりに次のようなコードがあると思います。
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : LED_Pin */
GPIO_InitStruct.Pin = LED_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
}
この部分はLEDが繋がっているピンを出力ピンに設定している(PA5をGPIO出力に設定している)コードになります。
STM32を直接C言語とHALで扱う場合、本来ならこれだけの文を書かなくてはならないのですが、先ほどのCubeMXの画面で設定することによって自動生成されています。とても便利ですね。
(ちなみにこの状態で実行すると、何も起きません。正確には、PA5ピンが出力モードに切り替わったりするのですが、出力モードになるだけでその後何も起きません。)
ということで、ユーザーは
/* USER CODE BEGIN * */
/* USER CODE END * */
と書かれた部分にのみ動作に関わるプログラムを追加していきます。
それでは実際にLEDを点滅させるプログラムを書いてみましょう。100行目あたりに次のように4行ほど追加します。
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); //LEDを点灯
HAL_Delay(500); //500ms待つ
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //LEDを消灯
HAL_Delay(500); //500ms待つ
}
/* USER CODE END 3 */
これで1秒周期でLEDが点滅するようになります。
HAL_GPIO_WitePin
は1つのピンの出力状態を変更する関数です。第1引数はポート、第2引数はピンの番号、第3引数はHIGIかLOWかの状態を指定します。
ここではポートA(GPIOA
)のピン5(GPIO_PIN_5
)をHIGH(GPIO_PIN_SET
)にするという意味なります。LOWにする場合はGPIO_PIN_RESET
です。
確かにLEDが繋がっているPA5を動かしているように見えますね。
ちなみにこのプログラムは次のように書くこともできます。
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); //LEDを点灯
HAL_Delay(500); //500ms待つ
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); //LEDを消灯
HAL_Delay(500); //500ms待つ
}
/* USER CODE END 3 */
GPIOA
,GPIO_PIN_5
がLED_GPIO_Port
,LED_Pin
に変わりました。
このようなことができる理由は先ほどのCubeMXの画面でPA5に「LED」というラベルを割り当てていたからです。
main.hを見ると
#define LED_Pin GPIO_PIN_5
#define LED_GPIO_Port GPIOA
というコードが自動生成されていることが分かります。
ラベルを割り当てることで、ポート名とピン番号ではなくラベル名で指定することができるようになります。
この機能をうまく使えば、使用するピンが増えたときに「あの素子はどのピンに繋がっていたっけな?」と回路図を探しに行かなくてもラベル名で直接入力すればよくなるので便利です。
書き込んでみよう
プログラムを書き終えたらビルドして書き込こみます。
最初にnucleoとPCをUSBケーブルで接続します。
まず左上にあるこのボタンを押します。
このボタンを押すとビルドが開始されます。プログラムにミスが無く正常にビルドされればconsole画面に
Finished building: ***.list
23:12:56 Build Finished. 0 errors, 0 warnings. (took 1s.100ms)
のような表示がされると思います。
エラーが表示された場合は入力ミスが無いか確認しましょう。
次にを押します。
これでプログラムの書き込みとデバッグが開始されます。
初回起動時のみ次のような画面が表示されます。
2つ目の画面ではデバッガのタブを選択し次のようになっているか確認します。
Startupのタブでは「Set breakpoint at」のチェックを外しておきましょう。
これを外さないと毎回main関数の最初でbreakpointが発生して動作が一時停止してしまいます。
以上の設定ができたらOKを押しましょう。
プログラムが書き込まれてLEDが点滅するのを確認できましたか?
もしファームウェアのアップデートが必要という画面が表示されたら、画面の指示に従ってnucleoのファームウェアを更新してください。
デバッグモードを使ってみよう
プログラムの書き込みが終わったと、次のようなダイアログが表示されていると思います。
Switchを押すとデバッグ用の画面に移行します。この画面ではマイコンでプログラムを走らせている最中にデバッグをすることができます。
例えば、HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
の行番号の左側をダブルクリックしてブレークポイントを張ってみましょう。
そうすると、ブレークポイント(上の図でいうと●がついている行)でプログラムの実行が中断されると思います。
この状態で変数をマウスオーバーすると変数の中身が表示されると思います。
このままだと少しわかりにくいので、もう少し分かりやすい例を見てみましょう。
まずは左上にある停止ボタンを押してデバッグを中止します。
次に、プログラムを次のように書き換えます。
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
for ( int i = 0; i<500 ; ){
i++;
}
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
そしてi++;
の行にブレークポイントを張ってにを押してみましょう。
ビルド→書き込み→実行→デバッグが始まり、i++;
の行で中断されると思います。
※変更を保存するか聞かれたらOKを押してください。デバッグモードに切り替えるか聞かれたらSwitchを押してください。
この状態で変数i
の上にマウスポインタを乗せると次のような画面が出てくるはずです。
これは現在のi
の値が1であることを示しています。
左上のを押して動作を1段階進めてみましょう。
そうすると今度はi
が1に変化していると思います。for文が1回回ってiの中身が1増加したことが分かります。
を押すたびにi
の中身が2,3,...と変化していくのが確認できると思います。
動作をある程度確認できたら今度は数値をクリックしてi
の値を書き換えてみましょう。
例えば、図のように800と入力してを押すとLEDが1回点滅して中断されi
の中身が0になると思います。
for文の継続条件はi<500
であるため、i
に800が代入されるとfor文を抜けHAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
,HAL_Delay(500);
などが実行されwhileによって再びfor文の最初に戻り、ブレークポイントを張った場所に戻ってきたということです。
この機能を使うとマイコンで実行中のプログラムの変数の中身をリアルタイムで確認することができるため、動作が怪しい箇所の検証などに非常に役立ちます。
まとめ
今回はCubeIDEのインストール方法と基本的な使い方について解説しました。
CubeIDEで開発する場合、まずGUI(CubeMX)で初期コードを生成してからC言語で書いていくことになります。
また、マイコンなのにデバッグ機能が簡単に使えで便利なことも分かりました。
次回はUART(送信)とprintfデバッグについて説明します。
-
nucleo STM32F446REは秋月電子などで購入することができます。STM32F446は秋月電子で扱われているSTM32シリーズの中では最も高性能なものになります(2019.07.18現在)。入門には機能が少なくて安いF042やF103の方が良いのかもしれませんが、FUTABAに搭載するマイコンがF446であるため今回の記事ではF446を採用しています。 ↩
-
CubeHAL HALとはHardware abstraction layer(ハードウェア抽象化レイヤ)の略称で、マイコンの型番ごとに異なるハードウェアの機能を共通の関数で簡単に扱えるようにしたライブラリのようなものです。CubeHALはSTM32用のHALの名称です。 ↩
-
LEDを点滅させるだけならこの操作は不要です。今回の記事ではラベルを付ける方法と利点を説明するためにあえてこのようにしています。 ↩