Add USB HID joystick support (#3366)
machine/usb: add USB HID joystick support
Этот коммит содержится в:
родитель
0566bbfeb4
коммит
a7ff2731b9
10 изменённых файлов: 482 добавлений и 14 удалений
27
src/examples/hid-joystick/main.go
Обычный файл
27
src/examples/hid-joystick/main.go
Обычный файл
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"machine/usb/joystick"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var js = joystick.Port()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(log.Lmicroseconds)
|
||||||
|
ticker := time.NewTicker(10 * time.Millisecond)
|
||||||
|
cnt := 0
|
||||||
|
const f = 3.0
|
||||||
|
for range ticker.C {
|
||||||
|
t := float64(cnt) * 0.01
|
||||||
|
x := 32767 * math.Sin(2*math.Pi*f*t)
|
||||||
|
button := cnt%100 > 50
|
||||||
|
js.SetButton(2, button)
|
||||||
|
js.SetButton(3, !button)
|
||||||
|
js.SetAxis(0, int(x))
|
||||||
|
js.SendState()
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
|
@ -151,7 +151,10 @@ func initEndpoint(ep, config uint32) {
|
||||||
usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
|
||||||
// TODO: not really anything, seems like...
|
val |= usbEpControlEndpointTypeInterrupt
|
||||||
|
usbDPSRAM.EPxControl[ep].Out.Set(val)
|
||||||
|
usbDPSRAM.EPxBufferControl[ep].Out.Set(USBBufferLen & usbBuf0CtrlLenMask)
|
||||||
|
usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlAvail)
|
||||||
|
|
||||||
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
|
||||||
val |= usbEpControlEndpointTypeBulk
|
val |= usbEpControlEndpointTypeBulk
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"machine/usb"
|
"machine/usb"
|
||||||
)
|
)
|
||||||
|
@ -89,8 +91,9 @@ var (
|
||||||
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
|
||||||
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
|
||||||
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||||
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
|
||||||
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||||
|
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -108,6 +111,8 @@ func sendDescriptor(setup usb.Setup) {
|
||||||
usbDescriptor = usb.DescriptorCDCHID
|
usbDescriptor = usb.DescriptorCDCHID
|
||||||
case (usbDescriptorConfig & usb.DescriptorConfigMIDI) > 0:
|
case (usbDescriptorConfig & usb.DescriptorConfigMIDI) > 0:
|
||||||
usbDescriptor = usb.DescriptorCDCMIDI
|
usbDescriptor = usb.DescriptorCDCMIDI
|
||||||
|
case (usbDescriptorConfig & usb.DescriptorConfigJoystick) > 0:
|
||||||
|
usbDescriptor = usb.DescriptorCDCJoystick
|
||||||
default:
|
default:
|
||||||
usbDescriptor = usb.DescriptorCDC
|
usbDescriptor = usb.DescriptorCDC
|
||||||
}
|
}
|
||||||
|
@ -260,3 +265,18 @@ func EnableMIDI(txHandler func(), rxHandler func([]byte), setupHandler func(usb.
|
||||||
usbRxHandler[usb.MIDI_ENDPOINT_OUT] = rxHandler
|
usbRxHandler[usb.MIDI_ENDPOINT_OUT] = rxHandler
|
||||||
usbTxHandler[usb.MIDI_ENDPOINT_IN] = txHandler
|
usbTxHandler[usb.MIDI_ENDPOINT_IN] = txHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableJoystick enables HID. This function must be executed from the init().
|
||||||
|
func EnableJoystick(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool, hidDesc []byte) {
|
||||||
|
idx := bytes.Index(usb.DescriptorCDCJoystick.Configuration, []byte{
|
||||||
|
0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22,
|
||||||
|
})
|
||||||
|
binary.LittleEndian.PutUint16(usb.DescriptorCDCJoystick.Configuration[idx+7:idx+9], uint16(len(hidDesc)))
|
||||||
|
usb.DescriptorCDCJoystick.HID[2] = hidDesc
|
||||||
|
usbDescriptorConfig |= usb.DescriptorConfigJoystick
|
||||||
|
endPoints[usb.HID_ENDPOINT_OUT] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut)
|
||||||
|
usbRxHandler[usb.HID_ENDPOINT_OUT] = rxHandler
|
||||||
|
endPoints[usb.HID_ENDPOINT_IN] = (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn)
|
||||||
|
usbTxHandler[usb.HID_ENDPOINT_IN] = txHandler
|
||||||
|
usbSetupHandler[usb.HID_INTERFACE] = setupHandler // 0x03 (HID - Human Interface Device)
|
||||||
|
}
|
||||||
|
|
|
@ -109,9 +109,58 @@ var DescriptorCDCMIDI = Descriptor{
|
||||||
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
||||||
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
||||||
0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00,
|
0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00,
|
||||||
0x09, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x05, 0x07, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x05, 0x25, 0x01, 0x01, 0x01,
|
0x05, 0x25, 0x01, 0x01, 0x01,
|
||||||
0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x05, 0x25, 0x01, 0x01, 0x03,
|
0x05, 0x25, 0x01, 0x01, 0x03,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DescriptorCDCJoystick = Descriptor{
|
||||||
|
Device: []byte{
|
||||||
|
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40,
|
||||||
|
0x41, 0x23, 0x36, 0x80, 0x00, 0x01, 0x01, 0x02,
|
||||||
|
0x03, 0x01,
|
||||||
|
},
|
||||||
|
Configuration: []byte{
|
||||||
|
// Configuration Descriptor Header
|
||||||
|
0x09, 0x02,
|
||||||
|
0x6b, 0x00, // Total Length: 0x006b(107)
|
||||||
|
0x03, 0x01, 0x00, 0xa0, 0xfa,
|
||||||
|
// InterfaceAssociation Descriptoy
|
||||||
|
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||||
|
// InterfaceSescriptor(CDC-Ctrl)
|
||||||
|
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||||
|
// Communication Descriptor
|
||||||
|
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||||
|
// Communication Descriptor
|
||||||
|
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||||
|
// Communication Descriptor
|
||||||
|
0x04, 0x24, 0x02, 0x06,
|
||||||
|
// Communication Descriptor
|
||||||
|
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||||
|
// ENDPOINT Descriptor
|
||||||
|
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x40,
|
||||||
|
// InterfaceSescriptor(CDC-Data)
|
||||||
|
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
// ENDPOINT Descriptor
|
||||||
|
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||||
|
// ENDPOINT Descriptor
|
||||||
|
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||||
|
// InterfaceSescriptor(HID)
|
||||||
|
0x09, 0x04, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
// HID Descriptor
|
||||||
|
0x09, // bLength: 9
|
||||||
|
0x21, // bDescriptorType: 0x21(HID)
|
||||||
|
0x11, 0x01, // bcdHID: 0x111
|
||||||
|
0x00, // bCountryCode: Not Supported
|
||||||
|
0x01, // bNumDescriptors: 1
|
||||||
|
0x22, // Type: HID Report
|
||||||
|
0x00, 0x00, // Length
|
||||||
|
// ENDPOINT Descriptor
|
||||||
|
0x07, 0x05, 0x84, 0x03, 0x40, 0x00, 0x01,
|
||||||
|
// ENDPOINT Descriptor
|
||||||
|
0x07, 0x05, 0x05, 0x03, 0x40, 0x00, 0x01,
|
||||||
|
},
|
||||||
|
HID: map[uint16][]byte{},
|
||||||
|
}
|
||||||
|
|
|
@ -14,10 +14,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hidEndpoint = 4
|
hidEndpoint = usb.HID_ENDPOINT_IN
|
||||||
|
|
||||||
usb_SET_REPORT_TYPE = 33
|
REPORT_TYPE_INPUT = 1
|
||||||
usb_SET_IDLE = 10
|
REPORT_TYPE_OUTPUT = 2
|
||||||
|
REPORT_TYPE_FEATURE = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type hidDevicer interface {
|
type hidDevicer interface {
|
||||||
|
@ -51,7 +52,7 @@ func handler() {
|
||||||
|
|
||||||
func setupHandler(setup usb.Setup) bool {
|
func setupHandler(setup usb.Setup) bool {
|
||||||
ok := false
|
ok := false
|
||||||
if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
|
if setup.BmRequestType == usb.SET_REPORT_TYPE && setup.BRequest == usb.SET_IDLE {
|
||||||
machine.SendZlp()
|
machine.SendZlp()
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
|
|
52
src/machine/usb/joystick/buffer.go
Обычный файл
52
src/machine/usb/joystick/buffer.go
Обычный файл
|
@ -0,0 +1,52 @@
|
||||||
|
package joystick
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime/volatile"
|
||||||
|
)
|
||||||
|
|
||||||
|
const bufferSize = 32
|
||||||
|
|
||||||
|
// 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][16]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)
|
||||||
|
}
|
95
src/machine/usb/joystick/joystick.go
Обычный файл
95
src/machine/usb/joystick/joystick.go
Обычный файл
|
@ -0,0 +1,95 @@
|
||||||
|
package joystick
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"machine/usb"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
jsEndpointOut = usb.HID_ENDPOINT_OUT // from PC
|
||||||
|
jsEndpointIn = usb.HID_ENDPOINT_IN // to PC
|
||||||
|
)
|
||||||
|
|
||||||
|
var js *Joystick
|
||||||
|
|
||||||
|
type Joystick struct {
|
||||||
|
State
|
||||||
|
buf *RingBuffer
|
||||||
|
waitTxc bool
|
||||||
|
rxHandlerFunc func(b []byte)
|
||||||
|
setupFunc func(setup usb.Setup) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func Enable(def Definitions, rxHandlerFunc func(b []byte),
|
||||||
|
setupFunc func(setup usb.Setup) bool, hidDesc []byte) *Joystick {
|
||||||
|
m := &Joystick{
|
||||||
|
buf: NewRingBuffer(),
|
||||||
|
State: def.NewState(),
|
||||||
|
}
|
||||||
|
m.State = def.NewState()
|
||||||
|
if setupFunc == nil {
|
||||||
|
setupFunc = m.setupFunc
|
||||||
|
}
|
||||||
|
machine.EnableJoystick(m.handler, rxHandlerFunc, setupFunc, hidDesc)
|
||||||
|
js = m
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port returns the USB Joystick port.
|
||||||
|
func Port() *Joystick {
|
||||||
|
if js == nil {
|
||||||
|
def := DefaultDefinitions()
|
||||||
|
js = &Joystick{
|
||||||
|
State: def.NewState(),
|
||||||
|
buf: NewRingBuffer(),
|
||||||
|
}
|
||||||
|
machine.EnableJoystick(js.handler, nil, js.setupFunc, def.Descriptor())
|
||||||
|
}
|
||||||
|
return js
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Joystick) sendUSBPacket(b []byte) {
|
||||||
|
machine.SendUSBInPacket(jsEndpointIn, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// from InterruptIn
|
||||||
|
func (m *Joystick) handler() {
|
||||||
|
m.waitTxc = false
|
||||||
|
if b, ok := m.buf.Get(); ok {
|
||||||
|
m.waitTxc = true
|
||||||
|
m.sendUSBPacket(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Joystick) setup(setup usb.Setup) bool {
|
||||||
|
if setup.BmRequestType == usb.SET_REPORT_TYPE && setup.BRequest == usb.SET_IDLE {
|
||||||
|
machine.SendZlp()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Joystick) rxHandler(b []byte) {
|
||||||
|
if m.rxHandlerFunc != nil {
|
||||||
|
m.rxHandlerFunc(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Joystick) tx(b []byte) {
|
||||||
|
if m.waitTxc {
|
||||||
|
m.buf.Put(b)
|
||||||
|
} else {
|
||||||
|
m.waitTxc = true
|
||||||
|
m.sendUSBPacket(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to InterruptOut
|
||||||
|
func (m *Joystick) SendReport(reportID byte, b []byte) {
|
||||||
|
m.tx(append([]byte{reportID}, b...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Joystick) SendState() {
|
||||||
|
b, _ := m.State.MarshalBinary()
|
||||||
|
m.tx(b)
|
||||||
|
}
|
213
src/machine/usb/joystick/state.go
Обычный файл
213
src/machine/usb/joystick/state.go
Обычный файл
|
@ -0,0 +1,213 @@
|
||||||
|
package joystick
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
type HatDirection uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
HatUp HatDirection = iota
|
||||||
|
HatRightUp
|
||||||
|
HatRight
|
||||||
|
HatRightDown
|
||||||
|
HatDown
|
||||||
|
HatLeftDown
|
||||||
|
HatLeft
|
||||||
|
HatLeftUp
|
||||||
|
HatCenter
|
||||||
|
)
|
||||||
|
|
||||||
|
type Constraint struct {
|
||||||
|
MinIn int
|
||||||
|
MaxIn int
|
||||||
|
MinOut int16
|
||||||
|
MaxOut int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type AxisValue struct {
|
||||||
|
constraint Constraint
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func fit(x, in_min, in_max int, out_min, out_max int16) int16 {
|
||||||
|
return int16((x-in_min)*(int(out_max)-int(out_min))/(in_max-in_min) + int(out_min))
|
||||||
|
}
|
||||||
|
|
||||||
|
func fitf(x, in_min, in_max, out_min, out_max float32) float32 {
|
||||||
|
return x - in_min*(out_max-out_min)/(in_max-in_min) + out_min
|
||||||
|
}
|
||||||
|
|
||||||
|
func limit(v, max int) int {
|
||||||
|
if v > max {
|
||||||
|
v = max
|
||||||
|
} else if v < -max {
|
||||||
|
v = -max
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type Definitions struct {
|
||||||
|
ReportID byte
|
||||||
|
ButtonCnt int
|
||||||
|
HatSwitchCnt int
|
||||||
|
AxisDefs []Constraint
|
||||||
|
descriptor []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Definitions) Descriptor() []byte {
|
||||||
|
if len(c.descriptor) > 0 {
|
||||||
|
return c.descriptor
|
||||||
|
}
|
||||||
|
// TODO: build hid descriptor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Definitions) NewState() State {
|
||||||
|
bufSize := 1
|
||||||
|
axises := make([]*AxisValue, 0, len(c.AxisDefs))
|
||||||
|
for _, v := range c.AxisDefs {
|
||||||
|
|
||||||
|
axises = append(axises, &AxisValue{
|
||||||
|
constraint: v,
|
||||||
|
Value: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
btnSize := (c.ButtonCnt + 7) / 8
|
||||||
|
bufSize += btnSize
|
||||||
|
if c.HatSwitchCnt > 0 {
|
||||||
|
bufSize++
|
||||||
|
}
|
||||||
|
bufSize += len(axises) * 2
|
||||||
|
initBuf := make([]byte, bufSize)
|
||||||
|
initBuf[0] = c.ReportID
|
||||||
|
return State{
|
||||||
|
buf: initBuf,
|
||||||
|
Buttons: make([]byte, btnSize),
|
||||||
|
HatSwitches: make([]HatDirection, c.HatSwitchCnt),
|
||||||
|
Axises: axises,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
buf []byte
|
||||||
|
Buttons []byte
|
||||||
|
HatSwitches []HatDirection
|
||||||
|
Axises []*AxisValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) MarshalBinary() ([]byte, error) {
|
||||||
|
s.buf = s.buf[0:1]
|
||||||
|
s.buf = append(s.buf, s.Buttons...)
|
||||||
|
if len(s.HatSwitches) > 0 {
|
||||||
|
hat := byte(0)
|
||||||
|
for _, v := range s.HatSwitches {
|
||||||
|
hat <<= 4
|
||||||
|
hat |= byte(v & 0xf)
|
||||||
|
}
|
||||||
|
s.buf = append(s.buf, hat)
|
||||||
|
}
|
||||||
|
for _, v := range s.Axises {
|
||||||
|
c := v.constraint
|
||||||
|
val := fit(v.Value, c.MinIn, c.MaxIn, c.MinOut, c.MaxOut)
|
||||||
|
s.buf = binary.LittleEndian.AppendUint16(s.buf, uint16(val))
|
||||||
|
}
|
||||||
|
return s.buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Button(index int) bool {
|
||||||
|
idx := index / 8
|
||||||
|
bit := uint8(1 << (index % 8))
|
||||||
|
return s.Buttons[idx]&bit > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) SetButton(index int, push bool) {
|
||||||
|
idx := index / 8
|
||||||
|
bit := uint8(1 << (index % 8))
|
||||||
|
b := s.Buttons[idx]
|
||||||
|
b &= ^bit
|
||||||
|
if push {
|
||||||
|
b |= bit
|
||||||
|
}
|
||||||
|
s.Buttons[idx] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Hat(index int) HatDirection {
|
||||||
|
return s.HatSwitches[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) SetHat(index int, dir HatDirection) {
|
||||||
|
s.HatSwitches[index] = dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Axis(index int) int {
|
||||||
|
return s.Axises[index].Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) SetAxis(index int, v int) {
|
||||||
|
s.Axises[index].Value = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultDefinitions() Definitions {
|
||||||
|
return Definitions{
|
||||||
|
ReportID: 1,
|
||||||
|
ButtonCnt: 16,
|
||||||
|
HatSwitchCnt: 1,
|
||||||
|
AxisDefs: []Constraint{
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
{MinIn: -32767, MaxIn: 32767, MinOut: -32767, MaxOut: 32767},
|
||||||
|
},
|
||||||
|
descriptor: []byte{
|
||||||
|
0x05, 0x01,
|
||||||
|
0x09, 0x04,
|
||||||
|
0xa1, 0x01, // COLLECTION (Application)
|
||||||
|
0x85, 0x01, // REPORT_ID (1)
|
||||||
|
0x05, 0x09, // USAGE_PAGE (Button)
|
||||||
|
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||||
|
0x29, 0x10, // USAGE_MAXIMUM (Button 16)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x95, 0x10, // REPORT_COUNT (16)
|
||||||
|
0x55, 0x00, // Unit Exponent (-16)
|
||||||
|
0x65, 0x00, // Unit (0x00)
|
||||||
|
0x81, 0x02, // INPUT (Data/Var/Abs)
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop Controls)
|
||||||
|
0x09, 0x39, // USAGE(Hat Switch)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x07, // LOGICAL_MAXIMUM (7)
|
||||||
|
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||||
|
0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM(315)
|
||||||
|
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
|
||||||
|
0x75, 0x04, // REPORT_SIZE (4)
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x81, 0x02, // INPUT (Data/Var/Abs)
|
||||||
|
0x09, 0x39, // USAGE(Hat Switch)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x07, // LOGICAL_MAXIMUM (7)
|
||||||
|
0x35, 0x00, // PHYSICAL_MINIMUM (0)
|
||||||
|
0x46, 0x3b, 0x01, // PHYSICAL_MAXIMUM(315)
|
||||||
|
0x65, 0x14, // UNIT (Eng Rot:Angular Pos)
|
||||||
|
0x75, 0x04, // REPORT_SIZE (4)
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x81, 0x02, // INPUT (Data/Var/Abs)
|
||||||
|
0x09, 0x01, // USAGE (Pointer)
|
||||||
|
0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767)
|
||||||
|
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
|
||||||
|
0x75, 0x10, // REPORT_SIZE (16bits)
|
||||||
|
0x95, 0x06, // REPORT_COUNT (6)
|
||||||
|
0xa1, 0x00, // COLLECTION (Physical)
|
||||||
|
0x09, 0x30, // USAGE(X)
|
||||||
|
0x09, 0x31, // USAGE(Y)
|
||||||
|
0x09, 0x32, // USAGE(Z)
|
||||||
|
0x09, 0x33, // USAGE(RX)
|
||||||
|
0x09, 0x34, // USAGE(RY)
|
||||||
|
0x09, 0x35, // USAGE(RZ)
|
||||||
|
0x81, 0x02, // INPUT (Data/Var/Abs)
|
||||||
|
0xc0, // END_COLLECTION
|
||||||
|
0xc0, // END_COLLECTION
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,12 @@ package midi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
|
"machine/usb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
midiEndpointOut = 5 // from PC
|
midiEndpointOut = usb.MIDI_ENDPOINT_OUT // from PC
|
||||||
midiEndpointIn = 6 // to PC
|
midiEndpointIn = usb.MIDI_ENDPOINT_IN // to PC
|
||||||
)
|
)
|
||||||
|
|
||||||
var Midi *midi
|
var Midi *midi
|
||||||
|
|
|
@ -9,6 +9,7 @@ const (
|
||||||
DescriptorConfigCDC = 1 << iota
|
DescriptorConfigCDC = 1 << iota
|
||||||
DescriptorConfigHID
|
DescriptorConfigHID
|
||||||
DescriptorConfigMIDI
|
DescriptorConfigMIDI
|
||||||
|
DescriptorConfigJoystick
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,7 +36,6 @@ const (
|
||||||
EndpointOut = 0x00
|
EndpointOut = 0x00
|
||||||
EndpointIn = 0x80
|
EndpointIn = 0x80
|
||||||
|
|
||||||
NumberOfEndpoints = 8
|
|
||||||
EndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024
|
EndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024
|
||||||
|
|
||||||
// standard requests
|
// standard requests
|
||||||
|
@ -51,7 +51,12 @@ const (
|
||||||
SET_INTERFACE = 11
|
SET_INTERFACE = 11
|
||||||
|
|
||||||
// non standard requests
|
// non standard requests
|
||||||
|
GET_REPORT = 1
|
||||||
|
GET_IDLE = 2
|
||||||
|
GET_PROTOCOL = 3
|
||||||
|
SET_REPORT = 9
|
||||||
SET_IDLE = 10
|
SET_IDLE = 10
|
||||||
|
SET_PROTOCOL = 11
|
||||||
|
|
||||||
DEVICE_CLASS_COMMUNICATIONS = 0x02
|
DEVICE_CLASS_COMMUNICATIONS = 0x02
|
||||||
DEVICE_CLASS_HUMAN_INTERFACE = 0x03
|
DEVICE_CLASS_HUMAN_INTERFACE = 0x03
|
||||||
|
@ -75,9 +80,11 @@ const (
|
||||||
CDC_ENDPOINT_ACM = 1
|
CDC_ENDPOINT_ACM = 1
|
||||||
CDC_ENDPOINT_OUT = 2
|
CDC_ENDPOINT_OUT = 2
|
||||||
CDC_ENDPOINT_IN = 3
|
CDC_ENDPOINT_IN = 3
|
||||||
HID_ENDPOINT_IN = 4
|
HID_ENDPOINT_IN = 4 // for Interrupt In
|
||||||
MIDI_ENDPOINT_OUT = 5
|
HID_ENDPOINT_OUT = 5 // for Interrupt Out
|
||||||
MIDI_ENDPOINT_IN = 6
|
MIDI_ENDPOINT_IN = 6 // for Bulk In
|
||||||
|
MIDI_ENDPOINT_OUT = 7 // for Bulk Out
|
||||||
|
NumberOfEndpoints = 8
|
||||||
|
|
||||||
// bmRequestType
|
// bmRequestType
|
||||||
REQUEST_HOSTTODEVICE = 0x00
|
REQUEST_HOSTTODEVICE = 0x00
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче