0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【チュートリアル】mruby/c ver4 をSTM32マイコンで動かす Chapter2: mruby/cでElChika(えるちか)

0
Last updated at Posted at 2026-08-05

島根県産業技術センター の東です。

mruby/c ver4 をSTM32マイコンで動かす記事

今回はその第2回、mruby/c のポーティングを行い、ElChika1(えるちか = LEDの点滅)を行います。

目標

ST社製マイコン評価ボード 「Nucleo-F401RE」 のオンボードLEDを、mruby/c のコードを使って点滅させる。

前提知識

mruby/c の実行の仕組み

mruby/c は、RubyのソースコードをPC上であらかじめバイトコード(中間コード)に変換し、それをターゲットマイコン上の VM (Virtual machine) を使って実行します。

20260802142801.png

バイトコードはmrubyとコンパチブルなので、mrubyプロジェクトから配布されているコンパイラ (mrbc.exe) をそのまま利用します。

20260802142901.png

VM自体は、C言語(C99) を使って書いてあります。
mruby/c プログラムを動作させるやり方は、以下の2種類が考えられます。

  1. あらかじめVMをROMに書き込んでおき、あとからバイトコードだけを書き込んで実行する方法
  2. mruby/cのバイトコードをC言語の配列へ出力し、他のC言語ファイルとともにビルドしマイコンに書き込む方法

今回は簡単のために2の方法を説明し、別記事で1の方法にトライします。
実際の開発時は、1の方法でできるようにしておくと、開発効率が圧倒的に上がります。

スケジューラ

mruby/cの特徴の一つとして、スケジューラを内包し、OSなしでも複数のrubyプログラムの同時実行が可能です。とはいえ、スケジューラと mruby/c VM 本体は疎結合であるように設計しており分離可能です。すなわち、mrubyのように VM 本体だけでも使う事ができます。具体的方法は、sample_c フォルダの sample_no_scheduler.c を見てください。
今回は、スケジューラを使う方針にします。

ハードウェアタイマー

スケジューラは、プリエンプティブな動作を実現するために、ハードウェアタイマーを必要とします。しかしポーティングの初期段階でいきなりフルスペック動作をさせるのはハードルが高いので、ハードウェアタイマーなしで疑似動作させるためのモードがあります。
今回は、簡単のためハードウェアタイマーを使わずに動作確認をすることにして、次回ハードウェアタイマーを使った方法に変更します。

