AVR

AVRでファミコン風音源YMZ294を使う - AVR †

YMZ294とは †

YMZ294は、ヤマハが提供する(orしていた?)SSG(Software-controlled Sound Generator)音源IC。PSG音源と言われるファミコンやMSXなどのレトロゲーム/PCに搭載されていた音源と同系列の音が出る。いわゆるピコピコシンセサイザー的な発音である。

入手 †

秋月で、クリスタル込み500円で入手できる。
ヤマハ音源IC(YMZ294)

周辺回路 †

周辺に用意すべき回路は、クリスタルを除けば基本的にはない。非常に単純な回路構成で使用することができる。ただし、出力にはノイズが乗りやすいので、フィルタを噛ませて出力するとよい。
今回は、AVRマイコンATmega328Pを使って、このICを制御してみる。

ライブラリ公開 †

検索エンジン等からたどり着いてライブラリだけ欲しい方はこちらからどうぞ。
libYMZ294(サンプルソース付き): filelibYMZ294.zip
詳しい内容についてはライブラリの仕様を御覧ください。

制御方法 †

YMZ294は、レジスタに値を書きこむことで音の周波数や音量、エンベロープなどを設定できるようになっている。レジスタに値を書き込むには、WR(書き込み許可),CS(チップセレクト),A0(アドレス指定)の3つの設定ピンのHIGH/LOWを切り替えて、D0〜7に値をセットする必要がある。
以下に手順を示す。表は、上から順に時系列順になっている。

レジスタの値をセットする †

  • 書き込み先アドレスのセット
    1. WR,CS,A0がすべてLOWの状態から始まる
    2. D0〜7に書きこむレジスタのアドレスをセットする
    3. WRとCSをHIGHにして、アドレスのセットを完了する
フェーズWRCSA0
初期化LLL
D0〜7にデータセットLLL
アドレスセット完了HHH
  • 書き込みデータのセット
    1. 書きこみ先アドレスをセットしたあとの状態から始まる
    2. WRとCSをLOWに、A0をHIGHにする
    3. D0〜7に書きこむデータをセットする
    4. WR,CS,A0をHIGHにして、データのセットを完了する
    5. WR,CS,A0をすべてLOWにする
フェーズWRCSA0
初期状態HHH
データ書き込み用に設定LLH
D0〜7にデータセットLLH
アドレスセット完了HHH
初期化LLL

各レジスタの説明 †

レジスタ番号説明文
00チャンネルAの周波数情報下位8bit
01チャンネルAの周波数情報上位4bit
02チャンネルBの周波数情報下位8bit
03チャンネルBの周波数情報上位4bit
04チャンネルCの周波数情報下位8bit
05チャンネルCの周波数情報上位4bit
06ノイズ音の周波数情報(5bit)
07ミキサーの設定情報(6bit)
08チャンネルAの音量コントロール情報(5bit)
09チャンネルAの音量コントロール情報(5bit)
0AチャンネルAの音量コントロール情報(5bit)
0Bエンベロープの周波数情報下位8bit
0Cエンベロープの周波数情報上位8bit
0Dエンベロープ形状(4bit)


00〜05 楽音の周波数情報

レジスタ00から05には、楽音の周波数情報を格納する。
レジスタ番号D7D6D5D4D3D2D1D0
00 or 02 or 04TP7TP6TP5TP4TP3TP2TP1TP0
01 or 03 or 05--------TP11TP10TP9TP8
TP0〜TP11は12bitの整数TPの各ビットを表す。このとき、発生する矩形波の周波数は、
ft=1/(8TP)
06 ノイズ音の周波数情報

ノイズの周波数は5bitの整数で表す。
レジスタ番号D7D6D5D4D3D2D1D0
06------NP4NP3NP2NP1NP0
このとき発生するノイズの周波数は、
fn=1/(8NP)
07 ミキサーの設定情報

レジスタ番号D7D6D5D4D3D2D1D0
07----NCNBNATCTBTA
1文字目のNはノイズ、Tは楽音、2文字目はチャンネルを表す。ビットが0の時、該当する音声を出力。
08〜0A 音量コントロール情報

