はじめに

1ビットCPUでは、PICのアセンブラは書きたくないといいましたが、あのクセのあるアセンブラ、たまには書いてみたくなります。
秋月のPICプログラマーは何年か前に買ってちょっと使っただけでほこりをかぶっていたのですが、最近、アップグレードキットを買ったこともあり、小さな8ピンのフラッシュタイプで何か作ってみたくなりました。
何かいいアプリケーションは……とネットで探すと、PICでオルゴールを作るというのがありました。3和音まで見つかりましたが、それを超えるものはありません。
そこで、4和音以上に挑戦しようと、PICオルゴールプロジェクトを始めます。

使用IC

PICに全力を出させるため、内部クロックではなくセラロックを使います。また、和音数が多いとデータ量も増えるので、外付けのI2CシリアルROMを使います。
すると、出力に使えるピンが1本しか残りません。2005年2月現在手に入るチップから選ぶと、PWMを持っているということで12F683に決定です。
I2Cは持っていないのでソフトで処理することにします。
他に、ROMは24C256、アンプはLM386といったところです。
全部8ピンなのがいい感じです。しかも、秋月で全部そろえようというのが見え見えです。

波形を作る

今回は単純な矩形波を作ります。
この場合、「一定時間経過したら出力を反転させる」を繰り返すだけで波形ができます。
まず、半波長分をカウントする波長カウンタを何ビットにするかです。PSGでは、10ビットとか12ビットあるようです。8ビットだと高音の音程がずれそうなので、16ビットにします。
ハードウェアのタイマを使って割り込みで処理する方式だと音が濁りそうなので、プログラムで数えます。
波長カウンタをv0tcl,v0tch、そのプリセット値をv0tpl,v0tph、出力値をv0vとすると、
    incfsz  v0tcl,f
    goto    l0
    incfsz  v0tch,f
    goto    l0
    comf    v0v,f
    incf    v0v,f
    movf    v0tpl,w
    movwf   v0tcl
    movf    v0tph,w
    movwf   v0tch
l0:
こんな感じです。実際には、直接l0にジャンプするのではなく、いったん別の場所にジャンプしてサイクル数を合わせてl0に飛ぶようにします。
上記ルーチンでは音の強さ(無音を含む)を指定できます。
最大で10サイクル、クロックを20MHzとすると2μSかかります。
4和音だと8μS、その他の波形加算、音長の処理を全部含めて10μS程度でしょうか。
この計算だとサンプリングレートは100kHzになります。
もっと低くてもいいような気もしますが、この周波数を分周して音程を決めることになるので、あまり低くすると高音がいい加減になります。
とりあえず4和音で進めてみます。

コンバータ

音楽データは標準MIDIファイル(SMF0/1)からコンバートします。そのためのコンバータをまず作ります。
MIDIファイルは、以前から扱ってみたかったんですが、ちょうどいいアプリケーションが無いのと、めんどくさそうなバイナリファイルなのでそのままになっていました。
とりあえず調べてみると、すばらしいサイトを見つけました。

詳説MIDI規格

これだけ詳しい情報があれば、コンバータを書けそうです。
せっかくなので、あとで使いまわせるようにパーサを独立したクラスとして作り、その上でコンバータを作ります。
オルゴール用に取り出す情報は、

タイムベース
ノートオン/ノートオフ
コントロールチェンジのメインボリュームとエクスプレッション
メタイベントのテンポ

です。その他の情報は無視します。また、リズムに使われるチャンネル10も無視します。
得られた情報を、以下のような2バイト単位のフォーマットで出力します。
(1) ノート
000v vsss snnn nnnn
v: ボイス番号 発振器を指定
s: 音の強さ   チャンネルのメインボリュームとエクスプレッション、
              ノートのベロシティーを掛け合わせた上位4ビット
n: ノート番号 MIDIノート番号

(2) ウェイト
1www wwww wwww wwww
波形を生成しながら指定回数ループする。wの部分は負の値。
ウェイト値が負の値なのはincfszを2回使った16ビットインクリメントを考慮しています。

