aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonald Delmar Davis <don@suspectdevices.com>2013-04-27 00:07:15 -0700
committerDonald Delmar Davis <don@suspectdevices.com>2013-04-27 00:07:15 -0700
commitf0e9aa9335052dcf11d258d5d27040be234d8770 (patch)
tree4d76c0223a84281c9661105fc0eba844a7bdf919
parentcd7f34a357c1035a2c69918131a923e4006544a0 (diff)
Refactor my first attempt into fork of the Lundin code. Add sysex. Create a board file for maple_midi.
-rw-r--r--Makefile5
-rw-r--r--libmaple/include/libmaple/midi_specs.h151
-rw-r--r--libmaple/include/libmaple/usb_midi_device.h58
-rw-r--r--libmaple/usb/rules.mk5
-rw-r--r--libmaple/usb/stm32f1/usb_midi_device.c409
-rw-r--r--support/make/board-includes/maple_midi.mk8
-rwxr-xr-xsupport/scripts/sysexreset.py152
-rw-r--r--wirish/boards/maple_midi/board.cpp105
-rw-r--r--wirish/boards/maple_midi/include/board/board.h85
-rw-r--r--wirish/include/wirish/usb_midi.h26
-rw-r--r--wirish/rules.mk4
-rw-r--r--wirish/usb_midi.cpp40
12 files changed, 874 insertions, 174 deletions
diff --git a/Makefile b/Makefile
index 1f19f37..8802d72 100644
--- a/Makefile
+++ b/Makefile
@@ -104,9 +104,14 @@ BOARD_USB_PRODUCT_ID := 0003
UPLOAD_ram := $(SUPPORT_PATH)/scripts/reset.py && \
sleep 1 && \
$(DFU) -a0 -d $(BOARD_USB_VENDOR_ID):$(BOARD_USB_PRODUCT_ID) -D $(BUILD_PATH)/$(BOARD).bin -R
+ifeq ($(USB_TYPE),midi)
+UPLOAD_flash := $(SUPPORT_PATH)/scripts/sysexreset.py && \
+ $(DFU) -a1 -d $(BOARD_USB_VENDOR_ID):$(BOARD_USB_PRODUCT_ID) -D $(BUILD_PATH)/$(BOARD).bin -R
+else
UPLOAD_flash := $(SUPPORT_PATH)/scripts/reset.py && \
sleep 1 && \
$(DFU) -a1 -d $(BOARD_USB_VENDOR_ID):$(BOARD_USB_PRODUCT_ID) -D $(BUILD_PATH)/$(BOARD).bin -R
+endif
# Conditionally upload to whatever the last build was
install: INSTALL_TARGET = $(shell cat $(BUILD_PATH)/build-type 2>/dev/null)
diff --git a/libmaple/include/libmaple/midi_specs.h b/libmaple/include/libmaple/midi_specs.h
new file mode 100644
index 0000000..293060d
--- /dev/null
+++ b/libmaple/include/libmaple/midi_specs.h
@@ -0,0 +1,151 @@
+/*--------------------------------------------------------------------------MidiSpecs.h
+ *
+ * These defines are based on specs created by the USB and MMA standards organizations.
+ * There are not a lot of other ways to code them so liscensing this is rather ludicrous.
+ * However, in order to be able to embed this in client projects, and avoid the stupidity
+ * of enforced open everything I will declare the following about this file.
+ *
+ * Copyright (c) 2011 Donald Delmar Davis, Suspect Devices
+ *
+ * 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.
+ *
+ */
+
+/*
+ * USB midi commands from
+ * MIDI10.pdf "Universal Serial Bus Device Class Definition for MIDI Devices"
+ * REV 1. (1999)
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ *
+ */
+
+#ifndef __LETS_MIDI_SPECS_H__
+#define __LETS_MIDI_SPECS_H__
+#include <stdint.h>
+#include <stdbool.h>
+// rework this for the different architectures....
+#if defined(__GNUC__)
+typedef struct
+{
+ unsigned cin : 4; // this is the low nibble.
+ unsigned cable : 4;
+// uint8_t cin;
+ uint8_t midi0;
+ uint8_t midi1;
+ uint8_t midi2;
+} __attribute__ ((__packed__)) MIDI_EVENT_PACKET_t ;
+#else
+typedef struct // may need to be adjusted for other compilers and bitfield order...
+{
+ unsigned cable : 4;
+ unsigned cin : 4;
+ uint8_t midi0;
+ uint8_t midi1;
+ uint8_t midi2;
+} MIDI_EVENT_PACKET_t ;
+#endif
+
+
+#define CIN_MISC_FUNCTION 0x00 /* Reserved for future extension. */
+#define CIN_CABLE_EVENT 0x01 /* Reserved for future extension. */
+#define CIN_2BYTE_SYS_COMMON 0x02 /* 2Bytes -- MTC, SongSelect, etc. */
+#define CIN_3BYTE_SYS_COMMON 0x03 /* 3Bytes -- SPP, etc. */
+#define CIN_SYSEX 0x04 /* 3Bytes */
+#define CIN_SYSEX_ENDS_IN_1 0x05 /* 1Bytes */
+#define CIN_SYSEX_ENDS_IN_2 0x06 /* 2Bytes */
+#define CIN_SYSEX_ENDS_IN_3 0x07 /* 3Bytes */
+#define CIN_NOTE_OFF 0x08 /* 3Bytes */
+#define CIN_NOTE_ON 0x09 /* 3Bytes */
+#define CIN_AFTER_TOUCH 0x0A /* 3Bytes */
+#define CIN_CONTROL_CHANGE 0x0B /* 3Bytes */
+#define CIN_PROGRAM_CHANGE 0x0C /* 2Bytes */
+#define CIN_CHANNEL_PRESSURE 0x0D /* 2Bytes */
+#define CIN_PITCH_WHEEL 0x0E /* 3Bytes */
+#define CIN_1BYTE 0x0F /* 1Bytes */
+//#define CIN_IS_SYSEX(cin) ((cin == CIN_SYSEX)||(cin == CIN_SYSEX_ENDS_IN_1)||(cin == CIN_SYSEX_ENDS_IN_2)||(cin == CIN_SYSEX_ENDS_IN_3))
+#define CIN_IS_SYSEX(cin) ( ((cin) & ~(0x08)) && ((cin) &0x04) )
+
+/*
+ * MIDI V1 message definitions these are from the MMA document
+ * http://www.midi.org/techspecs/midimessages.php
+ */
+#define MIDIv1_BAUD_RATE 31250
+
+/*
+ * parse midi (v1) message macros
+ */
+#define MIDIv1_IS_STATUS(b) ((b) & 0x80)
+#define MIDIv1_IS_VOICE(b) ((b) <= 0xEF)
+#define MIDIv1_VOICE_COMMAND(b) ((b) & 0xF0)
+#define MIDIv1_VOICE_CHANNEL(b) ((b) & 0x0F)
+#define MIDIv1_IS_SYSCOMMON(b) (((b) >= 0xF0) & ((b) < 0xF8))
+#define MIDIv1_IS_REALTIME(b) ((b) >= 0xF8)
+
+/*
+ * Voice category messages
+ */
+#define MIDIv1_NOTE_OFF 0x80 /* 2 bytes data -- CIN_NOTE_OFF */
+#define MIDIv1_NOTE_ON 0x90 /* 2 bytes data -- CIN_NOTE_ON */
+#define MIDIv1_AFTER_TOUCH 0xA0 /* 2 bytes data -- CIN_AFTER_TOUCH */
+#define MIDIv1_CONTROL_CHANGE 0xB0 /* 2 bytes data -- CIN_CONTROL_CHANGE */
+#define MIDIv1_PROGRAM_CHANGE 0xC0 /* 1 byte data -- CIN_PROGRAM_CHANGE */
+#define MIDIv1_CHANNEL_PRESSURE 0xD0 /* 1 byte data -- CIN_CHANNEL_PRESSURE */
+#define MIDIv1_PITCH_WHEEL 0xE0 /* 2 bytes data -- CIN_PITCH_WHEEL */
+
+/*
+ * System common category messages
+ */
+#define MIDIv1_SYSEX_START 0xF0
+#define MIDIv1_SYSEX_END 0xF7
+#define MIDIv1_MTC_QUARTER_FRAME 0xF1 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */
+#define MIDIv1_SONG_POSITION_PTR 0xF2 /* 2 bytes data -- CIN_3BYTE_SYS_COMMON */
+#define MIDIv1_SONG_SELECT 0xF3 /* 1 byte data -- CIN_2BYTE_SYS_COMMON */
+#define MIDIv1_TUNE_REQUEST 0xF6 /* no data -- CIN_1BYTE */
+
+/*
+ * Realtime category messages, can be sent anytime
+ */
+#define MIDIv1_CLOCK 0xF8 /* no data -- CIN_1BYTE */
+#define MIDIv1_TICK 0xF9 /* no data -- CIN_1BYTE */
+#define MIDIv1_START 0xFA /* no data -- CIN_1BYTE */
+#define MIDIv1_CONTINUE 0xFB /* no data -- CIN_1BYTE */
+#define MIDIv1_STOP 0xFC /* no data -- CIN_1BYTE */
+#define MIDIv1_ACTIVE_SENSE 0xFE /* no data -- CIN_1BYTE */
+#define MIDIv1_RESET 0xFF /* no data -- CIN_1BYTE */
+
+/*
+ * sysex universal id's
+ */
+#define MIDIv1_UNIVERSAL_REALTIME_ID 0x7F
+#define MIDIv1_UNIVERSAL_NON_REALTIME_ID 0x7E
+#define MIDIv1_UNIVERSAL_ALL_CHANNELS 0x7F
+/*
+ * Susbset of universal sysex (general info request)
+ * As described http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec.htm
+ */
+#define USYSEX_NON_REAL_TIME 0x7E
+#define USYSEX_REAL_TIME 0x7F
+#define USYSEX_ALL_CHANNELS 0x7F
+#define USYSEX_GENERAL_INFO 0x06
+#define USYSEX_GI_ID_REQUEST 0x01
+#define USYSEX_GI_ID_RESPONSE 0x02
+
+
+#endif
+
+
diff --git a/libmaple/include/libmaple/usb_midi_device.h b/libmaple/include/libmaple/usb_midi_device.h
index 3d48bd7..c77ab5b 100644
--- a/libmaple/include/libmaple/usb_midi_device.h
+++ b/libmaple/include/libmaple/usb_midi_device.h
@@ -3,6 +3,7 @@
*
* Copyright (c) 2011 LeafLabs LLC.
* Copyright (c) 2013 Magnus Lundin.
+ * Copyright (c) 2013 Donald Delmar Davis, SuspectDevices
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -36,6 +37,7 @@
#define _LIBMAPLE_USB_MIDI_DEVICE_H_
#include <libmaple/libmaple_types.h>
+#include <libmaple/midi_specs.h>
#include <libmaple/gpio.h>
#include <libmaple/usb.h>
@@ -44,8 +46,9 @@ extern "C" {
#endif
typedef union {
- uint8 byte[4];
- uint32 data;
+ uint8 byte[4];
+ uint32 data;
+ MIDI_EVENT_PACKET_t packet;
} USB_MIDI_Event_Packet;
/*
@@ -60,6 +63,27 @@ typedef union {
#define LEAFLABS_ID_VENDOR 0x1EAF
#define MAPLE_ID_PRODUCT 0x0014
+
+#define LEAFLABS_MMA_VENDOR_1 0x7D
+#define LEAFLABS_MMA_VENDOR_2 0x1E
+#define LEAFLABS_MMA_VENDOR_3 0x4F
+
+#define STANDARD_ID_RESPONSE_LENGTH 7
+
+ // move to LGL.h ???
+#define LGL_RESET_CMD 0x1e
+
+#define DEFAULT_MIDI_CHANNEL 0x0A
+#define DEFAULT_MIDI_DEVICE 0x0A
+#define DEFAULT_MIDI_CABLE 0x00
+
+ // eventually all of this should be in a place for settings which can be written to flash.
+extern volatile uint8 myMidiChannel;
+extern volatile uint8 myMidiDevice;
+extern volatile uint8 myMidiCable;
+extern volatile uint8 myMidiID[];
+
+
#define MAX_POWER (100 >> 1)
@@ -83,19 +107,23 @@ typedef union {
/*
* MIDI interface
*/
-
-void usb_midi_enable(gpio_dev*, uint8);
-void usb_midi_disable(gpio_dev*, uint8);
-
-void usb_midi_putc(char ch);
-uint32 usb_midi_tx(const uint8* buf, uint32 len);
-uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len);
-uint32 usb_midi_rx(uint8* buf, uint32 len);
-uint32 usb_midi_peek(uint8* buf, uint32 len);
-
-uint32 usb_midi_data_available(void); /* in RX buffer */
-uint16 usb_midi_get_pending(void);
-uint8 usb_midi_is_transmitting(void);
+ /*
+ * MIDI interface
+ */
+
+ void usb_midi_enable(gpio_dev*, uint8);
+ void usb_midi_disable(gpio_dev*, uint8);
+
+ void usb_midi_putc(char ch);
+ uint32 usb_midi_tx(const uint32* buf, uint32 len);
+ uint32 usb_midi_rx(uint32* buf, uint32 len);
+ uint32 usb_midi_peek(uint32* buf, uint32 len);
+
+ uint32 usb_midi_data_available(void); /* in RX buffer */
+ uint16 usb_midi_get_pending(void);
+ uint8 usb_midi_is_transmitting(void);
+ void usb_minimal_sysex_handler(uint32 *midiBufferRx, uint32 *offset, uint32 *unread);
+ uint32 usb_midi_send_sysex(uint8 *sysex, uint32 len);
#ifdef __cplusplus
}
diff --git a/libmaple/usb/rules.mk b/libmaple/usb/rules.mk
index e8ccc15..8692f40 100644
--- a/libmaple/usb/rules.mk
+++ b/libmaple/usb/rules.mk
@@ -19,7 +19,12 @@ ifeq ($(MCU_SERIES), stm32f1)
ifeq ($(MCU_F1_LINE), performance)
cSRCS_$(d) += $(MCU_SERIES)/usb.c
cSRCS_$(d) += $(MCU_SERIES)/usb_reg_map.c
+ifeq ($(USB_TYPE),midi)
+cSRCS_$(d) += $(MCU_SERIES)/usb_midi_device.c
+cSRCS_$(d) += usb_lib/usb_midi_descr.c
+else
cSRCS_$(d) += $(MCU_SERIES)/usb_cdcacm.c
+endif
cSRCS_$(d) += usb_lib/usb_core.c
cSRCS_$(d) += usb_lib/usb_init.c
cSRCS_$(d) += usb_lib/usb_mem.c
diff --git a/libmaple/usb/stm32f1/usb_midi_device.c b/libmaple/usb/stm32f1/usb_midi_device.c
index 02a0ba2..340c201 100644
--- a/libmaple/usb/stm32f1/usb_midi_device.c
+++ b/libmaple/usb/stm32f1/usb_midi_device.c
@@ -37,7 +37,7 @@
#define USETXBUFFER
#include <libmaple/usb_midi_device.h>
-
+#include <libmaple/midi_specs.h>
#include <libmaple/usb.h>
#include <libmaple/nvic.h>
#include <libmaple/delay.h>
@@ -64,7 +64,7 @@
*****************************************************************************/
#if !(defined(BOARD_maple) || defined(BOARD_maple_RET6) || \
- defined(BOARD_maple_mini) || defined(BOARD_maple_native))
+ defined(BOARD_maple_mini) || defined(BOARD_maple_midi) || defined(BOARD_maple_native))
#warning USB MIDI relies on LeafLabs board-specific configuration.\
You may have problems on non-LeafLabs boards.
#endif
@@ -88,22 +88,22 @@ static void usbSetDeviceAddress(void);
/* I/O state */
+/* I/O state */
/* Received data */
-static volatile uint8 midiBufferRx[USB_MIDI_RX_EPSIZE];
+static volatile uint32 midiBufferRx[USB_MIDI_RX_EPSIZE/4];
/* Read index into midiBufferRx */
static volatile uint32 rx_offset = 0;
/* Transmit data */
-static volatile uint8 midiBufferTx[USB_MIDI_TX_EPSIZE];
+static volatile uint32 midiBufferTx[USB_MIDI_TX_EPSIZE/4];
/* Write index into midiBufferTx */
static volatile uint32 tx_offset = 0;
/* Number of bytes left to transmit */
-static volatile uint32 n_unsent_bytes = 0;
+static volatile uint32 n_unsent_packets = 0;
/* Are we currently sending an IN packet? */
static volatile uint8 transmitting = 0;
/* Number of unread bytes */
-static volatile uint32 n_unread_bytes = 0;
-
+static volatile uint32 n_unread_packets = 0;
/*
* Endpoint callbacks
*/
@@ -189,118 +189,47 @@ void usb_midi_disable(gpio_dev *disc_dev, uint8 disc_bit) {
gpio_write_bit(disc_dev, disc_bit, 1);
}
-void usb_midi_putc(char ch) {
- while (!usb_midi_tx_buffered((uint8*)&ch, 1))
- ;
-/*
- while (!usb_midi_tx((uint8*)&ch, 1))
- ;
-*/
-}
+//void usb_midi_putc(char ch) {
+// while (!usb_midi_tx((uint8*)&ch, 1))
+// ;
+//}
/* This function is non-blocking.
*
* It copies data from a usercode buffer into the USB peripheral TX
* buffer, and returns the number of bytes copied. */
-uint32 usb_midi_tx(const uint8* buf, uint32 len) {
+uint32 usb_midi_tx(const uint32* buf, uint32 packets) {
+ uint32 bytes=packets*4;
/* Last transmission hasn't finished, so abort. */
if (usb_midi_is_transmitting()) {
/* Copy to TxBuffer */
return 0; /* return len */
}
-
- /* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */
- if (len > USB_MIDI_TX_EPSIZE) {
- len = USB_MIDI_TX_EPSIZE;
- }
-
- /* Queue bytes for sending. */
- if (len) {
- usb_copy_to_pma(buf, len, USB_MIDI_TX_ADDR);
- }
- // We still need to wait for the interrupt, even if we're sending
- // zero bytes. (Sending zero-size packets is useful for flushing
- // host-side buffers.)
- usb_set_ep_tx_count(USB_MIDI_TX_ENDP, len);
- n_unsent_bytes = len;
- transmitting = 1;
- usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID);
-
- return len;
-}
-
-/*
- * Theese functions replaces EP double buffering:
- *
- * usb_midi_tx_buffered(const uint8* buf, uint32 len)
- * usb_midi_tx_send_buffer()
- *
- * TODO: Use the modules EP double buffering instead
- *
- * */
-uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len) {
- if (usb_midi_is_transmitting()) {
- uint8 count;
- /* Copy to TxBuffer */
- /* Disable USB EP interrupts */
- USB_BASE->CNTR = USB_ISR_MSK&~USB_CNTR_CTRM;
- if (len>(USB_MIDI_TX_EPSIZE-tx_offset)) len = USB_MIDI_TX_EPSIZE-tx_offset;
- for (count=0;count<len;count++)
- {
- midiBufferTx[tx_offset+count]=buf[count];
- }
- tx_offset += len;
- /* Reenable USB EP interrupts */
- USB_BASE->CNTR = USB_ISR_MSK&~USB_CNTR_CTRM;
- return len; /* return len */
- }
-
+
/* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */
- if (len > USB_MIDI_TX_EPSIZE) {
- len = USB_MIDI_TX_EPSIZE;
+ if (bytes > USB_MIDI_TX_EPSIZE) {
+ bytes = USB_MIDI_TX_EPSIZE;
+ packets=bytes/4;
}
-
+
/* Queue bytes for sending. */
- if (len) {
- usb_copy_to_pma(buf, len, USB_MIDI_TX_ADDR);
+ if (packets) {
+ usb_copy_to_pma((uint8 *)buf, bytes, USB_MIDI_TX_ADDR);
}
// We still need to wait for the interrupt, even if we're sending
// zero bytes. (Sending zero-size packets is useful for flushing
// host-side buffers.)
- usb_set_ep_tx_count(USB_MIDI_TX_ENDP, len);
- n_unsent_bytes = len;
+ usb_set_ep_tx_count(USB_MIDI_TX_ENDP, bytes);
+ n_unsent_packets = packets;
transmitting = 1;
usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID);
-
- return len;
+
+ return packets;
}
-#ifdef USETXBUFFER
-uint32 usb_midi_tx_send_buffer() {
- if (usb_midi_is_transmitting()) {
- return 0;
- }
- /* Queue bytes for sending. */
- if (tx_offset) {
- usb_copy_to_pma((const uint8 *)midiBufferTx, tx_offset, USB_MIDI_TX_ADDR);
- }
- else return 0;
- // We still need to wait for the interrupt, even if we're sending
- // zero bytes. (Sending zero-size packets is useful for flushing
- // host-side buffers.)
- usb_set_ep_tx_count(USB_MIDI_TX_ENDP, tx_offset);
- n_unsent_bytes = tx_offset;
- tx_offset = 0;
- transmitting = 1;
- usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID);
-
- return n_unsent_bytes;
-}
-#endif
-
uint32 usb_midi_data_available(void) {
- return n_unread_bytes;
+ return n_unread_packets;
}
uint8 usb_midi_is_transmitting(void) {
@@ -308,47 +237,46 @@ uint8 usb_midi_is_transmitting(void) {
}
uint16 usb_midi_get_pending(void) {
- return n_unsent_bytes;
+ return n_unsent_packets;
}
/* Nonblocking byte receive.
*
* Copies up to len bytes from our private data buffer (*NOT* the PMA)
* into buf and deq's the FIFO. */
-uint32 usb_midi_rx(uint8* buf, uint32 len) {
+uint32 usb_midi_rx(uint32* buf, uint32 packets) {
/* Copy bytes to buffer. */
- uint32 n_copied = usb_midi_peek(buf, len);
-
+ uint32 n_copied = usb_midi_peek(buf, packets);
+
/* Mark bytes as read. */
- n_unread_bytes -= n_copied;
+ n_unread_packets -= n_copied;
rx_offset += n_copied;
-
+
/* If all bytes have been read, re-enable the RX endpoint, which
* was set to NAK when the current batch of bytes was received. */
- if (n_unread_bytes == 0) {
+ if (n_unread_packets == 0) {
usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE);
usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID);
rx_offset = 0;
}
-
+
return n_copied;
}
/* Nonblocking byte lookahead.
*
* Looks at unread bytes without marking them as read. */
-uint32 usb_midi_peek(uint8* buf, uint32 len) {
+uint32 usb_midi_peek(uint32* buf, uint32 packets) {
int i;
-
- if (len > n_unread_bytes) {
- len = n_unread_bytes;
+ if (packets > n_unread_packets) {
+ packets = n_unread_packets;
}
-
- for (i = 0; i < len; i++) {
+
+ for (i = 0; i < packets; i++) {
buf[i] = midiBufferRx[i + rx_offset];
}
-
- return len;
+
+ return packets;
}
/*
@@ -356,29 +284,29 @@ uint32 usb_midi_peek(uint8* buf, uint32 len) {
*/
static void midiDataTxCb(void) {
- n_unsent_bytes = 0;
+ n_unsent_packets = 0;
transmitting = 0;
-#ifdef USETXBUFFER
- usb_midi_tx_send_buffer();
-#endif
}
static void midiDataRxCb(void) {
+
usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_NAK);
- n_unread_bytes = usb_get_ep_rx_count(USB_MIDI_RX_ENDP);
+ n_unread_packets = usb_get_ep_rx_count(USB_MIDI_RX_ENDP) / 4;
/* This copy won't overwrite unread bytes, since we've set the RX
* endpoint to NAK, and will only set it to VALID when all bytes
* have been read. */
- usb_copy_from_pma((uint8*)midiBufferRx, n_unread_bytes,
+
+ usb_copy_from_pma((uint8*)midiBufferRx, n_unread_packets * 4,
USB_MIDI_RX_ADDR);
-
-
- if (n_unread_bytes == 0) {
+
+ usb_minimal_sysex_handler(midiBufferRx,&rx_offset,&n_unread_packets);
+
+ if (n_unread_packets == 0) {
usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE);
usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID);
rx_offset = 0;
}
-
+
}
/* NOTE: Nothing specific to this device class in this function, but depenedent on the device, move to usb_lib or stm32fxx*/
@@ -439,8 +367,8 @@ static void usbReset(void) {
SetDeviceAddress(0);
/* Reset the RX/TX state */
- n_unread_bytes = 0;
- n_unsent_bytes = 0;
+ n_unread_packets = 0;
+ n_unsent_packets = 0;
rx_offset = 0;
tx_offset = 0;
}
@@ -489,3 +417,236 @@ static void usbSetConfiguration(void) {
static void usbSetDeviceAddress(void) {
USBLIB->state = USB_ADDRESSED;
}
+
+#define MAX_SYSEX_SIZE 256
+
+/* ------------------------------------------------------------------------------------------
+ *
+ * Minimal Sysex Handler
+ *
+ *-------------------------------------------------------------------------------------------
+ * The idea is to provide enough sysex functionality to id the device, and reset it.
+ *------------------------------------------------------------------------------------------*/
+
+
+const uint8 standardIDResponse[]={
+ CIN_SYSEX,
+ MIDIv1_SYSEX_START,
+ USYSEX_NON_REAL_TIME,
+ USYSEX_ALL_CHANNELS,
+ CIN_SYSEX,
+ USYSEX_GENERAL_INFO,
+ USYSEX_GI_ID_RESPONSE,
+ LEAFLABS_MMA_VENDOR_1,
+ CIN_SYSEX,
+ LEAFLABS_MMA_VENDOR_2, // extended ID
+ LEAFLABS_MMA_VENDOR_3, // extended ID
+ 1, // family #1
+ CIN_SYSEX,
+ 2, // family #2
+ 1, // part #1
+ 2, // part #2
+ CIN_SYSEX,
+ 0, // version 1
+ 0, // version 2
+ 1, // version 3
+ CIN_SYSEX_ENDS_IN_2,
+ '!', // lgl compatible
+ MIDIv1_SYSEX_END,
+ 0
+};
+//#define STANDARD_ID_RESPONSE_LENGTH (sizeof(standardIDResponse))
+
+typedef enum {NOT_IN_SYSEX=0,COULD_BE_MY_SYSEX,YUP_ITS_MY_SYSEX,ITS_NOT_MY_SYSEX} sysexStates;
+volatile uint8 sysexBuffer[MAX_SYSEX_SIZE];
+volatile sysexStates sysexState;
+volatile int sysexFinger=0;
+volatile uint8 myMidiChannel = DEFAULT_MIDI_CHANNEL;
+volatile uint8 myMidiDevice = DEFAULT_MIDI_DEVICE;
+volatile uint8 myMidiCable = DEFAULT_MIDI_CABLE;
+volatile uint8 myMidiID[] = { LEAFLABS_MMA_VENDOR_1,LEAFLABS_MMA_VENDOR_2,LEAFLABS_MMA_VENDOR_3,0};
+
+/*
+ 0xF0 SysEx
+ 0x?? LEAFLABS_MMA_VENDOR_1
+ 0x?? LEAFLABS_MMA_VENDOR_2
+
+ 0x?? LEAFLABS_MMA_VENDOR_3
+ 0x10 LGL_DEVICE_NUMBER
+ 0xLE CMD: REBOOT
+
+ 0xf7 EOSysEx
+ */
+#define STACK_TOP 0x20000800
+#define EXC_RETURN 0xFFFFFFF9
+#define DEFAULT_CPSR 0x61000000
+#define RESET_DELAY 100000
+static void wait_reset(void) {
+ delay_us(RESET_DELAY);
+ nvic_sys_reset();
+}
+
+/* -----------------------------------------------------------------------------dealWithItQuickly()
+ * Note: at this point we have established that the sysex belongs to us.
+ * So we need to respond to any generic requests like information requests.
+ * We also need to handle requests which are meant for us. At the moment this is just the
+ * reset request.
+ *
+ */
+void dealWithItQuickly(){
+ switch (sysexBuffer[1]) {
+ case USYSEX_NON_REAL_TIME:
+ switch (sysexBuffer[3]) {
+ case USYSEX_GENERAL_INFO:
+ if (sysexBuffer[4]==USYSEX_GI_ID_REQUEST) {
+ usb_midi_tx((uint32 *) standardIDResponse, STANDARD_ID_RESPONSE_LENGTH);
+ }
+ }
+ case USYSEX_REAL_TIME:
+ break;
+ case LEAFLABS_MMA_VENDOR_1:
+ if (sysexBuffer[5]==LGL_RESET_CMD) {
+ uintptr_t target = (uintptr_t)wait_reset | 0x1;
+ asm volatile("mov r0, %[stack_top] \n\t" // Reset stack
+ "mov sp, r0 \n\t"
+ "mov r0, #1 \n\t"
+ "mov r1, %[target_addr] \n\t"
+ "mov r2, %[cpsr] \n\t"
+ "push {r2} \n\t" // Fake xPSR
+ "push {r1} \n\t" // PC target addr
+ "push {r0} \n\t" // Fake LR
+ "push {r0} \n\t" // Fake R12
+ "push {r0} \n\t" // Fake R3
+ "push {r0} \n\t" // Fake R2
+ "push {r0} \n\t" // Fake R1
+ "push {r0} \n\t" // Fake R0
+ "mov lr, %[exc_return] \n\t"
+ "bx lr"
+ :
+ : [stack_top] "r" (STACK_TOP),
+ [target_addr] "r" (target),
+ [exc_return] "r" (EXC_RETURN),
+ [cpsr] "r" (DEFAULT_CPSR)
+ : "r0", "r1", "r2");
+ /* Can't happen. */
+ ASSERT_FAULT(0);
+
+ }
+
+ default:
+ break;
+ }
+ ;//turn the led on?
+}
+
+/* ----------------------------------------------------------------------usb_minimal_sysex_handler()
+ * The idea here is to identify which Sysex's belong to us and deal with them.
+ */
+void usb_minimal_sysex_handler(uint32 *midiBufferRx, uint32 *rx_offset, uint32 *n_unread_packets) {
+ USB_MIDI_Event_Packet * midiPackets = (USB_MIDI_Event_Packet *) (midiBufferRx+(*rx_offset));
+ uint8 nPackets=((*n_unread_packets)-(*rx_offset));
+ uint32 cPacket;
+ uint8 soPackets=0;
+ /********************************* ACHTUNG! ignores usbmidi cable ********************************/
+ USB_MIDI_Event_Packet e;
+ for (cPacket=0;cPacket<nPackets;cPacket++){
+ e=midiPackets[cPacket];
+ if (!CIN_IS_SYSEX(e.packet.cin)) {
+ continue;
+ } // else {
+ if (!soPackets) {
+ soPackets=cPacket;//*4;
+ }
+ if ((sysexState==YUP_ITS_MY_SYSEX) && ((sysexFinger+3)>=MAX_SYSEX_SIZE)){
+ sysexState=ITS_NOT_MY_SYSEX; //eisenhower policy. Even if its mine I cant deal with it.
+ }
+ switch (e.packet.cin) {
+ case CIN_SYSEX:
+ switch (sysexState) {
+ case NOT_IN_SYSEX : // new sysex.
+ sysexFinger=0;
+ if (e.packet.midi0 == MIDIv1_SYSEX_START) {
+ if (e.packet.midi1==USYSEX_REAL_TIME
+ ||e.packet.midi1==USYSEX_NON_REAL_TIME) {
+ if ((e.packet.midi2==myMidiChannel)
+ ||(e.packet.midi2==USYSEX_ALL_CHANNELS)
+ ) {
+ sysexState=YUP_ITS_MY_SYSEX;
+ sysexBuffer[sysexFinger++]=MIDIv1_SYSEX_START;
+ sysexBuffer[sysexFinger++]=e.packet.midi1;
+ sysexBuffer[sysexFinger++]=e.packet.midi2;
+ break;
+ }
+ } else if ((e.packet.midi1==myMidiID[0])
+ && (e.packet.midi2==myMidiID[1])
+ ){
+ sysexState=COULD_BE_MY_SYSEX;
+ sysexBuffer[sysexFinger++]=MIDIv1_SYSEX_START;
+ sysexBuffer[sysexFinger++]=e.packet.midi1;
+ sysexBuffer[sysexFinger++]=e.packet.midi2;
+ break;
+ }
+ }
+ break;
+
+ case COULD_BE_MY_SYSEX:
+ if (e.packet.midi0==myMidiID[2]) {
+ sysexState=YUP_ITS_MY_SYSEX;
+ sysexBuffer[sysexFinger++]=e.packet.midi0;
+ sysexBuffer[sysexFinger++]=e.packet.midi1;
+ sysexBuffer[sysexFinger++]=e.packet.midi2;
+ } else {
+ sysexState=ITS_NOT_MY_SYSEX;
+ sysexFinger=0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+ break;
+ case CIN_SYSEX_ENDS_IN_1:
+ case CIN_SYSEX_ENDS_IN_2:
+ case CIN_SYSEX_ENDS_IN_3:
+ sysexBuffer[sysexFinger++]=e.packet.midi0;
+ sysexBuffer[sysexFinger++]=e.packet.midi1;
+ sysexBuffer[sysexFinger++]=e.packet.midi2;
+ if (sysexState==YUP_ITS_MY_SYSEX) {
+ if(cPacket>=(*n_unread_packets)){
+ *n_unread_packets = soPackets;
+ *rx_offset = soPackets;
+ } else {
+ uint32 s;
+ uint32 d;
+ for (s = cPacket, d=soPackets;
+ ((*n_unread_packets) && (s <= USB_MIDI_RX_EPSIZE/4));
+ d++,s++
+ ) {
+ midiPackets[d]=midiPackets[s];
+ (*n_unread_packets)--;
+ (*rx_offset)++;
+
+ }
+ // we need to reset the for loop variables to re process remaining data.
+ nPackets=((*n_unread_packets)-(*rx_offset));
+ cPacket=(*rx_offset);
+ }
+ dealWithItQuickly();
+
+ }
+ sysexFinger=0;
+ sysexState=NOT_IN_SYSEX;
+
+ break;
+ default:
+ return;
+ }
+ //}
+
+
+
+ }
+ // its our sysex and we will cry if we want to
+ return;
+}
diff --git a/support/make/board-includes/maple_midi.mk b/support/make/board-includes/maple_midi.mk
new file mode 100644
index 0000000..51b3131
--- /dev/null
+++ b/support/make/board-includes/maple_midi.mk
@@ -0,0 +1,8 @@
+MCU := STM32F103CB
+PRODUCT_ID := 0003
+ERROR_LED_PORT := GPIOB
+ERROR_LED_PIN := 1
+MCU_SERIES := stm32f1
+MCU_F1_LINE := performance
+USB_TYPE := midi
+LD_MEM_DIR := sram_20k_flash_128k
diff --git a/support/scripts/sysexreset.py b/support/scripts/sysexreset.py
new file mode 100755
index 0000000..2a57b4c
--- /dev/null
+++ b/support/scripts/sysexreset.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+
+
+'''---------------------------------------------------------------- sysexreset.py
+
+ this resets the maple midi using sysex.
+
+
+ Copyright (c) 2011 Donald Delmar Davis, Suspect Devices
+
+ 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.
+
+'''
+import rtmidi_python as rtmidi
+import time
+
+# this stuff should probably go in a constants file.
+SYSEX_BEGIN =0xf0
+SYSEX_NON_REAL_TIME =0x7e
+SYSEX_NON_REAL =0x7f
+SYSEX_ALL_CHANNELS =0x7f
+SYSEX_GENERAL_INFO =0x06
+SYSEX_IDENTITY_REQUEST =0x01
+SYSEX_IDENTITY_REPLY =0x02
+SYSEX_END_SYSEX =0xF7
+
+
+LEAFLABS_MMA_VENDOR_1 =0x7D
+LEAFLABS_MMA_VENDOR_2 =0x1E
+LEAFLABS_MMA_VENDOR_3 =0x4F
+LGL_RESET_CMD =0x1e
+
+
+askfortheids=[SYSEX_BEGIN,SYSEX_NON_REAL_TIME,SYSEX_ALL_CHANNELS,
+ SYSEX_GENERAL_INFO,SYSEX_IDENTITY_REQUEST,SYSEX_END_SYSEX]
+
+resetthedevice=[SYSEX_BEGIN,
+ LEAFLABS_MMA_VENDOR_1,
+ LEAFLABS_MMA_VENDOR_2,
+ LEAFLABS_MMA_VENDOR_3,
+ 0x0a, #device/channel is currently ignored...
+ LGL_RESET_CMD,
+ SYSEX_END_SYSEX]
+
+we_are_done_here=False
+
+'''
+ this is our callback routine for midi messages.
+ It handles sysexes sent to it and the universal sysex id response.
+'''
+def handleOurSysexes(message, time_stamp):
+ global we_are_done_here
+ #print "?"+str(message)+"?"
+ if (len(message)>4 and message[0]==SYSEX_BEGIN): #if its a sysex message
+ if (message[1]==125 and message[2]==51 and message[3]==51):
+ print "DBG:",decodeHexLine(message)
+ elif (message[1]==SYSEX_NON_REAL_TIME and message[3]==SYSEX_GENERAL_INFO
+ and message[4]==SYSEX_IDENTITY_REPLY ):
+ print 'MANUFACTURER ID: 0x%02X 0x%02X 0x%02X' % (
+ message[5],message[6],message[7])
+ print "FAMILY ID : 0x%02X%02x" % (
+ message[8], message[9])
+ print "PRODUCT ID : 0x%02X%02x" % (
+ message[10], message[11])
+ print "VERSION : 0x%02X%02x%02X%02x" % (
+ message[12],message[13],message[14],message[15])
+ if (message[15] == ord('!')) :
+ print "YAY LETS GET LOADED!!!"
+ midi_out.send_message(resetthedevice)
+ we_are_done_here=True #exit()
+
+
+
+#:llaaaattd1..dncs
+def decodeHexLine(payload):
+# print payload
+ #length = int(chr(payload[5])+chr(payload[6]),16)
+ #address = int(chr(payload[7])+chr(payload[8])+chr(payload[9])+chr(payload[10]),16)
+ #type = int(chr(payload[11])+chr(payload[12]),16)
+ #checksum = int(chr(payload[len(payload)-2])+chr(payload[len(payload)-2]),16)
+ high=None
+ decoded=''
+ for byte in payload[13:len(payload)-3] :
+ if high is None:
+ high=byte
+ else:
+ pair=chr(high)+chr(byte)
+ decoded=decoded+chr(int(pair,16))
+ high=None
+ return decoded
+
+# main code begins here.
+
+midi_in = rtmidi.MidiIn()
+midi_out = rtmidi.MidiOut()
+print midi_in.ports
+midi_in.callback = handleOurSysexes
+midi_in.ignore_types(False,False,False)
+midi_in.open_port(0)
+midi_out.open_port(0)
+time.sleep(2)
+midi_out.send_message(askfortheids)
+
+
+while not we_are_done_here:
+ time.sleep(1)
+ #print "requesting id"
+ #midi_out.send_message(askfortheids)
+
+
+"""
+ http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec.htm
+
+ 0xF0 SysEx
+ 0x7E Non-Realtime
+ 0x7F The SysEx channel. Could be from 0x00 to 0x7F. Here we set it to "disregard channel".
+ 0x06 Sub-ID -- General Information
+ 0x01 Sub-ID2 -- Identity Request
+ 0xF7 End of SysEx
+ Here is the Identity Reply message:
+ 0xF0 SysEx
+ 0x7E Non-Realtime
+ 0x7F The SysEx channel. Could be from 0x00 to 0x7F.
+ Here we set it to "disregard channel".
+ 0x06 Sub-ID -- General Information
+ 0x02 Sub-ID2 -- Identity Reply
+ 0xID Manufacturer's ID
+ 0xf1 The f1 and f2 bytes make up the family code. Each
+ 0xf2 manufacturer assigns different family codes to his products.
+ 0xp1 The p1 and p2 bytes make up the model number. Each
+ 0xp2 manufacturer assigns different model numbers to his products.
+ 0xv1 The v1, v2, v3 and v4 bytes make up the version number.
+ 0xv2
+ 0xv3
+ 0xv4
+ 0xF7 End of SysEx
+""" \ No newline at end of file
diff --git a/wirish/boards/maple_midi/board.cpp b/wirish/boards/maple_midi/board.cpp
new file mode 100644
index 0000000..009ec51
--- /dev/null
+++ b/wirish/boards/maple_midi/board.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 LeafLabs, LLC.
+ *
+ * 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.
+ *****************************************************************************/
+
+/**
+ * @file wirish/boards/maple_mini/board.cpp
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Maple Mini board file.
+ */
+
+#include <board/board.h>
+
+#include <libmaple/gpio.h>
+#include <libmaple/timer.h>
+
+#include <wirish/wirish_debug.h>
+#include <wirish/wirish_types.h>
+
+/* Since we want the Serial Wire/JTAG pins as GPIOs, disable both SW
+ * and JTAG debug support, unless configured otherwise. */
+void boardInit(void) {
+#ifndef CONFIG_MAPLE_MINI_NO_DISABLE_DEBUG
+ disableDebugPorts();
+#endif
+}
+
+extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = {
+
+ /* Top header */
+
+ {GPIOB, NULL, NULL, 11, 0, ADCx}, /* D0/PB11 */
+ {GPIOB, NULL, NULL, 10, 0, ADCx}, /* D1/PB10 */
+ {GPIOB, NULL, NULL, 2, 0, ADCx}, /* D2/PB2 */
+ {GPIOB, TIMER3, ADC1, 0, 3, 8}, /* D3/PB0 */
+ {GPIOA, TIMER3, ADC1, 7, 2, 7}, /* D4/PA7 */
+ {GPIOA, TIMER3, ADC1, 6, 1, 6}, /* D5/PA6 */
+ {GPIOA, NULL, ADC1, 5, 0, 5}, /* D6/PA5 */
+ {GPIOA, NULL, ADC1, 4, 0, 4}, /* D7/PA4 */
+ {GPIOA, TIMER2, ADC1, 3, 4, 3}, /* D8/PA3 */
+ {GPIOA, TIMER2, ADC1, 2, 3, 2}, /* D9/PA2 */
+ {GPIOA, TIMER2, ADC1, 1, 2, 1}, /* D10/PA1 */
+ {GPIOA, TIMER2, ADC1, 0, 1, 0}, /* D11/PA0 */
+ {GPIOC, NULL, NULL, 15, 0, ADCx}, /* D12/PC15 */
+ {GPIOC, NULL, NULL, 14, 0, ADCx}, /* D13/PC14 */
+ {GPIOC, NULL, NULL, 13, 0, ADCx}, /* D14/PC13 */
+
+ /* Bottom header */
+
+ {GPIOB, TIMER4, NULL, 7, 2, ADCx}, /* D15/PB7 */
+ {GPIOB, TIMER4, NULL, 6, 1, ADCx}, /* D16/PB6 */
+ {GPIOB, NULL, NULL, 5, 0, ADCx}, /* D17/PB5 */
+ {GPIOB, NULL, NULL, 4, 0, ADCx}, /* D18/PB4 */
+ {GPIOB, NULL, NULL, 3, 0, ADCx}, /* D19/PB3 */
+ {GPIOA, NULL, NULL, 15, 0, ADCx}, /* D20/PA15 */
+ {GPIOA, NULL, NULL, 14, 0, ADCx}, /* D21/PA14 */
+ {GPIOA, NULL, NULL, 13, 0, ADCx}, /* D22/PA13 */
+ {GPIOA, NULL, NULL, 12, 0, ADCx}, /* D23/PA12 */
+ {GPIOA, TIMER1, NULL, 11, 4, ADCx}, /* D24/PA11 */
+ {GPIOA, TIMER1, NULL, 10, 3, ADCx}, /* D25/PA10 */
+ {GPIOA, TIMER1, NULL, 9, 2, ADCx}, /* D26/PA9 */
+ {GPIOA, TIMER1, NULL, 8, 1, ADCx}, /* D27/PA8 */
+ {GPIOB, NULL, NULL, 15, 0, ADCx}, /* D28/PB15 */
+ {GPIOB, NULL, NULL, 14, 0, ADCx}, /* D29/PB14 */
+ {GPIOB, NULL, NULL, 13, 0, ADCx}, /* D30/PB13 */
+ {GPIOB, NULL, NULL, 12, 0, ADCx}, /* D31/PB12 */
+ {GPIOB, TIMER4, NULL, 8, 3, ADCx}, /* D32/PB8 */
+ {GPIOB, TIMER3, ADC1, 1, 4, 9}, /* D33/PB1 */
+};
+
+extern const uint8 boardPWMPins[BOARD_NR_PWM_PINS] __FLASH__ = {
+ 3, 4, 5, 8, 9, 10, 11, 15, 16, 25, 26, 27
+};
+
+extern const uint8 boardADCPins[BOARD_NR_ADC_PINS] __FLASH__ = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+#define USB_DP 23
+#define USB_DM 24
+
+extern const uint8 boardUsedPins[BOARD_NR_USED_PINS] __FLASH__ = {
+ BOARD_LED_PIN, BOARD_BUTTON_PIN, USB_DP, USB_DM
+};
diff --git a/wirish/boards/maple_midi/include/board/board.h b/wirish/boards/maple_midi/include/board/board.h
new file mode 100644
index 0000000..5eaf948
--- /dev/null
+++ b/wirish/boards/maple_midi/include/board/board.h
@@ -0,0 +1,85 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2011 LeafLabs, LLC.
+ *
+ * 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.
+ *****************************************************************************/
+
+/**
+ * @file wirish/boards/maple_mini/include/board/board.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Maple Mini board header.
+ *
+ * See wirish/boards/maple/include/board/board.h for more information
+ * on these definitions.
+ */
+
+#ifndef _BOARD_MAPLE_MINI_H_
+#define _BOARD_MAPLE_MINI_H_
+
+#define CYCLES_PER_MICROSECOND 72
+#define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */
+
+#define BOARD_BUTTON_PIN 32
+#define BOARD_LED_PIN 33
+
+#define BOARD_NR_USARTS 3
+#define BOARD_USART1_TX_PIN 26
+#define BOARD_USART1_RX_PIN 25
+#define BOARD_USART2_TX_PIN 9
+#define BOARD_USART2_RX_PIN 8
+#define BOARD_USART3_TX_PIN 1
+#define BOARD_USART3_RX_PIN 0
+
+#define BOARD_NR_SPI 2
+#define BOARD_SPI1_NSS_PIN 7
+#define BOARD_SPI1_MOSI_PIN 4
+#define BOARD_SPI1_MISO_PIN 5
+#define BOARD_SPI1_SCK_PIN 6
+#define BOARD_SPI2_NSS_PIN 31
+#define BOARD_SPI2_MOSI_PIN 28
+#define BOARD_SPI2_MISO_PIN 29
+#define BOARD_SPI2_SCK_PIN 30
+
+#define BOARD_NR_GPIO_PINS 34
+#define BOARD_NR_PWM_PINS 12
+#define BOARD_NR_ADC_PINS 9
+#define BOARD_NR_USED_PINS 4
+
+#define BOARD_JTMS_SWDIO_PIN 22
+#define BOARD_JTCK_SWCLK_PIN 21
+#define BOARD_JTDI_PIN 20
+#define BOARD_JTDO_PIN 19
+#define BOARD_NJTRST_PIN 18
+
+#define BOARD_USB_DISC_DEV GPIOB
+#define BOARD_USB_DISC_BIT 9
+#define BOARD_HAVE_MIDIUSB 1
+#define USEMIDIUSB 1
+#define MIDIUSB 1
+enum {
+ PB11, PB10, PB2, PB0, PA7, PA6, PA5, PA4, PA3, PA2, PA1, PA0, PC15, PC14,
+ PC13, PB7, PB6, PB5, PB4, PB3, PA15, PA14, PA13, PA12, PA11, PA10, PA9,
+ PA8, PB15, PB14, PB13, PB12, PB8, PB1
+};
+
+#endif
diff --git a/wirish/include/wirish/usb_midi.h b/wirish/include/wirish/usb_midi.h
index 3ee1989..3d2b385 100644
--- a/wirish/include/wirish/usb_midi.h
+++ b/wirish/include/wirish/usb_midi.h
@@ -38,22 +38,22 @@
/**
* @brief Virtual serial terminal.
*/
-class USBMidi : public Print {
+class USBMidi/* : public Print */ {
public:
USBMidi(void);
-
- void begin(void);
- void end(void);
-
+ void begin();
+ void end();
+
uint32 available(void);
-
- uint32 read(void *buf, uint32 len);
- uint8 read(void);
-
- void write(uint8);
- void write(const char *str);
- void write(const void*, uint32);
-
+
+ uint32 readPackets(void *buf, uint32 len);
+ uint32 readPacket(void);
+
+ void writePacket(uint32);
+ void writePackets(const void*, uint32);
+
+ void writeSysex(const void*, uint32);
+
uint8 isConnected();
uint8 pending();
};
diff --git a/wirish/rules.mk b/wirish/rules.mk
index 1cac74a..8ee9a12 100644
--- a/wirish/rules.mk
+++ b/wirish/rules.mk
@@ -30,7 +30,11 @@ cppSRCS_$(d) += HardwareTimer.cpp
cppSRCS_$(d) += Print.cpp
cppSRCS_$(d) += pwm.cpp
ifeq ($(MCU_SERIES), stm32f1)
+ifeq ($(USB_TYPE),midi)
+cppSRCS_$(d) += usb_midi.cpp
+else
cppSRCS_$(d) += usb_serial.cpp # HACK: this is currently STM32F1 only.
+endif
cppSRCS_$(d) += HardwareSPI.cpp # FIXME: port to F2 and fix wirish.h
endif
cppSRCS_$(d) += wirish_analog.cpp
diff --git a/wirish/usb_midi.cpp b/wirish/usb_midi.cpp
index 5a48efb..c315e5f 100644
--- a/wirish/usb_midi.cpp
+++ b/wirish/usb_midi.cpp
@@ -64,36 +64,31 @@ void USBMidi::end(void) {
#endif
}
-void USBMidi::write(uint8 ch) {
- this->write(&ch, 1);
+void USBMidi::writePacket(uint32 p) {
+ this->writePackets(&p, 1);
}
-void USBMidi::write(const char *str) {
- this->write(str, strlen(str));
-}
-
-void USBMidi::write(const void *buf, uint32 len) {
+void USBMidi::writePackets(const void *buf, uint32 len) {
if (!this->isConnected() || !buf) {
return;
}
-
+
uint32 txed = 0;
uint32 old_txed = 0;
uint32 start = millis();
-
+
uint32 sent = 0;
-
+
while (txed < len && (millis() - start < USB_TIMEOUT)) {
-// sent = usb_midi_tx_buffered((const uint8*)buf + txed, len - txed);
- sent = usb_midi_tx((const uint8*)buf + txed, len - txed);
+ sent = usb_midi_tx((const uint32*)buf + txed, len - txed);
txed += sent;
if (old_txed != txed) {
start = millis();
}
old_txed = txed;
}
-
-
+
+
if (sent == USB_MIDI_TX_EPSIZE) {
while (usb_midi_is_transmitting() != 0) {
}
@@ -102,28 +97,29 @@ void USBMidi::write(const void *buf, uint32 len) {
}
}
+
uint32 USBMidi::available(void) {
return usb_midi_data_available();
}
-uint32 USBMidi::read(void *buf, uint32 len) {
+uint32 USBMidi::readPackets(void *buf, uint32 len) {
if (!buf) {
return 0;
}
-
+
uint32 rxed = 0;
while (rxed < len) {
- rxed += usb_midi_rx((uint8*)buf + rxed, len - rxed);
+ rxed += usb_midi_rx((uint32*)buf + rxed, len - rxed);
}
-
+
return rxed;
}
/* Blocks forever until 1 byte is received */
-uint8 USBMidi::read(void) {
- uint8 b;
- this->read(&b, 1);
- return b;
+uint32 USBMidi::readPacket(void) {
+ uint32 p;
+ this->readPackets(&p, 1);
+ return p;
}
uint8 USBMidi::pending(void) {