過去にポケコンを使ってYMF825Boardでドレミファソを鳴らすというのをやっておりましたが、今回はMSX0でやってみたいと思います。
ポケコンの場合は、IOピンを直接制御してSPIのなんちゃって送信を行いましたが、MSX0は現状SPI通信はIoT BASICではサポートされていないため、例によってArduinoに仲介させて、MSX0からI2Cで演奏データを書き出し、ArduinoがSPI通信に変換してYMF825Boardに送るという方式をとります。
演奏データやアルゴリズムについては、
https://github.com/hasebems/YMF825_sample
にあるサンプルコードを参考にさせていただきました。
仲介役Arduinoの実装
おなじみMSX0で出来ない件を縁の下で支えるArduinoの実装です。
I2Cでマスタからデータを受信すると、バッファにため込み、予定のバイト数を受信したらSPIスレーブにデータを送信します。
YMF825では一回の命令を複数バイト一度に送るケースがあるため、1バイト受け取るごとにSPIに送信、とはしていません。
また、ここで注意したいのは、ArduinoのI2Cデータの受信バッファは32バイトであるということです。32バイト以上のデータを受け取りそのまま継続しようとするとスレーブデバイスとしてはハングアップのような状態になります。(エラー処理をちゃんと書けば回避できるかもしれないのですが試していないです)
そのため、I2Cマスター(MSX0)からは、いったん命令のバイト数を送信し、続けて命令をそのバイト数分送るようにしています。I2Cスレーブでも、まず最初の1バイトは命令のバイト数として受信し、次から命令のバイト数分をバッファ配列に溜めるようにしています。
#include "Wire.h"
int i2c_slave_addr = 0x08;
#include <SPI.h>
unsigned char buf[256];
unsigned int data_length = 0;
unsigned int bufpos = 0;
boolean command = true;
void setup(){
Serial.begin(115200);
Serial.println("I2C to SPI(YMF825)");
for(int i=0;i<256;i++) {
buf[i] = 0xff;
}
delay(500);
Wire.begin(i2c_slave_addr);
Wire.onReceive(dataReceive);
Wire.onRequest(requestEvent);
SPI.begin();
digitalWrite(SS,HIGH);
// YMF825 Reset
pinMode(9,OUTPUT);
digitalWrite(9,LOW);
delay(100);
digitalWrite(9,HIGH);
delay(100);
}
void loop(){
}
// I2C data receive from master
void dataReceive(int number) {
if (Wire.available()) {
if (command) {
data_length = Wire.read();
command=false;
Serial.println("length:"+String(data_length));
Serial.print("(");
} else {
buf[bufpos] = Wire.read();
Serial.print(String(buf[bufpos])+" ");
bufpos++;
if (bufpos==data_length) {
sendToSPI(buf,data_length);
command=true;
bufpos=0;
Serial.println(")");
}
}
}
}
// Send data to SPI slave
int sendToSPI(unsigned char* val,int number) {
Serial.print(">"+String(number));
Serial.print("[");
for(int i=0;i<number;i++) {
Serial.print(String(val[i],HEX)+" ");
}
Serial.print("]");
digitalWrite(SS,LOW);
SPI.transfer(val,number);
digitalWrite(SS,HIGH);
Serial.println();
return 0;
}
// I2C data send to master
void requestEvent() {
}
MSX0の実装
MSX0は通常のI2C通信で、あまり特別なことをしていません。
MSX0のIoT BASICでのI2C通信の基本的なことについては、別記事にまとめていますのでご覧ください。
演奏データはDATA文で書かれています。これをREAD文で読み、_IOTPUT()でひたすら書き出すという感じなのですが、今回のプログラム(YMF825へのデータ送信)では
- 2バイトずつ送信するケース(行20000~)
- 36バイト送信するケース(行21000~)
- 2バイト送信で2バイト目だけDATA文を使わないで任意の値を指定するケース(行22000~)
の3パターンがあるため、それぞれにサブルーチンを作ってそこに飛ばすようにしています。そのため少しプログラムが複雑に見えると思いますが、飛び先などの行番号にコメントを入れているので、参考になれば幸いです。
1 'SAVE"YMF825-2.BAS"
1000 'Init
1010 D$="08"
1020 _IOTFIND("device/i2c_a",C)
1030 PRINT "IOTFIND:";C
1040 IF C=0 THEN 1200:'Not Found
1050 _IOTFIND("device/i2c_a",A$(0),C)
1060 PRINT "Slave Address : ";
1070 FOR I=0 TO C-1
1080 PRINT A$(I);" ";
1090 IF A$(I)=D$ THEN 1300:'Create Device Path
1100 NEXT I
1200 'Not Found
1210 PRINT "Device not found."
1220 END
1300 'Create Device Path
1310 N$="device/i2c_a/"+A$(I)
2000 'Main
2010 PRINT
2020 PRINT "---- YMF825 ----"
3000 'INIT
3010 PRINT "---- INIT ----"
3100 RESTORE 40000:'INITDB
3120 FOR I=1 TO 18
3130 GOSUB 20000:'WRITE
3140 NEXT I
4000 'TONE
4010 PRINT "---- TONE ----"
4100 RESTORE 41000:'TONEDB
4110 GOSUB 20000:'Write Word
4120 GOSUB 20000:'Write Word
4130 GOSUB 21000:'Write Multi Bytes
5000 'CHANNEL
5010 PRINT "---- CHANNEL ----"
5100 RESTORE 42000:'CHANNELDB
5110 FOR I=1 TO 5
5120 GOSUB 20000:'Write Word
5130 NEXT I
6000 'LOOP
6010 PRINT "---- LOOP ----"
6100 KA=&H14
6110 KD=&H65
6120 GOSUB 30000:'Loop SubRoutine
6200 KA=&H1C
6210 KD=&H11
6220 GOSUB 30000:'Loop SubRoutine
6300 KA=&H1C
6310 KD=&H42
6320 GOSUB 30000:'Loop SubRoutine
6400 KA=&H1C
6410 KD=&H5D
6420 GOSUB 30000:'Loop SubRoutine
6500 KA=&H24
6510 KD=&H17
6520 GOSUB 30000:'Loop SubRoutine
6600 PRINT "----- LOOP END ----"
6610 GOTO 6000:'LOOP
20000 'Write Word
20100 _IOTPUT(N$,CHR$(2))
20110 READ V1$
20120 _IOTPUT(N$,CHR$(VAL("&H"+V1$)))
20130 READ V2$
20140 _IOTPUT(N$,CHR$(VAL("&H"+V2$)))
20150 PRINT "["+V1$+V2$+"]"
20200 RETURN
21000 'Write Multi Bytes
21100 READ CN
21110 _IOTPUT(N$,CHR$(CN))
21120 PRINT "[";
21230 FOR IM=1 TO CN
21240 READ V$
21250 PRINT V$;
21260 _IOTPUT(N$,CHR$(VAL("&H"+V$)))
21270 NEXT IM
21280 PRINT "]"
21300 RETURN
22000 'Write Custom Word
22100 _IOTPUT(N$,CHR$(2))
22110 _IOTPUT(N$,CHR$(VAL("&H"+V$ )))
22120 _IOTPUT(N$,CHR$(VAL("&H"+V2$)))
22200 RETURN
30000 'Loop SubRoutine
30100 GOSUB 31000:'KEYON
30110 FOR I=0 TO 320:NEXT I:'WAIT
30120 GOSUB 32000:'KEYOFF
30130 FOR I=0 TO 130:NEXT I:'WAIT
30140 RETURN
31000 'KEYON
31010 PRINT "---- KEYON ----"
31100 RESTORE 50000:'KEYONDB
31110 GOSUB 20000:'Write Word
31120 GOSUB 20000:'Write Word
31130 READ V$
31140 V2$=HEX$(KA)
31150 GOSUB 22000:'Write Custom Word
31160 READ V$
31170 V2$=HEX$(KD)
31180 GOSUB 22000:'Write Custom Word
31190 GOSUB 20000:'Write Word
31200 RETURN
32000 'KEYOFF
32010 PRINT "---- KEYOFF ----"
32100 RESTORE 51000:'KEYOFFDB
32110 GOSUB 20000:'Write Word
32120 RETURN
40000 'INITDB
40010 DATA 1D,00,02,0E
40020 DATA 00,01,01,00,1A,A3
40030 DATA 1A,00
40040 DATA 02,04
40050 DATA 02,00,19,F0,1B,3F,14,00,03,01,08,F6
40060 DATA 08,00,09,F8,0A,00,17,40,18,00
41000 'TONEDB
41010 DATA 08,F6
41020 DATA 08,00
41030 DATA 36,07,81,01,85,00,7F,F4,BB,00,10,40,00,AF,A0,0E,03,10,40,00,2F,F3,9B,00,20,41,00,AF,A0,0E,01,10,40,80,03,81,80
42000 'CHANNELDB
42010 DATA 0F,30
42020 DATA 10,71
42030 DATA 11,00
42040 DATA 12,08
42050 DATA 13,00
50000 'KEYDB
50010 DATA 0B,00
50020 DATA 0C,54
50030 DATA 0D
50040 DATA 0E
50050 DATA 0F,40
51000 'KEYOFFDB
51010 DATA 0F,00
MSX0とArduino、YMF825Boardの接続
今回は、Arduino(3.3V)とYMF825Board(5V)の電源をMSX0から共有してみようと思います。
とりあえず、仲介役のArduino Mini Proとは別に、Arduino UNOを用意します。
MSX0のPORT Aの[5V]と[GND]をArduino UNOの[VIN]と[GND]に繋ぎます。
MSX0(PORTA) ←→ Arduino UNO
[5V] ←→ [VIN]
[GND] ←→ [GND](どれでもOK)
すると、Arduino UNOの電源が入りますので、そこから[5V]をYMF825Boardに、[3.3V]をArduino Mini Proに繋ぎます。
Arduino UNO ←→ YMF825 Board
[5V] ←→ [5V]
[GND](どれでもOK) ←→ [GND]
Arduino UNO ←→ Arduino Pro Mini
[3.3V] ←→ [VCC]
[GND](どれでもOK) ←→ [GND](どれでもOK)
次にMSX0とArduino Pro Mini、Arduino Pro MiniとYMF825Boardを接続します。
MSX0(PORTA) ←→ Arduino Pro Mini
[SDA] ←→ [A4]
[SCK] ←→ [A5]
Arduino Pro Mini ←→ YMF825Board
[D10] ←→ [SS]
[D11] ←→ [MOSI]
[D12] ←→ [MISO]
[D13] ←→ [SCK]
[D9] ←→ [RST_N]
中の人はYMF825の仕様のことが全然わからないので、オリジナルの音色やメロディとかが作れず、参考にしたサンプルコードの通りに、ドレミファソと鳴らすだけになっておりますが、ひとまず動きました、という感じです。
現場からは以上です。
コメント
コメントを投稿