しまねソフト研究開発センター(略称 ITOC)にいます、東です。
先日来、STマイクロエレクトロニクス製のマイコンボード、「Nucleo-F401RE」と、開発環境「STM32CubeIDE」を使って、mruby/c の移植などを行ってきました。
ITOCには、他にNucleoシリーズの「Nucleo-F091RC」と、「Nucleo-L476RG」を所有しています。せっかくなので、これらのデバイスにも mruby/c の移植を行い、その手順を公開します。
予定
Nucleo-F091RC
前回までターゲットにしていた機種と比較して、より小規模のMPU搭載機種です。
CPU: Cortex-M0
RAM: 32KB
こちらは、mruby/c のバイトコードを C言語の配列として書き込む方法(この記事 の方法2)をやってみます。
すなわち、mruby/c移植チュートリアル記事 を、機種を Nucleo-F091RC に替えて実施することとなります。
Nucleo-L476RG
こちらは、より大規模のMPU搭載機種です。
CPU: Cortex-M4
RAM: 128KB
この機種では、次回以降に別記事として、バイトコードのみを書き込む方法(この記事 の方法1)を実施する計画です。
準備するもの
STM32マイコン評価ボード Nucleo-F091RC
(https://www.st.com/ja/evaluation-tools/nucleo-f091rc.html)
開発環境 STM32CubeIDE (ver 1.15.1)
(https://www.st.com/ja/development-tools/stm32cubeide.html)
作業手順
STM32CubeIDE 上で、MPUの種類を変更する方法が無いか探したのですが、おそらくメーカーが想定しているGUI操作の範囲内では用意は無さそうです。従って、新規にプロジェクトを作って、チュートリアルの手順を最短で組み入れる方法をとりました。
新規プロジェクトの作成
別記事「mruby/cをSTM32マイコンで動かす Chapter01: 環境構築」 に書いた方法と同様の手順でプロジェクトを作ります。
Board Selector をクリックしてボードセレクタタブに変更し、Commercial Part Number 欄に、"F901" と入力すれば NUCLEO-F091RC が検索されますので、ボードリスト欄で選べばOKです。
プロジェクト名は、任意でかまいません。今回私は、"F091RC_mrubyc" とつけました。
デフォルトデバイスの確認
プロジェクトが作成されましたら、デフォルトでマップされた以下のデバイスを確認します。
- オンボードLEDのポート番号 (前回F401REでは、PA5)
- オンボードスイッチのポート番号 (前回F401REでは、PC13)
- USBシリアルにマップされたUART番号 (前回F401REでは、UART2)
幸いなことに、今回のボード F091RC は、チュートリアルで使ったボード F401RE と同じポート番号、UART番号でした。よって、ポーティングは最小限の手順で行う事ができます。
ソースコードのダウンロードとマージ
「チュートリアル Chapter04: halの仕上げ」あたりのソースコードが一番使いやすいと思うので、こちらをつかいます。
このページから、Code > Download ZIP 等でダウンロードし、展開しておきます。
展開したソースツリーから、以下のディレクトリを今回のプロジェクトのCoreディレクトリへコピーします。
- Core/mrubyc
- Core/mrubyc_src
自動生成されたソースコードへ追記
Core/Src/ 以下のファイルを編集し、以下のコードを追記します。
mruby/c の開始指示
/* USER CODE BEGIN 2 */
void start_mrubyc(void);
start_mrubyc();
/* USER CODE END 2 */
割り込み処理の追加
/* USER CODE BEGIN SysTick_IRQn 0 */
void mrbc_tick(void);
mrbc_tick();
/* USER CODE END SysTick_IRQn 0 */
ビルドプロパティーの変更
メニューから、Project > Properties を選び、ダイアログを開きます。
ダイアログ左ペインの C/C++ Build > Settings をクリックします。
右ペインの Configuration: を、All configurations に変更します。
右ペインの Tool Settings タブをクリックし、MCU Settings をクリックします。
Use float with printf from newlib-nano 欄のチェックをつけます。
右ペインの Build Steps タブをクリックします。
Pre-build steps の Command: 欄へ、以下の通り入力します。
cd ..\\Core\\mrubyc; make
CPU Cortex-M0 に対応
Cortex-M0 は、メモリアクセスに32bit アライメントを要求するので、vm_config.h でコメントアウトしてある以下の定義を有効化します。
#define MRBC_REQUIRE_32BIT_ALIGNMENT
もしくは、IDEのビルドプロパティーで定義しても、おそらく問題ないでしょう。
mruby/c プログラムが使うワークメモリの縮小
チュートリアルでは、mruby/c プログラムが使うワークメモリに30KBほど確保するよう記述しました。一方このMPUの内蔵メモリは、トータルで32KBしかないので、このままだとリンクエラーになります。従って、以下のように約半分の16KBに縮小します。
/* mruby/c プログラムが使うワークメモリの確保 */
#define MRBC_MEMORY_SIZE (1024*16)
これでOKです。
動作確認
ビルドしてターゲットに書き込みます。
TeraTerm 等を使い、ターゲットボードの COM 番号を開いて、"Hello, mruby/c ..." 等と表示されればOKです。
その他の変更点
オンボードLEDのポート番号が違う場合
オンボードLEDの割り当てポートが PA5 ではない場合は、c_led_write 関数を変更します。以下の例は、PB15に割り当てる例です。
/*! オンボードLED ON/OFF メソッドの実装
*/
static void c_led_write(mrbc_vm *vm, mrbc_value v[], int argc)
{
int on_off = GET_INT_ARG(1);
HAL_GPIO_WritePin( GPIOB, GPIO_PIN_15, on_off );
}
UART 番号が違う場合
UARTが 2番ではない場合は、hal_write 関数を変更します。以下の例は、UART3に割り当てる例です。
int hal_write(int fd, const void *buf, int nbytes)
{
extern UART_HandleTypeDef huart3;
HAL_UART_Transmit( &huart3, buf, nbytes, HAL_MAX_DELAY );
return nbytes;
}
mruby/c ソースコードの入れ替え
バージョンアップやバグ対応等で、mruby/c VM のソースコードを更新したい場合の手順を記します。基本的には mrubyc_src ディレクトリの内容を入れ替えるだけです。
- Core/mrubyc_src/hal.h ファイル、及び vm_config.h ファイルを任意の場所にバックアップする
- Core/mrubyc_src ディレクトリの内容を、最新のmruby/c VM ソースコードの src ディレクトリの内容に全て入れ替える
- バックアップした hal.h を mrubyc_src ディレクトリ内へ戻す
- バックアップした vm_config.h を最新の vm_config.h と比較し、マージする
おまけ
https://qiita.com/HirohitoHigashi/items/a0acb20ec8b58a21a9d4 と同じ条件にして、ベンチマークを行ってみました。
MPU | Core | clock(MHz) | 最適化 | 実行時間(ms) |
---|---|---|---|---|
STM32F401RET6 | ARM Cortex M4 | 84 | -O2 | 2303 |
STM32F091RC | ARM Cortex M0 | 48 | -O2 | 9055 |
クロックが約1.8倍程度の差に対して、実行時間は約4倍の差がありますので、クロックあたりの性能比較では、約2.2倍の差が観測できました。
ただし、Cortex M0では、32bit align 回避が、このベンチマークにおいてはかなり不利になっていると思われ、コア単体の計算速度は、そこまで差が出ないはずと考えています。
おわりに
次回は、ペリフェラルライブラリとバイトコード書き込み機能を含め、もう一つの機種「Nucleo-L476RG」への移植にトライします。