mruby/cペリフェラルライブラリのSTM32マイコンへの実装 Chapter01: 準備編

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。

今回から数回に分けて、STM32マイコン評価ボード Nucleo-F401REに、mruby, mruby/c共通ペリフェラルインターフェースガイドラインに沿ったライブラリを実装する作業をします。作業を進めるにあたり、どのように行ったのかを記事にしてみようと思います。

* [STM32マイコン評価ボード Nucleo-F401RE](https://www.st.com/ja/evaluation-tools/nucleo-f401re.html)
* [mruby, mruby/c共通ペリフェラルインターフェースガイドライン](https://github.com/mruby/microcontroller-peripheral-interface-guide)

記事は、複数回に分けて以下の順で進めていきます。

1. 準備編（今回）
2. [GPIOクラス実装編](https://qiita.com/HirohitoHigashi/items/9be9124f01a180fb68b1)
3. [ADCクラス実装編](https://qiita.com/HirohitoHigashi/items/d1827ce1830e79821f67)
4. [PWMクラス実装編](https://qiita.com/HirohitoHigashi/items/83eebbc4f362a9f09a8a)
5. [I<sup>2</sup>Cクラス実装編](https://qiita.com/HirohitoHigashi/items/08ae28d6643b654ba34f)
6. [SPIクラス実装編](https://qiita.com/HirohitoHigashi/items/4bbfaf8c41d28c87e05a)
7. [UARTクラス実装編](https://qiita.com/HirohitoHigashi/items/94634a856a5e35cc0efb)
8. [バイトコード書き込み機能実装編](https://qiita.com/HirohitoHigashi/items/439494cde09da6350bde)


# 目標

最初にプロジェクト全体の目標を、以下のように決めました。

* mruby, mruby/c共通ペリフェラルインターフェースガイドラインを、Nucleo-F401RE に実装する
* ユーザーは、mrubyのバイトコードだけを転送するだけで使える環境を用意する

mruby/c プログラムを動作させるやり方はいくつかありますが、今回は、VMをあらかじめ書き込んだボードに mrubyバイトコードだけを転送し動かす方法を採用します。自由度は少ないので最終的なプロダクトには物足りないケースが多いかと思いますが、POCや教育現場など、ちょっと試してみたい場合にはとても便利に使う事ができます。


# 実現可能性調査、及び技術の選択

この目標を達成するために、どのような手段をとれば良いのかを決める必要があります。場合によっては、実現難易度などを考慮して、あきらめた方が良いと判断する部分がないかを検討します。
また、私の役割上、第3者が再現しやすい方法の採用が好ましく、そういったことも考慮の対象にしています。


## 開発ツール・環境

開発環境は、ST社製 STM32CubeIDE を使います。
Eclipseベースなので、動作が緩慢で嫌がる声も多く聞きます（個人の感想です）が、無料で利用できて多機能、かつメーカー純正という安心感から、こちらを選定します。

## ピンアサインの自由度

CubeIDEのピンアサイン、コード自動生成機能は、とても便利に使える反面、今回のように上位層（mruby/c）からピンアサインの指示、変更の要求とは、折り合いがつけにくそうです。ですので今回は、各ペリフェラルが利用する物理ピンは固定にしようと思います。


## ペリフェラルへのアクセス

ペリフェラルの使用方法は、現時点だと
1. ST社製 HALライブラリを使う
2. ST社製 LLライブラリを使う
3. レジスタを直接アクセスして使う

この３つの方法が考えられます。それぞれメリットデメリットがあります。

| | |
|-|:-:|
| 移植性 | 良い　1 <---> 3　悪い |
| フットプリント | 大きい　1 <---> 3　小さい |
| 速度 | 遅い　1 <---> 3　速い |
| 将来にわたって使えるか | 若干不安　1 <---> 3　問題なし |

ST社には非常に多くの STM32 シリーズがあります。今回のボードに使われている STM32F401RET6 は、Flashが512KBと大きく、また、他のマイコンにも容易に移植できる方がより良いだろうとの判断から、HALライブラリを使う事にしました。HALライブラリは、便利に使える分、抽象度も高いので、要求をフルスペックで満たしてくれることができないかもしれません。それは、今後進める上で都度判断します。


## フラッシュメモリの自己書き換え

今回は、VMをあらかじめ書き込んだボードに mrubyバイトコードだけを転送し動かす方法を採用します。そのため、書き込みプログラムが動作中に転送したバイトコードをフラッシュメモリに書き込む方法が用意されている必要があります。
つまり、
* Flash上でコードを実行中に、Flashに書き込むことができるか
　（できない場合は、書き込みプログラムをRAMに移してから実行させるなど、面倒な手順が必要になる）
* Flashの一部領域のみ、消去、書き込みができるか
　（あらかじめ書き込まれたVMを消さないようにする）
* 書き込んだ内容は、リニアなメモリとしてアクセスできるか
　（mruby/c VMがこれを要求）

等を検討します。
調査の結果、これもHALライブラリが用意してあるようなので、大丈夫そうです。


# 作業手順

では、実際の作業に入ります。

## CubeIDEによるプロジェクトの作成

別記事[「mruby/cをSTM32マイコンで動かす Chapter01: 環境構築」](https://qiita.com/HirohitoHigashi/items/7ef01d074baa27a3b2e7) に書いた方法と同様の手順でプロジェクトを作ります。併せて同記事連載全てで説明した通りの方法で、mruby/cポーティングを終えた状態からスタートさせます。


## ペリフェラル及び利用するピンの割り付け

ガイドラインで定められたペリフェラルは、以下の通りです。

  * [GPIO](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_GPIO_ja.md)
  * [ADC](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_ADC_ja.md)
  * [PWM](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_PWM_ja.md)
  * [UART](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_UART_ja.md)
  * [I2C](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_I2C_ja.md)
  * [SPI](https://github.com/HirohitoHigashi/microcontroller-peripheral-interface-guide/blob/main/mruby_io_SPI_ja.md)

これら全てを、あらかじめ最初にアサインします。
基板上のシルクなどを参考にしながら、今回は以下のように決めました。

| CN | pin | SILK | GPIO | Usage | (PWM) |
|-|-:|-|-|-|-|
| CN5 | 10 | SCL/D15 | PB8 | I2C1_SCL |  |
| CN5 | 9 | SDA/D14 | PB9 | I2C1_SDA |  |
| CN5 | 8 | AVDD | AVDD | --- |  |
| CN5 | 7 | GND | GND | --- |  |
| CN5 | 6 | SCK/D13 | PA5 | (LED2) |  |
| CN5 | 5 | MISO/D12 | PA6 |  | TIM3_CH1 |
| CN5 | 4 | PWM/MOSI/D11 | PA7 |  | TIM3_CH2 |
| CN5 | 3 | PWM/CS/D10 | PB6 |  | TIM4_CH1 |
| CN5 | 2 | PWM/D9 | PC7 |  | TIM3_CH2 |
| CN5 | 1 | D8 | PA9 | UART1_TX |  |

| CN | pin | SILK | GPIO | Usage | (PWM) |
|-|-:|-|-|-|-|
| CN9 | 8 | D7 | PA8 |  | TIM1_CH1 |
| CN9 | 7 | PWM/D6 | PB10 |  | TIM2_CH3 |
| CN9 | 6 | PMW/D5 | PB4 |  | TIM3_CH1 |
| CN9 | 5 | D4 | PB5 |  | TIM3_CH2 |
| CN9 | 4 | PWM/D3 | PB3 | (SWO) |  |
| CN9 | 3 | D2 | PA10 | UART1_RX |  |
| CN9 | 2 | TX/D1 | PA2 | (UART2_TX) |  |
| CN9 | 1 | RX/D0 | PA3 | (UART2_RX) |  |

| CN | pin | SILK | GPIO | Usage | (PWM) |
|-|-:|-|-|-|-|
| CN8 | 1 | A0 | PA0 | AD0 | TIM2_CH1 |
| CN8 | 2 | A1 | PA1 | AD1 | TIM2_CH2 |
| CN8 | 3 | A2 | PA4 | AD4 |  |
| CN8 | 4 | A3 | PB0 | AD8 | TIM3_CH3 |
| CN8 | 5 | A4 | PC1 | AD11 |  |
| CN8 | 6 | A5 | PC0 | AD10 |  |

| CN | pin | SILK | GPIO | Usage | (PWM) |
|-|-:|-|-|-|-|
| CN10 | 12 |  | PA12 | UART6_RX |  |
| CN10 | 14 |  | PA11 | UART6_TX |  |
| CN7 | 1 |  | PC10 | SPI3_SCK |  |
| CN7 | 2 |  | PC11 | SPI3_MISO |  |
| CN7 | 3 |  | PC12 | SPI3_MOSI | |

ADCは、シルクに書かれているとおりに、CN8の6チャネルを使います。
UARTは、ボード設計で利用が決められた UART2のほか、UART1, UART6 の３つをアサインします。
I2C, SPI は、それぞれ1つずつ、I2C1とSPI3をアサインします。

![STM32mrbc01-スクリーンショット 2024-06-21 15.02.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/11b9289a-b429-ab00-ad6e-52dd14835a60.png)

多少、コンフリクトしていると IDE が文句を言っていますが、後々問題が出たらそのときに対処します。


## ADC の割り当て

ADCは、シルクに書かれているとおりに、CN8の6チャネルを使います。

![STM32mrbc01-SS_PinAssign_ADC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/c0990be5-3599-2b38-f7fe-9c229d91ed4a.png)

  * Analog > ADC1 をクリックして開く
  * IN0, 1, 4, 8, 10, 11 をチェックすると、同時にピンアサインも完了する

## UART の割り当て

UARTは、ボード設計で利用が決められた UART2のほか、UART1, UART6 の３つをアサインします。
ガイドラインで、ボーレートのデフォルト値は 9600bps と定められているので、この段階で変更しておきます。

![STM32mrbc01-SS_PinAssign_UART.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/5768079b-1a52-238f-7d60-976b26ae4cf9.png)

  * Connectivity > USART1 をクリックして開く
  * Mode を、Asynchronous に変更する
  * 下の Parameter Settings タブ > Basic Parameters > Baud Rate を、9600 Bits/s に変更する
  * USART2, USART6 も同様に設定する
  * USART6のピンは、PA11,PA12に変更する

## I2C の割り当て

I2C は、I2C1 を割り当てます。

![STM32mrbc01-SS_PinAssign_I2C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/77150950-e266-9039-9e27-d1435fb3213c.png)

  * Connectivity > I2C1 をクリックして開く
  * I2C 欄を、I2C に変更する

## SPI の割り当て

SPI は SPI3 をアサインします。

![STM32mrbc01-SS_PinAssign_SPI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/dad0df0b-cf4d-a1ea-e829-3e4013446a53.png)

  * Connectivity > SPI3 をクリックして開く
  * Mode 欄を、Full-Duples Master に変更する

## タイマーの設定

次にPWMに使用するタイマーです。
PWM出力でピンが固定されると、単なるGPIOの入力/出力をしたい場合と被ってしまって困りますので、IDEからの利用は無理かなと考えていたところ、IDEの設定に、「PWM Generation No Output」 という選択肢がありました。恐らく タイマーの多段接続に利用する内部的な設定項目だと思いますが、ちょうど良いのでこれを利用します。

タイマーは、先の表のPWM欄に示したユニット、チャネルを、PWM Generation No Output モードに設定します。

（例：TIM2）
![STM32mrbc01-スクリーンショット 2024-06-21 16.13.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/982f62e3-07df-ff2a-e8f1-fbdb6fb31353.png)

## テストビルド

ここまでで、IDEでのペリフェラル割り付けは一旦終了です。保存するとコードを自動生成するか聞いてくるので、Yesで自動生成させます。
プロジェクトをビルドし、エラー無く終了するか確認しておきます。


# おわりに

ファイル全体は、github リポジトリにありますので、そちらをご覧ください。

https://github.com/HirohitoHigashi/mrubyc-nucleo-F401RE/tree/chapter01_prepare

ここまでで、準備は終了です。
次回から実際に、GPIOクラスを実装します。