STM32のファームウェアをSTM32cubeIDEをつかって書く 第１話 Lチカ

# STM32cubeとHAL, LLについて
STM32は、STMicroelectronics製のマイコンでARM Cortex-Mコアが搭載されています。開発ツールとしては、Mbed、Arduino、HAL、LLなどがあります。
Mbed、Arduinoなどのフレームワークは、複数種類のマイコン間で共通のライブラリを使用してプラグラムを書くことができます。しかし、ライブラリを共通化しているがゆえに、マイコン独自のインターフェースにアクセスすることが難しいことに加えて、処理が複雑で時間がかかります。
HAL(Hardware Abstraction Layer)、LL(Low Layer)はSTMicroelectronicsが提供しているSTM32のためのAPIで、マイコン独自のインターフェースにアクセスすることができます。LLは、名前の通りあまり抽象化されてないAPIで軽量で互換性が低いです。HALは、STM32間での互換性を確保したAPIです。

https://www.st.com/ja/embedded-software/stm32cube-mcu-mpu-packages.html

STM32cubeIDEは、そんなHALを使って開発を行うために、STMicroelectronicsが提供している統合開発環境です。どのペリフェラルをどのピンで使うか、クロックの分周器・逓倍器の設定などをGUIを使って設定し、そこからコード（ペリフェラル、クロックの初期化など）を生成することができます。そのほかにも、モーター制御用のツールや、LCDに表示するGUIの作成ツールなどの拡張パッケージになども提供されています。
本来、マイコンは、メモリの位置や、大きさ、メモリのどの位置にバイナリを配置するかなどの情報をコンパイラに渡す、リンカスクリプトなどを書かないといけないのですがこれも自動で生成されます。

インストールは↓からどうぞ。

https://www.st.com/ja/development-tools/stm32cubeide.html

# まずはLチカ
まずは、Lチカをしていきましょう。
## 使うボード
マイコンボードは大体LEDが載ってるのでそれを光らせましょう。今回は、NUCLEO-F446REというボードを使ってやっていきます。
nucleoは、STMicroelectronicsが提供しているSTM32の開発ボードのシリーズで結構種類があって、安価に購入できます。(しかしこのボードは結構古くて、新しく購入するならG431RBとかH753ZIとか使うのがいい気がする。古いボードは供給が少ない気がする)
![](https://www.st.com/bin/ecommerce/api/image.PF262063.en.feature-description-include-personalized-no-cpn-medium.jpg)
## コードの生成
arduinoなどと違うところで、まずペリフェラルの設定などを行って初期化などのコードを生成します。
### プロジェクトの作成
cubeIDEはプロジェクト単位でプログラムを管理します。
起動すると、まず下のような画面が出てくるので、左にある **Start new STM32 project** からプロジェクトを作成します。
(この画面でないときも、ツールバーの **File** > **New** > **STM32 project** からもいけます。よくあるやつ)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/076a8158-552d-a28e-25d2-d4bb1d138e47.png)
すると、ボードとかマイコンの情報のアップデートが始まって、それが終わると以下のような画面になります。
この画面で、どんなマイコンを使うか選択します。今回はSTMicroelectronics公式のボードを使っているので、上の **Board Selector** のタブを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/67294e69-4243-539d-a7ea-04ab4eef43d2.png)
そして、NUCLEO-F446REを探し出します。選択出来たら右下の **Next >** を押します
(公式でなかったり、自作であったりのボードを使用するときは、MCU/MPU Selector の方で使用するマイコンを選びます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/45bb1021-a3b1-bc12-c87c-5ad61b432e74.png)
    すると、以下のようになるので、 **Project Name** を入力し、Targeted Language は好きな方を選んで **Finish**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/83b50523-cd3e-6090-8278-1ce2c2e09b10.png)
