いつか使ってみようと思いながら、なかなか使わずに部品箱にずっと入れている人も多いと思われる音源ICですが、やってみると意外と簡単なので、基礎を説明しようと思います。
実際に動作させるまでの処理
IOの設定
まず、Arduinoに接続されたピンに対して便宜上名前を付けます。
const byte WRCS_PIN = 10;
const byte A0_PIN = 11;
const byte RESET_PIN = 12;
const int DATA_PIN[] = { 2, 3, 4, 5, 6, 7, 8, 9 };
setup()内ですべて出力に設定します。
for (int i = 0; i < 8; i++) {
pinMode(DATA_PIN[i], OUTPUT);
}
pinMode(WRCS_PIN, OUTPUT);
pinMode(A0_PIN, OUTPUT);
pinMode(RESET_PIN, OUTPUT);
レジスタ書き込みファンクション
音源ICの機能は全てレジスタにアクセスすることで制御できます。
これは同じYAMAHA製のFM音源ICも含めて基本的なことなので、この概念を理解すると他の音源ICもレジスタの仕様をチェックするだけで使えるようになります。
WRCS_PINがLOWの状態でDATA_PINにバイトデータをセットし、WRCS_PINをHIGHにするとバイトデータがYMZ294に送られます。このときA0_PINがLOWの場合はレジスタのアドレス、A0_PINがHIGHの場合はレジスタに入れる値となります。
この方法でレジスタに値をセットするファンクションを定義します。
void set_register(byte addr, byte value)
{
// addr
digitalWrite(WRCS_PIN, LOW);
digitalWrite(A0_PIN, LOW);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN[i], bitRead(addr, i));
}
digitalWrite(WRCS_PIN, HIGH);
// value
digitalWrite(WRCS_PIN, LOW);
digitalWrite(A0_PIN, HIGH);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN[i], bitRead(value, i));
}
digitalWrite(WRCS_PIN, HIGH);
}
YMZ294の初期化
動作の初めにYMZ294を初期化します。
全てのレジスタに0をセットし、ハードウエアリセットします。
ハードウエアリセットはWRCS_PINがHIGH、A0_PINがLOWの状態でRESET_PINをLOWにし、少し間をおいてからRESET_PINをHIGHにします。
この方法で初期化するファンクションを定義します。
void reset() {
set_register(0x00, 0);
set_register(0x01, 0);
set_register(0x02, 0);
set_register(0x03, 0);
set_register(0x04, 0);
set_register(0x05, 0);
set_register(0x06, 0);
set_register(0x07, 0);
set_register(0x08, 0);
set_register(0x09, 0);
set_register(0x0a, 0);
set_register(0x0b, 0);
set_register(0x0c, 0);
set_register(0x0d, 0);
digitalWrite(WRCS_PIN, HIGH);
digitalWrite(A0_PIN, LOW);
digitalWrite(RESET_PIN, LOW);
delay(10);
digitalWrite(RESET_PIN, HIGH);
}
このファンクションをsetup()内で実行します
reset();
周波数を指定して出力
指定した周波数の音を出力する場合は、125000/周波数(HZ)の整数値を12ビットで各チャンネルのレジスタにセットします。
レジスタは8ビット構成なので、上位4ビットと下位8ビットに分けて格納します。
チャンネル |
上位4ビットレジスタ |
下位8ビットレジスタ |
A |
#01 |
#00 |
B |
#03 |
#02 |
C |
#05 |
#04 |
チャンネルと周波数を設定するファンクションを定義
void SetFrequency(int ch, word freq) {
word cal_freqency = 0;
if (freq != 0) {
cal_freqency = 125000 / freq;
}
cal_freqency &= 0b0000111111111111;
set_register(0x00 + (ch * 2), cal_freqency & 0xff);
set_register(0x01 + (ch * 2), (cal_freqency >> 8) & 0xff);
}
演奏設定
今回はチャンネルABC共にトーンのみ出力します。
この設定はレジスタ#07へ0b00111000の値をセットすることにより行えます。
今回はチャンネルABC共にボリュームを0にします。
この設定はレジスタ#08,#09,#0Aへ0の値をセットすることにより行えます。
今回はチャンネルABC共に周波数を0にします。
この処理をsetup()内で実行します
set_register(0x07, 0b00111000);
set_register(0x08, 0);
set_register(0x09, 0);
set_register(0x0a, 0);
SetFrequency(0, 0);
SetFrequency(1, 0);
SetFrequency(2, 0);
演奏
チャンネルAにA(ラ)の音程の周波数の440Hzをセットします。
チャンネルAのボリュームを15にします。
1秒後にチャンネルAのボリュームを0にし、チャンネルAの周波数に0をセットして演奏を止めます。
周波数に0をセットしただけでは想定外の音が出力されたので、ボリュームも0にしました。
この処理は1回だけ実行するつもりだったので、setup()内の最後に追加しました。
SetFrequency(0, 440);
set_register(0x08, 15);
delay(1000);
set_register(0x08, 0);
SetFrequency(0, 0);
これで起動後、1秒間だけ音が出力されるはずです。
最後にソース全文を掲載するので、参考にどうぞ。
// Output Pins
const byte WRCS_PIN = 10;
const byte A0_PIN = 11;
const byte RESET_PIN = 12;
const int DATA_PIN[] = { 2, 3, 4, 5, 6, 7, 8, 9 };
void setup() {
for (int i = 0; i < 8; i++) {
pinMode(DATA_PIN[i], OUTPUT);
}
pinMode(WRCS_PIN, OUTPUT);
pinMode(A0_PIN, OUTPUT);
pinMode(RESET_PIN, OUTPUT);
reset();
set_register(0x07, 0b00111000);
set_register(0x08, 0);
set_register(0x09, 0);
set_register(0x0a, 0);
SetFrequency(0, 0);
SetFrequency(1, 0);
SetFrequency(2, 0);
delay(100);
SetFrequency(0, 440);
set_register(0x08, 15);
delay(1000);
set_register(0x08, 0);
SetFrequency(0, 0);
}
void loop() {
}
// 初期化
void reset() {
set_register(0x00, 0);
set_register(0x01, 0);
set_register(0x02, 0);
set_register(0x03, 0);
set_register(0x04, 0);
set_register(0x05, 0);
set_register(0x06, 0);
set_register(0x07, 0);
set_register(0x08, 0);
set_register(0x09, 0);
set_register(0x0a, 0);
set_register(0x0b, 0);
set_register(0x0c, 0);
set_register(0x0d, 0);
digitalWrite(WRCS_PIN, HIGH);
digitalWrite(A0_PIN, LOW);
digitalWrite(RESET_PIN, LOW);
delay(10);
digitalWrite(RESET_PIN, HIGH);
}
void SetFrequency(int ch, word freq) {
word cal_freqency = 0;
if (freq != 0) {
cal_freqency = 125000 / freq;
}
cal_freqency &= 0b0000111111111111;
set_register(0x00 + (ch * 2), cal_freqency & 0xff);
set_register(0x01 + (ch * 2), (cal_freqency >> 8) & 0xff);
}
// レジスタセット
void set_register(byte addr, byte value)
{
// addr
digitalWrite(WRCS_PIN, LOW);
digitalWrite(A0_PIN, LOW);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN[i], bitRead(addr, i));
}
digitalWrite(WRCS_PIN, HIGH);
// value
digitalWrite(WRCS_PIN, LOW);
digitalWrite(A0_PIN, HIGH);
for (int i = 0; i < 8; i++) {
digitalWrite(DATA_PIN[i], bitRead(value, i));
}
digitalWrite(WRCS_PIN, HIGH);
}