kopia lustrzana https://github.com/GuyCarver/MicroPython
Added PS2 controller driver
rodzic
68278b02eb
commit
f32ac20f72
|
@ -0,0 +1,220 @@
|
|||
#!/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)
|
Ładowanie…
Reference in New Issue