レジスタ番号D7D6D5D4D3D2D1D0
08 or 09 or 0A------ML3L2L1L0
各チャンネルの音量を設定する。Mが0のとき、L0〜4が表す整数のレベルに設定される。Mが1のときは、エンベロープによって生成された音量に設定される。
なお、音量は対数スケールで設定されるため、自然な減衰音を表現することができる。
0B〜0C エンベロープの周波数情報

レジスタ番号D7D6D5D4D3D2D1D0
0BEP7EP6EP5EP4EP3EP2EP1EP0
0CEP15EP14EP13EP12EP11EP10EP9EP8
エンベロープの繰り返し周波数を設定する。
fe=1/(128EP)
ただし、実際にエンベロープを生成する過程で使う周波数はこれとは異なる。
0D エンベロープの形状

レジスタ番号D7D6D5D4D3D2D1D0
0D--------CONTATTALTHOLD
CONT,ATT,ALT,HOLDの組み合わせによってエンベロープの形状を設定する。
CONTは繰り返しの有無、ATTは立ち上がり(1)と立ち下がり(0)、ALTは立ち上がりと立ち下がりを交互にするかどうか、HOLDは1周期分終わったあとに波形を固定するかどうか、をそれぞれ定める。

結局音を流すにはどうすればよいか †

  1. ミキサーの設定を行い、発音を有効にする
  2. 音量の設定を行い、音が聞こえるようにする
  3. 各種周波数の設定を行い、音のデータを設定する

→これで音が流れる

AVR向けライブラリの作成 †

これをもとに、AVR用のライブラリを作成する。その前に、AVRのどのピンをYMZ294のどのピンに割り当てるかを考える。ここでは、次の回路図のようにした。
ymz294sch.png
PORTBのPB0〜PB7をD0〜D7にそれぞれ割り当て、PORTCのPC0をリセットピン(IC)に、PC1をWRに、PC2をCSに、PC3をA0に割り当てた。合計で12本のピンを占有することになる。
リセットピンをLOWにすることでYMZ294をソフトリセットすることができる。AVRが使用している間は常にHIGHに設定しなければならない。また、今回は4MHzのクリスタルを使用しているため、4/6ピンは+5Vにつないでおく。6MHzのクリスタルを使用する場合はGNDにつなげばよい。SOはサウンドの出力ピンである。

以上の仕様を考慮して実装を行った。

ライブラリの仕様 †

ポートの初期化 †

 void ymz_init(void){
 	D_DDR=0b11111111;
 	SYS_DDR=0b00001111;
 	_delay_ms(100);
 	SYS_PORT|=(1<<YRST);
 	_delay_ms(200);
 }

YMZ294を利用するためのポートを初期化する。

YMZ294のリセット †

 void ymz_reset(void){
 	SYS_PORT&=~(1<<YRST);
 	_delay_ms(10);
 	SYS_PORT|=(1<<YRST);
 }

YMZ294をソフトリセットする。

レジスタへの値の設定 †

 void ymz_setRegister(char addr,char val){
 	SYS_PORT&=~((1<<YADR)|(1<<YCS)|(1<<YWR));
 	D_PORT=addr;
 	SYS_PORT|=(1<<YCS)|(1<<YWR);
 	SYS_PORT=(SYS_PORT&~((1<<YCS)|(1<<YWR)))|(1<<YADR);
 	D_PORT=val;
 	SYS_PORT|=(1<<YADR)|(1<<YCS)|(1<<YWR);
 	SYS_PORT&=~((1<<YADR)|(1<<YCS)|(1<<YWR));
 }

レジスタaddrにvalを書き込む。応答時間は非常に短いため、先述の順序通りにそのままコードを書けば良い。

楽音周波数の設定 †

 void ymz_setFreq(char channel,int freq){
 	if(channel>2){ return; }
 	ymz_setRegister(channel*2,freq&0b11111111);
 	ymz_setRegister(channel*2+1,((freq&0b111100000000)>>8));
 }

指定したチャンネルの周波数レジスタに値を書き込む。これを利用して、実際の周波数を書き込む関数を作成する。

 void ymz_setRealFreq(char channel,int freq){
 	freq=125000/freq;
 	ymz_setFreq(channel,freq);
 }

ymz_setRealFreq(CH_A,440) などとすれば、実際にその周波数の音声が出力される。

休符の挿入 †

 void ymz_setPause(char channel){
 	ymz_setFreq(channel,0);
 }

