mruby/cに必要な最低限のメモリ

ch32funを用いて、WCH社のマイコン用のmruby/c環境を構築しているが、どの程度のメモリでmruby/cを動かすことができるのかを調べてみた。 mruby/cは3.4.1を使っているが、CH32X035用の環境としては、flashの容量の関係で、mrblibとTaskとFloatを組み込まないようにしている。 この状態でmruby/cに割り当てるメモリを減らしていって、どこまで動くのかを確かめた。

メモリを使うように、配列のサイズを徐々に増やしていって、どの大きさでエラーが出るかを確かめた。 検証用のプログラムは以下のものを用いた。

g=GPIO.new(24+12,GPIO::OUT)
a=[]
i=0
while true
  g.write 1
  a[i]=i+=1
  print "#{i}\r\n"
  sleep_ms 100
  g.write 0
  sleep_ms 200
end

まずは、4kで試すと、177でエラーが生じた。 同様に、3.5kでは113、3.0kで49と、0.5k当たり64ずつ減っている。 GPIOにも多少のメモリを使っているだろうから、計算上では何もしない状態で、2.6kのメモリを占有していることになる。 そして、余った分のメモリを、プログラム中で使っているのだろう。 つまり、3.0kのメモリをmruby/cに割り当てれば、ほとんどメモリを使わないようなプログラムであれば動くということである。

一方、C言語用にもメモリを残しておかなければならない。 今度はmruby/cに割り当てるメモリの量を増やしていって、0.5kぐらいしかメモリが残らないようにして、簡単なプログラムを走らせてみたが、問題無く動いた。 動かすプログラムにもよるだろうが、C言語用にはあまりメモリを残しておかなくても良いのかも知れない。

これまで、mruby/cには10k程度のメモリを割り当てることが多かったが、mrblibなどを使わない場合には、もっと少なくても動くことが分かった。 mruby/cが一応まともに動くような、最小のスペックのマイコンはCH32X035などだと思っていたが、もう少しメモリが少なくても大丈夫なようだ。 具体的には、flashが64k、メモリ10k程度のマイコンなら、ギリギリ動きそうである。 この程度のスペックを持つマイコンとしては、STM32F103やPY32F030などもあるけど、AVR64DUは8bitだから厳しいかな。

Read more...

ch32x035のbootloaderの書き換え

ch32v203用のmruby/cで、mrbコードだけを書き換えるためには、comuのbootloaderとminichlinkを使うと良いことは、2026/8/20のブログに書いた。 そのときは、主にminichlinkの問題で、32k以上のコードを書き込むことができなかったが、その後にそれを修正して64kまで書き込めるようになり、通常の用途には十分になった。 これで、ch32v203のmruby/c環境には、大きな問題点は無くなったと考えている。

一方、ch32x035についても、mrbコードだけを書き換えられないという同じ問題が残っている。 こちらはch32funのexamples_usbのbootloaderを使うと良いだろうと考えていたので、試してみた。 それをch32x035用にコンパイルして、WCH-LinkEで接続して以下のコマンドを実行すると、ch32x035のbootloader領域を書き換えることができる。

sudo ./minichlink -w bootloader.bin bootloader

すると、bootモードで立ち上げると、新たなbootloaderが立ち上がり、アドレスを指定してminichlinkでコードを書き込めるようになる。 但し、そのアドレスはflashの消去の単位である256の倍数とする必要がある。

mrblibとTaskとFloatを無効にした状態で、現在のmruby/cを動かす本体のバイナリのサイズは57kよりも僅かに小さい。 mrbを57kのところに置くようにプログラムを書き換えて、make buildするとバイナリができる。 それを書き込むには、bootモードにして、以下のコマンドを使えば良い。

sudo ../minichlink -w ch32x035.bin flash

そして、以下のようにmrbコードのみを書き換えることもできる。

sudo ../minichlink -w temp.mrb flash+$((1024*57)) -b

