Forums

USB Device Class for MIDI - followup

Started by radim100 September 5, 2009
I was just wondering if anybody has some LPC214X or LPC2100 based
MIDI USB Class code available to share .
Thanks. Radim

An Engineer's Guide to the LPC2100 Series

The implementation of USB audio MIDI class is easy.
It's good for exercise of class implementation.
Then, try it.

0) Start with an existing USB example, such as HID, CDC (virtual COM port).
Confirm that this example works on your board - if not, fix it.

1) Descriptor work
Replace the descriptors following the example on the USB MIDI spec.

http://www.usb.org/developers/devclass_docs/midi10.pdf
Appendix B. Example: Simple MIDI Adapter (p37)

This PIC descriptor is coded with above example.
http://www.microchip.com/forums/tm.aspx?m71796&mpage=2#376451
Modifying a little for LPC, it is directly available for you.

Device descriptor
- Vendor ID / Product ID
Apply a temporary pair, such as 0xFFFF/0x0000, for development
Before releasing your device, get an official one.

Configuration set
Move IN and OUT endpoint address (bEndpointAddress) to EP2 for bulk.

With this change, your device is recognized as a MIDI device immediately, when it is connected to a PC. But to make it work, you have to add a little more code to it.

2) Endpoint work
In and OUT endpoints exchange USB-MIDI event packets, defined on this chapter of the spec.

http://www.usb.org/developers/devclass_docs/midi10.pdf
4 USB-MIDI Event Packets (p16)

As this chapter describes, USB add a prefix byte before usual 3-byte MIDI packet.
Put this 4-byte packet(s) to the IN endpoint 2, when the device sees key make/ release.

3) Clean up your code

That's all.

Tsuneo

--- In l..., "radim100" wrote:
>
> I was just wondering if anybody has some LPC214X or LPC2100 based
> MIDI USB Class code available to share .
> Thanks. Radim
>
Hello everyone,

I found this topic about USB-MIDI to be already very helpful in
designing a simple MIDI device using the LPC2148. Following the steps
provided I already got the device enumerating as a USB Audio Device (by
correctly updating the device descriptors, they are provided below for
other people that may be looking for them ...).

Still, it doesn't work as anticipated, which is why I have some
questions:

1) Configuration set
Move IN and OUT endpoint address (bEndpointAddress) to EP2 for bulk.

If I get it right, I simply change the bEndpointAddress descriptor to
the EP2 values (as is done in the descriptors below), but why would I do
such a thing? What's wrong with using EP1 for bulk, it's not currently
in use?
2) Endpoint work
In and OUT endpoints exchange USB-MIDI event packets, defined on this
chapter of
the spec.

As this chapter describes, USB add a prefix byte before usual 3-byte
MIDI
packet.
Put this 4-byte packet(s) to the IN endpoint 2, when the device sees key
make/
release.

I completely understand why this is required (it's the protocol
specified in the MIDI-USB specs) and how it works, but I have no idea of
how to actually implement it in my LPC2148 code!
What I basically would like to find out is HOW do I write a 32bit packet
into an endpoint. The USB stack is very complex and I am slowly getting
used to it but my overview is currently limited. If somebody could
provide me with a working piece of code (for instance a loop that
constantly sends a MIDI note on to the host) I would be very grateful!
Then there's the same thing around: where do I access the OUT endpoint,
which is the data provided by the host to the device? I would like the
device to respond to a MIDI note sent by an audio application, but again
have no idea of what function to use to make this happen?

3) Element Descriptor
The MIDI-USB specs state that an element can be described, but a basic
implementation apparently does not require this. Still: if I understand
it correct this instantiation defines how the different in and out jacks
are connected so why is there no element used here?

I have already come a long way in understanding the USB protocol, but
there is much, much still to be learned ;)
Thank you!

Nils
Descriptors used:
static const U8 abDescriptors[] = {

/* Device descriptor */
0x12,
DESC_DEVICE,
LE_WORD(0x0110), // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
MAX_PACKET_SIZE0, // bMaxPacketSize
LE_WORD(0xFFFF), // idVendor
LE_WORD(0x0001), // idProduct
LE_WORD(0x0100), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01, // bNumConfigurations

/* Configuration Descriptor */
0x09, // Size of this
descriptor in bytes
USB_DESCRIPTOR_CONFIGURATION, // CONFIGURATION
descriptor type
LE_WORD( 0x0069 ), // Total length of data
for this cfg
2, // Number of interfaces
in this cfg
1, // Index value of this
configuration
0, // Configuration string
index
_DEFAULT, // Attributes, see
usb_device.h
50, // Max power consumption
(2X mA)

// Audio control interface
/* Interface Descriptor */
0x09, // Size of this
descriptor in bytes
USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor
type
0, // Interface Number
0, // Alternate Setting
Number
0, // Number of endpoints
in this intf
0x01, // Class code -
Audio
0x01, // Subclass code -
Audio Control
0x00, // Protocol code -
no protocol
0, // Interface string
index

/* CS Interface descriptor - Audio control interface */
0x09, // bLength
0x24, // bDescriptorType -
class specific
0x01, // bDescriptorSubtype -
header
LE_WORD( 0x0100 ), // bcdADC -
spec revision, 1.0
LE_WORD( 0x0009 ), // wTotalLength -
total size of this CS interface
0x01, // bInCollection -
number of streaming interfaces
0x01, // baInterfaceNr -
streaming interface 1

// MIDIStreaming interface
/* Interface Descriptor */
0x09, // Size of this
descriptor in bytes
USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor
type
1, // Interface Number
0, // Alternate Setting
Number
2, // Number of endpoints
in this intf
0x01, // Class code -
Audio
0x03, // Subclass code -
MIDI Streaming
0x00, // Protocol code -
no protocol
0, // Interface string
index

/* CS Interface descriptor - MIDI streaming interface */
0x07, // bLength
0x24, // bDescriptorType -
class specific
0x01, // bDescriptorSubtype -
header
LE_WORD( 0x0100 ), // bcdADC -
spec revision, 1.0
LE_WORD( 0x0041 ), // wTotalLength -
total size of this CS interface

/* MIDI IN jack - embedded */
0x06, // bLength
0x24, // bDescriptorType -
class specific
0x02, // bDescriptorSubtype -
MIDI_IN_JACK
0x01, // bJackType -
EMBEDDED
0x01, // bJackID
0x00, // iJack

/* MIDI IN jack - external */
0x06, // bLength
0x24, // bDescriptorType -
class specific
0x02, // bDescriptorSubtype -
MIDI_IN_JACK
0x02, // bJackType -
EXTERNAL
0x02, // bJackID
0x00, // iJack

/* MIDI OUT jack - embedded */
0x09, // bLength
0x24, // bDescriptorType -
class specific
0x03, // bDescriptorSubtype -
MIDI_OUT_JACK
0x01, // bJackType -
EMBEDDED
0x03, // bJackID
0x01, // bNrInputPins
0x02, // baSourceID
0x01, // baSourcePin
0x00, // iJack

/* MIDI OUT jack - external */
0x09, // bLength
0x24, // bDescriptorType -
class specific
0x03, // bDescriptorSubtype -
MIDI_OUT_JACK
0x02, // bJackType -
EXTERNAL
0x04, // bJackID
0x01, // bNrInputPins
0x01, // baSourceID
0x01, // baSourcePin
0x00, // iJack

/* Endpoint Descriptor - OUT */
0x09,
USB_DESCRIPTOR_ENDPOINT, // bDescriptorType -
Endpoint Descriptor
_EP02_OUT, // bEndpointAddress
_BULK, // bmAttributes
LE_WORD( USBGEN_EP_SIZE ), // wMaxPacketSize
0, // bInterval
0x00, // bRefresh
0x00, // bSynchAddress

/* MS Bulk OUT Endpoint Descriptor */
0x05, // bLength
0x25, // bDescriptorType -
CS_ENDPOINT
0x01, // bDescriptorSubtype -
MS_GENERAL
0x01, // bNumEmbMIDIJack
0x01, // baAssocJackID

/* Endpoint Descriptor - IN */
0x09,
USB_DESCRIPTOR_ENDPOINT, // bDescriptorType -
Endpoint Descriptor
_EP02_IN, // bEndpointAddress
_BULK, // bmAttributes
LE_WORD( USBGEN_EP_SIZE ), // wMaxPacketSize
0, // bInterval
0x00, // bRefresh
0x00, // bSynchAddress

/* MS Bulk IN Endpoint Descriptor */
0x05, // bLength
0x25, // bDescriptorType -
CS_ENDPOINT
0x01, // bDescriptorSubtype -
MS_GENERAL
0x01, // bNumEmbMIDIJack
0x03, // baAssocJackID

// string descriptors
0x04,
DESC_STRING,
LE_WORD(0x0409),

// manufacturer string
0x0E,
DESC_STRING,
'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,

// product string
0x12,
DESC_STRING,
'M', 0, 'I', 0, 'D', 0, 'I', 0, 'T', 0, 'E', 0, 'S', 0, 'T', 0,

// serial number string
0x12,
DESC_STRING,
'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,

// terminator
0
};
Hello Nils,

