A library to send and receive MIDI messages over USB using the default USB-MIDI drivers on Win/Mac
Dependents: USBMIDI_HelloWorld USBMIDI_DrumExample USBMIDI_MonoSynth MIDI_Interface_ver_1 ... more
Revision 1:ff74eabe02cd, committed 2011-02-20
- Comitter:
- simon
- Date:
- Sun Feb 20 13:10:05 2011 +0000
- Parent:
- 0:56b095524cf2
- Child:
- 2:10d694d6ccdc
- Commit message:
- First version, supporting most USB MIDI functionality
Changed in this revision
--- a/MIDIMessage.h Sun Feb 06 17:26:29 2011 +0000 +++ b/MIDIMessage.h Sun Feb 20 13:10:05 2011 +0000 @@ -1,161 +1,254 @@ -/* MIDI Message Format - * - * [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ] - * - * MIDI Data Messages (Channel Specific) +/** @license The MIT License + * Copyright (c) 2011 mux, simon * - * Message msg n m - * --------------------------------------------- - * Note Off 0x8 Key Velocity - * Note On 0x9 Key Velocity - * Polyphonic Aftertouch 0xA Key Pressure - * Control Change 0xB Controller Value - * Program Change 0xC Program - - * Channel Aftertouch 0xD Pressure - - * Pitch Wheel 0xE LSB MSB + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ - #ifndef MBED_MIDIMESSAGE_H #define MBED_MIDIMESSAGE_H +#include "mbed.h" + +// MIDI Message Format +// +// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ] +// +// MIDI Data Messages (Channel Specific) +// +// Message msg n m +// --------------------------------------------- +// Note Off 0x8 Key Velocity +// Note On 0x9 Key Velocity +// Polyphonic Aftertouch 0xA Key Pressure +// Control Change 0xB Controller Value +// Program Change 0xC Program - +// Channel Aftertouch 0xD Pressure - +// Pitch Wheel 0xE LSB MSB + +#define CABLE_NUM (0<<4) + +/** A MIDI message container */ class MIDIMessage { public: - - // create messages - - static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0x80 | (channel & 0x0F); - msg.data[1] = key & 0x7F; - msg.data[2] = velocity & 0x7F; - return msg; - } - - static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0x90 | (channel & 0x0F); - msg.data[1] = key & 0x7F; - msg.data[2] = velocity & 0x7F; - return msg; - } - - static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0xA0 | (channel & 0x0F); - msg.data[1] = key & 0x7F; - msg.data[2] = pressure & 0x7F; - return msg; - } - - static MIDIMessage ControlChange(int control, int value, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0xB0 | (channel & 0x0F); - msg.data[1] = control & 0x7F; - msg.data[2] = value & 0x7F; - return msg; - } - - static MIDIMessage ProgramChange(int program, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0xC0 | (channel & 0x0F); - msg.data[1] = program & 0x7F; - msg.data[2] = 0x00; - return msg; - } - - static MIDIMessage ChannelAftertouch(int pressure, int channel = 0) { - MIDIMessage msg; - msg.data[0] = 0xD0 | (channel & 0x0F); - msg.data[1] = pressure & 0x7F; - msg.data[2] = 0x00; - return msg; - } - - static MIDIMessage PitchWheel(int pitch = 0, int channel = 0) { - MIDIMessage msg; - int p = pitch + 8192; // 0 - 16383, 8192 is center - msg.data[0] = 0xE0 | (channel & 0x0F); - msg.data[1] = p & 0x7F; - msg.data[2] = (p >> 7) & 0x7F; - return msg; - } - - static MIDIMessage AllNotesOff(int channel = 0) { - return ControlChange(123, 0, channel); - } - - // decode messages - enum MIDIMessageType { - ErrorType, - NoteOffType, - NoteOnType, - PolyphonicAftertouchType, - ControlChangeType, - ProgramChangeType, - ChannelAftertouchType, - PitchWheelType, - AllNotesOffType - }; - - MIDIMessageType type() { - switch((data[0] >> 4) & 0xF) { - case 0x8: return NoteOffType; - case 0x9: return NoteOnType; - case 0xA: return PolyphonicAftertouchType; - case 0xB: - if(controller() < 120) { // standard controllers - return ControlChangeType; - } else if(controller() == 123) { - return AllNotesOffType; - } else { - return ErrorType; // unsupported atm - } - case 0xC: return ProgramChangeType; - case 0xD: return ChannelAftertouchType; - case 0xE: return PitchWheelType; - default: return ErrorType; - } - } - - int channel() { - return (data[0] & 0x0F); - } - - int key() { - return (data[1] & 0x7F); - } - - int velocity() { - return (data[2] & 0x7F); - } + MIDIMessage() {} + + MIDIMessage(uint8_t *buf) { + *((uint32_t *)data) = *((uint32_t *)buf); + } + + // create messages + + /** Create a NoteOff message + * @param key Key ID + * @param velocity Key velocity (0-127, default = 127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x08; + msg.data[1] = 0x80 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = velocity & 0x7F; + return msg; + } + + /** Create a NoteOn message + * @param key Key ID + * @param velocity Key velocity (0-127, default = 127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x09; + msg.data[1] = 0x90 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = velocity & 0x7F; + return msg; + } + + /** Create a PolyPhonic Aftertouch message + * @param key Key ID + * @param pressure Aftertouch pressure (0-127) + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0A; + msg.data[1] = 0xA0 | (channel & 0x0F); + msg.data[2] = key & 0x7F; + msg.data[3] = pressure & 0x7F; + return msg; + } + + /** Create a Control Change message + * @param control Controller ID + * @param value Controller value (0-127) + * @param channel Controller channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ControlChange(int control, int value, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0B; + msg.data[1] = 0xB0 | (channel & 0x0F); + msg.data[2] = control & 0x7F; + msg.data[3] = value & 0x7F; + return msg; + } + + /** Create a Program Change message + * @param program Program ID + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ProgramChange(int program, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0C; + msg.data[1] = 0xC0 | (channel & 0x0F); + msg.data[2] = program & 0x7F; + msg.data[3] = 0x00; + return msg; + } + + /** Create a Channel Aftertouch message + * @param pressure Pressure + * @param channel Key channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage ChannelAftertouch(int pressure, int channel = 0) { + MIDIMessage msg; + msg.data[0] = CABLE_NUM | 0x0D; + msg.data[1] = 0xD0 | (channel & 0x0F); + msg.data[2] = pressure & 0x7F; + msg.data[3] = 0x00; + return msg; + } + + /** Create a Pitch Wheel message + * @param pitch Pitch (-8192 - 8191, default = 0) + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage PitchWheel(int pitch = 0, int channel = 0) { + MIDIMessage msg; + int p = pitch + 8192; // 0 - 16383, 8192 is center + msg.data[0] = CABLE_NUM | 0x0E; + msg.data[1] = 0xE0 | (channel & 0x0F); + msg.data[2] = p & 0x7F; + msg.data[3] = (p >> 7) & 0x7F; + return msg; + } + + /** Create an All Notes Off message + * @param channel Channel (0-15, default 0) + * @returns A MIDIMessage + */ + static MIDIMessage AllNotesOff(int channel = 0) { + return ControlChange(123, 0, channel); + } + + // decode messages + + /** MIDI Message Types */ + enum MIDIMessageType { + ErrorType, + NoteOffType, + NoteOnType, + PolyphonicAftertouchType, + ControlChangeType, + ProgramChangeType, + ChannelAftertouchType, + PitchWheelType, + AllNotesOffType + }; + + /** Read the message type + * @returns MIDIMessageType + */ + MIDIMessageType type() { + switch((data[1] >> 4) & 0xF) { + case 0x8: return NoteOffType; + case 0x9: return NoteOnType; + case 0xA: return PolyphonicAftertouchType; + case 0xB: + if(controller() < 120) { // standard controllers + return ControlChangeType; + } else if(controller() == 123) { + return AllNotesOffType; + } else { + return ErrorType; // unsupported atm + } + case 0xC: return ProgramChangeType; + case 0xD: return ChannelAftertouchType; + case 0xE: return PitchWheelType; + default: return ErrorType; + } + } - int value() { - return (data[2] & 0x7F); - } - - int pressure() { - if(type() == PolyphonicAftertouchType) { - return (data[2] & 0x7F); - } else { - return (data[1] & 0x7F); - } - } + /** Read the channel number */ + int channel() { + return (data[1] & 0x0F); + } + + /** Read the key ID */ + int key() { + return (data[2] & 0x7F); + } + + /** Read the velocity */ + int velocity() { + return (data[3] & 0x7F); + } - int controller() { - return (data[1] & 0x7F); - } + /** Read the controller value */ + int value() { + return (data[3] & 0x7F); + } + + /** Read the aftertouch pressure */ + int pressure() { + if(type() == PolyphonicAftertouchType) { + return (data[3] & 0x7F); + } else { + return (data[2] & 0x7F); + } + } - int program() { - return (data[1] & 0x7F); - } - - int pitch() { - int p = ((data[2] & 0x7F) << 7) | (data[1] & 0x7F); - return p - 8192; // 0 - 16383, 8192 is center - } - - uint8_t data[3]; -}; + /** Read the controller number */ + int controller() { + return (data[2] & 0x7F); + } + + /** Read the program number */ + int program() { + return (data[2] & 0x7F); + } + + /** Read the pitch value */ + int pitch() { + int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F); + return p - 8192; // 0 - 16383, 8192 is center + } + + uint8_t data[4]; +}; #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBMIDI.cpp Sun Feb 20 13:10:05 2011 +0000 @@ -0,0 +1,60 @@ +/** @license The MIT License + * Copyright (c) 2011 mux, simon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "USBMIDI.h" + +#include "mbed.h" +#include "usbcore.h" + +static void (*midi_evt)(MIDIMessage) = NULL; + +USBMIDI::USBMIDI() { + usb_init(); + usb_connect(); + while (!usb_configured()); +} + +void USBMIDI::write(MIDIMessage m) { + while (!writeable()); + ep_write(EP5, m.data, 4); +} + +bool USBMIDI::writeable() { + return (bool)ep_writable(EP5); +} + +void USBMIDI::attach(void (*fptr)(MIDIMessage)) { + midi_evt = fptr; +} + +void ep2_in() {} + +void ep2_out() { + uint8_t buf[MAX_EPn_PSIZE]; + int len = ep_read(EP4, buf); + + if (midi_evt != NULL) { + for(int i=0; i<len; i+=4){ + midi_evt(MIDIMessage(buf+i)); + } + } +}
--- a/USBMIDI.h Sun Feb 06 17:26:29 2011 +0000 +++ b/USBMIDI.h Sun Feb 20 13:10:05 2011 +0000 @@ -1,26 +1,50 @@ +/** @license The MIT License + * Copyright (c) 2011 mux, simon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #ifndef MBED_USBMIDI_H #define MBED_USBMIDI_H #include "MIDIMessage.h" +/** USBMIDI interface class for sending and receiving MIDI messages over USB */ class USBMIDI { public: - USBMIDI() {} - ~USBMIDI() {} - - // write messages - void write(MIDIMessage m) {} - int writeable() { return 0; } - - // read messages - MIDIMessage read() { return MIDIMessage::NoteOn(0); } - int readable() { return 0; } - - // notification of incoming message - void attach(void (*fptr)(void)) {} - - template <class T> - void attach(T *tptr, void (T::*mptr)(void)) {} + + /** Create the USBMIDI interface */ + USBMIDI(); + + /** Send a MIDIMessage + * @param m The MIDIMessage to send + */ + void write(MIDIMessage m); + + /** Check if it possible to send a MIDIMessage + * @returns True if there's an empty buffer for sending a MIDIMessage, otherwise False + */ + bool writeable(); + + /** Attach a callback for when a MIDIEvent is received */ + void attach(void (*fptr)(MIDIMessage)); + }; #endif
--- a/main.cpp Sun Feb 06 17:26:29 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -#include "mbed.h" -#include "USBMIDI.h" - -USBMIDI midi; - -int main() { - // send some notes - for(int i=0; i<127; i++) { - midi.write(MIDIMessage::NoteOn(i)); - wait(0.25); - midi.write(MIDIMessage::NoteOff(i)); - wait(0.5); - } - - // recieve notes - while(1) { - if(midi.readable()) { - MIDIMessage msg = midi.read(); - switch(msg.type()) { - case MIDIMessage::NoteOnType: - printf("NoteOn key:%d, velocity: %d, channel: %d\n", msg.key(), msg.velocity(), msg.channel()); - break; - case MIDIMessage::NoteOffType: - printf("NoteOff key:%d, velocity: %d, channel: %d\n", msg.key(), msg.velocity(), msg.channel()); - break; - case MIDIMessage::ControlChangeType: - printf("ControlChange controller: %d, data: %d\n", msg.controller(), msg.value()); - break; - case MIDIMessage::PitchWheelType: - printf("PitchWheel channel: %d, pitch: %d\n", msg.channel(), msg.pitch()); - break; - default: - printf("Another message\n"); - - } - } - } -}
--- a/mbed.bld Sun Feb 06 17:26:29 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/9a9732ce53a1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbcore.c Sun Feb 20 13:10:05 2011 +0000 @@ -0,0 +1,278 @@ +/** @license The MIT License + * Copyright (c) 2011 mux, simon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "usbcore.h" + +#include "mbed.h" + +// Serial Interface Engine +#define SIE_SET_ADDR (0xD0) +#define SIE_SET_STATUS (0xFE) +#define SIE_GET_STATUS (0xFE) +#define SIE_SET_MODE (0xF3) +#define SIE_CLR_BUFFER (0xF2) +#define SIE_VAL_BUFFER (0xFA) +#define SIE_SEL_EP (0x00) +#define SIE_SEL_CLR_EP (0x28) +#define SIE_SET_EP_STAT (0x40) +#define SIE_READ_ERROR (0xFB) +#define SIE_CONFIG_DEVICE (0xD8) + +// EP status +#define EP_FE (1<<0) // Full/Empty +#define EP_ST (1<<1) // Stalled endpoint +#define EP_STP (1<<2) // Setup packet +#define EP_PO (1<<3) // packet overwritten +#define EP_EPN (1<<4) // EP NAKed +#define B_1_FULL (1<<5) // buffer 1 status +#define B_2_FULL (1<<6) // buffer 2 status + +// USB device interrupts +#define DEV_FRAME (1<<0) +#define EP_FAST (1<<1) +#define EP_SLOW (1<<2) +#define DEV_STAT (1<<3) +#define RxENDPKT (1<<6) +#define TxENDPKT (1<<7) +#define EP_RLZED (1<<8) +#define CCEMPTY (0x10) +#define CDFULL (0x20) + +// USB device status bits +#define STAT_CON (1<<0) +#define STAT_CON_CH (1<<1) +#define STAT_SUS (1<<2) +#define STAT_SUS_CH (1<<3) +#define STAT_RST (1<<4) + +// end points interrupts +#define EP0RX_INT (1<<0) +#define EP0TX_INT (1<<1) +#define EP1RX_INT (1<<2) +#define EP1TX_INT (1<<3) +#define EP2RX_INT (1<<4) +#define EP2TX_INT (1<<5) + +// USB control register +#define RD_EN (1<<0) +#define WR_EN (1<<1) +#define PKT_RDY (1<<11) +#define LOG_ENDPOINT(ep) ((ep>>1)<<2) + +// configure state +static int configured = 0; + +// USB interrupt handler +void USB_IRQHandler(void); + +// Serial Interface Engine functions +void sie_command(uint32_t code) { + LPC_USB->USBDevIntClr = CCEMPTY; // clear CCEMPTY + LPC_USB->USBCmdCode = ((code<<16)|(0x05<<8)); // CMD_PHASE=Command + while (!(LPC_USB->USBDevIntSt & CCEMPTY)); // wait for CCEMPTY +} + +void sie_write(uint32_t data) { + LPC_USB->USBDevIntClr = CCEMPTY; // clear CCEMPTY + LPC_USB->USBCmdCode = ((data<<16)|(0x01<<8)); // CMD_PHASE=Write + while (!(LPC_USB->USBDevIntSt & CCEMPTY)); // wait for CCEMPTY +} + +uint8_t sie_read(uint32_t code) { + LPC_USB->USBDevIntClr = CDFULL; // clear CCEMPTY + LPC_USB->USBCmdCode = ((code<<16)|(0x02<<8)); // CMD_PHASE=Read + while (!(LPC_USB->USBDevIntSt & CDFULL)); // wait for CDFULL + return (uint8_t) LPC_USB->USBCmdData; + +} + +// end point functions +void ep_realize(uint8_t ep, uint32_t size) { + LPC_USB->USBDevIntClr = EP_RLZED; // clear EP_RLZED + LPC_USB->USBReEp |= (1<<ep); + LPC_USB->USBEpInd = ep; // set USBEpIn + LPC_USB->USBMaxPSize = size; // writing to EPn pointed to by USBEpInd + while (!(LPC_USB->USBDevIntSt & EP_RLZED)); // wait for EP_RLZED + LPC_USB->USBDevIntClr = EP_RLZED; // clear EP_RLZED +} + +void ep_stall(uint8_t ep) { + sie_command(SIE_SET_EP_STAT+ep); + sie_write(1); +} + +void ep_unstall(uint8_t ep) { + sie_command(SIE_SET_EP_STAT+ep); + sie_write(0); +} + +// initializes a pointer to the endpoint buffer +uint8_t ep_select(uint8_t ep) { + sie_command(SIE_SEL_EP+ep); + return sie_read(SIE_SEL_EP+ep); +} + +uint8_t ep_select_clear(uint8_t ep) { + LPC_USB->USBEpIntClr |= ep; // clear ep interrupt + while (!(LPC_USB->USBDevIntSt & CDFULL)); // wait for cmd finish + return LPC_USB->USBCmdData; +} + +int ep_readable(uint8_t ep) { + uint8_t st = ep_select(ep); + return (st & EP_FE); +} + +int ep_writable(uint8_t ep) { + uint8_t st = ep_select(ep); + return !(st & EP_FE); +} + +int ep_read(uint8_t ep, uint8_t *tbuf) { + uint32_t *buf = (uint32_t*) tbuf; + LPC_USB->USBCtrl = LOG_ENDPOINT(ep)|RD_EN; // RD_EN bit and LOG_ENDPOINT + while (!(LPC_USB->USBRxPLen & PKT_RDY)); // wait for packet to be fetched + int len = LPC_USB->USBRxPLen & 0x3FF; // read and mask packet length + while (!(LPC_USB->USBDevIntSt & RxENDPKT)) { + *buf++ = LPC_USB->USBRxData; + } + LPC_USB->USBCtrl = 0; + LPC_USB->USBDevIntClr |= RxENDPKT; + sie_command(SIE_SEL_EP+ep); // select endpoint + sie_command(SIE_CLR_BUFFER); // clear RX buffer + return len; +} + +void ep_write(uint8_t ep, uint8_t *tbuf, uint32_t len) { + uint32_t *buf = (uint32_t*) tbuf; + LPC_USB->USBCtrl = LOG_ENDPOINT(ep)|WR_EN; // RD_EN bit and LOG_ENDPOINT + LPC_USB->USBTxPLen |= (len & 0x3FF); // write and mask packet length + while (!(LPC_USB->USBDevIntSt & TxENDPKT)) { + LPC_USB->USBTxData = *buf++; + } + LPC_USB->USBCtrl = 0; + LPC_USB->USBDevIntClr |= TxENDPKT; + sie_command(SIE_SEL_EP+ep); // select endpoint + sie_command(SIE_VAL_BUFFER); // validate TX buffer +} + +// USB device controller initialization +void usb_init() { + // USB D+/D- pinsel functions + LPC_PINCON->PINSEL1 &= 0xC3FFFFFF; + LPC_PINCON->PINSEL1 |= 0x14000000; + +#if USB_UP_DEBUG + // USB_UP_LED pinsel function + LPC_PINCON->PINSEL3 &= 0xFFFFFFCF; + LPC_PINCON->PINSEL3 |= 0x00000010; +#endif + + // USB connect pinsel function + LPC_PINCON->PINSEL4 &= 0xFFFCFFFF; + LPC_PINCON->PINSEL4 |= 0x00040000; + LPC_SC->PCONP |= (1UL<<31); // enable the USB controller + LPC_USB->USBClkCtrl |= ((1<<1)|(1<<4)); // enable the AHB and DEV clocks + while ((LPC_USB->USBClkSt & 0x12) != 0x12); // wait for the clocks to init + + NVIC_SetVector(USB_IRQn, (uint32_t)&USB_IRQHandler); + NVIC_EnableIRQ(USB_IRQn); // enable USB interrupts + + usb_reset(); + usb_set_address(0); // default address +} + +void usb_reset() { + ep_realize(EP0, MAX_EP0_PSIZE); + ep_realize(EP1, MAX_EP0_PSIZE); + LPC_USB->USBEpIntClr = 0xFFFFFFFF; // clear end points interrupts + LPC_USB->USBEpIntEn = 0xFFFFFFFF; // enable end points interrupts + LPC_USB->USBEpIntPri = 0x0; // route to EP_SLOW + LPC_USB->USBDevIntClr = 0xFFFFFFFF; // clear USB device interrupts + LPC_USB->USBDevIntEn = (EP_SLOW|DEV_STAT); // enable USB device interrupts +} + +void usb_configure(uint8_t conf) { + sie_command(SIE_CONFIG_DEVICE); + sie_write(conf); + configured = 1; +} + +int usb_configured() { + return configured; +} + +void usb_set_address(uint8_t addr) { + sie_command(SIE_SET_ADDR); + sie_write(addr|0x80); // DEV_EN = 1 +} + +uint8_t usb_get_status() { + sie_command(SIE_GET_STATUS); + return sie_read(SIE_GET_STATUS); +} + +void usb_connect() { + sie_command(SIE_GET_STATUS); // read current status + uint8_t st = sie_read(SIE_GET_STATUS); + + sie_command(SIE_SET_STATUS); // set STAT_CON bit + sie_write(st|STAT_CON); +} + +void USB_IRQHandler(void) { + if (LPC_USB->USBDevIntSt & DEV_STAT) { // DEV_STAT interrupt + LPC_USB->USBDevIntClr |= DEV_STAT; + if (usb_get_status() & STAT_RST) { // bus reset + usb_reset(); + } + return; + } + + if (LPC_USB->USBDevIntSt & EP_SLOW) { // EP_SLOW interrupt + if (LPC_USB->USBEpIntSt & EP0RX_INT) { + if (ep_select_clear(EP0RX_INT) & EP_STP) { // setup transfer + ep0_setup(); + } else { + ep0_out(); + } + } + + if (LPC_USB->USBEpIntSt & EP0TX_INT) { + ep_select_clear(EP0TX_INT); + ep0_in(); + } + + if (LPC_USB->USBEpIntSt & EP2RX_INT) { + ep_select_clear(EP2TX_INT); + ep2_out(); + } + + if (LPC_USB->USBEpIntSt & EP2TX_INT) { + ep_select_clear(EP2TX_INT); + ep2_in(); + } + + // EP_SLOW should be cleared after clearing EPs interrupts + LPC_USB->USBDevIntClr |= EP_SLOW; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbcore.h Sun Feb 20 13:10:05 2011 +0000 @@ -0,0 +1,56 @@ +/** @license The MIT License + * Copyright (c) 2011 mux, simon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef USB_CORE_H +#define USB_CORE_H + +#include "mbed.h" + +#define MAX_EP0_PSIZE (8) +#define MAX_EPn_PSIZE (64) + +// physical endpoint numbers +#define EP0 (0) +#define EP1 (1) +#define EP2 (2) +#define EP3 (3) +#define EP4 (4) +#define EP5 (5) + +void usb_init(); +void usb_reset(); +void usb_connect(); +void usb_set_address(uint8_t); +void usb_configure(uint8_t); +int usb_configured(); +void ep_realize(uint8_t ep, uint32_t psize); +int ep_read(uint8_t ep, uint8_t *buf); +void ep_write(uint8_t ep, uint8_t *buf, uint32_t len); +int ep_readable(uint8_t ep); +int ep_writable(uint8_t ep); +void ep0_setup(); +void ep0_in(); +void ep0_out(); +void ep2_in(); +void ep2_out(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbdevice.c Sun Feb 20 13:10:05 2011 +0000 @@ -0,0 +1,181 @@ +/** @license The MIT License + * Copyright (c) 2011 mux, simon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "usbcore.h" +#include "mbed.h" + +#define STANDARD_DEVICE_REQUEST (0x00) +#define STANDARD_INTERFACE_REQUEST (0x01) +#define STANDARD_ENDPOINT_REQUEST (0x02) +#define CLASS_DEVICE_REQUEST (0x20) +#define CLASS_INTERFACE_REQUEST (0x21) +#define CLASS_ENDPOINT_REQUEST (0x22) +#define VENDOR_DEVICE_REQUEST (0x40) +#define VENDOR_INTERFACE_REQUEST (0x41) +#define VENDOR_ENDPOINT_REQUEST (0x42) +#define GET_STATUS (0x00) +#define CLEAR_FEATURE (0x01) +#define SET_FEATURE (0x03) +#define SET_ADDRESS (0x05) +#define GET_DESCRIPTOR (0x06) +#define SET_DESCRIPTOR (0x07) +#define GET_CONFIGURATION (0x08) +#define SET_CONFIGURATION (0x09) +#define DEVICE_DESCRIPTOR (0x01) +#define CONFIG_DESCRIPTOR (0x02) +#define STRING_DESCRIPTOR (0x03) +#define INTERFACE_DESCRIPTOR (0x04) +#define ENDPOINT_DESCRIPTOR (0x05) +#define QUALIFIER_DESCRIPTOR (0x06) +#define unpack(x) (x & 0xFF),((x >> 8) & 0xFF) + +// setup packet +struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)) setup = {0}; + +// data packet +struct { + uint8_t *data; + uint8_t size; + uint8_t sent; +} transfer = {0}; + +uint8_t device_descriptor[] = { + 0x12, // Descriptor size in bytes (12h) + DEVICE_DESCRIPTOR, // The constant DEVICE (01h) + unpack(0x0200), // US2B specification release number (BCD) + 0x00, // Class code + 0x00, // Subclass code + 0x00, // Protocol Code + MAX_EP0_PSIZE, // Maximum packet size for endpoint zero + unpack(0x0763), // Vendor ID + unpack(0x0198), // Product ID + unpack(0x0001), // Device release number (BCD) + 0x00, // Index of string descriptor for the manufacturer + 0x00, // Index of string descriptor for the product + 0x00, // Index of string descriptor for the serial number + 0x01, // Number of possible configurations +}; + +uint8_t config_descriptor[]={ + 0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50, // configuration descriptor + // The Audio Interface Collection + 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor + 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor + 0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors + 0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor + + // MIDI IN JACKS + 0x06, 0x24, 0x02, 0x01, 0x01, 0x00, + 0x06, 0x24, 0x02, 0x02, 0x02, 0x00, + + // MIDI OUT JACKS + 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, + 0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00, + + // OUT endpoint descriptor + 0x09, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x25, 0x01, 0x01, 0x01, + + // IN endpoint descriptor + 0x09, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x25, 0x01, 0x01, 0x03, +}; + +void ep0_in(); + +void data_in_stage(uint8_t *desc, uint8_t length) { + transfer.sent = 0; + transfer.data = desc; + transfer.size = length; + ep0_in(); +} + +void status_in_stage() { + ep_write(EP1, 0, 0); // ZLEP for status stage +} + +void ep0_setup() { + ep_read(EP0,(uint8_t*) &setup); + + switch (setup.bmRequestType & 0x7f) { // mask direction + case STANDARD_DEVICE_REQUEST: + switch (setup.bRequest) { + case GET_DESCRIPTOR: + switch ((setup.wValue>>8)) { + case DEVICE_DESCRIPTOR: // device descriptor request + case QUALIFIER_DESCRIPTOR: // device qualifier descriptor + data_in_stage(device_descriptor, sizeof(device_descriptor)); + break; + case CONFIG_DESCRIPTOR: // configuration descriptor + data_in_stage(config_descriptor, setup.wLength); + break; + case STRING_DESCRIPTOR: + break; + default: + break; + } + break; + case SET_ADDRESS: + usb_set_address((uint8_t) (setup.wValue & 0xFF)); + status_in_stage(); + break; + case SET_CONFIGURATION: + if (!setup.wValue) { + break; + } + ep_realize(EP4, MAX_EPn_PSIZE); + ep_realize(EP5, MAX_EPn_PSIZE); + status_in_stage(); + usb_configure(1); + break; + default: + break; + } + break; + default: + break; + } +} + +void ep0_in() { + if ((setup.bmRequestType & 0x80) && transfer.size) { // device to host + if (transfer.size > MAX_EP0_PSIZE) { + transfer.sent = MAX_EP0_PSIZE; + } else { + transfer.sent = transfer.size; + } + ep_write(EP1, transfer.data, transfer.sent); + transfer.data += transfer.sent; + transfer.size -= transfer.sent; + } +} + +void ep0_out() { + uint8_t buf[64]; + ep_read(EP0, buf); +}