最初にWCH-LinkEを使わないといけないのが面倒だが、一旦bootloaderとメインのバイナリを書き込んだら、その後はUSBからmrbコードだけを書き換えればよいので、mruby/cの運用が楽になった。 minichlinkはそのままだと32k以上のflashの消去に失敗するだろうが、2026/8/20のブログの追記のように変更すれば、問題は起こらなかった。 ch32x035については、ほとんど使わないmethodを消すなど、使い易くするための様々な工夫がまだ考えられるが、mrbコードのみの書き換えが出来るようになったのは大きいだろう。

また、このbootloaderが気に入らなければ、unbrickすると、元のbootloaderに戻るらしい。 これは試してはいないが、元に戻せるのなら、安心してbootloaderを書き換えることができるだろう。 しかし、全体を一括してしか書き換えられないwchispよりも、部分的に書き換えのできるminichlinkがうまく動けば、そのままで通常は問題ないだろう。

Read more...

ESP32とESP8266のDeep SleepとLight Sleep

ESP32やESP8266などのマイコンは、WiFiにアクセスできるのだが、消費電力が大きいという欠点がある。 これを常時稼動させておくと、発熱もするし、電池などでの稼動には適さない。 時々、何かをモニタしたり、WiFiに繋いだりするのであれば、それ以外の時には、消費電力を抑えた状態にしておけば良く、そのような機能がある。

最も電力を小さくできるのは、Deep Sleepであり、電池駆動の場合には、これを使うと良い。 これを実行すると、メモリの内容なども消えて、復帰したときには、プログラムは最初からもう一度実行される。 多少電力は消費しても、メモリやレジスタの内容を保持したいときには、Light Sleepを使うと良い。 様々な機能をoffにすることによって、消費電力を抑えるが、復帰した時には、プログラムの続きから実行できる。

ArduinoでESP32のsleepを使うのは、非常に簡単である。 以下のようにヘッダファイルをincludeして、sleepする時間をマイクロ秒単位で指定して、sleepの開始をすれば良い。

#include <esp_sleep.h>
esp_sleep_enable_timer_wakeup(5 * 1000000ULL); //us
esp_light_sleep_start();

Deep Sleepの場合には、開始命令のlightをdeepにすれば良い。 ただし、それ以降の命令は実行されることは無く、setupに戻るのが、Light Sleepとの違いである。

一方、ESP8266では、少し複雑である。 Deep Sleepはプログラムは以下のようにするだけだが、RSTをD0ピンに繋いでおかないと、自動で復帰ができないので、注意が必要である。

ESP.deepSleep(5 * 1000000ULL); //us

また、Light SleepはForce Power Savingという機能を使って実現しているので、設定が少し面倒である。 以下のようにすると、消費電力が小さいLight Sleepが実行できる。

extern "C" {
  #include "user_interface.h"
}
  uint32_t sleep_ms;
  sleep_ms=sleep_s*1000;
  wifi_station_disconnect();
  wifi_set_opmode(NULL_MODE);
  wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
  wifi_fpm_open();
  wifi_fpm_set_wakeup_cb(NULL);
  wifi_fpm_do_sleep(sleep_ms * 1000);
  delay(sleep_ms + 1);
  wifi_fpm_do_wakeup();
  wifi_fpm_close();
  wifi_set_opmode(STATION_MODE);

これらの機能もmruby/cに組込もうと思う。 これで、ESP32やESP8266でmruby/cから使いたいと思っていた機能がほぼそろったことになる。 ESP32ではBlueToothも使えるけど、今のところ使う予定は無いので、まだしばらくは放置で良いかな。 ESP32上のmicropythonで動かしていたプログラムを、mruby/cに置き換えようかな。

Read more...

VirtualPC上でWin2k

