YM2612 + SN76489 メガドライブ音源を ESP32 で鳴らす (クロックジェネレーター編)

すごいぞメガドライブミニ!(収録ソフト2回目発表後)

というわけで「YM2612 + SN76489 メガドライブ音源を ESP32 で鳴らす (クロックジェネレーター編)」です。この記事で使われているソースコードや結線などは github で公開しています。

https://github.com/h1romas4/esp32-genesis-player

esp32-genesis-player (work in progress)

メガドライブの音源となっています YM2612 と SN76489 はそれぞれ 7.670453MHz と 3.579545MHz で動いていますが、3.579545MHz のクリスタルオシレーターの入手は容易であるものの、7.670453MHz みたことない。。

というわけでクロックジェネレーターを使うにあたって起きた顛末です。

LTC6904

最初に試したのは I2C で任意のクロックを設定できる LTC6904 で、YM2612 を鳴らしている海外勢の方々もよく使われているチップです。

LTC6904 データシートおよび製品情報 | アナログ・デバイセズ

シリアル・ポートでプログラム可能な1kHz~68MHz発振器

MSOP パッケージでしたので DIP 変換して動作させています。(老眼には厳しいはんだ付けでした。。。

I2C 的には ADR(4) でアドレスを 1bit 分設定できるので 2つまで同時にぶら下げることができます。 この機能を利用して YM と SN 用でふたつ接続しました。

このチップで最初につまずいたのが I2C による周波数の設定がうまくいったりいかなかったり… 具体的には I2C のコマンドが ACK が返ってこないことがあって原因が分からず。

I2C のプルアップ値などを変えたりいろいろためしていったところ、どうやら発振している間はコマンドが失敗することが多かったので OE(7) を L に落としてからコマンドを送ることで確実に受け付けるようになりました。

次にでた問題は I2C のコマンドは通るものの、クロック周波数が設定値にならないパターンです。

7.67MHz を設定するものの 8MHz くらいになったりで、、これについては最後まで原因不明。。電源を入れたりリセットしたりしているとうまくいくのですが、なにぶん 2こ接続しているので両方がぴたっと揃う確率が低く… テストしすぎて絶対音感に目覚めるところでした(目覚めない)

パスコンなどの入れ方が悪いのか?電源を変更してみよう?などいろいろ試行錯誤したのですが残念ながら安定させることができませんでした。

ちなみに Arduino 向けの LTC6904 ライブラリーを書いている方の ソースコード を見ていたところ次のようなコメントアウトを発見。いったん完全に電源を落としてから 100ms 待つというのが NECESSARY TO USE となっているので、もしかするとちょっと癖があるのかもしれません。

// NECESSARY TO USE
//#define pwrpin PORTC2
//#define gndpin PORTC3
//DDRC |= _BV(pwrpin) | _BV(gndpin);
//PORTC &=~ _BV(gndpin);
//PORTC |=  _BV(pwrpin);
//delay(100); // wait for things to stabilize
//Wire.begin();

Si5351A (AE-Si5351A-B)

次に試したのは秋月さんで売っている AE-Si5351A-B でこちらはうまく動きました。 🙂

LTC6904 同様に I2C で周波数を設定できるクロックジェネレーターになっています。残念ながら I2C アドレスはひとつに固定ですが、3ch 分のクロックを取り出すことができますので、YM と SN で 2ch 使っています。

クロックの設定方法が秋月さんの取扱説明書に記載されていますが、基本は PLL / Multisynth という式の割り算でほしいクロックをつくれば OK です。少数も使えますがなんとなく整数値で次のようにしています。

# 本当は PLL(4725) / Multisynth(616) で正確
YM2612(7.670453MHz)  PLL(675) / Multisynth(88) 
SN76489(3.579545MHz)PLL(630) / Multisynth(176) 

AE-Si5351A-B はリセット後などの操作でも安定して動作させられましたのでこちらを使うことにしました。

動作確認用に簡略化されていますが、LTC6904 と Si5351A とも esp-idf 用のソースコードを github においてあります。

以上、というわけで部品が定まりましたのでいよいよ次回は基板をつくっていきたいと思います。 🙂

関連

ESP32/M5Stack の開発環境構築(esp-idf と Arduino Core)

2020/1/19 更新

ESP32/M5Stack 開発環境構成の整理がてら構築手順をまとめてみました。Arduino IDE は使わずに VS Code でプログラムがかけるところまで書いてみます。

Windows 向けに書いていますが、ツールチェインの導入部分が異なるだけで他の OS でも基本的な考え方や手順は同じです。

ひとつずつ確認しながら進む手順になっていますので、フルコンパイルが何度かかかり少し時間がかかりますがご勘弁を。慣れてきたら飛ばしてやってください。

また、この記事は ESP32/M5Stack 系のツールチェインやライブラリー依存関係の参考にもなるかもしれません。(PlatformIO や Arduino IDE + VS Code Arduino extention などが行っている内部的な動作が分かりますので、コンパイルエラーなどが出たときに対応がしやすくなると思います)

参考リポジトリー

本手順を元につくられた M5Stack サンプルアプリとビルド手順は次の github リポジトリーから参照できます。手順とソースコードの構成を合わせて見ると分かりやすいと思います。

https://github.com/h1romas4/m5stack-synth-emulation

M5Stack Core2 向けの記事

M5Stack Core2 環境構築は、以下の記事を参考にしてください。(本記事の M5Stack Core から使うライブラリーなどが変わっています)

ESP32/M5Stack の開発系の構成

ESP32/M5Stack オフィシャル開発系の構成は次のようになっています。

  • xtensa-esp32 – ツールチェイン(gccクロスコンパイラ) Windows/Linux/macOS 用に分かれています。
  • esp-idf – ESP32 用のフレームワーク(FreeRTOS + ライブラリ+ 転送ツール等)
  • Arduino Core for the ESP32– ESP32 で Arduino 風の関数やコンポーネントを使うライブラリ(要 esp-idf)
  • M5Stack-IDF – M5Stack から提供される esp-idf + Arduino Core + M5用のライブラリー

Arduino Core for the ESP32 に関しては esp-idf の追加コンポーネントのような形となっていますので ESP32 開発で必須なのは xtensa-esp32 と esp-idf です。

拡張として Arduino Core は esp-idf に依存し、さらに M5Stack は Arduino Core に依存しているという構成になります。(といってもプロジェクトテンプレートがうまくできているので導入は簡単です)

バージョン依存

Arduino Core、M5Stack は上位モジュールを参照しているため、バージョンによる依存関係があります。この記事の解説では次のバージョンが導入されます。

esp-idfv3.1.3
Arduino Core1.0.0
M5Stack0.2.1

2020/1 現在の新しいバージョンの組み合わせは以下のようになっているようです。

esp-idfv3.2.3
Arduino Core1.0.4
M5Stack0.2.9

手順でも補足していますが、最新にする場合は esp-idf 及び Arduino Core の git clone 時のバージョンを変更してください。なお、Arduino Core のリリースに、依存する esp-idf のバージョンがかかれています。

本記事に対応した github の参考リポジトリーではビルド環境依存を防ぐため、リポジトリーに esp-idf も submodule で加え、後述する IDF_PATH 環境変数の切り替えをビルド手順に加え、数行のコマンドでビルドできるようにしています。

git clone --recursive https://github.com/h1romas4/m5stack-synth-emulation.git
cd m5stack-synth-emulation
# This repository includes eps-idf v3.2.3
export IDF_PATH=$(pwd)/esp-idf
make

また、Github Actions の機能を使ったスクリプトでビルドしてできたバイナリーを自動的にリリースできるようにしてみました。

ツールチェインの導入

まずコンパイラや周辺ツールを含むツールチェインを導入します。

Windows 上の任意の場所に開発系の作業フォルダーを作成し、Toolchain Setup からダウンロードしたツールチェインの .zip を展開します。

Toolchain Setup

The quick setup is to download the Windows all-in-one toolchain & MSYS2 zip file from dl.espressif.com:

執筆時点での安定版ツールチェインは

esp32_win32_msys2_environment_and_toolchain-20181001.zip

でした。

本手順では作業用ディレクトリを以下のパスにします。

C:\develop\esp

ツールチェインの .zip が展開できたら

C:\develop\esp\msys32

の形にフォルダーを移動後、以下のファイルをダブルクリックしてビルド操作用のターミナルを起動します。(この後はこの窓で作業していきます)

C:\develop\esp\msys32\mingw32.exe

mingw32 が起動したらホームディレクトリに作業用のディレクトリを作成してカレントディレクトリを移しておきます

$ mkdir ~/esp
$ cd ~/esp

なおこれらの Windows 的なファイルの実態は

C:\develop\esp\msys32\home\[ユーザー名]\esp

になります。またツールチェイン(コンパイラ)は

C:\develop\esp\msys32\opt\xtensa-esp32-elf\bin

にあり事前にパスが通っています。

それぞれをエクスプローラーで確認しておきましょう。

esp-idf の導入

引き続き mingw シェル上で git を使い esp-idf を導入します。

Get ESP-IDF

$ cd ~/esp
$ git clone -b v3.1.3 --recursive https://github.com/espressif/esp-idf.git

ディレクトリ esp-idf が作成され esp-idf が格納されます。

git clone で指定している v3.1.3 が esp-idf のバージョンになります。より新しいバージョンを指定するには v3.2.3 などとします。

前述の「バージョン依存」の通り Arduino Core や M5Stack は依存する esp-idf のバージョンが決まっていますので注意します。これらを利用しない場合は任意の最新バージョンの esp-idf を使うことができます。

なお、esp-idf は v3.3 系が LTS(長期サポート版)になっているようです。

ESP-IDF Release v3.3 (LTS)

Version 3.3 is the first ESP-IDF Long Term Support release. ESP-IDF v3.3.x will be supported for 2.5 years, until March 2022.

次に OS の IDF_PATH 環境変数に上記で得られた esp-idf のパスを設定します。(この機能により複数の esp-idf のバージョンを切り替えることができます)

公式では mingw 上のシェル環境に設定することになっていますが、Windows 側で設定した方が後述の VS Code からも見ることができるので、システム詳細設定でユーザ環境設定で IDF_PATH を追加します。

新規ボタンで IDF_PATH を追加し

C:\develop\esp\msys32\home\[ユーザー名]\esp\esp-idf

を設定します。できたらいったん mingw32 ターミナルを exit やウインドウの☒で落として再び、

C:\develop\esp\msys32\mingw32.exe

をダブルクリックして再起動し設定を反映させます。

$ echo $IDF_PATH
# 設定した値が取れれば OK!
C:\develop\esp\msys32\home\hirom\esp\esp-idf

M5Stack の場合はこの後は飛ばし、手順「M5Stack テンプレート のコピー」まで進んでください。

esp-idf 開発の テンプレートとなる hello world ソースファイルをコピー

引き続き mingw32 シェル上で、テンプレートとなるソースファイルをコピーして開発の準備をします。

$ cd ~/esp
$ cp -r $IDF_PATH/examples/get-started/hello_world .
$ cd hello_world
$ make menuconfig

make 設定のメニューが表示されますのであれば必要な設定をします。(ここでは Component Config > ESP32-specific > CPU frequrncy で CPU クロックを 160MHz から 240MHz に変えてみました)

<Exit> で終了させるとプロジェクトフォルダーの sdkconfig ファイルが更新されますのでいったんここでコンパイルしてみます。 sdkconfig 更新後のコンパイルはフルコンパイルになりますので少し時間がかかります。

$ make -j4
# エラーがでなければコンパイル成功
To flash all build output, run 'make flash' or:
python /mnt/c/develop/esp32/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyS3 --baud 115200 --before default_r
eset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 /mnt/c/develop/esp32/hello_world/build/b
ootloader/bootloader.bin 0x10000 /mnt/c/develop/esp32/hello_world/build/hello-world.bin 0x8000 /mnt/c/develop/esp32/hello_world/build/partit
ions_singleapp.bin

また、menuconfig では使うコンポーネントの選択を Component config 項から行うことができます。もしコンパイル時にヘッダーファイルが足りないといったエラーがでた場合は、プログラムで使っているコンポーネントを有効にします。

これらの設定も sdkconfig ファイルに保存されます。

Arduino Core を使わずに esp-idf のみで開発する方はこの手順で終了です。この後は飛ばし、手順「プログラムの転送」まで進みます。

Arduino Core コンポーネントの追加

ESP32 で Arduino Core を使う場合は、プロジェクトに components フォルダーを作成し Arduino Core コンポーネントを追加します。

To use as a component of ESP-IDF

$ cd ~/esp/hello_world
$ mkdir -p components
$ cd components
$ git clone -b 1.0.0 https://github.com/espressif/arduino-esp32.git arduino
$ cd arduino
$ git submodule update --init --recursive
$ cd ../..
$ make menuconfig

git clone で指定している 1.0.0 が Arduino Core for the ESP32 のバージョンになります。例えば esp-idf の導入で v3.2.3 を指定した場合は 1.0.4 などとなります。

もし後述の make が通らないようであれば esp-idf と Arduino Core の依存バージョンが不整合していますので最新の情報を確認してみてください。

Arduino Core コンポーネントを追加すると menuconfig に「Arduino Configuration」が追加されますので「Autostart Arduino setup and loop on boot」を enable にします。プログラムを loop/setup の Arduino 構成でかくことができるようになります。

また、デフォルトの sdkconfig であると Flash の容量が 2M 設定になっているので「Serial flasher config」から「Flash Size」を 4M に変更します

最後に Arduino Core で C++ の例外ハンドリングに依存しているソースがありますので設定を有効化します。「Compiler Options」から「Enable C++ Exceptions」

exit して menuconfig を終了させ sdkconfig を保存します。

Arduino Core を使った場合の main/hello_world_main.cpp は次のようになります。

ESP32 のテンプレートは hello_world_main.c と C になっていますので .cpp にリネームします。実ファイルが

C:\develop\esp\msys32\home\[ユーザ名]\esp\hello_world

にありますので任意の Windows 操作でリネーム、ファイルの編集を行います。

#include "Arduino.h"

void setup() {
    Serial.begin(115200);
}

void loop() {
    Serial.println("loop");
    delay(1000);
}

ソースファイルを変更後 make してみてコンパイルが通れば成功です。(Arduino Core が大きいので初回コンパイルには結構時間がかかります)

$ make clean # 手順の流れで ESP32 でコンパイルしているのでいったんクリーン 
$ make -j4
# エラーがでなければ OK!
To flash all build output, run 'make flash' or:
python /home/hirom/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0xe000 /home/hirom/esp/hello_world/components/arduino/tools/partitions/boot_app0.bin 0x1000 /home/hirom/esp/hello_world/build/bootloader/bootloader.bin 0x10000 /home/hirom/esp/hello_world/build/hello-world.bin 0x8000 /home/hirom/esp/hello_world/build/default.bin

Arduino Code のセットアップはこの手順で終了です。「プログラムの転送 」まで進みます。

M5Stack テンプレートのコピー

M5Stack の場合は、M5 が事前に準備してくれている git リポジトリをクローンすることで Arduino Core と M5Stack-IDF コンポーネントが入った hello world テンプレートプロジェクトを得ることができます。

$ cd ~/esp
$ git clone --recursive https://github.com/m5stack/M5Stack-IDF.git
$ mv M5Stack-IDF/ hello_m5stack # 分りやすい名前にリネーム
$ cd hello_m5stack
$ make -j4
# ウイザードでいろいろ質問がありますが全て ENTER で OK
# コンパイル完了後、エラーがでなければ OK!
To flash all build output, run 'make flash' or:
python /home/hirom/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 /home/hirom/esp/hello_m5stack/components/arduino/tools/partitions/boot_app0.bin 0x1000 /home/hirom/esp/hello_m5stack/build/bootloader/bootloader.bin 0x10000 /home/hirom/esp/hello_m5stack/build/app-template.bin 0x8000 /home/hirom/esp/hello_m5stack/build/default.bin

M5Stack-IDF のリポジトリの Arduino Core 及び M5Stack ライブラリーは最新に追従していないようです。 esp-idf 3.1 より新しい esp-idf を使う場合は、make する前に以下のように依存コンポーネント(Arduino Core 及び M5Stack ライブラリー)をアップデートします。

例えば esp-idf v3.2.3 を指定した場合はそれぞれ 1.0.4 / 0.2.9 となります。

$ cd components/arduino
$ rm -Rf libraries/BLE/
$ git checkout 1.0.4 --recurse-submodules
$ cd ../m5stack/
$ git checkout 0.2.9 --recurse-submodules
$ cd ../..
$ make menuconfig # 質問は全てデフォルト(enter)で設定
# menuconfig から以下を有効化
Component Config -> mbedTLS -> TLS Key Exchange Methods -> 
  [*] Enable pre-shared-key ciphersuits
    [*] Enable PSK based ciphersuite modes
Component Config -> Bluetooth
  [*] Bluetooth
# menuconfig を終了させビルド
$ make -j4

初回コンパイルは少し時間がかかります。

プログラムの転送

コンパイルしたプログラムをマイコンへ転送してみます。

ESP32 Devkit もしくは M5stack を USB で PC に接続すると Windows 10 であれば自動的に認識して COM ポートが増えます。

COM ポート番号を覚えて make menuconfig から Serial flasher config でポートを設定します。

保存したら準備完了です。次のコマンドでプログラムの転送後リセットがかかりプログラムが動作します。 ハローハッピーワールド!

$ make -j4 flash monitor
# フラッシュ後起動すれば OK!
Flashing binaries to serial port COM3 (app at offset 0x10000 )...
esptool.py v2.6
Serial port COM3
Connecting....
Chip is ESP32D0WDQ6 (revision 1)

なお、起動後のシリアルモニターは ctrl + ] で停止することができます。

