秋月電子で売ってる40円のマイコン(CH32V003J4M6)がTwitterで話題になっていてちょっと気になっていたのですが、かんぱぱさん(@kanpapa)のブログにArduino IDEでの使い方がまとめられていて簡単に使えそうだったので試しに買ってみることにしました。
40円は税込価格で本体は37円。1個づつ個別包装です。袋とシリカゲルで数円分ぐらいかかってるのではないだろうか。書き込みにはWCH−LinkEエミュレーターなるものが必要とのことなので別途購入しましたが、これも750円と安価です。
スペックを見ると、
- プログラムメモリ:16kB
- RAM:2KB
とあります。このくらいあればBASICインタプリタが動くのではと思い、試してみることにしました。
とりあえず端末と通信できないことにはどうにもならないので、まずは通信周りについて調査。WCH-LinkEエミュレータにTX、RXなる端子があるのでこれを使えばUART通信ができるのかな?と思ってググってみたところ、こちらのブログとGitHubに関連情報を見つけました。
これによると、TXはPD_5=Pin 8、RXはPD_6=Pin 1のようです。
WCH-LinkEのTXをCH32のRX(Pin 1)、WCH-LinkEのRXをCH32のTX(Pin 8)に継げます。(ややこしいです。いつも混乱します。)
Pin 8はプログラム用の信号入力SWDIOも継がっているので共用になります。そのせいでトラブル発生。プログラムを書き替えようとしても継がらなくなってしまいました。
そういえば秋月のFAQに何か書いてあったような気がします。
32ビットRISC-Vマイコン CH32V003J4M6の質問と回答
なるほど。でもこれだけのためにMounRiver Studioを使うのは大袈裟だなあと思っていたところ、WCH-LinkUtilityにもこの機能があることを発見。こちらを使って無事クリアすることができました。
しかしすぐには動いてくれず、CH32→PCは継がるのに、PC→CH32の通信が出来ないという状態。原因を見つけるのに結構苦労したのですが、Serial.available()が常に-1なのが原因でした。RXにデータが来てるかどうかのチェックを、
if(Serial.available() > 0){ c = Serial.read(); }
ではなく、
c = Serial.read(); if(c > 0){ ... }
とすることで通信できるようになりました。Arduino用の環境がCH32V003F4用に作られているせいなのかもしれませんがよくわかりません。
ともかくUARTで通信ができるようになったので、いよいよBASICインタプリタの実装に移ります。1から作るのは大変なので何か使えるものはないかと探してみたところ、電脳伝説さん(@vintagechips)の著書「タイニーBASICをCで書く」に掲載されている豊四季Tiny BASICがコンパクトで良さそうだったので試してみることにしました。ソースは本に記載されているサイトからダウンロードしたのですが、GitHubにほぼ同じものが公開されていました。
Arduino用に作られているのでそのままコンパイル可能ですが、プログラム領域256byteではASCIIART.BASが入らないので512に変更しました。
#define SIZE_LIST 512 //List buffer size
最適化オプションが"Smallest (-Os default)"だと2kBほどサイズオーバーになり、"smallest (-Os) with LTO"だと16kB以内に収まりました。しかし、with LTOの方だと通信周りがちゃんと動かないようで、最初の1文字"T"が出て止まってしまいました。通信周りを直すか、別の開発環境を使えばうまくいくような気もしますが、とりあえずASCIIART.BASを走らせることを優先しようと思い、インタプリタの機能を削って小さくすることにしました。
やったことは、
- エラーメッセージの文字数削減
- ASCIIART.BASで使っていない機能を削る
です。
"2."の方はかなり乱暴で、配列、INPUT、GOSUB、RETURN、STOP、REM、RND、ABSを削りました。
これでやっと、
Sketch uses 16184 bytes (98%) of program storage space. Maximum is 16384 bytes.
Global variables use 1512 bytes (73%) of dynamic memory, leaving 536 bytes for local variables. Maximum is 2048 bytes.
に収まって無事起動。Serial.read, Serial.available周りに修正が必要かと思っていたのですが何も変更せずに動作しました。 ASCIIART.BASの方は、以前にFairchild F8 Family (F3850)とZilog Z8671 BASIC/Debug用に作った整数型BASIC用のプログラムをくっつけて作りました。
10 F=50
20 FOR Y=-12 TO 12
30 FOR X=-39 TO 39
40 C=X*229/100
50 D=Y*416/100
60 A=C; B=D
70 I=0
90 Q=B/F; S=B-Q*F
100 T=(A*A-B*B)/F+C
110 B=2*(A*Q+A*S/F)+D
120 A=T
130 P=A/F; Q=B/F
140 IF (P*P+Q*Q)>4 GOTO 200
150 I=I+1;IF I<=15 GOTO 90
160 PRINT " ",
170 GOTO 300
200 IF I<10 PRINT I,; GOTO 300
210 IF I=10 PRINT "A",; GOTO 300
220 IF I=11 PRINT "B",; GOTO 300
230 IF I=12 PRINT "C",; GOTO 300
240 IF I=13 PRINT "D",; GOTO 300
250 IF I=14 PRINT "E",; GOTO 300
260 IF I=15 PRINT "F",
300 NEXT X
310 PRINT
320 NEXT Y
動作している様子です。8秒です。さすが21世紀のマイコンは速いですね。
今回は動作させることを最優先にしたので機能を削るという乱暴な方法で実装しましたが、おそらくライブラリが余計なメモリを食っているような気がするので、開発環境を精査すれば16KBもあればフルセットで実装できると思います。
※追記(2023/6/30)
Serial関連のライブラリがかなりメモリを食ってる感じなのでそこを自分で書き直すのが良さそうかも。
※追記(2023/7/1)
メーカー推奨の開発環境MounRiver Studioだとフルセットでもオブジェクトサイズ9KB以下に収まりました。
※コメント投稿者のブログIDはブログ作成者のみに通知されます