スティルハウスの書庫の書庫

はてなダイアリーで書いてた「スティルハウスの書庫」を移転してきました。

DE0でMIDIをデコードしてみた。

前回のDE0で正弦波を出してみた。の続き。音は鳴ったものの、440Hzしか出ない。つまらない。しょぼい正弦波のみの音源ではあるが、やっぱりキーボードで鳴らしてみたいなぁ。。その野望を叶えるためには、まずMIDIでつなげるようにせねば。

DE0にMIDIをつなぐ

もちろんDE0にはMIDIインタフェースなどないので、どうやってつなぐか考えた。USB経由の方向はプロトコルとか面倒そうな気がしたので調べてない。RS-232Cが付いてるので、MIDIから232Cへのコンバーターかなぁ、、いやしかし、MIDI信号について調べてみると、フォトカプラを入れるだけで割と簡単に信号読めそうだな、、パーツ屋でフォトカプラ買ってくればいいのかな?いや、フォトカプラやコネクタ等ひととおりボードに載ってるArduino用のMIDIインタフェースがあるな!これ流用しよう。

ということで届いた。それっぽい端子をテスターで探して、MIDIキーボードとDE0につないで内蔵ロジアナで波形を観測する。おぉ、意外にあっさり波形がとれたぞ。


MIDIは非同期シリアルだった

つづいては、この0と1の羅列からなんとかして「どの鍵盤を押したか」って情報を取り出すためのHDLコードを書かねばならない。まず俺はMIDIがどんなプロトコルなのかを知らない。なんだかやけに遅いシリアルって印象。。いろいろググったら、このサイトの図がとってもわかりやすかった。



MIDIはUARTっていうRS-232cと同じ非同期シリアルのプロトコルを採用していて(しかし信号電圧等は232cとは異なる)、ボーレートは31.25Kbpsで固定。鍵盤を押すと、8ビットのデータが3つから成る「MIDIメッセージ」が鍵盤側で作られて、各8ビットの前後にスタートビットとストップビットをつけた10ビット×3つがずらずら送られてくる。受け取り側は、そこから3バイトのMIDIメッセージを取り出せばよい。

ということで、HDLで非同期シリアル信号をデコードするコードを作成すればよい。Verilog HDLで書かれた既存のUARTライブラリも見つけたけど、そのまま使っては練習にならないので、自分で書くことにした。

非同期シリアルの読み方

非同期シリアルという名前が示す通り、同期用のクロック信号がない。ではどういうタイミングで信号を読めばいいのか。ネット上で見つけたいくつかの説明のなかでは、ここが参考になった。



つまり、31.25Kbpsより数倍速いクロックで信号を常時チェックしながら、スタートビットがはじまったな?と検知したら、31.25Kbps=32μs間隔で8ビットのデータを読んでいく。結構単純な方法でいいらしい。そこでこんなコードを書いてみた。

// decoding MIDI_IN
reg [5:0] dec_cnt;
reg [7:0] dec_reg;
always @(posedge CLK) begin
	if (sp_clk) begin
		case (dec_cnt)
			0, 1, 2: // wait for a start bit
				if (MIDI_IN) begin
					dec_cnt = 0;
					dec_reg = 0;
				end else
					dec_cnt = dec_cnt + 1;
			6, 10, 14, 18, 22, 26, 30, 34: // sampling data bits
				begin
					dec_reg = {MIDI_IN, dec_reg[7:1]}; // right shift
					dec_cnt = dec_cnt + 1;
				end
			38: // stop bit
					dec_cnt = 0;
			default:
				dec_cnt = dec_cnt + 1;
		endcase
	end
end
wire dec_done; // decoding a byte finished
assign dec_done = sp_clk & dec_cnt == 36;

ここでsp_clkっていうのは31.25Kbps x 4倍のクロック。そのタイミングでMIDI_IN(MIDIの入力波形)をチェックする。dec_cntっていうカウンタを用意して、スタートビットからストップビットまでの10ビット分流れてくる間に0〜38まで数える。0〜2のタイミングでスタートビットを検出したら、6、10、14、18...といった具合に4つ増えるごとにデータビットを読み取って、シフトレジスタdec_regに詰めていく。36に来たら8ビットのデータが溜まっているので、dec_doneフラグを立てる。これで1バイト分を読み取りできたことになる。

さらに、MIDIメッセージの3バイト分を貯めるレジスタを用意して、dec_doneフラグが立つたびに1バイトずつ入れてくコードを書いた。キーボードを叩いてはロジアナで波形を観測しながら繰り返しデバッグすることしばし。。。ついに「90h xxh xxh」っていう3バイトのMIDIメッセージ取れた!わーい!

ロジアナで動きを見るとこんな感じ:


ノートナンバーをLEDで表示してみる

しかし音を出すにはまだまだやることがある。3バイトの中身は「ステータスバイト」「データバイト1」「データバイト2」って分かれてて、例えば「90h 3Ch 40h」なら「ノートオン(打鍵)」「真ん中のC」「強弱は中くらい」っていう意味になる。これを解釈して出力する周波数に変換しなければならない。ちょっと今日中にはムリなので、次回やることにした。

とりあえずは、取れたノートナンバーをLEDに表示してみた。こんな感じ:

ちょっとみづらいけど、Cを押したら30hといった具合にノートナンバーが表示された。いぇ〜い!

次の野望:次こそいよいよキーボードで正弦波を鳴らしてみたいっ。

今回書いたソースコードはここに置いておいた。