古いPC上のWindowsで動いているシステムを、新しいPCで動かそうと思い、久々にWindowsをいじったが、やはり面倒なOSだと感じる。 最初はLinuxからWineで動かそうと思ったが、癖のありそうなソフトだったので、VirtualPCでWindowsを立ち上げて動かすことにした。 以前、WindowsNTで動いているシステムを、Debian上のVirtualBoxで動かしたことがあるが、その時はレジストリをいじらないといけないので、それなりに苦労したが、なんとかうまく動かすことに成功した。 今回は、より新しいWindows2000だったので、それほど大変では無いと思ったが、Windowsに苦しめられている。 Linux上で動かすVirtualPCとしては、qemuが良いということだったので、まずはqemuで試してみた。 qemuのインストールはaptを使えば問題無くできたが、使い方がVirtualBoxとは違っていたので、いくつかの点で少し戸惑った。 まず、VirtualMachineを作るときにCD-ROMのisoを要求されたが、おそらくOSによって仕様を調整するためであろう。 また、VirtualPCの画面と設定画面が切り替えられるようになっているのだが、ディスクの入れ替えは、設定画面に行かないといけないことに、なかなか気付けなかった。 うまく行かなかったのが、画面の解像度の設定である。 OSが古過ぎるために、ディスプレイのドライバーが対応していないようなのである。 VBEMPというドライバーや、Bochs VBEドライバーを試したがうまく行かなくて、ディスプレイの設定は諦めた。 一方、目的であるシステムは無事に動き、VirtualPCは有効であることが分かった。 そこで、Windows11上でVirtualPCを立ち上げて、同じシステムを動かそうとしたら、結局敗退した。 WindowsではVirtualBoxが簡単だということで、それをインストールしたが、さらにそこにWindows2000を入れるところまでは問題無くできた。 しかし、PCに接続したUSB-serialをVirtualPCのシリアルとして認識させようとしたら、なぜか立ち上がらなくなってしまった。 幾つか設定をいじったが、症状が変らないので、ホストPCとしてWindowsを使うのは適切では無いという結論に達した。 そこで、Linux上でVirtualBoxにWindows2000を立ち上げて、そこでシステムを運用することにした。 Debian13にVirtualBoxをインストールする方法は、大きく分けて二通りあり、Oracleのレポジトリを利用する方法と、Debianのfasttrackを利用する方法である。 後者の方がより安定しているらしいので、そちらを試してみることにした。 まず、backportsを有効にするために、/etc/apt/sources.listに以下の行を加えて、apt updateする。 deb http://deb.debian.org/debian/ trixie-backports main contrib non-free non-free-firmware そして、fasttrack-archive-keyringをインストールする。 fasttrack用に、以下の内容の/etc/apt/sources.list.d/fasttrack.sourcesというファイルを作成してからapt updateする。 Types: deb URIs: https://fasttrack.debian.net/debian-fasttrack Suites: trixie-fasttrack trixie-backports-staging Components: main contrib Signed-By: /usr/share/keyrings/fasttrack-archive-keyring.gpg カーネルヘッダーを入れてからVirtualBoxをインストールして、ユーザーをvboxusersグループとシリアルを使うためにdialoutグループに登録して、再起動する。 sudo apt install linux-headers-$(uname -r) build-essential dkms sudo apt install virtualbox sudo adduser $USER vboxusers sudo adduser $USER dialout sudo reboot ディスプレイの解像度を変更するためには、古いGuest Additionsをインストールすると良いらしい。 私の場合には、 versionが5.2.44のVBoxGuestAddisionsのisoを使った。 そして、フロッピーディスクを使うために、ストーリッジのところにフロッピーのコントローラーとドライブを追加した。 シリアルも/dev/ttyUSB0などをホストデバイスで指定して作った。 Homeキーがデフォルトだと右のctrlであったが、環境設定の高度の入力の仮想マシンから、左ctrl+左altに変更した。
Read more...

古いFD

古いPCをいじる必要があり、かなり久々に3.5インチのフロッピーディスクを使った。 まずは、ディスクを探すところから始める必要があったが、家のダンボールをあさって出て来たものをつかってみようとしたが、エラーが出て使えなかった。 フォーマットもしてみたが、駄目だった。 最初はWindowでフォーマットしたけれど、失敗したという情報しか得られなかったので、Linuxでフォーマットしたら、どうやら最初のセクタの部分がいかれているらしく、その場合はまったく使えないようだ。 結局、三枚試して全滅だった。 FDは沢山もっているが、そのほとんどが駄目になっているかもしれない。