download(mid2pic.tar.gz)

チェッカー

さて、ちゃんとコンバートできているのかどうか確認しないと、あとでコンバータのバグなのかPIC側のバグなのか分からなくなります。
そこで、簡易に確認できるツールを作りました(Mac OS X用)。PICでやる処理と本質的には同じなので、これをアセンブラに落とせばいいともいえます。
このツールでコンバータをデバッグし、音数が足りないときにどのボイスを奪うのがいいかとか、そういう作りこみをしました。
Macが奏でるPSGサウンドは、なかなか味があっていいものです。

download(orgel.tar.gz)

デバッグ

作りっぱなしで一発で動くことはまず無いので、デバッグツールが必要です。
LEDデバッグ(特定の場所に来たらLEDを点灯する)も、I2Cのデバッグには使えません。
そこで、スパルタン3スターターキットで簡易ロジアナを作りました(iic.v)。
PICのリセットを解除したあと一定間隔ごとにSCL,SDAをサンプリングしてメモリに貯めておき、その後、文字('0'〜'3')に変換しながらRS-232Cに出力し、Windowsマシンのターミナルでログを取ります。
はじめはベタに8192文字送っていたのですが、ログを取るとゴミが混じるので、64文字ごとに改行を送るようにしました。
写真はデバッグのための基板をスパ3ボードに接続したところです。12F683だと他の状態を見たいときにポートが余っていないので、16F819でデバッグしています。



得られるログは以下のようなものです。



これでは分かりにくいので、perlでフィルタを作り(view)、lessで観察しています。



左がSCL、右がSDAです。上のスクリーンショットはスタートコンディションのところです。

download(iic.tar.gz)

波形を作る2

音が出ました。
が、やっぱり高音が不正確です。
今までは「分周する」という考え方で作ってきましたが、元になる周波数が低いとやっぱり厳しいです。
そこで、v0tl,v0thの2バイトに音程テーブルを引いてきたv0dl,v0dhを加算していき、キャリーが出たときにv0vを反転させる以下のようなコードを考えました。
    movf    v0dl,w
    addwf   v0tl,f
    movf    v0dh,w
    btfsc   STATUS,C
    addlw   1
    addwf   v0th,f
    movf    v0v,w
    btfsc   STATUS,C
    sublw   0
    movwf   v0v
かかる時間は10サイクルで、以前のルーチンと変わりません。
こちらのコードだと、高音は正確になる反面、低音の誤差が大きくなります。しかし、低音の誤差は目立たないと思うので、これで試してみます。

完成

回路図です。電源は省略しています。


曲によっては32kバイトでは足りないので、ROMは24C512にしました。

追記(2006/04/23)
24C512のSDAのピン番号を修正しました。

これを、下の写真のように作りました。



低音も出すために、大きめのスピーカーにつないでいます。あとはプログラムです。

download(pic_orgel.tar.gz)

追記(2006/04/23)
mid2picを実行するときは-fオプションを付けてください。

ROMのSCLの最小パルス幅は0.4μSにしてあるので、使用できないものもあるかもしれません。
新しい方の波形生成ルーチンは、fsが低くても高音が正確で、低音の音程も問題ないので、12和音まで対応できました。
ただ、和音が多くなるとROMを読む時間もかかり、その間発音処理ができないので音が途切れます。ごく短い時間ですが、このせいで持続音が「いがらっぽい」感じになってしまいます。
聴いた感じでは、8和音くらいがちょうどいいようです。
下のファイルは8和音でコンバートしたサンプル曲です。昔(パソコン通信の)ニフティから落としたMIDIファイルなんで古い曲ばかりですが(その時点でも古かったし)。
ZIPファイルを展開すると5つのMP3ファイルになります。聴いてみてください。

download(サンプル曲)

どうですか?とてもPICが出している音とは思えません。
矩形波に郷愁を感じる世代におすすめです。


このページのプログラムは、非営利目的に限り自由に使用できます。
また、このページの情報(回路とプログラムを含む)は、いっさいの保証もサポートもありません。