YMF825をArduino Leonardoで鳴らす

## UNOとLeonardoは何が違う
Aruduino LeonardoはMPUにAtmega32U4(以下32U4)を使用したマイコンボードです
32u4はUSBインターフェイスを備え、HIDやUSB-MIDIなどのUSBデバイスを作る事が出来I/Oポート数も多いためPINの取り出しがいくらかUNOとは異なります
SPI回りがISP端子のみとなっておりSPIインターフェイスを使用した場合入出力ポートを３本多く使う事が出来、キー数が稼げるので以前本家Sample2を使ってUNOで作ったYMF825のキーボードをLeonardoで作って見ました。



![SANY0197.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205783/97e6775e-cc7c-e745-3235-73870781730e.jpeg)

## UNOからの変更点
ソースはほぼそのままfmsd1_ino.cppのSS_PINの設定を変えただけ、YMF825のリセットは鍵盤数を稼ぐためパワーオンリセットに任せてISP端子からMOSI,SCKのみを取り出しSSはA5をデジタル出力として使用しています。
![SANY0201.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205783/5f623540-0595-3f59-6469-86967fadfae9.jpeg)
![SANY0208.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205783/2e5dd144-29fc-3f6c-6fa3-29aea3debb1d.jpeg)

<details>
<summary>fsd1_ino.cpp</summary><div>

```fsd1_ino.cpp

// fmsd1_ino.cpp
#include	<SPI.h>
#define SS_PIN A5
void writeSingleCPP( unsigned char adrs, unsigned char data )
{
	digitalWrite(SS_PIN, LOW);	//	select slave
	SPI.transfer(adrs);
	SPI.transfer(data);
	digitalWrite(SS_PIN, HIGH);
	delay(1);
}
extern "C" void writeSingle( unsigned char adrs, unsigned char data ){ writeSingleCPP(adrs,data);}
extern "C" void writeBurst( unsigned char adrs, unsigned char* data, int count )
{
	digitalWrite(SS_PIN, LOW);
	SPI.transfer(adrs);
	for (int i = 0; i<count; i++) {
		SPI.transfer(*(data + i));
	}
	digitalWrite(SS_PIN, HIGH);
	delay(1);
}
extern "C" void delayMs( int ms ){	delay(ms);}

void initSPI(void)
{
	//	init SPI
	/*
	* Conditions only for Arduino UNO
	*  SPI master
	*  RST_N- 
	*  SS   - A5
	*  MOSI - MOSI
	*  MISO - 
	*  SCK  - SCK
	*/
	pinMode(SS_PIN, OUTPUT);
	digitalWrite(SS_PIN, HIGH);
	SPI.setBitOrder(MSBFIRST);
	SPI.setClockDivider(SPI_CLOCK_DIV4);	//	4MHz
	SPI.setDataMode(SPI_MODE0);
	SPI.begin();
}
void initSD1( void )
{
	//	1.	powerOnReset( void );
	pinMode(9, OUTPUT);
	digitalWrite(9, LOW);

	//	2. wait 100usec
	delay(1);

	//	3.	RST_N : high
	digitalWrite(9, HIGH);

	//	4.	DRV_SEL	: low
	writeSingleCPP(29, 0);  //  5v

	//	5.	AP0 : 0
	writeSingleCPP(2, 0x0e);

	//	6.	wait for Quarz stability
	delay(1);

	//	7.	CLKE : "1"
	writeSingleCPP(0, 0x01);

	//	8.	ALRST : low
	writeSingleCPP(1, 0x00);

	//	9.	SFTRST	:	0xa3
	writeSingleCPP(26, 0xa3);
	delay(1);

	//	10.	SFTRST	:	0x00
	writeSingleCPP(26, 0x00);

	//	11.	wait 30msec
	delay(30);

	//	12.	AP1 AP3: "0"
	writeSingleCPP(2, 0x04);

	//	13.	wait 10usec
	delayMicroseconds(10);

	//	14.	AP2: "0"
	writeSingleCPP(2, 0x00);

	writeSingleCPP(25, 0x60);	//	0[dB]
	writeSingleCPP(27, 0x3f);	//	set itp max
	writeSingleCPP(20, 0x00);	//	set itp on
	writeSingleCPP(3, 0x01);	//	amp gain(6.5[dB])
	writeSingleCPP(9, 0xb8);	//	Sequencer Volume

```
</div>
</details>



##ブレッドボード上でキーボードにする
![SANY0207.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205783/1a36183b-5792-fb7b-701d-1dd658fc5ac2.jpeg)

手持ちのタクトスイッチがブレッドボードに上手く刺さったので、使用できる入力ポートを全てINPUT_PULLUPとしてキーが押されたらGNDへ落とすように配線しました
チャタリング対策は一度LOWへ落ちたらカウンタをセットし一定時間は入力を無視するようにソフトウェアで対策し全キーをスキャンして押されたキーに対応するMIDIコマンドをsample2のMIDIライブラリに送って音を鳴らしています
A4ポートに繋がるキーをCOMMANDキーに設定し、このキーが押されたら次に押されたキーにより音色の変更やオクターブシフトを行えるようにし音程用にA3～A0,0～13PINの順にキーを接続します。
###追加機能
せっかくUSBデバイスに出来るので
MIDI-USBライブラリをインクルードしてUSB-MIDIキーボード兼用にしてみました。
sample2のソースに、ここのソース２つ貼り付けてコンパイルすれば鳴るはず。
<details>
<summary>tinyYmf825keyboarlleonardoMIDI.ino</summary><div>

```tinyYmf825KeyboardleonardoMIDI.ino
#include <frequencyToNote.h>
#include <MIDIUSB.h>
#include <MIDIUSB_Defs.h>
#include <pitchToFrequency.h>
#include <pitchToNote.h>

extern "C" {
#include "fmif.h"
}
#include "fmsd1.h"

#define COMMAND_KEY  A4
#define SS_PIN  A5


midiEventPacket_t event;

//                      C  #C   D  #D   E   F  #F   G  #G   A  #A   B   C  #C   D    #D    E    F
uint8_t pin_no[18] = { A3, A2, A1, A0,  0,  1,  2,  3  , 4,  5  , 6,  7,  8,  9,  10,  11,  12,  13};
uint8_t state[18];
uint8_t note_no[18];

int pg_no = 0;
uint8_t pg_state = 0;
int tone_offset = 60;
unsigned char  master_vol = 0x60;

void setup() {
  initSPI();
  initSD1();
  Fmdriver_init();

  TIMSK0 = 0;

  for (int i = 0; i < 18; i++) {
    pinMode(pin_no[i], INPUT_PULLUP);
    state[i] = 0;
  }
  pinMode(COMMAND_KEY, INPUT_PULLUP);


}

void loop() {
  int command;
  for (int i = 0; i < 18; i++) {
    if (HIGH == digitalRead(pin_no[i])) {

      if (state[i] == 1) {
        state[i] = 0;
        Fmdriver_sendMidi(0x90);
        Fmdriver_sendMidi(note_no[i]);
        Fmdriver_sendMidi(0);

        event = {9, 0x90, note_no[i], 0};
        MidiUSB.sendMIDI(event);
      } else {
        if (state[i] > 1)
          state[i] = state[i] - 1;
      }
    } else {
      if (state[i] == 0) {
        state[i] = 255;

        uint8_t note = i + tone_offset ;
        note_no[i] = note;
        Fmdriver_sendMidi(0x90);
        Fmdriver_sendMidi(note);
        Fmdriver_sendMidi(120);

        event = {9, 0x90, note, 120};
        MidiUSB.sendMIDI(event);
      }
    }
    if (digitalRead(COMMAND_KEY) == LOW) {
      if (pg_state == 0) {
        pg_state = 1;
        while (1) {
          for ( command = 0; command < 15; command++) {
            if (digitalRead(pin_no[command]) == LOW)
              break;
          }
          if (command < 14)
            break;
        }

        switch (command) {
          case 0:   //プログラムチェンジ +
            pg_no--;
            if (pg_no < 0)
              pg_no = 7;
            Fmdriver_sendMidi(0xc0);
            Fmdriver_sendMidi(pg_no);
            break;

          case 1:   //プログラムチェンジ -
            pg_no++;
            if (pg_no > 7)
              pg_no = 0;
            Fmdriver_sendMidi(0xc0);
            Fmdriver_sendMidi(pg_no);
            break;


          case 2:   //オクターブシフトー
            tone_offset -= 12;
            if (tone_offset < 0)
              tone_offset += 12;
            break;

          case 3:  //オクターブシフト＋
            tone_offset += 12;
            if (tone_offset > 108)
              tone_offset -= 12;
            break;

          case 4:
            tone_offset = 60;
            break;

          case 5:
            tone_offset--;
            if (tone_offset < 0)
              tone_offset = 0;
            break;
          case 6:
            tone_offset++;
            if (tone_offset > 108)
              tone_offset = 108;
            break;

          case 7:  //volume-
            master_vol -= 8;
            if (master_vol < 0x20)
              master_vol = 0x10;
            Fmdriver_sendMidi(0xB0);
            Fmdriver_sendMidi(0x07);
            Fmdriver_sendMidi(master_vol);
            break;

          case 8:  //volume+
            master_vol += 8;
            if (master_vol > 0x78)
              master_vol = 0x78;
            Fmdriver_sendMidi(0xb0);
            Fmdriver_sendMidi(0x07);
            Fmdriver_sendMidi(master_vol);
            break;

          default:
            break;
        }
        delay(500); //チャタリング対策
      }

    } else {
      if ( pg_state == 1) {
        pg_state = 0;
      }
    }
  }
  MidiUSB.flush();
}
 
```
</div>
</detail>

