change how APDS is interacted with at a I/O level; eliminate heap allocs

This commit is contained in:
soypat
2023-12-26 04:35:51 -08:00
parent fd21e6ac9b
commit 5dfacc3c73
+176 -112
View File
@@ -17,6 +17,8 @@ type Device struct {
Address uint8 Address uint8
mode uint8 mode uint8
gesture gestureData gesture gestureData
buf [32]byte
_txerr error
} }
// Configuration for APDS-9960 device. // Configuration for APDS-9960 device.
@@ -57,6 +59,25 @@ type enableConfig struct {
PON bool PON bool
} }
type encfg uint8
// data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
const (
enPON encfg = 1 << iota
enAEN
enPEN
enWEN
enAIEN
enPIEN
enGEN
)
func (e encfg) write7bits(b []byte) {
for i := uint8(0); i < 7; i++ {
b[i] = byte(e>>(6-i)) & 1
}
}
// New creates a new APDS-9960 connection. The I2C bus must already be // New creates a new APDS-9960 connection. The I2C bus must already be
// configured. // configured.
// //
@@ -68,9 +89,8 @@ func New(bus drivers.I2C) Device {
// Connected returns whether APDS-9960 has been found. // Connected returns whether APDS-9960 has been found.
// It does a "who am I" request and checks the response. // It does a "who am I" request and checks the response.
func (d *Device) Connected() bool { func (d *Device) Connected() bool {
data := []byte{0} d.txNew()
legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data) return d.txRead8(APDS9960_ID_REG) == 0xAB && d.txErr() == nil
return data[0] == 0xAB
} }
// GetMode returns current engine mode // GetMode returns current engine mode
@@ -79,56 +99,73 @@ func (d *Device) GetMode() uint8 {
} }
// DisableAll turns off the device and all functions // DisableAll turns off the device and all functions
func (d *Device) DisableAll() { func (d *Device) DisableAll() error {
d.enable(enableConfig{}) err := d.enable(0)
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00}) if err != nil {
d.mode = MODE_NONE return err
d.gesture.detected = GESTURE_NONE }
d.txWrite8(APDS9960_GCONF4_REG, 0)
err = d.txErr()
if err == nil {
d.mode = MODE_NONE
d.gesture.detected = GESTURE_NONE
}
return err
} }
// SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64) // SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64 // default: 16, 64
func (d *Device) SetProximityPulse(length, count uint8) { func (d *Device) SetProximityPulse(length, count uint8) error {
legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) d.txNew()
d.txWrite8(APDS9960_PPULSE_REG, getPulseLength(length)<<6|getPulseCount(count))
return d.txErr()
} }
// SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64) // SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64)
// default: 16, 64 // default: 16, 64
func (d *Device) SetGesturePulse(length, count uint8) { func (d *Device) SetGesturePulse(length, count uint8) error {
legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) d.txNew()
d.txWrite8(APDS9960_GPULSE_REG, getPulseLength(length)<<6|getPulseCount(count))
return d.txErr()
} }
// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms) // SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms)
// default: 4 (~10 ms) // default: 4 (~10 ms)
func (d *Device) SetADCIntegrationCycles(cycles uint16) { func (d *Device) SetADCIntegrationCycles(cycles uint16) error {
if cycles > 256 { if cycles > 256 {
cycles = 256 cycles = 256
} }
legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)}) d.txNew()
d.txWrite8(APDS9960_ATIME_REG, uint8(256-cycles))
return d.txErr()
} }
// SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x) // SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x)
// default: 1, 1, 4 // default: 1, 1, 4
func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) { func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) error {
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)}) d.txNew()
legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5}) d.txWrite8(APDS9960_CONTROL_REG, getProximityGain(proximityGain)<<2|getALSGain(colorGain))
d.txWrite8(APDS9960_GCONF2_REG, getProximityGain(gestureGain)<<5)
return d.txErr()
} }
// LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%)) // LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%))
// default: 100 // default: 100
func (d *Device) LEDBoost(percent uint16) { func (d *Device) LEDBoost(percent uint16) error {
var v uint8 var v uint8
switch percent { switch {
case 100: case percent < 125:
v = 0 v = 0
case 150: case percent < 175:
v = 1 v = 1
case 200: case percent < 250:
v = 2 v = 2
case 300: default:
v = 3 v = 3 // Maximum case.
} }
legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4}) d.txNew()
d.txWrite8(APDS9960_CONFIG2_REG, 0x01|(v<<4))
return d.txErr()
} }
// Setthreshold sets threshold (0~255) for detecting gestures // Setthreshold sets threshold (0~255) for detecting gestures
@@ -147,20 +184,27 @@ func (d *Device) Setsensitivity(s uint8) {
} }
// EnableProximity starts the proximity engine // EnableProximity starts the proximity engine
func (d *Device) EnableProximity() { func (d *Device) EnableProximity() error {
if d.mode != MODE_NONE { if d.mode != MODE_NONE {
d.DisableAll() err := d.DisableAll()
if err != nil {
return err
}
} }
d.enable(enableConfig{PON: true, PEN: true, WEN: true}) err := d.enable(enPON | enPEN | enWEN)
d.mode = MODE_PROXIMITY if err == nil {
d.mode = MODE_PROXIMITY
}
return err
} }
// ProximityAvailable reports if proximity data is available // ProximityAvailable reports if proximity data is available
func (d *Device) ProximityAvailable() bool { func (d *Device) ProximityAvailable() bool {
if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") { if d.mode != MODE_PROXIMITY {
return true return false
} }
return false status, err := d.ReadStatus()
return err == nil && status.PVALID()
} }
// ReadProximity reads proximity data (0~255) // ReadProximity reads proximity data (0~255)
@@ -168,26 +212,30 @@ func (d *Device) ReadProximity() (proximity int32) {
if d.mode != MODE_PROXIMITY { if d.mode != MODE_PROXIMITY {
return 0 return 0
} }
data := []byte{0} d.txNew()
legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data) val := d.txRead8(APDS9960_PDATA_REG)
return 255 - int32(data[0]) return 255 - int32(val)
} }
// EnableColor starts the color engine // EnableColor starts the color engine
func (d *Device) EnableColor() { func (d *Device) EnableColor() error {
if d.mode != MODE_NONE { if d.mode != MODE_NONE {
d.DisableAll() d.DisableAll()
} }
d.enable(enableConfig{PON: true, AEN: true, WEN: true}) err := d.enable(enPON | enAEN | enWEN)
d.mode = MODE_COLOR if err == nil {
d.mode = MODE_COLOR
}
return err
} }
// ColorAvailable reports if color data is available // ColorAvailable reports if color data is available
func (d *Device) ColorAvailable() bool { func (d *Device) ColorAvailable() bool {
if d.mode == MODE_COLOR && d.readStatus("AVALID") { if d.mode != MODE_COLOR {
return true return false
} }
return false status, err := d.ReadStatus()
return err == nil && status.AVALID()
} }
// ReadColor reads color data (red, green, blue, clear color/brightness) // ReadColor reads color data (red, green, blue, clear color/brightness)
@@ -195,28 +243,36 @@ func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) {
if d.mode != MODE_COLOR { if d.mode != MODE_COLOR {
return return
} }
data := []byte{0, 0, 0, 0, 0, 0, 0, 0} d.txNew()
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1]) data := d.buf[:8]
legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2]) const numLowRegs = APDS9960_GDATAH_REG - APDS9960_CDATAL_REG + 1
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3]) for i := uint8(0); i < numLowRegs; i++ {
legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4]) data[i] = d.txRead8(i + APDS9960_CDATAL_REG)
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5]) }
legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6]) data[numLowRegs] = d.txRead8(APDS9960_BDATAL_REG)
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7]) data[numLowRegs+1] = d.txRead8(APDS9960_BDATAH_REG)
legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:]) if d.txErr() != nil {
return
}
clear = int32(uint16(data[1])<<8 | uint16(data[0])) clear = int32(uint16(data[1])<<8 | uint16(data[0]))
r = int32(uint16(data[3])<<8 | uint16(data[2])) r = int32(uint16(data[3])<<8 | uint16(data[2]))
g = int32(uint16(data[5])<<8 | uint16(data[4])) g = int32(uint16(data[5])<<8 | uint16(data[4]))
b = int32(uint16(data[7])<<8 | uint16(data[6])) b = int32(uint16(data[7])<<8 | uint16(data[6]))
return return r, g, b, clear
} }
// EnableGesture starts the gesture engine // EnableGesture starts the gesture engine
func (d *Device) EnableGesture() { func (d *Device) EnableGesture() error {
if d.mode != MODE_NONE { if d.mode != MODE_NONE {
d.DisableAll() err := d.DisableAll()
if err != nil {
return err
}
}
err := d.enable(enPON | enPEN | enGEN | enWEN)
if err != nil {
return err
} }
d.enable(enableConfig{PON: true, PEN: true, GEN: true, WEN: true})
d.mode = MODE_GESTURE d.mode = MODE_GESTURE
d.gesture.detected = GESTURE_NONE d.gesture.detected = GESTURE_NONE
d.gesture.gXDelta = 0 d.gesture.gXDelta = 0
@@ -224,6 +280,7 @@ func (d *Device) EnableGesture() {
d.gesture.gXPrevDelta = 0 d.gesture.gXPrevDelta = 0
d.gesture.gYPrevDelta = 0 d.gesture.gYPrevDelta = 0
d.gesture.received = false d.gesture.received = false
return nil
} }
// GestureAvailable reports if gesture data is available // GestureAvailable reports if gesture data is available
@@ -231,22 +288,16 @@ func (d *Device) GestureAvailable() bool {
if d.mode != MODE_GESTURE { if d.mode != MODE_GESTURE {
return false return false
} }
d.txNew()
data := []byte{0, 0, 0, 0} gstatus := d.txRead8(APDS9960_GSTATUS_REG)
if gstatus&1 == 0 {
// check GVALID
legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1])
if data[0]&0x01 == 0 {
return false return false
} }
availableDataSets := d.txRead8(APDS9960_GFLVL_REG)
// get number of data sets available in FIFO
legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1])
availableDataSets := data[0]
if availableDataSets == 0 { if availableDataSets == 0 {
return false return false
} }
data := d.buf[:]
// read up, down, left and right proximity data from FIFO // read up, down, left and right proximity data from FIFO
var dataSets [32][4]uint8 var dataSets [32][4]uint8
for i := uint8(0); i < availableDataSets; i++ { for i := uint8(0); i < availableDataSets; i++ {
@@ -360,59 +411,72 @@ func (d *Device) configureDevice(cfg Configuration) {
} }
} }
func (d *Device) enable(cfg enableConfig) { func (d *Device) enable(cfg encfg) error {
var gen, pien, aien, wen, pen, aen, pon uint8 d.txNew()
cfg.write7bits(d.buf[:7])
if cfg.GEN { d.txWrite(APDS9960_ENABLE_REG, d.buf[:7])
gen = 1 err := d.txErr()
} if err == nil && cfg&enPON != 0 {
if cfg.PIEN {
pien = 1
}
if cfg.AIEN {
aien = 1
}
if cfg.WEN {
wen = 1
}
if cfg.PEN {
pen = 1
}
if cfg.AEN {
aen = 1
}
if cfg.PON {
pon = 1
}
data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon}
legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data)
if cfg.PON {
time.Sleep(time.Millisecond * 10) time.Sleep(time.Millisecond * 10)
} }
return err
} }
func (d *Device) readStatus(param string) bool { func (d *Device) txErr() error { return d._txerr }
data := []byte{0}
legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data)
switch param { func (d *Device) txNew() { d._txerr = nil }
case "CPSAT":
return data[0]>>7&0x01 == 1 func (d *Device) txRead8(addr uint8) uint8 {
case "PGSAT": if d._txerr != nil {
return data[0]>>6&0x01 == 1 return 0
case "PINT":
return data[0]>>5&0x01 == 1
case "AINT":
return data[0]>>4&0x01 == 1
case "PVALID":
return data[0]>>1&0x01 == 1
case "AVALID":
return data[0]&0x01 == 1
default:
return false
} }
d.buf[0] = addr
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:1], d.buf[1:2])
return d.buf[1]
}
func (d *Device) txWrite8(addr uint8, val uint8) {
if d._txerr != nil {
return
}
d.buf[0] = addr
d.buf[1] = val
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:2], nil)
}
func (d *Device) txWrite(addr uint8, data []byte) {
if d._txerr != nil {
return
} else if len(data) > len(d.buf)-1 {
panic("txWrite: data too long")
}
d.buf[0] = addr
copy(d.buf[1:], data)
d._txerr = d.bus.Tx(uint16(d.Address), d.buf[:len(data)+1], nil)
}
type status uint8
const (
statusAVALID status = 1 << iota
statusPVALID
_
_
statusAINT
statusPINT
statusPGSAT
statusCPSAT
)
func (s status) CPSAT() bool { return s&statusCPSAT != 0 }
func (s status) PGSAT() bool { return s&statusPGSAT != 0 }
func (s status) PINT() bool { return s&statusPINT != 0 }
func (s status) AINT() bool { return s&statusAINT != 0 }
func (s status) PVALID() bool { return s&statusPVALID != 0 }
func (s status) AVALID() bool { return s&statusAVALID != 0 }
func (d *Device) ReadStatus() (status, error) {
d.txNew()
return status(d.txRead8(APDS9960_STATUS_REG)), d.txErr()
} }
func getPulseLength(l uint8) uint8 { func getPulseLength(l uint8) uint8 {