kopia lustrzana https://github.com/GuyCarver/MicroPython
221 wiersze
7.0 KiB
Python
221 wiersze
7.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
#LynxMotion PS2 wireless controller driver.
|
|
#See http://www.lynxmotion.com/images/files/ps2cmd01.txt for command list.
|
|
#See http://sophiateam.undrgnd.free.fr/psx/index.html for explanation of communication protocol.
|
|
|
|
from machine import Pin
|
|
from time import sleep_us
|
|
|
|
class ps2():
|
|
'''PS2 wireless controller driver. Each button tracks up/down state as well
|
|
as just pressed/released state. These values may be read directly or
|
|
state changes may be reported to a given callback function. The L/R Joystick
|
|
values are in the range of +/- 256 and must be read using the joy() function.
|
|
The PS2 controller is put into analog mode without pressure sensitivity on the buttons.
|
|
Rumble is not enabled. Enabling rumble for some reason causes input to be extremely slow.'''
|
|
|
|
#Button values. Bit 1 = changed, Bit 0 = Down state.
|
|
_UP = const(0)
|
|
_DOWN = const(1) #Button is down.
|
|
_RELEASED = const(2) #Indiciates button was just released.
|
|
_PRESSED = const(3) #Indicate button was just pressed.
|
|
|
|
#_buttons array indexes
|
|
_SELECT = const(0)
|
|
_L_HAT = const(1)
|
|
_R_HAT = const(2)
|
|
_START = const(3)
|
|
_DPAD_U = const(4)
|
|
_DPAD_R = const(5)
|
|
_DPAD_D = const(6)
|
|
_DPAD_L = const(7)
|
|
_L_TRIGGER = const(8)
|
|
_R_TRIGGER = const(9)
|
|
_L_SHOULDER = const(10)
|
|
_R_SHOULDER = const(11)
|
|
_TRIANGLE = const(12)
|
|
_CIRCLE = const(13)
|
|
_CROSS = const(14)
|
|
_SQUARE = const(15)
|
|
|
|
#_joys array indexes.
|
|
_RX = const(0)
|
|
_RY = const(1)
|
|
_LX = const(2)
|
|
_LY = const(3)
|
|
|
|
#Controller commands.
|
|
_qmode = (1,0x41,0,0,0) #Add the below bytes in to read analog (analog button mode needs to be set)
|
|
_qdata = (1,0x42,0,0,0,0,0,0,0) #,0,0,0,0,0,0,0,0,0,0,0,0,0)
|
|
_enter_config = (1,0x43,0,1,0)
|
|
_exit_config = (1,0x43,0,0,0x5A,0x5A,0x5A,0x5A,0x5A)
|
|
_set_mode = (1,0x44,0,1,3,0,0,0,0) #1 = analog stick mode, 3 = lock mode button.
|
|
# _ds2_native = (1,0x4F,0,0xFF,0xFF,03,00,00,00)
|
|
# _enable_analog = (1,0x4F,0,0xFF,0xFF,3,0,0,0) #enable analog pressure input from button.
|
|
# _enable_rumble = (0x01,0x4D,0,0,1)
|
|
# _type_read= bytearray((1,0x45,0,0,0,0,0,0,0))
|
|
|
|
def __init__( self, aCmd, aData, aClk, aAtt, aCallback = None ):
|
|
'''Create a ps2 object with the given Command, Data, Clock, Attn and Callback values.'''
|
|
self._cmd = Pin(aCmd, Pin.OUT, Pin.PULL_DOWN)
|
|
self._data = Pin(aData, Pin.IN)
|
|
self._clk = Pin(aClk, Pin.OUT, Pin.PULL_DOWN)
|
|
self._att = Pin(aAtt, Pin.OUT, Pin.PULL_DOWN)
|
|
self._res = bytearray(9) #Set this to 22 for analog button data reading.
|
|
#Double buffered buttons.
|
|
self._buttons = (bytearray(16), bytearray(16))
|
|
self._buttonIndex = 0 #Index into _buttons array.
|
|
self._joys = [0, 0, 0, 0]
|
|
#If we don't set these high to start the 1st command doesn't work.
|
|
self._att(1)
|
|
self._clk(1)
|
|
self._callback = None
|
|
self._initpad()
|
|
#Set callback after _initpad() because button states change during init.
|
|
self.callback = aCallback
|
|
|
|
def _shiftinout( self, aChar ):
|
|
'''Shift bits of aChar out on the _cmd pin while reading bits
|
|
in from _data pin. Returns 8 bit data value.'''
|
|
value = 0
|
|
for i in range(0, 8):
|
|
self._cmd(aChar & 1) #Set/Clear pin for bit of aChar.
|
|
aChar >>= 1 #Next bt.
|
|
self._clk(0) #Clock pin low to start send.
|
|
sleep_us(4) #Wait a bit.
|
|
value |= self._data() << i #Read bit from data pin.
|
|
self._clk(1) #Set clock high.
|
|
sleep_us(1)
|
|
return value
|
|
|
|
def _sendrcv( self, aSend ):
|
|
'''Send data in aSend while reading data into _res'''
|
|
self._att(0) #Get attention of controller.
|
|
sleep_us(1) #Wait a bit before sending.
|
|
|
|
#Send each byte and receive a byte.
|
|
for i, b in enumerate(aSend):
|
|
self._res[i] = self._shiftinout(b)
|
|
self._att(1) #Tell controller we are done.
|
|
return self._res
|
|
|
|
def _initpad( self ):
|
|
'''Initialize the gamepad in analog stick mode.'''
|
|
self.qdata()
|
|
sleep_us(100)
|
|
self._sendrcv(ps2._enter_config)
|
|
sleep_us(1)
|
|
self._sendrcv(ps2._set_mode)
|
|
sleep_us(1)
|
|
#Put these in to enable rumble and analog button modes.
|
|
# self.display()
|
|
# self._sendrcv(ps2._enable_rumble)
|
|
# sleep_us(1)
|
|
# self.display()
|
|
# self._sendrcv(ps2._enable_analog)
|
|
# sleep_us(1)
|
|
# self.display()
|
|
self._sendrcv(ps2._exit_config)
|
|
#Read data a few times to get junk out of the way.
|
|
for i in range(0, 6):
|
|
sleep_us(1)
|
|
self.qdata()
|
|
|
|
# def qmode( self ):
|
|
# ''' '''
|
|
# return self._sendrcv(ps2._qmode)
|
|
|
|
@property
|
|
def callback( self ):
|
|
return self._callback
|
|
|
|
@callback.setter
|
|
def callback( self, aCallback ):
|
|
self._callback = aCallback
|
|
|
|
@property
|
|
def curbuttons( self ):
|
|
return self._buttons[self._buttonIndex]
|
|
|
|
@property
|
|
def prevbuttons( self ):
|
|
return self._buttons[not self._buttonIndex]
|
|
|
|
def button( self, aIndex ):
|
|
return self.curbuttons[aIndex]
|
|
|
|
def joy( self, aIndex ):
|
|
return self._joys[aIndex]
|
|
|
|
def qdata( self ):
|
|
'''Read button/joystick data from controller. Data will be in _res.'''
|
|
self._sendrcv(ps2._qdata)
|
|
|
|
#Swap buffer for current button values.
|
|
self._buttonIndex = not self._buttonIndex
|
|
#Get previous and current button buffers.
|
|
prev = self.prevbuttons
|
|
buttons = self.curbuttons
|
|
b = self._res[3] | (self._res[4] << 8) #Merge 16 bits of button data.
|
|
for i in range(16):
|
|
bv = not (b & 1) #Button on if bit is 0 so swap that.
|
|
if bv != (prev[i] & 1): #If button changed, set bit 1.
|
|
bv |= 2
|
|
buttons[i] = bv
|
|
|
|
#If value not _UP and we have a callback function, then call it.
|
|
if bv and self._callback:
|
|
self._callback(i, bv)
|
|
|
|
b >>= 1 #Next button bit.
|
|
|
|
#Convert joystick values 0-0xFF with 0x80 in center to values +/- 0-256
|
|
sgn = 1
|
|
for i in range(5, 9):
|
|
self._joys[i - 5] = ((self._res[i] - 0x80) << 1) * sgn
|
|
sgn = -sgn #Every other value (y) needs to be reversed so +y is up.
|
|
|
|
return self._res
|
|
|
|
# def displaymode( self ):
|
|
# self.display(self.qmode())
|
|
|
|
# def displaydata( self ):
|
|
# self.display(self.qdata())
|
|
|
|
# def displaymodel( self ):
|
|
# self.display(self._sendrcv(ps2._type_read))
|
|
|
|
# def display( self, aBuf = None ):
|
|
# if aBuf == None :
|
|
# aBuf = self._res
|
|
#
|
|
# for b in aBuf:
|
|
# print(hex(b), end='')
|
|
# print(',', end='')
|
|
# print(';', end='\r')
|
|
|
|
# def test( self ):
|
|
# while 1:
|
|
# self.qdata()
|
|
# print(self.curbuttons, end=' ')
|
|
# print(self._joys, end='\r')
|
|
# sleep_us(50000)
|
|
|
|
#btnnames = ['SELECT', 'L_HAT', 'R_HAT', 'START', 'DPAD_U', 'DPAD_R',
|
|
# 'DPAD_D', 'DPAD_L', 'L_TRIGGER', 'R_TRIGGER', 'L_SHOULDER',
|
|
# 'R_SHOULDER', 'TRIANGLE', 'CIRCLE', 'CROSS', 'SQUARE']
|
|
#
|
|
#statenames = ['UP', 'DOWN', 'RELEASED', 'PRESSED']
|
|
#
|
|
#def MyCallback( aIndex, aState ):
|
|
# ''' '''
|
|
# print('{} : {}'.format(btnnames[aIndex], statenames[aState]))
|
|
#
|
|
#def test( ):
|
|
# p = ps2('X8', 'X7', 'X6', 'Y9', MyCallback)
|
|
# while 1:
|
|
# p.qdata()
|
|
# sleep_us(50000)
|