##おまけ
<details>
<summary>変な音色含んだfmtone.c</summary><div>

```fmtone.c
//	fmtone.c
#include	"fmtype.h"
#include	"fmtone.h"
#include	"fmsd1.h"

#define		IMMUTABLE_TONE_MAX		8
#define		MUTABLE_TONE_MAX		IMMUTABLE_TONE_MAX
#define		AVAILABLE_TONE_NUMBER	(IMMUTABLE_TONE_MAX+MUTABLE_TONE_MAX)
#define		MAX_EXCLUSIVE_HEADER_SIZE	5

#define 	MAX_ELEMENT_PRM			2
#define		OPERATOR_PRM_REG_SZ		7
#define 	MAX_TONE_PRM_SZ 		(MAX_FM_OPERATOR*OPERATOR_PRM_REG_SZ + MAX_ELEMENT_PRM)

typedef enum {
	WAIT_DATA,
	DURING_SETTING,
	SET_STATE_MAX
} SET_STATE;

//	Variable
static SET_STATE _toneSetState;
static int _tprmIndex;
static ToneData _userTone[MUTABLE_TONE_MAX];
static const ToneData TPRM[IMMUTABLE_TONE_MAX] = {

	{	//	GrandPiano
0x2b,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x01,0x0f,0x07,0x00,0x06,0x0f,0x27,0x00,0x01,0x08}, 
   {0x07,0x0a,0x03,0x02,0x03,0x03,0x1c,0x00,0x06,0x00}, 
   {0x00,0x0b,0x03,0x02,0x04,0x01,0x18,0x01,0x01,0x00}, 
   {0x06,0x0d,0x03,0x02,0x06,0x04,0x00,0x01,0x01,0x00}, 
}

    /*
		0x0b,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x01,0x0f,0x07,0x00,0x06,0x0f,0x27,0x00,0x01,0x08},	// op1
			{0x07,0x0e,0x03,0x02,0x03,0x02,0x28,0x00,0x05,0x00},	// op2
			{0x00,0x0d,0x01,0x01,0x04,0x03,0x22,0x01,0x01,0x00},	// op3
			{0x06,0x0d,0x02,0x02,0x06,0x04,0x00,0x01,0x01,0x00}		// op4
		}
   */
	},
	{	// E.Piano
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x54,0x0f,0x04,0x05,0x0c,0x0b,0x23,0x44,0x07,0x12},	// op1
			{0x02,0x0f,0x02,0x01,0x08,0x0f,0x04,0x45,0x01,0x00},	// op2
			{0x25,0x0f,0x00,0x01,0x0b,0x01,0x12,0x44,0x01,0x00},	// op3
			{0x04,0x0f,0x02,0x01,0x07,0x0f,0x04,0x41,0x01,0x00}		// op4
		}
	},
	{	//	TenorSax
0x35,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x70,0x07,0x03,0x01,0x05,0x01,0x18,0x00,0x22,0x00}, 
   {0x00,0x07,0x02,0x03,0x06,0x03,0x00,0x00,0x42,0x00}, 
   {0x70,0x08,0x03,0x01,0x05,0x01,0x1a,0x00,0x21,0x00}, 
   {0x02,0x08,0x02,0x03,0x06,0x03,0x08,0x03,0x12,0x00}, 
}

    /*
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x36,0x07,0x03,0x00,0x00,0x00,0x05,0x44,0x01,0x01},	// op1
			{0x00,0x07,0x02,0x00,0x09,0x00,0x0f,0x43,0x01,0x08},	// op2
			{0x36,0x07,0x03,0x00,0x00,0x00,0x08,0x44,0x01,0x09},	// op3
			{0x02,0x07,0x02,0x00,0x09,0x00,0x0d,0x43,0x01,0x00}		// op4
		}*/
	},
	{	//	PickBass
//SynsTrumpet
0x27,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x50,0x07,0x00,0x00,0x04,0x00,0x08,0x17,0x41,0x02}, 
   {0x00,0x07,0x02,0x06,0x05,0x06,0x0c,0x10,0x61,0x06}, 
   {0x00,0x09,0x03,0x05,0x04,0x05,0x08,0x00,0x31,0x00}, 
   {0x00,0x0b,0x04,0x00,0x04,0x00,0x0c,0x30,0x31,0x04}, 
}
    
    /*
		0x0b,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x56,0x0f,0x07,0x02,0x03,0x01,0x13,0x44,0x01,0x00},	// op1
			{0x04,0x0c,0x0b,0x04,0x06,0x07,0x15,0x44,0x07,0x00},	// op2
			{0x06,0x0f,0x09,0x02,0x06,0x02,0x17,0x44,0x02,0x00},	// op3
			{0x04,0x0b,0x02,0x06,0x08,0x06,0x00,0x44,0x01,0x00}		// op4
		}*/
	},
	{	//	TnklBell
0x37,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x30,0x08,0x02,0x00,0x05,0x08,0x00,0x10,0x41,0x09}, 
   {0x00,0x07,0x04,0x03,0x03,0x02,0x08,0x30,0x31,0x00}, 
   {0x02,0x06,0x06,0x00,0x04,0x00,0x00,0x05,0x41,0x09}, 
   {0x00,0x07,0x05,0x00,0x04,0x02,0x06,0x03,0x31,0x1b}, 
}

 /*   
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x31,0x0f,0x06,0x03,0x04,0x05,0x10,0x44,0x0e,0x00},	// op1
			{0x02,0x0c,0x06,0x07,0x06,0x0e,0x0b,0x44,0x02,0x00},	// op2
			{0x00,0x0c,0x06,0x02,0x02,0x05,0x1e,0x44,0x77,0x01},	// op3
			{0x00,0x0f,0x05,0x04,0x05,0x0d,0x01,0x54,0x06,0x00}		// op4
		}
   */
	},
	{	//	NewAgePd
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x54,0x0f,0x0f,0x03,0x03,0x00,0x26,0x44,0x07,0x01},	// op1
			{0x02,0x0f,0x07,0x04,0x04,0x00,0x0b,0x44,0x05,0x00},	// op2
			{0x62,0x06,0x01,0x00,0x01,0x00,0x18,0x03,0x71,0x01},	// op3
			{0x02,0x08,0x01,0x00,0x05,0x01,0x00,0x03,0x01,0x00}		// op4
		}
	},
	{	//	Rim Shot
//Scratch
0x48,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x00,0x0f,0x07,0x05,0x0f,0x0b,0x00,0x00,0x01,0x18}, 
   {0x00,0x0f,0x0f,0x05,0x0f,0x00,0x00,0x00,0x01,0x06}, 
   {0x13,0x0f,0x0f,0x00,0x02,0x03,0x26,0x00,0x02,0x08}, 
   {0x02,0x0a,0x0f,0x00,0x07,0x00,0x02,0x01,0x01,0x08}, 
}
    /*
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x7c,0x0f,0x00,0x05,0x05,0x00,0x05,0x44,0x0c,0x02},	// op1
			{0x0c,0x0f,0x07,0x07,0x07,0x07,0x00,0x44,0x0b,0x00},	// op2
			{0x08,0x0f,0x0a,0x06,0x06,0x08,0x00,0x44,0x0c,0x00},	// op3
			{0x08,0x0f,0x07,0x07,0x07,0x07,0x00,0x44,0x07,0x02}		// op4
		}
   */
	},
	{	//	Castanet

0x5c,  // VoiceCommon
{//  KC | AR | DR | SR | RR | SL | TL | VB | DT | WS
   {0x24,0x0c,0x07,0x05,0x03,0x06,0x0d,0x44,0x0b,0x00}, 
   {0x04,0x09,0x00,0x00,0x03,0x04,0x31,0x04,0x02,0x00}, 
   {0x02,0x0d,0x04,0x01,0x06,0x02,0x24,0x00,0x00,0x00}, 
   {0x00,0x09,0x09,0x02,0x08,0x01,0x00,0x00,0x01,0x00}, 
}

/*
		0x0d,	//	VoiceCommon
		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
			{0x68,0x0f,0x07,0x05,0x09,0x0f,0x02,0x44,0x07,0x01},	// op1
			{0x0c,0x0a,0x08,0x05,0x0f,0x0f,0x00,0x44,0x05,0x06},	// op2
			{0x08,0x0f,0x05,0x06,0x05,0x00,0x27,0x44,0x02,0x05},	// op3
			{0x08,0x0c,0x0a,0x09,0x09,0x0a,0x14,0x44,0x05,0x00}		// op4
		} */
	}

};
const unsigned char tExcCheck[MAX_EXCLUSIVE_HEADER_SIZE] = {
	0x43,	//	Exclusive:1, Yamaha ID
	0x7f,	//	Exclusive:2, Make/DIY ID1
	0x02,	//	Exclusive:3, Make/DIY ID2
	0x00,	//	Exclusive:4, YMF825 ID
	0x00	//	Exclusive:5, reserved
};
void Tone_init( void )
{
	int i;
	for ( i=0; i<MUTABLE_TONE_MAX; i++ ){
		_userTone[i] = TPRM[i];
	}
	_toneSetState = WAIT_DATA;
	_tprmIndex = 0;

	Tone_sendTone();
}
void Tone_setToneExc( unsigned char data, int excNum )
{
	if ( _toneSetState == WAIT_DATA ){
		if (( excNum == 1 ) && ( data == tExcCheck[0] )){
			_toneSetState = DURING_SETTING;
		}
	}
	else if ( _toneSetState == DURING_SETTING ){
		if ( excNum-1 < MAX_EXCLUSIVE_HEADER_SIZE ){
		 	if ( data != tExcCheck[excNum-1] ){ _toneSetState = WAIT_DATA; }
		}
		else if ( excNum == 6 ){
			if ( data < MUTABLE_TONE_MAX ){ _tprmIndex = data; }
			else { _toneSetState = WAIT_DATA; }
		}
		else if ( excNum == 7 ){
			_userTone[_tprmIndex].voiceCommon = data;
		}
		else if ( excNum < 48 ){
				_userTone[_tprmIndex].opPrm[(excNum-8)/MAX_OPERATOR_PRM][(excNum-8)%MAX_OPERATOR_PRM] = data;
			}
		else { _toneSetState = WAIT_DATA; }
	}
}
void Tone_sendTone( void )
{
	int	i,j;
	unsigned char regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 1 + 4]; // 485

	//	top
	regImage[0] = 0x80 + AVAILABLE_TONE_NUMBER;

	for ( i=0; i<AVAILABLE_TONE_NUMBER; i++ ){
		unsigned char* riPtr = &regImage[MAX_TONE_PRM_SZ*i + 1];

		ToneData* td;
		if ( i < IMMUTABLE_TONE_MAX ){
			td = (ToneData*)&(TPRM[i]);
		}
		else {
			td = (ToneData*)&(_userTone[i-IMMUTABLE_TONE_MAX]);
		}

		riPtr[0] = (td->voiceCommon & 0x60)>>5;
		riPtr[1] = ((td->voiceCommon & 0x18)<<3) | (td->voiceCommon & 0x07);

		for ( j=0; j<MAX_FM_OPERATOR; j++ ){
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+0] = (td->opPrm[j][3] << 4) | (td->opPrm[j][0] & 0x08) | ((td->opPrm[j][0] & 0x04)>>2);
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+1] = (td->opPrm[j][4] << 4) | td->opPrm[j][2];
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+2] = (td->opPrm[j][1] << 4) | td->opPrm[j][5];
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+3] = (td->opPrm[j][6] << 2) | (td->opPrm[j][0] & 0x03);
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+4] = td->opPrm[j][7];
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+5] = ((td->opPrm[j][8] & 0x0f) << 4) | ((td->opPrm[j][8] & 0xf0) >> 4);
			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+6] = (td->opPrm[j][9] << 3) | ((td->opPrm[j][0] & 0x70) >> 4);
		}
	}

	//	end 
	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 1] = 0x80;
	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 2] = 0x03;
	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 3] = 0x81;
	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 4] = 0x80;

	//	 Soft Reset
//    writeSingle(26,0xa3);
//    writeSingle(26,0x00);
//	writeSingle(8,0xf6);
  writeSingle(8,0x16);
	delayMs(1);
	writeSingle(8,0x00);

	writeBurst( 7, regImage, MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 5 );
}
```
</div>
</details>