STM32入門 環境構築「CubeIDEのインストールとSTM32F446でLチカ」

※この記事は九工大衛星開発プロジェクトADCS新人教育用資料を一般公開しているものです。

# はじめに
今回の記事の内容はSTM32の開発環境の構築です。STM32CubeIDEのインストールからLチカまでの解説を行います。
nucleo STM32F446RE[^1]を例に説明していきます。

ちなみに、STM32の開発環境にはmbedやArduinoなどを使うこともできます。これらの環境の方が簡単ではありますが動作が遅かったり機能制限があったりといったデメリットが存在します。
C言語で記述されたCubeHAL[^2]を使うとSTM32の全ての機能を活用することがでます。CubeIDEはCubeHAL用の統合開発環境になります。
[^1]: nucleo STM32F446REは[秋月電子などで購入](http://akizukidenshi.com/catalog/g/gM-10176/)することができます。STM32F446は秋月電子で扱われているSTM32シリーズの中では最も高性能なものになります(2019.07.18現在)。入門には機能が少なくて安いF042やF103の方が良いのかもしれませんが、FUTABAに搭載するマイコンがF446であるため今回の記事ではF446を採用しています。
[^2]: CubeHAL HALとはHardware abstraction layer(ハードウェア抽象化レイヤ)の略称で、マイコンの型番ごとに異なるハードウェアの機能を共通の関数で簡単に扱えるようにしたライブラリのようなものです。CubeHALはSTM32用のHALの名称です。

## CubeIDEでの開発の流れ
1. プロジェクトの生成
2. **GUIでピンやクロックの設定**
3. **初期コードの自動生成**
4. C言語の記述
5. 書き込み&デバッグ  

CubeIDE(CubeMX)最大の特徴は画面上でマウスでポチポチするだけで初期コードが自動生成されることだと思います。
この機能によって面倒な設定を自分で記述しなくてもプログラムを書き始められます。
そしてユーザーがプログラムを書き加えた後からピンを再設定しコードの再生成をしても、ユーザーが書いた部分は消えません。

# インストールしよう
まずはCubeIDEをSTMicroelectronics社のHPからダウンロードしてきます。
ダウンロード先はここです→ https://www.st.com/ja/development-tools/stm32cubeide.html
ページの下の方にある「ソフトウェアの入手」というところから自分の環境にあったインストーラーをダウンロードします。
Windowsの場合は一番下の「STM32CubeIDE-Win」になります。
ダウンロードするときにユーザー登録するように言われるので、登録していない人は登録しましょう。登録済みの人はログインするとダウンロードできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/2304f353-4514-3b62-3a9e-d3e43ee3ce26.png)
ダウンロードが完了したらZIPファイルを解凍して出てきた「st-stm32cubeide_＊＊＊＊.exe」のようなファイル名のインストーラーを実行します。
※**このときインストーラーが日本語を含むパスの下にあると起動に失敗します。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/d3ea7a32-58dd-c18b-17fe-1e6dee935d91.png)
インストーラーが起動したら画面の指示に従って順にインストールを進めていきます。

# ワークスペースを設定しよう
インストールが完了したらCubeIDEを起動してみましょう。下のような画面が表示されると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/7d400ddc-c676-153a-8e5d-05a315a7346e.png)
Workspaceというのは今後書くプログラムを保存しておく場所のことです。新しいフォルダを作成してそこを指定するのをおすすめします。
右側の「Browse...」というボタンを押してWorkspaceにする場所を指定します。指定できたら「Launch」を押してCubeIDEを起動します。

2回目以降は直前に開いたWorkspaceの場所が最初から入力された状態になるため、「Launch」を押すだけで起動できるようになります。
(「Use this as default and do not ask again」にチェックを入れると次回以降この画面を省略して、初回で指定したWorkspaceで起動するようにできます。)

# プロジェクトを作成しよう
CubeIDEが起動したら下のような画面が表示されると思います。**「Start new STM32 project」のボタンを押して新しいプロジェクトを作成**しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/58c925ce-136a-fe64-e116-e3b0fbb715f1.png)
右上のメニューから File > New > STM32 project と選択しても意味は同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/e255f686-2ef2-df2b-dc56-c10c67fd6014.png)