can you tell us what you have taken as base for your implementation.
I never have implemented MIDI with lpcusb, but when midi uses
bulk endpoints, then main_serial.c is perhaps a good starting point
to integrate your descriptors (and adapt andpoint numbers).

May i ask where your midi data is coming from ? Do you connect
a serial midi device or some chip like VS1053B ?

Best regards,

Martin
--- In l..., "nilstijtgat" wrote:
>
> Hello everyone,
>
> I found this topic about USB-MIDI to be already very helpful in
> designing a simple MIDI device using the LPC2148. Following the steps
> provided I already got the device enumerating as a USB Audio Device (by
> correctly updating the device descriptors, they are provided below for
> other people that may be looking for them ...).
>
> Still, it doesn't work as anticipated, which is why I have some
> questions:
>
> 1) Configuration set
> Move IN and OUT endpoint address (bEndpointAddress) to EP2 for bulk.
>
> If I get it right, I simply change the bEndpointAddress descriptor to
> the EP2 values (as is done in the descriptors below), but why would I do
> such a thing? What's wrong with using EP1 for bulk, it's not currently
> in use?
> 2) Endpoint work
> In and OUT endpoints exchange USB-MIDI event packets, defined on this
> chapter of
> the spec.
>
> As this chapter describes, USB add a prefix byte before usual 3-byte
> MIDI
> packet.
> Put this 4-byte packet(s) to the IN endpoint 2, when the device sees key
> make/
> release.
>
> I completely understand why this is required (it's the protocol
> specified in the MIDI-USB specs) and how it works, but I have no idea of
> how to actually implement it in my LPC2148 code!
> What I basically would like to find out is HOW do I write a 32bit packet
> into an endpoint. The USB stack is very complex and I am slowly getting
> used to it but my overview is currently limited. If somebody could
> provide me with a working piece of code (for instance a loop that
> constantly sends a MIDI note on to the host) I would be very grateful!
> Then there's the same thing around: where do I access the OUT endpoint,
> which is the data provided by the host to the device? I would like the
> device to respond to a MIDI note sent by an audio application, but again
> have no idea of what function to use to make this happen?
>
> 3) Element Descriptor
> The MIDI-USB specs state that an element can be described, but a basic
> implementation apparently does not require this. Still: if I understand
> it correct this instantiation defines how the different in and out jacks
> are connected so why is there no element used here?
>
> I have already come a long way in understanding the USB protocol, but
> there is much, much still to be learned ;)
> Thank you!
>
> Nils
> Descriptors used:
> static const U8 abDescriptors[] = {
>
> /* Device descriptor */
> 0x12,
> DESC_DEVICE,
> LE_WORD(0x0110), // bcdUSB
> 0x00, // bDeviceClass
> 0x00, // bDeviceSubClass
> 0x00, // bDeviceProtocol
> MAX_PACKET_SIZE0, // bMaxPacketSize
> LE_WORD(0xFFFF), // idVendor
> LE_WORD(0x0001), // idProduct
> LE_WORD(0x0100), // bcdDevice
> 0x01, // iManufacturer
> 0x02, // iProduct
> 0x03, // iSerialNumber
> 0x01, // bNumConfigurations
>
> /* Configuration Descriptor */
> 0x09, // Size of this
> descriptor in bytes
> USB_DESCRIPTOR_CONFIGURATION, // CONFIGURATION
> descriptor type
> LE_WORD( 0x0069 ), // Total length of data
> for this cfg
> 2, // Number of interfaces
> in this cfg
> 1, // Index value of this
> configuration
> 0, // Configuration string
> index
> _DEFAULT, // Attributes, see
> usb_device.h
> 50, // Max power consumption
> (2X mA)
>
> // Audio control interface
> /* Interface Descriptor */
> 0x09, // Size of this
> descriptor in bytes
> USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor
> type
> 0, // Interface Number
> 0, // Alternate Setting
> Number
> 0, // Number of endpoints
> in this intf
> 0x01, // Class code -
> Audio
> 0x01, // Subclass code -
> Audio Control
> 0x00, // Protocol code -
> no protocol
> 0, // Interface string
> index
>
> /* CS Interface descriptor - Audio control interface */
> 0x09, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x01, // bDescriptorSubtype -
> header
> LE_WORD( 0x0100 ), // bcdADC -
> spec revision, 1.0
> LE_WORD( 0x0009 ), // wTotalLength -
> total size of this CS interface
> 0x01, // bInCollection -
> number of streaming interfaces
> 0x01, // baInterfaceNr -
> streaming interface 1
>
> // MIDIStreaming interface
> /* Interface Descriptor */
> 0x09, // Size of this
> descriptor in bytes
> USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor
> type
> 1, // Interface Number
> 0, // Alternate Setting
> Number
> 2, // Number of endpoints
> in this intf
> 0x01, // Class code -
> Audio
> 0x03, // Subclass code -
> MIDI Streaming
> 0x00, // Protocol code -
> no protocol
> 0, // Interface string
> index
>
> /* CS Interface descriptor - MIDI streaming interface */
> 0x07, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x01, // bDescriptorSubtype -
> header
> LE_WORD( 0x0100 ), // bcdADC -
> spec revision, 1.0
> LE_WORD( 0x0041 ), // wTotalLength -
> total size of this CS interface
>
> /* MIDI IN jack - embedded */
> 0x06, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x02, // bDescriptorSubtype -
> MIDI_IN_JACK
> 0x01, // bJackType -
> EMBEDDED
> 0x01, // bJackID
> 0x00, // iJack
>
> /* MIDI IN jack - external */
> 0x06, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x02, // bDescriptorSubtype -
> MIDI_IN_JACK
> 0x02, // bJackType -
> EXTERNAL
> 0x02, // bJackID
> 0x00, // iJack
>
> /* MIDI OUT jack - embedded */
> 0x09, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x03, // bDescriptorSubtype -
> MIDI_OUT_JACK
> 0x01, // bJackType -
> EMBEDDED
> 0x03, // bJackID
> 0x01, // bNrInputPins
> 0x02, // baSourceID
> 0x01, // baSourcePin
> 0x00, // iJack
>
> /* MIDI OUT jack - external */
> 0x09, // bLength
> 0x24, // bDescriptorType -
> class specific
> 0x03, // bDescriptorSubtype -
> MIDI_OUT_JACK
> 0x02, // bJackType -
> EXTERNAL
> 0x04, // bJackID
> 0x01, // bNrInputPins
> 0x01, // baSourceID
> 0x01, // baSourcePin
> 0x00, // iJack
>
> /* Endpoint Descriptor - OUT */
> 0x09,
> USB_DESCRIPTOR_ENDPOINT, // bDescriptorType -
> Endpoint Descriptor
> _EP02_OUT, // bEndpointAddress
> _BULK, // bmAttributes
> LE_WORD( USBGEN_EP_SIZE ), // wMaxPacketSize
> 0, // bInterval
> 0x00, // bRefresh
> 0x00, // bSynchAddress
>
> /* MS Bulk OUT Endpoint Descriptor */
> 0x05, // bLength
> 0x25, // bDescriptorType -
> CS_ENDPOINT
> 0x01, // bDescriptorSubtype -
> MS_GENERAL
> 0x01, // bNumEmbMIDIJack
> 0x01, // baAssocJackID
>
> /* Endpoint Descriptor - IN */
> 0x09,
> USB_DESCRIPTOR_ENDPOINT, // bDescriptorType -
> Endpoint Descriptor
> _EP02_IN, // bEndpointAddress
> _BULK, // bmAttributes
> LE_WORD( USBGEN_EP_SIZE ), // wMaxPacketSize
> 0, // bInterval
> 0x00, // bRefresh
> 0x00, // bSynchAddress
>
> /* MS Bulk IN Endpoint Descriptor */
> 0x05, // bLength
> 0x25, // bDescriptorType -
> CS_ENDPOINT
> 0x01, // bDescriptorSubtype -
> MS_GENERAL
> 0x01, // bNumEmbMIDIJack
> 0x03, // baAssocJackID
>
> // string descriptors
> 0x04,
> DESC_STRING,
> LE_WORD(0x0409),
>
> // manufacturer string
> 0x0E,
> DESC_STRING,
> 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,
>
> // product string
> 0x12,
> DESC_STRING,
> 'M', 0, 'I', 0, 'D', 0, 'I', 0, 'T', 0, 'E', 0, 'S', 0, 'T', 0,
>
> // serial number string
> 0x12,
> DESC_STRING,
> 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,
>
> // terminator
> 0
> };
>