FDDからスマートメディアに書き込むアダプタを以前もらったので、保有しているが、そういうのを使うのも方法かもしれない。 FDDのかわりに取り付けてSDなどに書き込めるFDDエミュレーターなどを使うことも考えても良いかもしれない。 arduinoで作ってみようかな。

あるCDRのデータを吸い出そうとしたら、半分ぐらいで止まってしまった。 CDRの多くも使えなくなっている可能性がある気がする。 長期保存するデータをどこに保存すべきなのかは、難しいところである。 HDDがましだとは思っているけど、古いノートを立ち上げようとしたら、そのHDDの一部が動かなくなっていたし、HDDも安心ではない。 SDDは長期保存には向かないらしいし、難しいなあ。 調べてみると、CD-RやDVD-Rが比較的寿命が長いらしい。 CD-Rなどは駄目になったことが何度もあるんだけどな。

Read more...

comuのbootloader

mruby/cをCH32V203用にカスタマイズしているが、現状では幾つかの課題があった。 その最も重要なものは、USBからはmrbcだけを書き換えることが出来無いことである。 CH32V203はUSBにハードウェアで対応でき、内蔵のブートローダーを立ち上げると、USBからflashを書き換えることができる。 しかし、その仕様ではflashの先頭から書き換えるようになっている。 一方、CH32V203用のmruby/cでは、flashの先頭にはmruby/cを動かすためのコードがあり、その後ろにユーザー用のmrbcコードを配置するようにしている。 mruby/cを使う際には、このmrbcの部分だけを書き換えることが出来れば、便利である。 SWDで接続している場合には、minichlinkを使って、アドレスを指定すればmrbcのみを書き換えることができる。 しかし、USB接続の場合にはその仕様のためにこれが出来無いのである。

ch32funのUSBDのコードが最近修正されたことに先日のブログで触れたが、いろいろと調べていたら、その修正を提案した人が作っているcomuというプロジェクトのことを知った。 comuというのは、CH32V203を使ってUSB-Aポートに収まるような小型のマイコンボードであり、それ用のプログラムなどが開発されている。 その中では、CH32V203用のブートローダーが公開されていて、これを応用するとmrbcのみの書き換えができるのではと思い、試してみた。

comuのブートローダーは、flashの先頭の2kの領域に書き込まれて、USBを介してminichlinkを用いてflashへの書き込みなどを可能にする。 ユーザーのコードはアドレスが2k以降の領域に書き込むことになるので、それだけ使えるflashの容量は減るが、USB用のbootloaderで2kというのは、かなり小さい部類に入る。 CH32V203用のmruby/cのメインのコードは、幸い62kより僅かに小さいぐらいの大きさなので、comuのブートローダーを丁度組み込める。

comuのブートローダーはch32funでコンパイルして、wchispで書き込めば良い。 すると、起動直後の5秒間はそのブートローダーが動き、その間はminichlinkからのアクセスが可能となるが、何も無ければそのままユーザーコードに移行する。 ユーザー用のプログラムの例は、comuのexampleにあるが、2k以降に配置するという指定のためにcomu.ldを使うようになっている。 mrubyのコードは、拡張flashを使うように修正したcomu.ldを組み込んでコンパイルて、minichlinkを使って2k以降と拡張flashにアドレスを指定して書き込めばよいはずである。 ところが、なかなかうまく行かずに、かなり苦労した。