こんなのが出てくるので Yes
(これを Yes にすると、ボードでマイコンに接続されている機能が最初から有効化された状態でプロジェクトが作成される)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/42132dd1-fe4e-bc0b-62a9-c8801824cd82.png)
プロジェクトが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/3382e8ea-c5b9-be0f-db32-7188f811e3f8.png)
### GUIを使った設定とコード生成
まずはクロックを設定します。
上のClock Configuration のタブを選択
いっぱいクロックがありますが、枠が水色になっているところに数値を入力すると、自動的にクロックを設定してくれます。今回は、取りあえず最高クロックを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/3a413e19-c7f7-731a-da90-e43d2f409b88.png)
真ん中の、**HCLK (MHz)** ってところに 180 と入力すると。。。
なんかロードが始まって。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/1f0a61f1-f0bf-ffb5-73f7-9a621870c3bf.png)
自動的に設定されました！
ほかにも、マルチプレクサみたいになってるところでどっちから来てるクロックを使うかとか設定できます。今回は外部の水晶は実装されていないので、左の真ん中ぐらいにある HSI/HSE の選択を HSI にしてます。(External と Internal だと思う)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/fe46023e-e4c2-fdcb-2991-f040ec8c891e.png)
次に LED の出力のピンを設定するので、上の **Pinout & Configuration** のタブに戻ってきてください。
よく見なくても、電源とGND以外のピンで緑色と黄色と灰色のピンがあります。
- 灰色 : 何も設定されていない
- 黄色 : ペリフェラルに接続されているが、ペリフェラルが有効化されていない
- 緑色 : ペリフェラルが接続されていて、有効化もされている。

プロジェクト作成時に、ボードではなくマイコンを選択するとすべてが灰色の状態でプロジェクトが作成されます。
ピンを直接クリックして、どの機能を使うか選択するか、左のTimerとかConnectivityとか書いてるところから機能を有効化して設定することができます。また、複数のピンで使えるペリフェラルの場合、ctrlを押しながらマウスでドラッグするとピンを動かせます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/951af8d8-10c4-9363-babd-f3e80af31168.png)
今回はボード上のLEDは既に設定されています。
左の System Core > GPIO を選択すると、PA5 に LD2 [Green Led] という名前で設定されていることが分かります。このように名前を付けてわかりやすくすることもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/c2ec45da-48ab-5a9e-ef69-9a83b3641e20.png)
ペリフェラルの設定が終わったので、コードを生成していきます。
左上の Save ボタンか Ctrl + S で保存すると、コードを生成するか聞かれるので Yes を押してコードを生成します。
(保存じゃなくても、![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/dee081b2-9437-02d8-f179-22c46b35de17.png)このボタンから生成することもできます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/1ac22337-e459-24f8-4b32-6ac260f2dd0b.png)
親切なことに、コードのエディタを開くかどうかも聞いてくれますので、Yesを押して開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/9fc9d9bb-7a57-562d-cfa9-246168f0df2e.png)
コードが生成されました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/5bda0773-cf1e-6e2a-9a81-2f9c0a015499.png)
### コードを書いて書き込む
あとはArduinoとかと一緒！
main.cの`/* USER CODE BEGIN xxx */`と`/* USER CODE END xxx */`の間にコードを書いていきます。これ以外のところに書くと、もう一度コードを生成したときに消えます。
生成されたコードを見てみると、`MX_GPIO_Init()`とかが呼ばれてますが、こういう初期化の後でないとGPIOとかは使えません。
今回はwhileの中の2行に、GPIOを反転させて250ms待つ処理を加えました。
GPIOの設定の時のラベルがmain.hで`LD2_GPIO_Port``LD2_Pin`宣言されてます。
(ctrl + space で自動補完が動きます)

```c:main.c
//略
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); //書き足したところ
      HAL_Delay(250);                             //書き足したところ
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
//略
```
コンパイルして書き込みます。コンパイルのみは![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/7c9e199e-5ec7-6e2c-d277-1af040fb4992.png)のマーク、書き込みもするなら![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/ada3b8d2-34e1-f133-fd7b-4c9e8ac66cb2.png)のマーク
nucleoを接続して、後者を押すとコンパイルが始まり、エラー0でコンパイルが終わると。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/ffaf207d-3b50-77f7-f450-3f996c540033.png)
最初にプログラムを書き込むときは以下のようなウィンドウが出てきます。ここでは、書き込みやデバッグの設定ができますが特にいじるところはないので OK で次に
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/bad88cac-8485-7984-d0a5-604fa9df08c6.png)
こんな感じにSTLinkのアップデートがあるよって知らせてくれることがありますが、別に問題ないので No でよし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/00b8a735-eccc-1cf7-ca00-cd586113c998.png)
未保存のファイルがあると、ここで教えてくれます。Saveしてもっかいコンパイル。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/6dbe2a1e-349c-4955-b411-36ce1ac8dfb7.png)
コンパイルが終わると、勝手に接続して書き込んでくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/66972662-55ad-4a03-3d2a-726325133d57.png)
書き込み成功
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1708525/e415fb7a-5c34-5c64-711e-670b9bd8c884.png)
ちゃんと光ってますね

https://twitter.com/parico_paripal/status/1607646066815692800

# 次回
GPIO以外のペリフェラルについても紹介します。