GuyCarver-MicroPython/lib/apds.py

670 wiersze
18 KiB
Python

# MicroPython apds9960 motion detection device driver.
#todo: Have a mix of styles here. Some use properties for setters while others use functions.
import pyb
from utime import *
class apds(object) :
_ADDRESS = const(0x39)
_RAM = const(0x00)
_ENABLE = const(0x80)
_ATIME = const(0x81)
_WTIME = const(0x83)
_AILTL = const(0x84)
_AILTH = const(0x85)
_AIHTL = const(0x86)
_AIHTH = const(0x87)
_PILT = const(0x89)
_PIHT = const(0x8B)
_PERS = const(0x8C)
_CONFIG1 = const(0x8D)
_PPULSE = const(0x8E)
_CONTROL = const(0x8F)
_CONFIG2 = const(0x90)
_ID = const(0x92)
_STATUS = const(0x93)
_CDATAL = const(0x94)
_CDATAH = const(0x95)
_RDATAL = const(0x96)
_RDATAH = const(0x97)
_GDATAL = const(0x98)
_GDATAH = const(0x99)
_BDATAL = const(0x9A)
_BDATAH = const(0x9B)
_PDATA = const(0x9C)
_POFFSET_UR = const(0x9D)
_POFFSET_DL = const(0x9E)
_CONFIG3 = const(0x9F)
_GPENTH = const(0xA0)
_GEXTH = const(0xA1)
_GCONF1 = const(0xA2)
_GCONF2 = const(0xA3)
_GOFFSET_U = const(0xA4)
_GOFFSET_D = const(0xA5)
_GOFFSET_L = const(0xA7)
_GOFFSET_R = const(0xA9)
_GPULSE = const(0xA6)
_GCONF3 = const(0xAA)
_GCONF4 = const(0xAB)
_GFLVL = const(0xAE)
_GSTATUS = const(0xAF)
_IFORCE = const(0xE4)
_PICLEAR = const(0xE5)
_CICLEAR = const(0xE6)
_AICLEAR = const(0xE7)
_GFIFO_U = const(0xFC)
_GFIFO_D = const(0xFD)
_GFIFO_L = const(0xFE)
_GFIFO_R = const(0xFF)
_PON = const(0x01)
_AEN = const(0x02)
_PEN = const(0x04)
_WEN = const(0x08)
_AIEN = const(0x10)
_PIEN = const(0x20)
_GEN = const(0x40)
_GVALID = const(0x01)
_THRESHOLD_OUT = const(10)
_SENSITIVITY_1 = const(50)
_SENSITIVITY_2 = const(20)
_ERROR = const(0xFF)
#Device IDs
_ID_1 = const(0xAB)
_ID_2 = const(0x9C)
_FIFO_PAUSE_TIME = const(30) # Wait period (ms) between FIFO reads
#P Gain
_PGAIN_1 = const(0) #1x gain
_PGAIN_2 = const(1) #2x gain
_PGAIN_4 = const(2) #4x gain
_PGAIN_8 = const(3) #8x gain
#LED Drive
_LEDDRIVE_100MA = const(0) #100mA
_LEDDRIVE_50MA = const(1) #50mA
_LEDDRIVE_25MA = const(2) #25mA
_LEDDRIVE_12_5MA = const(3) #12.5mA
#LED Boost
_LEDBOOST_100 = const(0)
_LEDBOOST_150 = const(1)
_LEDBOOST_200 = const(2)
_LEDBOOST_300 = const(3)
#G Gain
_GGAIN_1 = const(0)
_GGAIN_2 = const(1)
_GGAIN_4 = const(2)
_GGAIN_8 = const(3)
#G Gain
_AGAIN_1 = const(0)
_AGAIN_4 = const(1)
_AGAIN_16 = const(2)
_AGAIN_64 = const(3)
#Following are the bytes describing the data packed into each 8 bit value on the chip.
#Nibble 1 = position, 0 = bits
_POWER = const(0)
_AMBIENT_LIGHT = const(1)
_PROXIMITY = const(2)
_WAIT = const(3)
_AMBIENT_LIGHT_INT = const(4)
_PROXIMITY_INT = const(5)
_GESTURE = const(6)
_ALL = const(7)
# Gesture wait time values
GWTIME_0MS = const(0)
GWTIME_2_8MS = const(1)
GWTIME_5_6MS = const(2)
GWTIME_8_4MS = const(3)
GWTIME_14_0MS = const(4)
GWTIME_22_4MS = const(5)
GWTIME_30_8MS = const(6)
GWTIME_39_2MS = const(7)
_DEFAULT_ATIME = const(219) # 103ms
_DEFAULT_WTIME = const(246) # 27ms
_DEFAULT_PROX_PPULSE = const(0x87) # 16us, 8 pulses
_DEFAULT_GESTURE_PPULSE = const(0x89) # 16us, 10 pulses
_DEFAULT_POFFSET_UR = const(0) # 0 offset
_DEFAULT_POFFSET_DL = const(0) # 0 offset
_DEFAULT_CONFIG1 = const(0x60) # No 12x wait (WTIME) factor
_DEFAULT_LDRIVE = const(0) #_LED_DRIVE_100MA
_DEFAULT_PGAIN = const(2) #_PGAIN_4
_DEFAULT_AGAIN = const(1) #_AGAIN_4
_DEFAULT_PILT = const(0) # Low proximity threshold
_DEFAULT_PIHT = const(50) # High proximity threshold
_DEFAULT_AILT = const(0xFFFF) # Force interrupt for calibration
_DEFAULT_AIHT = const(0)
_DEFAULT_PERS = const(0x11) # 2 consecutive prox or ALS for int.
_DEFAULT_CONFIG2 = const(0x01) # No saturation interrupts or LED boost
_DEFAULT_CONFIG3 = const(0) # Enable all photodiodes, no SAI
_DEFAULT_GPENTH = const(40) # Threshold for entering gesture mode
_DEFAULT_GEXTH = const(30) # Threshold for exiting gesture mode
_DEFAULT_GCONF1 = const(0x40) # 4 gesture events for int., 1 for exit
_DEFAULT_GGAIN = const(2) #_GGAIN_4
_DEFAULT_GLDRIVE = const(0) #_LED_DRIVE_100MA
_DEFAULT_GWTIME = const(1) #_GWTIME_2_8MS
_DEFAULT_GOFFSET = const(0) # No offset scaling for gesture mode
_DEFAULT_GPULSE = const(0xC9) # 32us, 10 pulses
_DEFAULT_GCONF3 = const(0) # All photodiodes active during gesture
_DEFAULT_GIEN = const(0) # Disable gesture interrupts
_DIR_NONE = const(0)
_DIR_LEFT = const(1)
_DIR_RIGHT = const(2)
_DIR_UP = const(3)
_DIR_DOWN = const(4)
_DIR_NEAR = const(5)
_DIR_FAR = const(6)
_DIR_ALL = const(7)
_NA_STATE = const(0)
_NEAR_STATE = const(1)
_FAR_STATE = const(2)
_ALL_STATE = const(3)
def __init__( self, aLoc ) :
"""aLoc I2C pin location is either 1, 'X', 2 or'Y'."""
self.i2c = pyb.I2C(aLoc, pyb.I2C.MASTER)
self._ud_delta = 0
self._lr_delta = 0
self._ud_count = 0
self._lr_count = 0
self._near_count = 0
self._far_count = 0
self._state = 0
self._motion = _DIR_NONE
self._udata = bytearray(32)
self._ddata = bytearray(32)
self._ldata = bytearray(32)
self._rdata = bytearray(32)
self._index = 0
self._total_gestures= 0
self._in_threshold = 0
self._out_threshold = 0
self._b1 = bytearray(1)
sleep_us(50)
self.init()
def read( self, aLoc ) :
"""Read 8 bit value and return."""
self.i2c.mem_read(self._b1, _ADDRESS, aLoc)
# print('Read {:02x} from {:02x}.'.format(self._b1[0], aLoc))
return self._b1[0]
def readblock( self, aLoc, aDest ) :
return self.i2c.mem_read(aDest, _ADDRESS, aLoc)
def write( self, aLoc, aVal ) :
"""Write 8 bit value to given address. aVal may be an int buffer."""
self.i2c.mem_write(aVal, _ADDRESS, aLoc)
# print('write {:02x} to {:02x}.'.format(aVal, aLoc))
def init( self ) :
id = self.read(_ID)
if id != _ID_1 and id != _ID_2 :
raise Exception('Incorrect apds9960 ID. {}'.format(id))
self.setmode(_ALL, False)
self.write(_ATIME, _DEFAULT_ATIME)
self.write(_WTIME, _DEFAULT_WTIME)
self.write(_PPULSE, _DEFAULT_PROX_PPULSE)
self.write(_POFFSET_UR, _DEFAULT_POFFSET_UR)
self.write(_POFFSET_DL, _DEFAULT_POFFSET_DL)
self.write(_CONFIG1, _DEFAULT_CONFIG1)
self.setleddrive(_DEFAULT_LDRIVE)
self.setproximitygain(_DEFAULT_PGAIN)
self.setambientlightgain(_DEFAULT_AGAIN)
self.setproxintlowthresh(_DEFAULT_PILT)
self.setproxinthighthresh(_DEFAULT_PIHT)
self.setlightintlowthresh(_DEFAULT_AILT)
self.setlightinthighthresh(_DEFAULT_AIHT)
self.write(_PERS, _DEFAULT_PERS)
self.write(_CONFIG2, _DEFAULT_CONFIG2)
self.write(_CONFIG3, _DEFAULT_CONFIG3)
self.setgestureenterthresh(_DEFAULT_GPENTH)
self.setgestureexitthresh(_DEFAULT_GEXTH)
self.write(_GCONF1, _DEFAULT_GCONF1)
self.setgesturegain(_DEFAULT_GGAIN)
self.setgestureleddrive(_DEFAULT_GLDRIVE)
self.setgesturewaittime(_DEFAULT_GWTIME)
self.write(_GOFFSET_U, _DEFAULT_GOFFSET)
self.write(_GOFFSET_D, _DEFAULT_GOFFSET)
self.write(_GOFFSET_L, _DEFAULT_GOFFSET)
self.write(_GOFFSET_R, _DEFAULT_GOFFSET)
self.write(_GPULSE, _DEFAULT_GPULSE)
self.write(_GCONF3, _DEFAULT_GCONF3)
self.setgestureintenable(_DEFAULT_GIEN)
def getmode( self ) :
return self.read(_ENABLE)
def setmode( self, aMode, aEnable ) :
v = self.getmode()
if 0 <= aMode <= 6 :
aMode = 1 << aMode
if aEnable :
v |= aMode
else:
v &= ~aMode
elif aMode == _ALL :
v = 0x7F if aEnable else 0
self.write(_ENABLE, v)
def enablelightsensor( self, aInt ) :
self.setambientlightgain(_DEFAULT_AGAIN)
self.setambientlightintenable(aInt)
self.enablepower()
self.setmode(_AMBIENT_LIGHT, True)
def disablelightsensor( self ) :
self.setambientlightintenable(False)
self.setmode(_AMBIENT_LIGHT, False)
def enableproximitysensor( self, aInt ) :
self.setproximitygain(_DEFAULT_PGAIN)
self.setleddrive(_DEFAULT_LDRIVE)
self.setproximityintenable(aInt)
self.enablepower()
self.setmode(_PROXIMITY, True)
def disableproximitysensor( self ) :
self.setproximityintenable(False)
self.setmode(_PROXIMITY, False)
def enablegesturesensor( self, aInt ) :
self.resetgestureparams()
self.write(_WTIME, 0xFF)
self.write(_PPULSE, _DEFAULT_GESTURE_PPULSE)
self.setledboost(_LEDBOOST_300)
self.setgestureintenable(aInt)
self.setgesturemode(True)
self.enablepower()
self.setmode(_WAIT, True)
self.setmode(_PROXIMITY, True)
self.setmode(_GESTURE, True)
def disablegesturesensor( self ) :
self.resetgestureparams()
self.setgestureintenable(False)
self.setgesturemode(True)
self.setmode(_GESTURE, False)
def isgestureavailable( self ) :
v = self.read(_GSTATUS)
return (v & _GVALID) != 0
def readgesture( self ) :
if self.isgestureavailable() and (self.getmode() & 0x41) :
while True :
time.sleep_us(_FIFO_PAUSE_TIME)
gstatus = self.read(_GSTATUS)
if (gstatus & _GVALID) == _GVALID :
fifo_level = self.read(_GFLVL)
if fifo_level > 0 :
bread = self.readblock(_GFIFO_U, self._fifo_data, fifo_level * 4)
if bread >= 4 :
for i in range(0, bread):
self._udata[self.index] = self._fifo_data[i]
self._ddata[self.index] = self._fifo_data[i + 1]
self._ldata[self.index] = self._fifo_data[i + 2]
self._rdata[self.index] = self._fifo_data[i + 3]
self._index += 1
self._total_gestures += 1
if self.processgesturedata() :
self._index = 0
self._total_gestures = 0
else:
time.sleep_us(_FIFO_PAUSE_TIME)
self.decodegesture()
self.resetgestureparams()
return self._motion
return _DIR_NONE
def enablepower( self, aOn ) :
self.setmode(_POWER, aOn)
def readlight( self, aData ) :
l = self.read(aData)
h = self.read(aData + 1)
return (h << 8) | l
def readambientlight( self ) :
return self.readlight(_CDATAL)
def readredlight( self ) :
return self.readlight(_RDATAL)
def readgreenlight( self ) :
return self.readlight(_GDATAL)
def readbluelight( self ) :
return self.readlight(_BDATAL)
def readproximity( self ) :
return self.read(_PDATA)
def resetgestureparams( self ) :
self._index = 0
self._total_gestures = 0
self._ud_delta = 0
self._lr_delta = 0
self._ud_count = 0
self._lr_count = 0
self._near_count = 0
self._far_count = 0
self._state = 0
self._motion = _DIR_NONE
def processgesturedata( self ) :
if self._total_gestures > 4 :
if 0 < self._total_gestures <= 32 :
found = False
for i in range(0, self._total_gestures) :
u = self._udata[i]
d = self._ddata[i]
l = self._ldata[i]
r = self._rdata[i]
if u > _THRESHOLD_OUT and d > _THRESHOLD_OUT and l > _THRESHOLD_OUT and r > _THRESHOLD_OUT :
ufirst = u
dfirst = d
lfirst = l
rfirst = r
found = True
break
if found :
for i in range(self._total_gestures - 1, -1, -1) :
u = self._udata[i]
d = self._ddata[i]
l = self._ldata[i]
r = self._rdata[i]
ulast = u
dlast = d
llast = l
rlast = r
break
ud_ratio_first = ((ufirst - dfirst) * 100) / (ufirst + dfirst)
lr_ratio_first = ((lfirst - rfirst) * 100) / (lfirst + rfirst)
ud_ratio_last = ((ulast - dlast) * 100) / (ulast + dlast)
lr_ratio_last = ((llast - rlast) * 100) / (llast + rlast)
ud_delta = ud_ratio_last - ud_ratio_first
lr_delta = lr_ratio_last - lr_ratio_first
self._ud_delta += ud_delta
self._lr_delta += lr_delta
if self._ud_delta >= _SENSITIVITY_1 :
self._ud_count = 1
elif self._ud_delta <= -_SENSITIVITY_1 :
self._ud_count = -1
else:
self._ud_count = 0
if self._lr_delta >= _SENSITIVITY_1 :
self._lr_count = 1
elif self._lr_delta <= -_SENSITIVITY_1 :
self._lr_count = -1
else:
self._lr_count = 0
if self._ud_count == 0 and self._lr_count == 0 :
if abs(ud_delta) < _SENSITIVITY_2 and abs(lr_delta) < _SENSITIVITY_2 :
if ud_delta == 0 and lr_delta == 0 :
self._near_count += 1
elif ud_delta != 0 and lr_delta != 0 :
self._far_count += 1
if self._near_count >= 10 and self._far_count >= 2 :
if ud_delta == 0 and lr_delta == 0 :
self._state = _NEAR_STATE
elif ud_delta != 0 and lr_delta != 0 :
self._state = _FAR_STATE
return True
else:
if abs(ud_delta) < _SENSITIVITY_2 and abs(lr_delta) < _SENSITIVITY_2 :
if ud_delta == 0 and lr_delta == 0 :
self._near_count += 1
if self._near_count >= 10 :
self._ud_count = 0
self._lr_count = 0
self._ud_delta = 0
self._lr_delta = 0
return False
def decodegesture( self ) :
if self._state == _NEAR_STATE :
self._motion = _DIR_NEAR
return True
elif self._state == _FAR_STATE :
self._motion = _DIR_FAR
return True
if self._ud_count == -1 and self._lr_count == 0 :
self._motion = _DIR_UP
elif self._ud_count == 1 and self._lr_count == 0 :
self._motion = _DIR_DOWN
elif self._ud_count == 0 and self._lr_count == 1 :
self._motion == _DIR_RIGHT
elif self._ud_count == 0 and self._lr_count == -1 :
self._motion == _DIR_LEFT
elif self._ud_count == -1 and self._lr_count == 1 :
if abs(self._ud_delta) > abs(self._lr_delta) :
self._motion = _DIR_UP
else:
self._motion = _DIR_RIGHT
elif self._ud_count == 1 and self._lr_count == -1 :
if abs(self._ud_delta) > abs(self._lr_delta) :
self._motion = _DIR_DOWN
else:
self._motion = _DIR_LEFT
elif self._ud_count == -1 and self._lr_count == -1 :
if abs(self._ud_delta) > abs(self._lr_delta) :
self._motion = _DIR_UP
else:
self._motion = _DIR_LEFT
elif self._ud_count == 1 and self._lr_count == 1 :
if abs(self._ud_delta) > abs(self._lr_delta) :
self._motion = _DIR_DOWN
else:
self._motion = _DIR_RIGHT
else:
return False
return True
def getproxintlowthresh( self ) :
return self.read(_PILT)
def setproxintlowthresh( self, aThresh ) :
self.write(_PILT, aThresh)
def getproxinthighthresh( self ) :
return self._read(_PIHT)
def setproxinthighthresh( self, aThresh ) :
self.write(_PIHT, aThresh)
def getleddrive( self ) :
v = self.read(_CONTROL)
return (v >> 6) & 0x03
def setleddrive( self, aDrive ) :
v = self.read(_CONTROL)
aDrive &= 0x03
aDrive <<= 6
v &= 0x111111
v |= aDrive
self.write(_CONTROL, v)
def getproximitygain( self ) :
v = self.read(_CONTROL)
return (v >> 2) & 0x03
def setproximitygain( self, aGain ) :
v = self.read(_CONTROL)
aGain &= 0x03
aGain <<= 2
v &= 0xF3
v |= aGain
self.write(_CONTROL, v)
def getambientlightgain( self ) :
return self.read(_CONTROL) & 0x03
def setambientlightgain( self, aGain ) :
v = self.read(_CONTROL)
aGain &= 0x03
v &= 0xFC
v |= aGain
self.write(_CONTROL, v)
def getledboost( self ) :
v = self.read(_CONFIG2)
return (v >> 4) & 0x03
def setledboost( self, aBoost ) :
v = self.read(_CONFIG2)
aBoost &= 0x03
aBoost <<= 4
v &= 0xFC
v |= aBoost
self.write(_CONFIG2, v)
def getproxgaincompenable( self ) :
v = self.read(_CONFIG3)
return (v >> 5) & 0x01
def setproxgaincompenable( self, aEnable ) :
v = self.read(_CONFIG3)
aEnable &= 0x01
aEnable <<= 5
v &= 0xDF
v |= aEnable
self.write(_CONFIG3, v)
def setgestureenterthresh( self, aThresh ) :
self.write(_GPENTH, aThresh)
def setgestureexitthresh( self, aThresh ) :
self.write(_GEXTH, aThresh)
def setgesturegain( self, aGain ) :
v = self.read(_GCONF2)
aGain &= 0x03
aGain <<= 5
v &= 0x9F
v |= aGain
self.write(_GCONF2, v)
def setgestureleddrive( self, aDrive ) :
v = self.read(_GCONF2)
aDrive &= 0x03
aDrive <<= 3
v &= 0xE7
v |= aDrive
self.write(_GCONF2, v)
def setgesturewaittime( self, aTime ) :
v = self.read(_GCONF2)
aTime &= 0x07
v &= 0xF8
v |= aTime
self.write(_GCONF2, v)
def setlightintlowthresh( self, aThresh ) :
vl = aThresh & 0xFF
vh = (aThresh >> 8) & 0xFF
self.write(_AILTL, vl)
self.write(_AILTH, vh)
def setlightinthighthresh( self, aThresh ) :
vl = aThresh & 0xFF
vh = (aThresh >> 8) & 0xFF
self.write(_AIHTL, vl)
self.write(_AIHTH, vh)
def setambientlightintenable( self, aEnable ) :
v = self.read(_ENABLE)
aEnable &= 0x01
aEnable <<= 4
v &= 0xEF
v |= aEnable
self.write(_ENABLE, v)
def setproximityintenable( self, aEnable ) :
v = self.read(_ENABLE)
aEnable &= 0x01
aEnable <<= 5
v &= 0xDF
v |= aEnable
self.write(_ENABLE, v)
def setgestureintenable( self, aEnable ) :
v = self.read(_GCONF4)
aEnable &= 0x01
aEnable <<= 1
v &= 0xFD
v |= aEnable
self.write(_GCONF4, v)
def setgesturemode( self, aMode ) :
v = self.read(_GCONF4)
aMode &= 0x01
v &= 0xFE
v |= aMode
self.write(_GCONF4, v)