[stm32f429i-discovery] Added usbmidi example.

This board connects the USB HS interface to the micro usb connector as
opposed to the USB FS interface on the stm32f4-discovery board. This is
why we are using different pins, different periph and different driver.
At the end it should(TM) behave the same as the HS interface implements
FS too.
This commit is contained in:
Piotr Esden-Tempski
2015-01-14 16:50:44 -08:00
parent e8694d561f
commit a0d379f460
3 changed files with 462 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## This library is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this library. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = usbmidi
LDSCRIPT = ../stm32f429i-discovery.ld
include ../../Makefile.include

View File

@@ -0,0 +1,49 @@
------------------------------------------------------------------------------
README
------------------------------------------------------------------------------
This example implements a USB MIDI device to demonstrate the use of the
USB device stack. It implements the device configuration found in Appendix
B of the Universal Serial Bus Device Class Definition for MIDI Devices.
The 'USER' button sends note on/note off messages.
The board will also react to identity request (or any other data sent to
the board) by transmitting an identity message in reply.
Board connections:
------------------
CN6 (USB_OTG_HS) USB acting as device, connect to computer
Testing
-------
To list midi devices, which should include this demo device
$ amidi -l
Dir Device Name
IO hw:2,0,0 MIDI demo MIDI 1
$
To record events, while pushing the user button
$ amidi -d -p hw:2,0,0
90 3C 40 -- key down
80 3C 40 -- key up
90 3C 40
80 3C 40^C
12 bytes read
$
To query the system identity, note this dump matches sysex_identity[] in the
source.
$ amidi -d -p hw:2,0,0 -s Sysexdump.syx
F0 7E 00 7D 66 66 51 19 00 00 01 00 F7
^C
13 bytes read
$

View File