VS Code によるソースの編集とコンパイル

ここまでの手順でコンパイルとフラッシュができるようになりましたので、これを Visual Studio Code でソースの編集とともにできるようにします。

VS Code インストール後、拡張機能「ms-vscode.cpptools」を導入します。

次に編集したいプロジェクトをフォルダー指定で VS Code で開きます。この手順では M5Stack の手順つくったフォルダーを指定しています。

C:\develop\esp\msys32\home\[ユーザ名]\esp\hello_m5stack

プロジェクトフォルダーに上のように .vscode/settings.json を新規作成しワークスペース設定を次のようにします。

.vscode/settings.json
{
    "terminal.integrated.shell.windows": "C:\\develop\\esp\\msys32\\usr\\bin\\bash.exe",
    "terminal.integrated.env.windows": {
        "MSYSTEM": "MINGW32",
        "CHERE_INVOKING": "1"
    },
    "terminal.integrated.shellArgs.windows": [
        "--login"
    ],
    "C_Cpp.intelliSenseEngine": "Default"
}

設定後、統合ターミナルを開くと mingw32 がターミナルで使えるようになります。初回起動時は変更の可否を聞いてきますので「許可」して、再度統合ターミナルを起動し直してください。

mingw32 窓と同じ操作ができるようになりますので、make flash monitor してコンパイルからのフラッシュが VS Code 上からできるようになります。

次に C/C++ のコード補完ができるように 拡張機能「ms-vscode.cpptools」を設定します。先ほどつくった settings.json と同じ階層に c_cpp_properties.json を新規作成し次のように設定します。

.vscode/c_cpp_properties.json
{
    "configurations": [
        {
            "name": "ESP32",
            "includePath": [
                "${workspaceRoot}/**",
                "${IDF_PATH}/components/**",
                "${IDF_PATH}/../../../../opt/xtensa-esp32-elf/**"
            ],
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

設定後ソースファイル(main/main.cpp)を開くとソースコード補完などができるようになります。

portTICK_PERIOD_MS などのビルド時に生成される一部の define が vscode にエラー申告される場合はソースコード上部に以下のコードをいれると良いでしょう。(__INTELLISENSE__ 定数は vscode のインテリセンス実行時のみ設定されます)

#ifdef __INTELLISENSE__
#include "build/include/sdkconfig.h"
#endif

ここまでで環境構築はおしまいです。

さて、便宜上プロジェクトフォルダーを mingw32 配下

C:\develop\esp\msys32\home\[ユーザー名]\esp

において作業を進めましたが、設定完了後は Windows 上の任意の位置に配置できますのでお好みの場所に移したりコピーしたりしてください。(なお、フォルダーを移した場合はいったん make clean した後 make してください)

ESP32/M5Stack の資料

開発系のオフィシャル資料は次から参照することが出来ます。

  • ESP-IDF Programming Guide – esp-idf プログラミングガイド。ページトップが lasted バージョンのドキュメントになっていますので、必ず画面左下の選択が stable(お使いのバージョン) になっていることを確認してください。(結構 API 変わっています)
  • ESP32 Technical Reference Manual – ESP32 ハードウェアに直接アクセスする場合など、EPS32 そのものについて記載があるテクニカルマニュアルです。
  • Arduino Core for the ESP32 – ESP32 特有のドキュメントは見つけられなかったのですが、component/arduino の中にソースといくつかの example がありますのでなんとかなるかと思います。 🙂
  • M5Stack Arduino API – M5Stack 特有の LCD やスピーカーなど拡張された便利な API の記載があります。

ESP32 はドキュメントがしっかりしていて使いやすいですね。

てなわけで hello happy coding!


Windows でビルドの速度が遅い件

当初 Windows 上の WSL(Windows Service for Linux)を使い Linux 用のツールチェインを使っていたのですが、コンパイルがちょっと待てないくらい遅いので普通の Windows 向けツールチェインを使うように変更しました。

また、WSL から Windows のツールチェインに変更してもどうにも Linux と比較してコンパイルが遅いので、さくさく開発を求める方は生か仮想の Linux 上で作業するといいかもです。なお、現在開発版(v4.0)の esp-idf には新 Windows のツールチェインが提供されており、将来的にコンパイル速度は恐らく今後改善されるのではないかと思います。

現時点、本記事では以下の文書を参考に make 時に -j4 オプションをつけて CPU コアを活用してコンパイル時間を改善した手順としました。(8コアの方は -j8 にしてください) Windows Defender をお使いの場合、プロジェクトフォルダを監視から外すのも有効なようです。

[Advice] Boost ESP-IDF Compile time on Windows 10

I have been building ESP-IDF projects for a while now, and as many of you I think have noticed, compile time on Windows 10 is VERY slow.

I just want to share some steps i have done to boost ESP-IDF project compile time on Windows 10.

関連

YM2612 + SN76489 メガドライブ音源を ESP32 で鳴らす (SPI接続編)

メガドライブのゲームミュージックをどうしても今再び実チップで鳴らしてみたい…。DNA がそうしろと言っているのだからしょうがない。

あれこれ部品を集めてがんばっておりましたが、なんとか PCM 付きで YM2612(FM音源) と SN76489(PSG音源)を発声させることができましたので、顛末を少しずつブログに書いていきたいと思います。

ハードウェアは次のように構成しています。

  • YM2612 + SN76489(メガドライブ搭載音源)
  • ESP32 DevkitC(制御用マイコン)
  • MCP23S17(SPI I/Oエクスパンダー)
  • LTC6904 * 2(I2C クロックジェネレーター)

作成中のものでソースコードも未整理、まだ回路図も書いていませんが(結線だけ README.md にメモしてあります) github に一式コミットしてあります。一応、現段階のものでフルスピードで PCM 付き .vgm が再生できています。:)

https://github.com/h1romas4/esp32-genesis-player

構成ですが、ESP32 マイコンは GPIO が少ないのと、将来的に音源を増やしたり、PC からも音源制御をしたいと考え、SPI 制御の I/Oエクスパンダー MCP23S17 を使ってシリアルパレラル変換を試みています。

音源制御のマイコン部分は、実際にゲームから送信された音源制御コマンドを44100Hz でサンプリングして保存した VGM Format 形式を事前に準備して、マイコンからデーターを読み取り音源に送信する形式をとっています。

さて、このシリーズの最初の難関は SPI MCP23S17 を使って 44100Hz(22us) に間に合う形で音源にコマンドを送りつけることで、最初はどうやっても間に合わず途方にくれてしまいした。。

esp-idf SPI Master Driver の spi_device_transmit でコマンドを送信すると、50us 程度次のコマンドを送るまでに間隔があいてしまいます。いきなり企画倒れか!がびーん。

これはどうやら SPI Master Driver が FreeRTOS マルチタスクに対応しており、処理に mutex と割り込みを使っていてその時間が乗っかってきているという事情のようです。

このため杓子定規に音源制御シーケンスを、YM2612 データーバスをセットして、CS と WR を落として…というふうに MCP23S17 に送ると、音楽がものすごいゆっくりに…。かつ YM2612 は 6ch 目を PCM に割り当てることができ、PCM の大きいデーターをひっきりなしに送信しますので、PCM を活用した楽曲ではまったく太刀打ちできず。

ということで、まずは音源制御のコマンドをなるべくまとめて送信すべく、MCP23S17 のデーターシートを読んでいたところ、良さそうなモードを発見。

特殊なモード(IOCON.BANK = 0のバイトモード)にすると、関連付けられたA/Bレジスタペア間でアドレスポインタが切り換わります。例えばBANKビットをクリアし、アドレスポインタをアドレス12h (GPIOA)またはアドレス13h (GPIOB)に初期設定した場合、アドレスポインタがGPIOAとGPIOBとの間で切り換わります。アドレスポインタは最初レジスタペアのどちらのアドレスを指していてもかまいません。

MCP23S17 の I/O は GPIOA と GPIOB の 8本ずつに分かれていますが、これらに ひとつのSPI シーケンス中にコマンドを連続で送ると A->B->A->B… とくるくる回ってくれます。

MCP23S17 と YM2612 の接続を、GPIOB に YM2612 データバスに、GPIOA に制御系の足を接続として、次のようにコマンドをおくる用に修正。赤い線の中がひとつのSPI シーケンスのイメージです。

(下の図はプロトでつくった I2C 版 MCP23012 で行ったものなので速度は無視してください… I2C 版でも同じ動きになります)

最初(0xB1)にアドレスバス(GPIOB)をセットし、次に CS/WR を落として同時にA0/A1 を設定(0x04)、次は GPIOB に戻ってしまうので最初と同じデーターをセットし、最後に CS/WR を上げて完了です。ひとつの音源コマンドを、ひとつのSPI シーケンスで送れるようになりました。

また、YM2612 より SN76489 は遅く、このような最短シーケンスだと音源が反応できなかったので(WR を下げてから上げるまで 12us くらいいるみたいです)ダミーの同一データーをコマンドに挿入して時間待ちし、こちらも同じようにひとつの SPI シーケンスで送れるようにしています。

ということで、これにて 4つの SPI シーケンスがいるところをひとつにまとめられシーケンス間隔問題を 1/4 削減できましたが、依然としてまとめられないシーケンス間に 50us 程度間隔があいてしまう問題は残りました。

いくつか考えて、ダミーデーターを送り続けてシーケンスを繋ぎっぱなしにすればいいじゃないかと思いつきましたがちょっと普通にやるとコマンドのタイミングを取るのが難しそうです。 一応鳴るにはなりましたがテンポがずこっとなりがちでした。。

再び途方に暮れましたが、いろいろ調べていたところ ESP32 本家のサンプルで SPI の液晶コントローラーをつかった NES(ファミコン)エミュレーターがあることを思い出す。

既存のエミュレーター移植なので、最終的にメモリーにレンダリングされた 320x240x8bit という大量データーを 60fps で SPI で転送しているとすれば何かあるハズ…とソースを確認。

https://github.com/espressif/esp32-nesemu/blob/master/components/nofrendo-esp32/spi_lcd.c#L402

かくしてそこで見つけたのは ESP32 の SPI ペリフェラルを直接たたくソースでした。

ESP32 Technical Reference Manual とにらめっこで命令を理解しつつ、はたして…。

1コマンド 7us キター! 😀

YM2612 は実際には 2コマンドで1セットなので 14us になりますが 1サンプルには十分間に合っていますのでこれで大丈夫そうです。(ここもまとめようと思えばできるハズです)

そんなこんなでメガドライブ楽曲が実チップで鳴るところまでごきつけました。嬉しい…。ベアナックル2が奏でられてしばし放心…。

タイムリーなことに、ぼくのゲームミュージック好きの原点にして頂点である古代祐三さんが MUCOM88 音源ドライバーをベアナックル2を含むサンプル曲付きで CC 公開されており、mucom MML を VGM に変換するプログラム mucomMD2vgm を kumatan さんが公開されています。

現時点残念ながら mucomMD2vgm で変換した .vgm はぼくの vgm パーサーだと unknown のコマンドがでてしまってまだ完全に発声できないのですが、おそらく vgmpos ずれだと思うのでちょっと直してみたいと思います。

他にも回路図をかいたり、基板をおこしたり、クロックを安定させたりまだ仕事が残っていますので、引き続きこの未来ガジェット製作をやっていきたいと思います。:D

関連