勉強会で見せていただいて気になっていた M5Stack を買ってみました。 🙂
内蔵スピーカーもついていますので hello world がてら、 PSG 音源をエミュレーションしてみようと始めたところ、なかなか鳴ってくれなくて苦労しました…
「エンディングI/SORCERIAN/Copyright© Nihon Falcom Corporation」
追記。FM音源 + YM2612 の DAC もエミュレートできて無事メガドライブ(GENESIS/MEGADRIVE)音源になりました 😀
勢いがあるうちに忘れないようメモしておきます。
ソースは github で公開しています。(力尽きたので不出来、手抜きの部分は大目に見てください)
https://github.com/h1romas4/m5stack-synth-emulation
GENESIS/MEGADRIVE(YM2612+SN76496) VGM player on ESP32/M5Stack
開発環境
PSG エミュレーターを移植するのに gcc ツールチェインを使いたかったので開発環境は(Arduino ではなく) ESP-IDF を使いました。 リンク先の通りに SDK ファイルを展開して、 $IDF_PATH を設定してあげれば OK です。
今回は Linux を使ってやりましたが、Windows 10 の場合は WSL を使うと楽かもしれません。(未確認)
M5Stack ESP-IDF 用の雛形が github に提供されていますので、手順の通り git clone して make menuconfig してシリアルを設定して、make flash monitor すれば hello world できると思います。
https://github.com/m5stack/M5Stack-IDF
To use as a M5Stack component of ESP-IDF
ちなみに monitor の抜け方は ctrl + [ です。
自分がやったときは Makefile にバグがありいきなり、make flash できないという不具合に遭遇しています。
Detected overlap at address: 0xe000 for file:
これは Makefile にパッチする次のワークアラウンドで解消できました。(m5stack リポジトリは既に修正済みだと思います)
(esp-idf) make flash fails due to the overlapping of offset addresses #1724
https://github.com/espressif/arduino-esp32/issues/1724
static heap の上限
static heap をたくさん使おうとすると
region `dram0_0_seg' overflowed by 67900 bytes
とリンクエラーになります。 がびーん。
どうやら static heap にサイズ上限が決められているようなので、static で取得するのをやめて動的に heap_caps_malloc() して回避しました。
build/ 下に app-template.map というファイルができていて、static heap のサイズが分かります。(この場合 DECAY_TO_ATTACK 変数に 0x4000 取られています)
.bss._ZL15DECAY_TO_ATTACK 0x000000003ffbd1cc 0x4000 /home/..../libsynth.a(ym2612.o)
また、make size すると次のように現在のメモリーサイズが表示できます。
Total sizes: DRAM .data size: 6488 bytes DRAM .bss size: 118632 bytes Used static DRAM: 125120 bytes ( 55616 available, 69.2% used) Used static IRAM: 36140 bytes ( 94932 available, 27.6% used) Flash code: 181978 bytes Flash rodata: 64104 bytes Total image size:~ 288710 bytes (.bin may be padded larger)
static heap を回避しても malloc で失敗することもありますので、あちこち移動させてうまいことメモリーに載せていきます。 なお、heap_caps_get_free_size() で動作中の残りメモリーを知ることができるようです。
内蔵 DAC の鳴らし方
なかなか鳴らなくて PSG エミュレーションが悪いのか DAC の操作が悪いのかで苦労しました…。
I2S 経由の DAC は次のようなイメージで鳴ると思います。
i2s_config_t i2s_config = { .mode = static_cast(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), .sample_rate = SAMPLING_RATE, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = static_cast(I2S_COMM_FORMAT_I2S_MSB), .intr_alloc_flags = 0, .dma_buf_count = 16, .dma_buf_len = 512, .use_apll = false, .tx_desc_auto_clear = true, // esp-idf 3.2 で追加 .fixed_mclk = 0 }; i2s_driver_install(i2s_num, &i2s_config, 0, NULL); i2s_set_pin(i2s_num, NULL); size_t bytes_written = 0; uint16_t buf[2]; buf[0] = 0x0000; // right sample buf[1] = 0x0000; // left sample i2s_write((i2s_port_t)i2s_num, buf, sizeof(uint16_t) * 2, &bytes_written, portMAX_DELAY);
buf には符号付き符号なしリトルエンディアンのステレオサンプリングを渡します。(内蔵 DAC の場合は符号なしでした… どうにも音が割れると思ったら…)上のサンプルソースは 1フレームですが、この config で 4096 フレーム * 2(ステレオ)が一気に渡せました。
flash への任意データーの書き込み
partitions.csv ファイルをつくり make menuconfig (sdkconfig) で指定してあげることにより、内蔵の flash メモリーの任意の位置に esptool.py でバイナリを書き込みプログラムから read できます。
esptool.py --chip esp32 --port "/dev/ttyUSB0" --baud 115200 write_flash -fs 4MB 0x211000 "バイナリファイル"
プログラムから読み込むときは、
esp_partition_find_first() して esp_partition_mmap() でポインターがもらえます。便利。 🙂
その他
- 普通に printf すると make flash monitor するだけで実行してコンソール出力されるので嬉しい。
- プログラムが大きくなったせいか途中から “Make error of dangerous relocation” と怒られてリンクできなくなりました。 CFLAGS に -mlongcalls をつけることで解消しました。
- M5STACK の内蔵スピーカーはそこそこ音が割れます。
PSG くらいがちょうど良さそうです。 (実はがんばって FM音源も移植したのですが、いい音で鳴りませんでした… さすが ESP32 だけあって処理速度は十分です。 ソースに残骸がありますので、気になる方は鳴らしてみてください…)- FM音源(YM2612)も鳴らすことができました!
- YM2612 + SN76489 + YM2612 の内蔵 PCM をエミュレートしたら CPU の 1コアを使い切っているようです。
以前から充電できるポケステがあったらいいのになぁと思っていたのですが、ちょうどでてきてくれた M5Stack さん。 久しぶりに夢中になってしまいました。
今回の hello world で一通りの困りそうなことは踏んだ気がしますので、引き続き何か作っていきたいと思います。 🙂