@@ -0,0 +1,388 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2014 Daniel Thompson <daniel@redfelineninja.org.uk>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/audio.h>
#include <libopencm3/usb/midi.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
/*
* All references in this file come from Universal Serial Bus Device Class
* Definition for MIDI Devices, release 1.0.
*/
/*
* Table B-1: MIDI Adapter Device Descriptor
*/
static const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200, /* was 0x0110 in Table B-1 example descriptor */
.bDeviceClass = 0, /* device defined at interface level */
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x6666, /* Prototype product vendor ID */
.idProduct = 0x5119, /* dd if=/dev/random bs=2 count=1 | hexdump */
.bcdDevice = 0x0100,
.iManufacturer = 1, /* index to string desc */
.iProduct = 2, /* index to string desc */
.iSerialNumber = 0,
.bNumConfigurations = 1,
};
/*
* Midi specific endpoint descriptors.
*/
static const struct usb_midi_endpoint_descriptor midi_bulk_endp[] = {{
/* Table B-12: MIDI Adapter Class-specific Bulk OUT Endpoint
* Descriptor
*/
.head = {
.bLength = sizeof(struct usb_midi_endpoint_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT,
.bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL,
.bNumEmbMIDIJack = 1,
},
.jack[0] = {
.baAssocJackID = 0x01,
},
}, {
/* Table B-14: MIDI Adapter Class-specific Bulk IN Endpoint
* Descriptor
*/
.head = {
.bLength = sizeof(struct usb_midi_endpoint_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT,
.bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL,
.bNumEmbMIDIJack = 1,
},
.jack[0] = {
.baAssocJackID = 0x03,
},
} };
/*
* Standard endpoint descriptors
*/
static const struct usb_endpoint_descriptor bulk_endp[] = {{
/* Table B-11: MIDI Adapter Standard Bulk OUT Endpoint Descriptor */
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 0x40,
.bInterval = 0x00,
.extra = &midi_bulk_endp[0],
.extralen = sizeof(midi_bulk_endp[0])
}, {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 0x40,
.bInterval = 0x00,
.extra = &midi_bulk_endp[1],
.extralen = sizeof(midi_bulk_endp[1])
} };
/*
* Table B-4: MIDI Adapter Class-specific AC Interface Descriptor
*/
static const struct {
struct usb_audio_header_descriptor_head header_head;
struct usb_audio_header_descriptor_body header_body;
} __attribute__((packed)) audio_control_functional_descriptors = {
.header_head = {
.bLength = sizeof(struct usb_audio_header_descriptor_head) +
1 * sizeof(struct usb_audio_header_descriptor_body),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_AUDIO_TYPE_HEADER,
.bcdADC = 0x0100,
.wTotalLength =
sizeof(struct usb_audio_header_descriptor_head) +
1 * sizeof(struct usb_audio_header_descriptor_body),
.binCollection = 1,
},
.header_body = {
.baInterfaceNr = 0x01,
},
};
/*
* Table B-3: MIDI Adapter Standard AC Interface Descriptor
*/
static const struct usb_interface_descriptor audio_control_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_AUDIO_SUBCLASS_CONTROL,
.bInterfaceProtocol = 0,
.iInterface = 0,
.extra = &audio_control_functional_descriptors,
.extralen = sizeof(audio_control_functional_descriptors)
} };
/*
* Class-specific MIDI streaming interface descriptor
*/
static const struct {
struct usb_midi_header_descriptor header;
struct usb_midi_in_jack_descriptor in_embedded;
struct usb_midi_in_jack_descriptor in_external;
struct usb_midi_out_jack_descriptor out_embedded;
struct usb_midi_out_jack_descriptor out_external;
} __attribute__((packed)) midi_streaming_functional_descriptors = {
/* Table B-6: Midi Adapter Class-specific MS Interface Descriptor */
.header = {
.bLength = sizeof(struct usb_midi_header_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MS_HEADER,
.bcdMSC = 0x0100,
.wTotalLength = sizeof(midi_streaming_functional_descriptors),
},
/* Table B-7: MIDI Adapter MIDI IN Jack Descriptor (Embedded) */
.in_embedded = {
.bLength = sizeof(struct usb_midi_in_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EMBEDDED,
.bJackID = 0x01,
.iJack = 0x00,
},
/* Table B-8: MIDI Adapter MIDI IN Jack Descriptor (External) */
.in_external = {
.bLength = sizeof(struct usb_midi_in_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EXTERNAL,
.bJackID = 0x02,
.iJack = 0x00,
},
/* Table B-9: MIDI Adapter MIDI OUT Jack Descriptor (Embedded) */
.out_embedded = {
.head = {
.bLength = sizeof(struct usb_midi_out_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EMBEDDED,
.bJackID = 0x03,
.bNrInputPins = 1,
},
.source[0] = {
.baSourceID = 0x02,
.baSourcePin = 0x01,
},
.tail = {
.iJack = 0x00,
}
},
/* Table B-10: MIDI Adapter MIDI OUT Jack Descriptor (External) */
.out_external = {
.head = {
.bLength = sizeof(struct usb_midi_out_jack_descriptor),
.bDescriptorType = USB_AUDIO_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK,
.bJackType = USB_MIDI_JACK_TYPE_EXTERNAL,
.bJackID = 0x04,
.bNrInputPins = 1,
},
.source[0] = {
.baSourceID = 0x01,
.baSourcePin = 0x01,
},
.tail = {
.iJack = 0x00,
},
},
};
/*
* Table B-5: MIDI Adapter Standard MS Interface Descriptor
*/
static const struct usb_interface_descriptor midi_streaming_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_AUDIO_SUBCLASS_MIDISTREAMING,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = bulk_endp,
.extra = &midi_streaming_functional_descriptors,
.extralen = sizeof(midi_streaming_functional_descriptors)
} };
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = audio_control_iface,
}, {
.num_altsetting = 1,
.altsetting = midi_streaming_iface,
} };
/*
* Table B-2: MIDI Adapter Configuration Descriptor
*/
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0, /* can be anything, it is updated automatically
when the usb code prepares the descriptor */
.bNumInterfaces = 2, /* control and data */
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80, /* bus powered */
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char * usb_strings[] = {
"libopencm3.org",
"MIDI demo",
};
/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];
/* SysEx identity message, preformatted with correct USB framing information */
const uint8_t sysex_identity[] = {
0x04, /* USB Framing (3 byte SysEx) */
0xf0, /* SysEx start */
0x7e, /* non-realtime */
0x00, /* Channel 0 */
0x04, /* USB Framing (3 byte SysEx) */
0x7d, /* Educational/prototype manufacturer ID */
0x66, /* Family code (byte 1) */
0x66, /* Family code (byte 2) */
0x04, /* USB Framing (3 byte SysEx) */
0x51, /* Model number (byte 1) */
0x19, /* Model number (byte 2) */
0x00, /* Version number (byte 1) */
0x04, /* USB Framing (3 byte SysEx) */
0x00, /* Version number (byte 2) */
0x01, /* Version number (byte 3) */
0x00, /* Version number (byte 4) */
0x05, /* USB Framing (1 byte SysEx) */
0xf7, /* SysEx end */
0x00, /* Padding */
0x00, /* Padding */
};
static void usbmidi_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep;
char buf[64];
int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);
/* This implementation treats any message from the host as a SysEx
* identity request. This works well enough providing the host
* packs the identify request in a single 8 byte USB message.
*/
if (len) {
while (usbd_ep_write_packet(usbd_dev, 0x81, sysex_identity,
sizeof(sysex_identity)) == 0);
}
gpio_toggle(GPIOC, GPIO5);
}
static void usbmidi_set_config(usbd_device *usbd_dev, uint16_t wValue)
{
(void)wValue;
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64,
usbmidi_data_rx_cb);
usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, NULL);
}
static void button_send_event(usbd_device *usbd_dev, int pressed)
{
char buf[4] = { 0x08, /* USB framing: virtual cable 0, note on */
0x80, /* MIDI command: note on, channel 1 */
60, /* Note 60 (middle C) */
64, /* "Normal" velocity */
};
buf[0] |= pressed;
buf[1] |= pressed << 4;
while (usbd_ep_write_packet(usbd_dev, 0x81, buf, sizeof(buf)) == 0);
}
static void button_poll(usbd_device *usbd_dev)
{
static uint32_t button_state = 0;
/* This is a simple shift based debounce. It's simplistic because
* although this implements debounce adequately it does not have any
* noise suppression. It is also very wide (32-bits) because it can
* be polled in a very tight loop (no debounce timer).
*/
uint32_t old_button_state = button_state;
button_state = (button_state << 1) | (GPIOA_IDR & 1);
if ((0 == button_state) != (0 == old_button_state)) {
button_send_event(usbd_dev, !!button_state);
}
}
int main(void)
{
usbd_device *usbd_dev;
rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_120MHZ]);
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_OTGHS);
/* USB pins */
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE,
GPIO13 | GPIO14 | GPIO15);
gpio_set_af(GPIOB, GPIO_AF12, GPIO13 | GPIO14 | GPIO15);
/* Button pin */
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO0);
usbd_dev = usbd_init(&otghs_usb_driver, &dev, &config,
usb_strings, 2,
usbd_control_buffer, sizeof(usbd_control_buffer));
usbd_register_set_config_callback(usbd_dev, usbmidi_set_config);
while (1) {
usbd_poll(usbd_dev);
button_poll(usbd_dev);
}
}