Arduino NanoにUSBMIDIを搭載

ATmega328p搭載のArduino Nanoに回路を追加し、USBMIDIを使えるようにします。回路図は次の通りです。USBのコネクタを追加し、Nanoに接続します。

usbconfig.hをハードウェアに合わせて修正します。usbconfig.hは、Arduinoのスケッチを保存しているディレクトリ、私の場合はDocuments\Arduino\libraries\USBMIDI\srcにあります。メモ帳で開いて以下の3つの行を確認します。もち違った値が記載されていたら修正して下さい。

/* ---------------------------- Hardware Config ---------------------------- */

#ifndef USB_CFG_IOPORTNAME
#define USB_CFG_IOPORTNAME      D
#endif
/* This is the port where the USB bus is connected. When you configure it to
 * "B", the registers PORTB, PINB and DDRB will be used.
これは、USBバスが接続されているポートです。
「B」に設定すると、レジスタPORTB、PINB、DDRBが使用されます。
 */
#ifndef USB_CFG_DMINUS_BIT
#define USB_CFG_DMINUS_BIT      3
#endif
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
 * This may be any bit in the port.
これは、USB D-ラインが接続されているUSB_CFG_IOPORTのビット番号です。
これは、ポートの任意のビットである可能性があります。
 */
#ifndef USB_CFG_DPLUS_BIT
#define USB_CFG_DPLUS_BIT       2
#endif
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
 * This may be any bit in the port. Please note that D+ must also be connected
 * to interrupt pin INT0! [You can also use other interrupts, see section
 * "Optional MCU Description" below, or you can connect D- to the interrupt, as
 * it is required if you use the USB_COUNT_SOF feature. If you use D- for the
 * interrupt, the USB interrupt will also be triggered at Start-Of-Frame
 * markers every millisecond.]
これは、USB D +ラインが接続されているUSB_CFG_IOPORTのビット番号です。 
これは、ポートの任意のビットである可能性があります。 
D +も割り込みピンINT0に接続する必要があることに注意してください!
 [他の割り込みを使用することもできます。
以下の「オプションのMCUの説明」のセクションを参照してください。
または、USB_COUNT_SOF機能を使用する場合に必要なため、D-を割り込みに接続できます。
割り込みにD-を使用すると、USB割り込みもミリ秒ごとにフレーム開始マーカーで
トリガーされます。
 */

*
USBMIDIの安定化

USBMIDIのデバイスをPCにつないだ時、USBのデバイスとして認識できる場合とできない場合があります。あまり安定しているとは言い難い状況です。おまじないかもしれませんが、二つの事をやってみました。USBデバイスとして認識できるためには、usbmidi_vsb.cppのDescriptorを返す関数がきち呼ばれているかどうかを確認する必要があります。以下のようにSerial.printを入れてみました。

usbMsgLen_t usbFunctionDescriptor(usbRequest_t * rq)
{
//	Serial.println("usbFunctionDescriptor");

	if (rq->wValue.bytes[1] == USBDESCR_DEVICE)
	{
		Serial.println("USBDESCR_DEVICE");
		usbMsgPtr = (usbMsgPtr_t)deviceDescrMIDI;
		return sizeof(deviceDescrMIDI);
	}
	else if (rq->wValue.bytes[1] == USBDESCR_CONFIG)
	{
		Serial.println("USBDESCR_CONFIG");
		usbMsgPtr = (usbMsgPtr_t)configDescrMIDI;
		return sizeof(configDescrMIDI);
	}
	else if (rq->wValue.bytes[1] == USBDESCR_STRING)
	{
		Serial.println("USBDESCR_STRING");
		if (rq->wValue.bytes[0] == 1)
		{
			const uint8_t *data;
			usbMsgLen_t n = _usbmidi_get_vendor_string(data);
			usbMsgPtr = (usbMsgPtr_t)data;
			return n;
		}
		else if (rq->wValue.bytes[0] == 2)
		{
			const uint8_t *data;
			usbMsgLen_t n = _usbmidi_get_product_string(data);
			usbMsgPtr = (usbMsgPtr_t)data;
			return n;
		}
	}
	return 0;
}

PCがデバイスを認識するためには、usbFunctionDescriptorが複数回呼ばれる必要があります。Serial.Monitorで見ると以下のように表示されます。

USBDESCR_DEVICE
USBDESCR_DEVICE
USBDESCR_COFG
USBDESCR_STRING
USBDESCR_DEVICE
USBDESCR_CONFIG
USBDESCR_CONFIG
USBDESCR_CONFIG
USBDESCR_DEVICE
USBDESCR_DEVICE
USBDESCR_DEVICE
USBDESCR_DEVICE
SDESCR_DEVICE
USBDESCR_DEVICE
USBDESCR_DEVICE
USBDESCR_DEVICE
USBDEC_EIE
USBDESCR_DEVIE
USBDESCR_DEVICE
USBDESCR_DEVICE

ところどころシリアルが文字化けをしています。この情報が得られない場合は接続ができていません。

もう一つおまじないで、割り込みのポーリング間隔を短くします。解説を見ると低スペックのCPUの時は10msecより短くしてはいけない、と書かれています。とすると高スペックであれば短くすることができます。

#define USB_CFG_INTR_POLL_INTERVAL      8

エンドポイント 1 (割り込み入力) でバージョンをコンパイルすると、これがポーリング間隔になります。 値はミリ秒単位であり、低速デバイスの場合は 10 ミリ秒未満であってはなりません。