次に、下のような画面が表示されます。ここでは開発対象のマイコンの型番を指定します。
※初回起動時は設定ファイルのダウンロードが発生し時間がかかるかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/90870beb-13d5-6650-2f35-fe36ed0d8505.png)
今回はNUCLEO-F446RE(評価ボード)を使用するので、「MCU/MPU selector」ではなく「**Board selector**」を選択します。
右下に選択可能なマイコンボードが大量に表示されると思います。このままだと探しにくいので、**左上の検索ボックスに「F446」と入力**します。
NUCLEO-F446REが右下に表示されたら**NUCLEO-F446REを選択**し、右下の**「next」を押します**。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/cace398c-9a0e-2562-b2d6-ac62f6714586.png)
次に表示された画面で**プロジェクト名を入力**して、右下の**「Finish」を押して**プロジェクトを生成します。
そのあと何回かダイアログが表示されて質問されますが、「Yes」を押しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/4d706263-d7c3-99d0-bd28-e04ed163ee4c.png)
上の図の②の部分は今回はC言語で書くため何もいじらずにそのまま進みましたが、C++も選択できるようです。
また、「Finish」ではなく「next」を押すとCubeHALのバージョンを選択できたりするようです。
# CubeMXでピンの割り当てをしよう
プロジェクトが生成されると次のような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/0dd296ba-82d1-807a-102d-97c446952b5e.png)
ここではマイコンのピンの機能を割り当てることができます。
右の画面でどれか1つのピンをクリックすると、そのピンに割り当てることができる機能が表示されます。
左の列にはこのマイコンが使える機能が表示されており、それを選択するとその機能の設定ができます。(詳しくは[次回]()解説)
「Clock Configuration」のタブを開くと、マイコンの動作速度などを設定することができます。

今回はボードを指定してプロジェクトを生成したので、nucleoに内蔵されたLEDやUARTの設定が既にされています。
この画面を見るとnucleoに内蔵されたLEDはPA5に接続されていることが分かります。
わかりやすくするために**PA5を右クリック**して**Enter User Labelを選択**し、**「LED」と入力しなおして**みましょう[^3]。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/5f668f65-b6e4-d278-0052-2b1d981efc7d.png)

設定が終わったら**「Ctrl+S」を押して保存**します。保存と同時に次のような画面が表示されます。
ここで**「Yes」を押すとコードが自動で生成されます**。
※初回起動時はライブラリのダウンロードが発生し時間がかかるかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/71613a61-17e0-13cf-e147-e7c64159ddf2.png)

C言語でプログラムを書いている最中にピンの設定を変える場合は、Project explorerから「{プロジェクト名}.ioc」を開くと先ほどの画面になります。
ピンの設定を変えたら再び「Ctrl+S」を押してコードを再生成しましょう。この時、自分が書き足したプログラムが消されることはありません。設定を変更した箇所に対応するコードのみが書き換えられます。

[^3]: LEDを点滅させるだけならこの操作は不要です。今回の記事ではラベルを付ける方法と利点を説明するためにあえてこのようにしています。

# C言語を書いてみよう
コードの自動生成が終わったらプログラムを書いていきましょう。
まずは**「main.c」を開きます**。
「main.c」が見つからない場合、下の図のような手順で開くことができると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/913b3106-24d2-7d55-43c5-e1c9bc1d0e15.png)
「main.c」を開くとすでにいろいろとプログラムが書かれた状態になっています。これが先ほど自動生成されたコードになります。
例えば、下の方を見ていくと200行目あたりに次のようなコードがあると思います。

```c
/**
  * @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ピンが出力モードに切り替わったりするのですが、出力モードになるだけでその後何も起きません。)

ということで、ユーザーは

```c
/* USER CODE BEGIN * */

