秋月電商で販売しているヤマハ音源IC(YMZ294)を Arduinoで制御して音出してみます。このLSIの発売日は2002年と10年以上前のモノになります。YMZ294で検索すると、何件かの ホームページを見つけることができます。いくつか参考とさせていただきました。
ArduinoはLeonardoを想定しています。 YMZ294の8ビットのデータバスに、LeonardoのD2,D3,...D9を接続します。 また、制御ピンWrite(/WR)、Chip Select(/CS)、AOにそれぞれ、D10、D13,D1を接続します。 リセット(/IC)にD12を接続します。 なを、Arduino Unoでも同じ接続で良いです。
秋月で購入したYMZ294には、4MHzの水晶発信子が付属していました。いつも接続に戸惑ってしまうので、下に実物の写真と共に掲載しておきます。 上から見て黒丸がNCで、左周りにGround, Output, +VDDです。
ピン番号とGPIOの対応づけをログラムに定義しておきます。
// データバス #define PIN_D0 2 #define PIN_D1 3 #define PIN_D2 4 #define PIN_D3 5 #define PIN_D4 6 #define PIN_D5 7 #define PIN_D6 8 #define PIN_D7 9 // 制御ピン #define PIN_WR 10 #define PIN_A0 11 #define PIN_IC 12 #define PIN_CS 13
各ピンを初期設定します。
void setup() { pinMode(PIN_WR, OUTPUT); pinMode(PIN_A0, OUTPUT); pinMode(PIN_IC, OUTPUT); pinMode(PIN_CS, OUTPUT); pinMode(PIN_D0, OUTPUT); pinMode(PIN_D1, OUTPUT); pinMode(PIN_D2, OUTPUT); pinMode(PIN_D3, OUTPUT); pinMode(PIN_D4, OUTPUT); pinMode(PIN_D5, OUTPUT); pinMode(PIN_D6, OUTPUT); pinMode(PIN_D7, OUTPUT);
8ビットのデータをYMZ294に書き込むには、データを1ビットづつGPIOに設定します。
// Set 8bit data to 8 Pins void send(uint8_t d) { uint8_t c=d&0x01; digitalWrite(PIN_D0,c); d>>=1; c=d&0x01; digitalWrite(PIN_D1,c); d>>=1; c=d&0x01; digitalWrite(PIN_D2,c); d>>=1; c=d&0x01; digitalWrite(PIN_D3,c); d>>=1; c=d&0x01; digitalWrite(PIN_D4,c); d>>=1; c=d&0x01; digitalWrite(PIN_D5,c); d>>=1; c=d&0x01; digitalWrite(PIN_D6,c); d>>=1; c=d&0x01; digitalWrite(PIN_D7,c); init294(); //YMZ294の初期化(後述) }
YMZ294のレジスタにデータを書き込むには2つの値(アドレスとデータ)、をそれぞれ書き込みます。書き込みはA0, /CS, /WRの3つのピンを使います。 ちなみに、"/"は通常は正、使いたい時にゼロに設定するピンであることを示しています。A0が0の時レジスタのアドレスを設定、 A0が1の時、レジスタにデータを書き込みます。 書き込みの手順(シーケンス)は次の図を見て下さい。
// Set Address and Data // a : address // d : data void set_address_data(uint8_t cs, uint8_t a, uint8_t d){ digitalWrite(PIN_A0,0); digitalWrite(PIN_WR,0); digitalWrite(PIN_CS,0); send(a); delay(1); digitalWrite(PIN_CS,1); digitalWrite(PIN_WR,1); delay(1); digitalWrite(PIN_A0,1); digitalWrite(PIN_WR,0); digitalWrite(PIN_CS,0); send(d); delay(1); digitalWrite(PIN_CS,1); digitalWrite(PIN_WR,1); delay(1); }
YMZ294をリセットし、各レジスタのレジスタに初期値を設定します。
void init294() { // Reset digitalWrite(PIN_IC,0); delay(100); digitalWrite(PIN_IC,1); // YMZ294 Register Initialize // 楽音周波数 send_data(0x00,0x00); //Channel A Low send_data(0x01,0x00); //Channel A High send_data(0x02,0x00); //Channel B Low send_data(0x03,0x00); //Channel B High send_data(0x04,0x00); //Channel C Low send_data(0x05,0x00); //Channel C High delay(10); send_data(0x06,0x00); //ノイズ周波数の設定 // ミキサーの設定 send_data(0x07,0x38); //楽音ChannelA,B,C ON delay(10); // 音量コントロールどDAC send_data(0x08,0x00); //Channel A send_data(0x09,0x00); //Channel B send_data(0x0A,0x00); //Channel C delay(10); // エンベロープ周波数 send_data(0x0B,0x00); //Low send_data(0x0C,0x00); //High // エンベロープの形状 send_data(0x0D,0x0D); }
音を出すには周波数を設定し、音量を上げます。ここでは、MIDIのキーコードを与えると音が出る void NoteOn(uint8_t keycode)関数を示します。Channel Aの周波数を設定し、音量を上げています。
// Convert MIDI Note Number to Frequency Register Value uint16_t mCoef[128]= { 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4050, 3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 2025, 1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204, 1136, 1073, 1012, 956, 902, 851, 804, 758, 716, 676, 638, 602, 568, 536, 506, 478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253, 239, 225, 213, 201, 190, 179, 169, 159, 150, 142, 134, 127, 119, 113, 106, 100, 95, 89, 84, 80, 75, 71, 67, 63, 60, 56, 53, 50, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 12, 11, 11, 10 };
void NoteOn(uint8_t keycode) { uint16_t x = mCoef[keycode]; uint8_t hb=(x>>8)&0x0F; uint8_t lb=(x&0xFF); send_data(0x00,lb); send_data(0x01,hb); send_data(0x08,0x08); //音量を上げる }
音を止める時は、音量をゼロにします。
void NoteOn() { send_data(0x08,0x00); //音量を下げる }