comuの例にあるblinkやprintfは、使っているボード用に少し修正してコンパイルして書き込んだら、うまく動いた。 ちなみに、printfはUSBを使うものなので、ブートローダーの後でも、ユーザーコードでUSBを使えることを示している。 しかし、mruby/cを使おうとすると、全く動かない。 ブートローダーが使った機能やメモリの初期化が完全では無いために動かないのかと予想して、ブートローダーのソースを改造したりもしたが、それらはうまく行かなかった。 書き込みがすぐに終るので、書き込みがうまく行っていないかも知れないと思って、プログラムを書き込んだ後でその領域を読み出して、書き込んだファイルと内容を比較すると、256バイト目以降が一致していないことが判明した。 flashに書き込む際には、その直前にflashの消去が行われる。 消去がうまく行って書き込みが出来ていない場合には書き込めていない部分は規則的なデータになるはずだが、消去がうまく行かずに書き込みは出来ている場合にはその部分のデータは不規則になる。 また、flashの消去はch32v203では256バイト単位で行われるらしく、症状は消去が出来ていないことを示していた。 そこで、wchispでflash全体を消去してから、wchispでブートローダーを書き込んで、minichlinkでmruby/cのコードを書き込んで、その内容を読み出すと、データは一致して、うまく書き込めていることが分かり、mrbのコードが動き始めた。 flashの消去には或る程度の時間がかかるが、消去する領域が大きくなると、消去が終わる前に書き込みが始まり、データが化けてしまっていたのであろう。 書き込むプログラムが小さい時には、消去もすぐに終わるので、問題は起きないのである。 確実なのは、bootloaderとメインのコードを、サイズを調整して結合して、それをwchispで書き込む方法である。

私の目的としては、上記のようにしてmruby/cを一度書き込めば、その後はmrbを書き換えるだけであり、mrbは小さいことが多いので、それほど問題にならないかも知れない。 今のところ、mrbの書き込みで失敗は無いように思われる。 しかし、非常に気持が悪いので、どのように変更すれば良いかを検証してみた。 まず、boot_usercode_addressなどの変数はch32funのbootloaderで使われていたが、comuではユーザーコードへの移行の仕方が異っているために、使われていないようだったので、それらを取り除いたが、これは機能には影響しないはずである。 ブートローダーでは、minichlinkから送られてきたデータをメモリ上にコピーして、それをプログラムとして実行するということが行われている。 メモリ上のコードに実行を移す前後に、USBがそれぞれnakとackを返すように設定するルーチンを加えて、実行が終わるまではUSBから新たなデータが入って来ないようにしても、あまりうまく行かない。 また、minichlinkに待ち時間を入れたりもしたが、変化は見られない。 何が起きているのだろう。 comuの作者がこのバグに気が付いて、改善してくれるのを待つことにしよう。

これで、このブートローダーを使って、当初の課題であったmrbcのみの書き換えができる仕組みが一応出来上がった。 mrbの書き換えがうまく行かない症状が出る可能性が残っているので、注意して使わないといけないが。

2026/9/7追記 書き込むサイズが大きいときに、flashの消去がうまくいかないのは、comuのbootloaderよりも、minichlinkの問題が大きいことが分かった。 32k以上のflashを消去するときには、符号付き16bitとして解釈するとサイズが負の数になるため、256バイトを一度消去したら、もう最後まで行ったと勘違いしてしまっていたので、これを符号無し16bitと解釈させるようにした。 具体的には、pgm-b003fun.cのerase_block_binの14バイト目を0x16から0x56に変更すれば良い。 このminichlinkの問題は、flashが32k以上あるch32x035やch32v006などでも起きそうなので、修正されると良いけど。 そのままだと、Timeoutエラーが出たので、comuの方はscratchpadexecの実行の前後に、以下のそれぞれのコードを入れて、flashの消去中にUSBのアクセスを受け付けないようにしたら、64k足らずまではうまく消去できるようになった。

USBD->CNTR &= ~USBD_CTRM;
USBD->CNTR |= USBD_CTRM;

64k丁度のときには16bitでは表せないので、65535を指定するようにすれば良いはずだ。 それ以上だと、機械語部分を手直ししないといけないので、ちょっと面倒になる。

Read more...

ch32funのUSBDの更新