/* USER CODE END * */
```

と書かれた部分にのみ動作に関わるプログラムを追加していきます。

それでは実際にLEDを点滅させるプログラムを書いてみましょう。100行目あたりに次のように4行ほど追加します。

```c
/* 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を動かしているように見えますね。

ちなみにこのプログラムは次のように書くこともできます。

```c
  /* 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を見ると

```c
#define LED_Pin GPIO_PIN_5
#define LED_GPIO_Port GPIOA
```

というコードが自動生成されていることが分かります。
ラベルを割り当てることで、**ポート名とピン番号ではなくラベル名で指定することができるようになります**。
この機能をうまく使えば、使用するピンが増えたときに「あの素子はどのピンに繋がっていたっけな？」と回路図を探しに行かなくてもラベル名で直接入力すればよくなるので便利です。

# 書き込んでみよう
プログラムを書き終えたらビルドして書き込こみます。
**最初にnucleoとPCをUSBケーブルで接続します。**

まず左上にあるこのボタン![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/594d2edc-bfa1-8918-9200-85e82896fedf.png)を押します。
このボタンを押すとビルドが開始されます。プログラムにミスが無く正常にビルドされればconsole画面に
`Finished building: ***.list`
`23:12:56 Build Finished. 0 errors, 0 warnings. (took 1s.100ms)`
のような表示がされると思います。
エラーが表示された場合は入力ミスが無いか確認しましょう。

次に![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/20498b3a-66d7-93fb-85c4-05d35742ad50.png)を押します。
これでプログラムの書き込みとデバッグが開始されます。

初回起動時のみ次のような画面が表示されます。
>下の画面ではSTM32の方を選択してOKを押します。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/1b1ad25d-1594-44d9-9d2d-99cd17ecdcaf.png)
>
>2つ目の画面ではデバッガのタブを選択し次のようになっているか確認します。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/ff1490fe-f7fb-8b89-70e3-630a7a02f457.png)
>Startupのタブでは「Set breakpoint at」のチェックを外しておきましょう。
>これを外さないと毎回main関数の最初でbreakpointが発生して動作が一時停止してしまいます。
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/09d68e1b-b21e-5e60-a34b-320e1b1c5deb.png)
>以上の設定ができたらOKを押しましょう。

プログラムが書き込まれてLEDが点滅するのを確認できましたか？

もしファームウェアのアップデートが必要という画面が表示されたら、画面の指示に従ってnucleoのファームウェアを更新してください。

# デバッグモードを使ってみよう
プログラムの書き込みが終わったと、次のようなダイアログが表示されていると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/a6a08263-fc73-5be8-6be6-5ef8b95dff72.png)
Switchを押すとデバッグ用の画面に移行します。この画面ではマイコンでプログラムを走らせている最中にデバッグをすることができます。

例えば、`HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); `の**行番号の左側をダブルクリック**してブレークポイントを張ってみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/988bb05b-f916-d7b0-3b75-c78b10154aa2.png)
そうすると、ブレークポイント(上の図でいうと●がついている行)でプログラムの実行が中断されると思います。
この状態で変数をマウスオーバーすると変数の中身が表示されると思います。

このままだと少しわかりにくいので、もう少し分かりやすい例を見てみましょう。

まずは左上にある停止ボタン![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/95346510-fede-3ea9-3a49-e7c2d1fd8fa4.png)を押してデバッグを中止します。
次に、プログラムを次のように書き換えます。

```c
  /* 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++;`の行にブレークポイントを張ってに![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/20498b3a-66d7-93fb-85c4-05d35742ad50.png)を押してみましょう。
ビルド→書き込み→実行→デバッグが始まり、`i++;`の行で中断されると思います。
※変更を保存するか聞かれたらOKを押してください。デバッグモードに切り替えるか聞かれたらSwitchを押してください。

この状態で変数`i`の上にマウスポインタを乗せると次のような画面が出てくるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/9873e663-0652-0999-16e1-0e1aff73b257.png)
これは現在の`i`の値が1であることを示しています。
左上の![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/b5697d68-a37d-82b8-6634-775d729f143a.png)を押して動作を1段階進めてみましょう。
そうすると今度は`i`が1に変化していると思います。for文が1回回ってiの中身が1増加したことが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/b5697d68-a37d-82b8-6634-775d729f143a.png)を押すたびに`i`の中身が2,3,...と変化していくのが確認できると思います。

動作をある程度確認できたら今度は数値をクリックして`i`の値を書き換えてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/85ea2e93-3dbe-aa81-b919-1a594361db8e.png)
例えば、図のように800と入力して![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/b5697d68-a37d-82b8-6634-775d729f143a.png)を押すと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デバッグについて説明します。