I used the HID example which comes as an example with the LPCUSB stack
(main_hid.c). I used this file because Tsuneo suggested to start with a
HID or CDC example and modify it. The example works correctly and is
recognized as a standard HID device. Next up I simply modified the
descriptors as in my previous post, so that the device is now a
'Standard Audio Device'.

What I would like to do first now, is write a piece of code that sends
out a MIDI pattern to the host (in my case: my PC running Ableton Live,
with the Audio Device used as MIDI input for a VSTi), just to see if I
can get MIDI-USB data transferred. Next, I would like to be able to send
a certain command to the LPC2148 (let's say a note on) and have the
microprocessor read this and react accordingly.

Now if I understand what you are saying correctly: I should use the
serial example to find out how bulk data is transferred over the USB
bus? I will look at it right away, but I am very new to this and all
help is greatly appreciated ;)

Nils
Hello Nils,

sorry, last email was very quick, my family called for lunch :-)

If you have a look into main_serial.c
There is a function
static void BulkOut(U8 bEP, U8 bEPStatus)
In/Out is always from point of view of PC.
So BulkOut means the PC sends something to LPC2148.
Inside function BulkOut you receive data which the PC sends out.

Next direction LPC2148 to PC:
static void BulkIn(U8 bEP, U8 bEPStatus)
directly calls
static void SendNextBulkIn(U8 bEP, BOOL fFirstPacket)
It is called when the PC requests data.
Data is put into send queue with
USBHwEPWrite(bEP, abBulkBuf, iLen);

So when you take the serial example, try to put data with some
function (i have not looked which one) into txfifo,
or try to directly call USBHwEPWrite with some MIDI data
and look if PC receives it. (Perhaps do some mechanism / counter /
timer to send a command by button press or by a ctr / timer e.g.
every second).

Pay attention: It is long time ago since i used lpcusb the last time
and never have done MIDI, so above could be wrong...

Best regards,

Martin