CH32用にmruby/cを使えるようにするために、ch32funを利用している。 ch32funは、それなりの頻度で更新が行われており、更新があった際にはそれに対応してコードを修正する必要がある場合もある。 先日、USBFSのコードが更新され、それに対応するためにCH32X035用のmruby/cのコードを一部修正した。 そして、USBDのコードも更新の提案があったので、更新されるのを待っていたが、つい最近更新されたので、CH32V203用のmruby/cのコードも修正した。 これらの修正によって、USBFSとUSBDの扱い方にほとんど差が無くなって、使い易くなったように感じる。 また、USBDでもHIDのコードが扱えるようになった。

ch32funを使ったmruby/cについての情報を、私のホームページで公開しているが、使うch32funを一旦固定しようと思う。 githubで特定の時点でのファイルをダウンロードする方法を知らなかったが、少し調べたらやり方が分かった。 githubのページの右側に時計マークとcommitの数が表示されているので、そこを押すと更新履歴が表示される。 どの時点の更新を参照するかを考えて、その右の<>マークを押すと、その時点のファイルが表示される。 そこで、Code-Download ZIPとすれば良いのである。

Read more...

mruby/cで複数のtask

mruby/cでは複数のtaskを走らせることができる。 その機能を使ったことはこれまで無かったが、折角使えるので使い方を考えてみた。 私が構築しているシステムでは、mrbのコードはflashの特定のアドレス以降に書いておいて、そこを書き換えることによって、mrbだけを入れ替えることができるようにしている。 そこに複数のmrbコードを並べて、それらを読み込んで実行できるようにすれば良いだろう。 一つのmrbのすぐ後に、またmrbのコードがある場合には、それを次のtaskとして登録するようにするのである。 そのための関数は以下のように定義することができる。

void load_continuous_tasks(const uint8_t *start_addr) {
  for (int i = 0; MAX_VM_COUNT > i ; i++) {
    if (memcmp(start_addr, "RITE", 4) != 0) break;
    mrbc_create_task(start_addr, 0);
    start_addr += // big endian
      ((uint32_t)start_addr[8]  << 24) |
      ((uint32_t)start_addr[9]  << 16) |
      ((uint32_t)start_addr[10] <<  8) |
      ((uint32_t)start_addr[11] );
  }
}

試しに、GPIOを使うmrbと、printを使うmrbを同時に走らせてみたら、問題なく動いた。 変数などはそれぞれのtaskで独立のようだ。 グローバル変数を使うと、taskを越えて使えるそうだが、まだ試していない。 マイコンでマルチタスクを使ったことは無いので、どう使うと便利なのかも考えてみたい。

Read more...

基板設計の半自動化

最近はマイコンとして、安価なCH32シリーズに注目している。 中でも比較的性能の高いCH32V203を使う機会が多くなってきている。 しかし、CH32V203のマイコンボードで入手しやすいblue pill+は比較的大きいので、pro micro程度の小型のものを欲しいと思っている。 そのようなものも販売されているが、部品の価格と比べて、かなり高額なものしか見当たらない。 そこで、自分でボードを設計しようかとも思うのだが、いくつか課題がある。 一つは回路を設計するのが面倒であるという点であり、もう一つはボードを注文すると個人で使うには過大な枚数になってしまうという点である。 設計が面倒だという点については、AIと配線の自動化を使うと解決できるということを知ったので、これを試してみた。

まず、AIにボードのピンとマイコンのピンの割り当てを考えてもらう。 そして、それに基いてkicad用のネットリストを作ってもらう。 pro microの端子にSWD用の端子を付け加えると、32pinのCH32V203K8T6の場合、外部振動子を除くと、一つだけピンが余るので、それをLEDに割り当てることができた。 最初は、kicadで読み込むとエラーは出ないが何も表示されなくて苦労したが、version Eということを指定して作ってもらったら、うまく行った。 必要なら、部品のパッケージなどを変更するように指定すると良いだろう。

