usb: add support for midi
Этот коммит содержится в:
родитель
2f843af286
коммит
8fed063820
6 изменённых файлов: 246 добавлений и 11 удалений
4
Makefile
4
Makefile
|
@ -595,11 +595,13 @@ endif
|
|||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usbhid
|
||||
# test usb
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
|
|
54
src/examples/usb-midi/main.go
Обычный файл
54
src/examples/usb-midi/main.go
Обычный файл
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"machine/usb/midi"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
m := midi.New()
|
||||
m.SetCallback(func(b []byte) {
|
||||
led.Set(!led.Get())
|
||||
fmt.Printf("% X\r\n", b)
|
||||
m.Write(b)
|
||||
})
|
||||
|
||||
prev := true
|
||||
chords := []struct {
|
||||
name string
|
||||
keys []byte
|
||||
}{
|
||||
{name: "C ", keys: []byte{60, 64, 67}},
|
||||
{name: "G ", keys: []byte{55, 59, 62}},
|
||||
{name: "Am", keys: []byte{57, 60, 64}},
|
||||
{name: "F ", keys: []byte{53, 57, 60}},
|
||||
}
|
||||
index := 0
|
||||
|
||||
for {
|
||||
current := button.Get()
|
||||
if prev != current {
|
||||
led.Set(current)
|
||||
if current {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x08, 0x80, c, 0x40})
|
||||
}
|
||||
index = (index + 1) % len(chords)
|
||||
} else {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x09, 0x90, c, 0x40})
|
||||
}
|
||||
}
|
||||
prev = current
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ var usbDescriptor = descriptorCDC
|
|||
const (
|
||||
usbDescriptorConfigCDC = 1 << iota
|
||||
usbDescriptorConfigHID
|
||||
usbDescriptorConfigMIDI
|
||||
)
|
||||
|
||||
var usbDescriptorConfig uint8 = usbDescriptorConfigCDC
|
||||
|
@ -137,11 +138,13 @@ const (
|
|||
usb_HID_INTERFACE = 2 // HID
|
||||
|
||||
// Endpoint
|
||||
usb_CONTROL_ENDPOINT = 0
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
usb_HID_ENDPOINT_IN = 4
|
||||
usb_CONTROL_ENDPOINT = 0
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
usb_HID_ENDPOINT_IN = 4
|
||||
usb_MIDI_ENDPOINT_OUT = 5
|
||||
usb_MIDI_ENDPOINT_IN = 6
|
||||
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
|
@ -170,11 +173,13 @@ var (
|
|||
usbSetupHandler [numberOfInterfaces]func(USBSetup) bool
|
||||
|
||||
endPoints = []uint32{
|
||||
usb_CONTROL_ENDPOINT: usb_ENDPOINT_TYPE_CONTROL,
|
||||
usb_CDC_ENDPOINT_ACM: (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
usb_CDC_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
usb_CDC_ENDPOINT_IN: (usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
|
||||
usb_HID_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||
usb_CONTROL_ENDPOINT: usb_ENDPOINT_TYPE_CONTROL,
|
||||
usb_CDC_ENDPOINT_ACM: (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
usb_CDC_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
usb_CDC_ENDPOINT_IN: (usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
|
||||
usb_HID_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||
usb_MIDI_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||
usb_MIDI_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -222,6 +227,8 @@ func sendDescriptor(setup USBSetup) {
|
|||
// composite descriptor
|
||||
if (usbDescriptorConfig & usbDescriptorConfigHID) > 0 {
|
||||
usbDescriptor = descriptorCDCHID
|
||||
} else if (usbDescriptorConfig & usbDescriptorConfigMIDI) > 0 {
|
||||
usbDescriptor = descriptorCDCMIDI
|
||||
} else {
|
||||
usbDescriptor = descriptorCDC
|
||||
}
|
||||
|
@ -360,3 +367,12 @@ func EnableHID(txHandler func(), rxHandler func([]byte), setupHandler func(USBSe
|
|||
usbTxHandler[usb_HID_ENDPOINT_IN] = txHandler
|
||||
usbSetupHandler[usb_HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
|
||||
}
|
||||
|
||||
// EnableMIDI enables MIDI. This function must be executed from the init().
|
||||
func EnableMIDI(txHandler func(), rxHandler func([]byte), setupHandler func(USBSetup) bool) {
|
||||
usbDescriptorConfig |= usbDescriptorConfigMIDI
|
||||
endPoints[usb_MIDI_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut)
|
||||
endPoints[usb_MIDI_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)
|
||||
usbRxHandler[usb_MIDI_ENDPOINT_OUT] = rxHandler
|
||||
usbTxHandler[usb_MIDI_ENDPOINT_IN] = txHandler
|
||||
}
|
||||
|
|
52
src/machine/usb/midi/buffer.go
Обычный файл
52
src/machine/usb/midi/buffer.go
Обычный файл
|
@ -0,0 +1,52 @@
|
|||
package midi
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize][4]byte
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val []byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
79
src/machine/usb/midi/midi.go
Обычный файл
79
src/machine/usb/midi/midi.go
Обычный файл
|
@ -0,0 +1,79 @@
|
|||
package midi
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
const (
|
||||
midiEndpointOut = 5 // from PC
|
||||
midiEndpointIn = 6 // to PC
|
||||
)
|
||||
|
||||
var Midi *midi
|
||||
|
||||
type midi struct {
|
||||
buf *RingBuffer
|
||||
callbackFuncRx func([]byte)
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
if Midi == nil {
|
||||
Midi = newMidi()
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *midi {
|
||||
return Midi
|
||||
}
|
||||
|
||||
func newMidi() *midi {
|
||||
m := &midi{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
machine.EnableMIDI(m.Callback, m.CallbackRx, nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *midi) SetCallback(callbackRx func([]byte)) {
|
||||
m.callbackFuncRx = callbackRx
|
||||
}
|
||||
|
||||
func (m *midi) Write(b []byte) (n int, err error) {
|
||||
i := 0
|
||||
for i = 0; i < len(b); i += 4 {
|
||||
m.tx(b[i : i+4])
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// sendUSBPacket sends a MIDIPacket.
|
||||
func (m *midi) sendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(midiEndpointIn, b)
|
||||
}
|
||||
|
||||
// from BulkIn
|
||||
func (m *midi) Callback() {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *midi) tx(b []byte) {
|
||||
if m.waitTxc {
|
||||
m.buf.Put(b)
|
||||
} else {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
// from BulkOut
|
||||
func (m *midi) CallbackRx(b []byte) {
|
||||
if m.callbackFuncRx != nil {
|
||||
m.callbackFuncRx(b)
|
||||
}
|
||||
}
|
|
@ -71,3 +71,35 @@ var descriptorCDCHID = USBDescriptor{
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
var descriptorCDCMIDI = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0xaf, 0x00, 0x04, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
0x08, 0x0b, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x03,
|
||||
0x09, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00,
|
||||
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00,
|
||||
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
|
||||
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
||||
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
||||
0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00,
|
||||
0x09, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x01,
|
||||
0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x03,
|
||||
},
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче