ch32funでmruby/c
mrubyc_arduinoを使って,rubyからマイコンを使える環境を整えて来た。 Wifiを使うなら、EPS8266に各種の機能を実装できたので、それを使うと良いだろう。 Wifiが必要ないのなら、価格や性能などを考えると、CH32V203が最適であると感じている。 しかし、CH32V203はマイコンボードの入手性があまり良くない。 BluePill+が日本でも600円程度で入手可能だが、ボードが少し大きめである。 同じようにUSBが使えるマイコンにはCH32X035があり、400円程度でボードが容易に手に入る。 しかし、かなり機能を削らないとflashに収まらない。
これまでは、環境の構築の容易さや、汎用性を重視して、mrubyc_arduinoを使っていたが、 CH32については、ch32funの方が、コンパイルサイズが小さいので、ch32funとmruby/cを組み合せて使ってみた。 ArduinoはC++なので、C言語と組み合せるときに工夫が必要だが、 ch32funはC言語なので、mruby/cと相性が良いというメリットもある。 しかし、機能を実装するのが面倒という大きなデメリットがあり、今回はArduinoっぽく使えるようになっているGPIOとADCを使えるようにしてみた。
まず、必要なファイルを揃えないといけないので、 例えばmrubyc_ch32funというフォルダを作って、その中に以下のようにファイルやフォルダを配置する。 ch32funの最新のファイルをzipで取って来て、その中のch32funとextralibsのフォルダを入れる。 mruby/cのリリースは3.4.1が最新なので、それを持って来て、mrubycというフォルダを作って、その下にsrcとsupportフォルダを入れる。 さらに、hal/minimal.hをmrubyc/src/hal.hにコピーする。 hal.hの中では、“ch32fun.h"をincludeするようにして、delayをDelay_Msに変更する。 必要なファイルを生成するために、mrubyc/srcフォルダの中で、make autogenを実行する。 mrubyc/src/vm_config.hでは、MRBC_USE_FLOATを0にして、MRBC_REQUIRE_32BIT_ALIGNMENTのコメントアウトを外してこれを有効にする。 FLOATを0にすると、そのままではコンパイルできないので、2026/04/15のブログを参考にして、いくつかのファイルを変更する。 mrblibを組み込まないclass.cの括弧を除いた最後の二行をコメントアウトする。 ch32fun用のコンパイラは、 Debianの場合は2025/11/25のブログを参考にしてインストールできる。 これで、準備は完了である。
ch32x035用のプログラムを作るために、先のフォルダの中にCH32X035というフォルダを作って、必要なファイルをつくる。 コンパイルに必要なMakefileは、以下の内容にする。
all : flash TARGET:=main TARGET_MCU?=CH32X035 MRUBYC_SRC := $(wildcard ../mrubyc/src/*.c) ADDITIONAL_C_FILES += $(MRUBYC_SRC) CFLAGS += -I../mrubyc/src -DNDEBUG CFLAGS += -Os -flto include ../ch32fun/ch32fun.mk flash : cv_flash clean : cv_clean
ch32funで組み込まれるfunconfig.hは、特に何の指定も必要無いので、以下のようにする。
#ifndef _FUNCONFIG_H #define _FUNCONFIG_H #endif
中心となるmain.cは、以下のようにした。
#include "ch32fun.h"
#include <stdio.h>
#include "mrubyc.h"
#include "mrbc_gpio.h"
#include "mrbc_adc.h"
#define MEMORY_SIZE (1024*10)
static uint8_t memory_pool[MEMORY_SIZE];
#define FLASH_CODE (0x8000000+54684) //ch32x035
int hal_write(int fd, const void *buf, int nbytes) {
_write(0, (char*)buf, nbytes);
return nbytes;
}
int hal_flush(int fd) {
return 0;
}
void hal_abort(const char *s) {}
long atol(const char *str) {
long res = 0;
int sign = 1;
while (*str == ' ' || (*str >= 9 && *str <= 13)) { str++; }
if (*str == '-') { sign = -1; str++; }
else if (*str == '+') { str++; }
while (*str >= '0' && *str <= '9') {
res = res * 10 + (*str - '0');
str++;
}
return res * sign;
}
int main()
{
SystemInit();
funGpioInitAll();
funAnalogInit();
mrbc_init(memory_pool, MEMORY_SIZE);
mrbc_init_class_digital();
mrbc_init_class_adc();
mrbc_create_task( (uint8_t *) FLASH_CODE, 0 );
mrbc_run();
while(1){}
}
mrbc_gpio.*やmrbc_adc.*については、mrubyc_arduino用のファイルから以下のように簡単に作ることができるので、これらをmrubyc/srcの中に置く。 Arduino.hの替わりにch32fun.hを組み込むようにして、pinModeなどの替わりにfunPinModeなどを使うようにして、多少の変更を加えれば良い。 これで、make buildとすると、コンパイルが実行される。 main.binのファイルサイズがFLASH_CODEのところの指定と違っていたら、それを変更してコンパイルをして、それらが一致するようにする。
rubyのスクリプトは、例えばtemp.rbとして以下のようなものを作る。
g=GPIO.new(28,GPIO::OUT) while true g.write(0) sleep 1 g.write(1) sleep 1 end
これをバイトコードに変換して、バイナリと結合する。
mrbc --remove-lv temp.rb cat main.bin temp.mrb >temp.binこのファイルはUSBを介して書き込むので、wchispを準備しておく。 CH32X035をBOOTを押しながらUSBに接続して、以下のコマンドで書き込む。sudo ./wchisp flash temp.binmrbのバイトコードを除いて、62kのflashの約86%となったが、FLOATとmrblibを除いた状態でこのサイズなのは、mrubyc_arduinoと比べるとかなり小さくなったようだ。 小さなマイコンではほとんど使わないTaskを消せば、さらに小さくなるだろう。
ここで、注意しないといけいないのが、ピンの指定についてである。 ch32funのGPIOのピンは、以下の規則で数値と対応している。 PAnは十六進法で0x0n、PBnは0x1n、PCnは0x2nなどとなっており、Arduinoとは番号が異なっている。 GPIOを使うときには、この数値で指定している。 また、ch32funのADCで指定する数値は、ピン番号では無くADCのチャンネル番号である。 PA0からPA7は、これらの番号が0から7となっているので、空いていたらこれらのピンを使うと混乱が少ないだろう。
mruby/cの標準のAPIとしては、その他にPWM,I2C,SPI,UARTなどがあるが、これらをch32funから扱うには、マイコン毎にプログラムしなければならない。 また、USB Serialを標準入出力にするという拡張も必要だろう。