参考
このほかにも、PicoRuby (https://github.com/picoruby/picoruby) という、

  • コンパイラを独自開発
  • VMは mruby/c を使う

という処理系もあります。

今回の方針

  • mruby/c のバイトコードをC言語のバイト列へ変換し、STM32CubeIDE 上でリンクする方式とする
  • スケジューラを使う
  • ハードウェアタイマーを使わない

準備するもの

mruby/c リリース版のダウンロードは、該当ページの画面下の方へスクロールして、mrubyc-release4.0.0.tar.gz をクリックしてダウンロードします。
Source code の方も同等ですが、ビルド時に別途 Ruby (CRuby) が必要になります。

mrubyコンパイラは、mruby_compiler_4.0.0 の方を、ダウンロードします。

前回の記事 を参考に、開発環境構築を終わらせておきます。

作業手順

mruby/c VM のポーティングから Rubyプログラムを動かすまでの手順は、以下の順番で行います。

  1. mruby/c のソースコードを開発環境へ持ってくる
  2. タイマー未使用を宣言する
  3. mruby/c 用の hal ファイルを作る
  4. mruby/c を実行するC関数を作る
  5. Ruby (mruby/c) のプログラムを作る
  6. mrbc.exe を使って、mrubyバイトコードファイルを作る
  7. 全体をビルドし、ターゲットデバイスに書き込む

途中、フォルダの作成など、現時点でのベストプラクティスと思う方法を紹介します。
それではやっていきます。

1. mruby/c のソースコードを開発環境へ持ってくる

ダウンロードした mruby/c ソースコード mrubyc-release4.0.0.tar.gz を任意の場所へ展開します。7-Zip等の展開用ソフトウェアを使っても良いですが、コマンドラインでも展開することができます。

cd Downloads
tar xfz mrubyc-release4.0.0.tar.gz

CubeIDEを起動し、左ペイン Project Explorer 上、Core の上で右クリックし、New > Folder を選びます。
20260802145121.png

New Folder ダイアログで、Folder name 欄へ、mrubyc_src と入力し、[Finish] をクリックし、フォルダを作ります。
20260802145353.png

mruby/cのソースコードを展開したフォルダの src フォルダにある全ファイルを、先ほどの mrubyc_src フォルダへコピーします。
コピーの方法は、全ファイルのドラッグ&ドロップでも良いですし、以下に示すコピー&ペーストでも可能です。

  • srcフォルダで [Ctrl]-[A] で全ファイルを選び、[Ctrl]-[C] でコピー
  • CubeIDEの mrubyc_src を1度クリックして選ばれた状態にしてから [Ctrl]-[V]

以下のとおり、mrubyc_src 以下に全てのファイルがコピーされたのを確認します。
20260802154847.png

2. タイマー未使用を宣言する

この章では簡単のため、ハードウェアタイマーを使わずに動かします。そのための宣言をプロジェクト設定に追加します。

  • メニューから、Project > Properties を選び、ダイアログを開きます
  • ダイアログ左ペインの C/C++ General > Paths and Symbols をクリックし、右ペインの Symbols タブをクリックします
  • Languages が GNU C になっていることを確認し、[Add...] ボタンをクリックします

20260802155503.png

  • Add symbol ダイアログが開くので、Name欄に、MRBC_NO_TIMER と入力して [OK] をクリックします

20260802155426.png

[Apply and Close] をクリックしてダイアログを閉じます。2

3. mruby/c 用の hal ファイルを作る

STM32環境にもHALがあり、ややこしいですがそれとは別物です。
以下の手順で、Core/mrubyc_src フォルダへ、hal.h ファイルを作ります。

  • CubeIDEの左ペイン Project Explorer の、先ほど作った mrubyc_src フォルダの上で右クリックし、New > Header File を選びます
  • New Header File ダイアログが開くので、Header file: 欄へ、hal.h と入力して [Finish] をクリックします

20260802160205.png

  • 作成されたhal.hをダブルクリックして開き、以下の内容をコピーします
Core/mrubyc_src/hal.h
#ifndef MRBC_SRC_HAL_H_
#define MRBC_SRC_HAL_H_

#include "main.h"

#define MRBC_TICK_UNIT 1
#define MRBC_TIMESLICE_TICK_COUNT 10

#define mrbc_hal_init()        ((void)0)
#define mrbc_hal_enable_irq()  ((void)0)
#define mrbc_hal_disable_irq() ((void)0)
#define mrbc_hal_idle_cpu()    (HAL_Delay(MRBC_TICK_UNIT), mrbc_tick())

int mrbc_hal_write(int fd, const void *buf, int nbytes);
int mrbc_hal_flush(int fd);
void mrbc_hal_abort(const char *s);

#endif

mruby/c で必要とする hal は、4つのマクロ (define) と、3つの関数、たったこれだけです。
今回のレポートではとりあえず動かすことを重要視し、ダミーで定義して後々拡張します。

名前から大まかな動作は想像できると思いますが、簡単にそれぞれを説明します。

mrbc_hal_init
hal独自の初期化が必要になる場合、このマクロに定義する。
mrbc_hal_enable_irq
CPUの割り込み許可処理関数を記述する。
mrbc_hal_disable_irq
CPUの割り込み禁止処理関数を記述する。
mrbc_hal_idle_cpu
CPUをアイドルモードにするための処理関数を記述する。
int mrbc_hal_write(int fd, const void *buf, int nbytes)
mruby/c の putc などの標準出力先を記述する。
int mrbc_hal_flush(int fd)
標準出力にバッファを持つ場合のバッファフラッシュを記述する。
void mrbc_hal_abort(const char *s)
なんらかの原因で abort する場合にコールする関数を記述する。

mrbc_hal_idle_cpu マクロに関して、今回のようにハードウェアタイマーを使用しないモードでCPUを止めてしまうと、起きるためのトリガーがなくなって本当に動作が完全停止してしまいます。そのため、1ミリ秒の時間待ちと、併せて mruby/c のスケジューラ内部処理に必要な関数 mrbc_tick() を記述します。詳細は次の章を見てください。

4. mruby/c を実行するC関数を作る

mruby/c を実行する関数を、新たに Core/mrubyc/start_mrubyc.c ファイルを作って記述します。

  • 先ほどと同じように、CubeIDEの左ペイン Core の上で右クリックし、New > Folder を選びます
  • 表示されたダイアログの Folder name 欄へ、mrubyc と入力し、[Finish] をクリックし、フォルダを作ります
  • できた mrubycフォルダ上で右クリックし、New > Source File と選びます
  • 表示されたダイアログの Source file 欄へ、start_mrubyc.c と入力します

20260802161454.png

  • 作成されたstart_mruby.cをダブルクリックして開き、以下の内容をコピーします
Core/mrubyc/start_mrubyc.c
#include "main.h"
#include "../mrubyc_src/mrubyc.h"

static void c_led_write(mrbc_vm *vm, mrbc_value v[], int argc);

/* mruby/c プログラムが使うワークメモリの確保 */
#define MRBC_MEMORY_SIZE (1024*30)
static uint8_t memory_pool[MRBC_MEMORY_SIZE];

/*! mruby/c プログラムの実行開始
*/
void start_mrubyc( void )
{
  mrbc_init(memory_pool, MRBC_MEMORY_SIZE);

  // ユーザ定義メソッドの登録
  mrbc_define_method(0, 0, "led_write", c_led_write);

  // タスクの登録
  extern const uint8_t task1[];
  mrbc_create_task( task1, 0 );

  // 実行開始
  mrbc_run();
}


/*! オンボード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( GPIOA, GPIO_PIN_5, on_off );
}

/*! HAL(ダミー)
*/
int mrbc_hal_write(int fd, const void *buf, int nbytes)
{
  return 0;
}
int mrbc_hal_flush(int fd)
{
  return 0;
}
void mrbc_hal_abort(const char *s)
{
}

前回、C言語で動作確認をした時に書き換えた Core/Src/main.c を、以下の通り start_mrubyc 関数をコールするように書き換えます。

Core/Src/main.c
  /* USER CODE BEGIN 2 */
  void start_mrubyc(void);
  start_mrubyc();
  /* USER CODE END 2 */

5. Ruby (mruby/c) のプログラムを作る

mruby/c のソースファイル task1.rb を作ります。

  • 左ペイン Project Explorer 上、mrubyc の上で右クリックし、New > File を選びます
  • 表示されたダイアログの File name 欄へ、task1.rb と入力し、ファイルを作ります

20260802162448.png

作成された task1.rb をダブルクリックして開き、以下の内容をコピーします

Core/mrubyc/task1.rb
while true
  led_write( 1 )
  sleep 1
  led_write( 0 )
  sleep 1
end

6. mrbc.exe を使って、mrubyバイトコードファイルを作る

mrubyコンパイラを使って、Rubyソースコードをコンパイルするとともに、バイトコードをC言語のバイト列へ変換します。

  • ダウンロードした mruby コンパイラ mruby_compiler_4.0.0_***.zip を展開します
  • mrbc.exe を、先ほどの mrubyc フォルダへコピーします
  • コマンドプロンプトを起動し、先のフォルダへ cd します
cd \PATH\TO\PROJECT\mrubyc_v4\Core\mrubyc
  • mrbc.exe コンパイラで、rubyソースコードをバイトコードに変換します
mrbc.exe -Btask1 task1.rb

Rubyソースコードがバイトコードに変換されて、task1.c ファイルが生成されます。
task1.c ファイルを表示して、どのように出力されたかを確認しておくと良いでしょう。

task1.c
#include <stdint.h>
#ifdef __cplusplus
extern
#endif
const uint8_t task1[] = {
0x52,0x49,0x54,0x45,0x30,0x34,0x30,0x30,0x00,0x00,0x00,0x6e,0x4d,0x41,0x54,0x5a,
(snip)
0x73,0x6c,0x65,0x65,0x70,0x00,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
};

ここでできた task1 配列が、「4. mruby/c を実行するC関数を作る」 の箇所で記述したプログラムのタスクの登録部で使われます。

7. 全体をビルドし、ターゲットデバイスに書き込む

ビルド

メニューから、Project > Build Project を選びます。
下部コンソールペインに、Build Finished. と表示されるのを確認します。

書き込み

メニューから、Run > Run を選んでターゲットへ書き込みます。

オンボードのLEDが点滅したでしょうか。

2026080401.png

mruby/c プログラムの書き換え

プログラムを書き換えて、点滅の速度をもっと早くしてみましょう。

task1.rb を、以下のように書き換えて保存します。

Core/mrubyc/task1.rb
while true
  led_write( 1 )
  sleep 0.1        # ここと
  led_write( 0 )
  sleep 0.5        # ここ
end

保存するのを忘れないようにしましょう。
次に、コマンドプロンプトで、書き換えたrubyソースコードをバイトコードに変換します。

mrbc.exe -Btask1 task1.rb

CubeIDE で、ビルドしてターゲットへ書き込みます。

メニューから、Run > Run を選んでターゲットへ書き込みます。
先ほどは、ビルドと書き込みを別々に行っていましたが、じつは書き込み前に自動的にビルドされるので、書き込みの操作だけでOKです。

実行が始まると、LEDが先ほどより早く点滅します。

2026080402.png

ダウンロード

ファイル全体は、以下のリンクからダウンロード出来ます。
CubeMXプロジェクトファイルダウンロード

おわりに

今回は、STM32(Nucleo-F401RE)上で mruby/c を動かし、オンボードLEDを点滅させる「えるちか」までを解説しました。

ハードウェアタイマーを使わないモード(MRBC_NO_TIMER)とC言語配列によるバイトコードの埋め込みを利用することで、まずは最小限の手順で動作確認ができました。一度環境を構築してしまえば、Rubyコード側で sleep の数値を書き換えるだけで点滅速度を自由に変更できる mruby/c の手軽さを実感していただけたかと思います。

次回は、今回はスキップしたハードウェアタイマーを使った、より正確なタイミングを有するスケジューリング動作にチャレンジしていく予定です。

  1. ElChika : Electro Luminescence Chika Chika ですんで、LED (Light Emitting Diode) に限らずそれっぽいものを点滅させてます。

  2. このほかにも、同 Properties ダイアログの、C/C++ Build > Settings > Tool Settings > MCU GCC Compiler > Preprocessor > Define symbols の画面でも同様の定義ができます。試した結果、どちらでも同じ効果でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?