周波数0の音を設定すれば休符となる。

MML風フォーマットからの周波数の取得 †

 const int noteFreq[] = {
 0,9,9,10,10,11,12,12,13,14,15,15,16,17,18,19,21,22,23,25,26,28,29,31,33,35,37,39,41,44,46,49,52,
 55,58,62,65,69,73,78,82,87,93,98,104,110,117,124,131,139,147,156,165,175,185,196,208,220,233,247,
 262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659,699,740,784,831,880,932,988,
 1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976,2093,2218,2349,2489,2637,2794,2960,
 3136,3322,3520,3729,3951,4186,4435,4699,4978,5274,5587,5920,6272,6645,7040,7459,7902,8372,8870,
 9397,9956,10548,11175,11840,12544
 };

この周波数テーブルを利用する。

 int ymz_calcFreqByMML(char octave,char note,char sharp){
 	int pos=0;
 	switch(note){
 		case 'C':
 		case 'c':
 			break;
 		case 'D':
 		case 'd':
 			pos=2;
 			break;
 		case 'E':
 		case 'e':
 			pos=4;
 			break;
 		case 'F':
 		case 'f':
 			pos=5;
 			break;
 		case 'G':
 		case 'g':
 			pos=7;
 			break;
 		case 'A':
 		case 'a':
 			pos=9;
 			break;
 		case 'B':
 		case 'b':
 			pos=11;
 			break;
 		default:
 			break;
 	}
 	return noteFreq[octave*12+pos+(sharp&1)];
 }

MML形式「D#6」などをばらして ymz_calcFreqByMML(6,'D',1) と書けば良い。

ノイズの周波数設定 †

 void ymz_setNoiseFreq(char freq){
 	ymz_setRegister(0x06,freq&0b11111);
 }
 void ymz_setNoiseRealFreq(int freq){
 	freq=125000/freq;
 	ymz_setNoiseFreq(freq);
 }

先述の内容そのまんま。freqに周波数(情報)を設定する。

ミキサーの設定 †

 void ymz_setMixer(char noisemsk,char tonemsk){
 	if(noisemsk>0b111||tonemsk>0b111){ return; }
 	ymz_setRegister(0x07,(noisemsk<<3)|tonemsk);
 }

noisemskでノイズを出力するチャンネルの設定、tonemskで楽音を出力するチャンネルの設定を行う。
noisemsk,tonemskで0にセットされているビットに対応するチャンネルから音が出る。

音量の設定 †

 void ymz_setVol(char env,char channel,char level){
 	ymz_setRegister(0x08+channel,(env<<4)|(level&0b1111));
 }

env=0のとき音量はlevelになり、env=1のとき音量はエンベロープから出力された値に従う。

エンベロープの周波数設定 †

 void ymz_setEnvFreq(int freq){
 	ymz_setRegister(0x0b,freq&0b11111111);
 	ymz_setRegister(0x0c,(freq&0b1111111100000000)>>8);
 }
 void ymz_setEnvRealFreq(int freq){
 	freq=125000/freq;
 	ymz_setEnvFreq(freq);
 }

そのまんま。freqに周波数(情報)を設定する。

エンベロープの形状設定 †

 void ymz_setEnvShape(char cont,char att,char alt,char hold){
 	ymz_setRegister(0x0D,(cont<<3)|(att<<2)|(alt<<1)|hold);
 }

エンベロープの形状を設定する。詳しくはデータシートなどの参考資料を参照のこと。

サンプルソース †

みなさんご存知のあの曲の冒頭部分っぽいものがループされるサンプルです。

参考にしたサイトなど †

ヤマハ音源IC(YMZ294) - 秋月電子
音源LSI(YMZ294) - 実験室

コメントはこちらへ †

  • 参考になりました.ありがとうございます. -- 2013-10-05 (土) 20:28:46
  • ずっとエンベロープで困っていました、ありがとうございます。 -- ちじたぜ? 2017-07-29 (土) 16:41:42
  • &bgcolor(red){&color(yellow){ヷヴィヴヹヺ、参考になりました.}} -- NKMR? &new{2017-09-11 (月) 12:29:28};


添付ファイル: filelibYMZ294.zip 777件 [詳細] fileymz294sch.png 1279件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-09-11 (月) 12:29:29 (202d)