それをkicadのpcb editorでファイル-インポート-ネットリストとして読み込むと部品が表示される。 部品の配置は、人間がやらないといけない。 配置も自由度がそれなりにあり、悩むところなのだが、マイコンのICを配線が単純になるような向きに置くことが最も重要で、それ以外の部品は、使い易い位置にすれば良いだろう。 USBの二本のデータ線は、同じ長さにしなければならないので、他の配線の邪魔にならないように意識しながら、あらかじめ配線しておく。 また、忘れないように、基板の外形を描く。 そして、Specctra DSN形式でエクスポートする。

次に、Freeroutingを実行して、DSNファイルを開いて、自動配線をする。 配線が無事に終ったら、ses形式でセーブする。 うまく配線が出来無い場合には、kicadに戻って部品の配置やUSBの配線などを改良してから、自動配線を試みる。 sesファイルをkicadにSpecctraセッションとしてインポートすると、配線が取り込まれる。 無駄な部分を修正するために、ツール-配線とビアをクリーンアップとする。 配線が不用意に蛇行したりすることもあるので、配線を選択してDを押してから、場所を調整すると良い。 これで、ボードの設計は終了である。

確かに、全部自分でやるよりは、かなり楽になった。 部品の配置は考えないといけないが、これは人によって好みが異なるので、完全な自動化は難しいだろう。 このやり方は初めてだったので、今回は多少の試行錯誤が必要だったが、次に似たような基板を設計する時には、比較的短時間でできると思う。 次に問題なのは、このボードのPCBを注文するかどうかである。

Read more...

ESP32用のmruby/c

ESP32用のmruby/cとしては,FemtoRubyを使ったR2P2があるが,PWMを使おうとしたらエラーが出たので,自分でもESP32用にmruby/cの環境を作ってみることにした. 自分のブログを見直してみたら,私がmruby/cを最初に使ってみたのはESP32だった. そのときには,Arduinoを使ってmrbのコードをソースに埋め込んでコンパイルしていた. そのため,rubyのコードを変えると,mrbを作って,それを埋め込んでArduinoでコンパイルして,マイコンに書き込むという作業をする必要があり,時間もかかるし面倒だった. これまで,何種類かのマイコン用にmruby/cの環境を作ってきた. それらにおいては,mrbファイルはflashに書き込んで,そのアドレスを使って直接mrbコードを読み出して使っている. しかし,同じことをESP32でやろうとしたら,そのままではうまく行かなかった. ESP32では,フラッシュに直接アドレスが割り当てられるわけでは無く,その内容が使われるときにcacheにコピーされるので,単にflashに書き込むだけでは駄目である. また,mrbコードをプログラムに埋め込んでおいて,その部分だけを書き換えようとしても,チェックデジットや誤り訂正などがあり,その内容を不用意に書き換えるとエラーが出てしまった. いろいろ試行錯誤した結果,使っていないflash領域にmrbを書き込んで,それをメモリに割り当てることで,ほぼ同じ感覚で使えるようになった. まず,私のホームページに載っているようにArduino用のmruby/cをセットアップする. そして,Arduinoで以下のコードをコンパイルして,ESP32に書き込む. #include "spi_flash_mmap.h" #include <mrubyc.h> #define MEMORY_SIZE (1024*30) static uint8_t memory_pool[MEMORY_SIZE]; #define FLASH_CODE 0x10000 + 0x120000 //esp32 spi_flash_mmap_handle_t s_data_map_handle; const uint8_t *flash_code = NULL; int hal_write(int fd, const void *buf, int nbytes) { Serial.write((char*)buf,nbytes); return (nbytes); } int hal_flush(int fd) { return 0; } void hal_abort(const char *s){} unsigned char hal_read(int fd) { while (Serial.available() == 0); return Serial.read(); } void setup() { uint32_t data_flash_offset = FLASH_CODE; // running->address + 1114112; uint32_t map_size = 0x10000; // 64KB esp_err_t err = spi_flash_mmap( data_flash_offset, map_size, SPI_FLASH_MMAP_DATA, // as rodata (const void **)&flash_code, &s_data_map_handle); Serial.
Read more...