diff --git a/apds9960/apds9960.go b/apds9960/apds9960.go index 27e5c9893..9d9e505e5 100644 --- a/apds9960/apds9960.go +++ b/apds9960/apds9960.go @@ -8,15 +8,16 @@ import ( "time" "tinygo.org/x/drivers" - "tinygo.org/x/drivers/internal/legacy" ) // Device wraps an I2C connection to a APDS-9960 device. type Device struct { bus drivers.I2C + _txerr error + gesture gestureData + buf [8]byte Address uint8 mode uint8 - gesture gestureData } // Configuration for APDS-9960 device. @@ -46,15 +47,24 @@ type gestureData struct { received bool } -// for enabling various device function -type enableConfig struct { - GEN bool - PIEN bool - AIEN bool - WEN bool - PEN bool - AEN bool - PON bool +// for enabling various device functions. +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 @@ -68,9 +78,8 @@ func New(bus drivers.I2C) Device { // Connected returns whether APDS-9960 has been found. // It does a "who am I" request and checks the response. func (d *Device) Connected() bool { - data := []byte{0} - legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data) - return data[0] == 0xAB + d.txNew() + return d.txRead8(APDS9960_ID_REG) == 0xAB && d.txErr() == nil } // GetMode returns current engine mode @@ -79,65 +88,82 @@ func (d *Device) GetMode() uint8 { } // DisableAll turns off the device and all functions -func (d *Device) DisableAll() { - d.enable(enableConfig{}) - legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00}) - d.mode = MODE_NONE - d.gesture.detected = GESTURE_NONE +func (d *Device) DisableAll() error { + err := d.enable(0) + if err != nil { + return err + } + 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 -func (d *Device) SetProximityPulse(length, count uint8) { - legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) +func (d *Device) SetProximityPulse(length, count uint8) error { + 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 -func (d *Device) SetGesturePulse(length, count uint8) { - legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) +func (d *Device) SetGesturePulse(length, count uint8) error { + 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) -// default: 4 (~10 ms) -func (d *Device) SetADCIntegrationCycles(cycles uint16) { +// SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1..256, 1 cycle = 2.78 ms) +// default: 4 (approx. 10 ms) +func (d *Device) SetADCIntegrationCycles(cycles uint16) error { if 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) // default: 1, 1, 4 -func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) { - legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)}) - legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5}) +func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) error { + d.txNew() + 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 (%)) // default: 100 -func (d *Device) LEDBoost(percent uint16) { +func (d *Device) LEDBoost(percent uint16) error { var v uint8 - switch percent { - case 100: + switch { + case percent < 125: v = 0 - case 150: + case percent < 175: v = 1 - case 200: + case percent < 250: v = 2 - case 300: - v = 3 + default: + 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 // default: 30 func (d *Device) Setthreshold(t uint8) { d.gesture.threshold = t } -// Setsensitivity sets sensivity (0~100) for detecting gestures +// Setsensitivity sets sensivity (0..100) for detecting gestures // default: 20 func (d *Device) Setsensitivity(s uint8) { if s > 100 { @@ -147,47 +173,69 @@ func (d *Device) Setsensitivity(s uint8) { } // EnableProximity starts the proximity engine -func (d *Device) EnableProximity() { +func (d *Device) EnableProximity() error { if d.mode != MODE_NONE { - d.DisableAll() + err := d.DisableAll() + if err != nil { + return err + } + } + err := d.enable(enPON | enPEN | enWEN) + if err == nil { + d.mode = MODE_PROXIMITY } - d.enable(enableConfig{PON: true, PEN: true, WEN: true}) - d.mode = MODE_PROXIMITY + return err +} + +// Err returns the current error state of the device if encountered during I2C communication. +// After a call to Err the error is cleared. +func (d *Device) Err() error { + err := d.txErr() + d.txNew() + return err } // ProximityAvailable reports if proximity data is available func (d *Device) ProximityAvailable() bool { - if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") { - return true + if d.mode != MODE_PROXIMITY { + 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) func (d *Device) ReadProximity() (proximity int32) { if d.mode != MODE_PROXIMITY { return 0 } - data := []byte{0} - legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data) - return 255 - int32(data[0]) + d.txNew() + val := d.txRead8(APDS9960_PDATA_REG) + return 255 - int32(val) } // EnableColor starts the color engine -func (d *Device) EnableColor() { +func (d *Device) EnableColor() (err error) { if d.mode != MODE_NONE { - d.DisableAll() + err = d.DisableAll() + if err != nil { + return err + } } - d.enable(enableConfig{PON: true, AEN: true, WEN: true}) - d.mode = MODE_COLOR + err = d.enable(enPON | enAEN | enWEN) + if err == nil { + d.mode = MODE_COLOR + } + return err } // ColorAvailable reports if color data is available func (d *Device) ColorAvailable() bool { - if d.mode == MODE_COLOR && d.readStatus("AVALID") { - return true + if d.mode != MODE_COLOR { + return false } - return false + status, err := d.ReadStatus() + return err == nil && status.AVALID() } // 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 { return } - data := []byte{0, 0, 0, 0, 0, 0, 0, 0} - legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:]) + d.txNew() + data := d.buf[:8] + const numLowRegs = APDS9960_GDATAH_REG - APDS9960_CDATAL_REG + 1 + for i := uint8(0); i < numLowRegs; i++ { + data[i] = d.txRead8(i + APDS9960_CDATAL_REG) + } + data[numLowRegs] = d.txRead8(APDS9960_BDATAL_REG) + data[numLowRegs+1] = d.txRead8(APDS9960_BDATAH_REG) + if d.txErr() != nil { + return + } clear = int32(uint16(data[1])<<8 | uint16(data[0])) r = int32(uint16(data[3])<<8 | uint16(data[2])) g = int32(uint16(data[5])<<8 | uint16(data[4])) b = int32(uint16(data[7])<<8 | uint16(data[6])) - return + return r, g, b, clear } // EnableGesture starts the gesture engine -func (d *Device) EnableGesture() { +func (d *Device) EnableGesture() error { 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.gesture.detected = GESTURE_NONE d.gesture.gXDelta = 0 @@ -224,6 +280,7 @@ func (d *Device) EnableGesture() { d.gesture.gXPrevDelta = 0 d.gesture.gYPrevDelta = 0 d.gesture.received = false + return nil } // GestureAvailable reports if gesture data is available @@ -231,29 +288,26 @@ func (d *Device) GestureAvailable() bool { if d.mode != MODE_GESTURE { return false } - - data := []byte{0, 0, 0, 0} - - // check GVALID - legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1]) - if data[0]&0x01 == 0 { + d.txNew() + gstatus := d.txRead8(APDS9960_GSTATUS_REG) + if gstatus&1 == 0 { return false } - - // get number of data sets available in FIFO - legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1]) - availableDataSets := data[0] + availableDataSets := d.txRead8(APDS9960_GFLVL_REG) if availableDataSets == 0 { return false } - + data := d.buf[:] // read up, down, left and right proximity data from FIFO var dataSets [32][4]uint8 + const numAddrs = APDS9960_GFIFO_R_REG - APDS9960_GFIFO_U_REG + 1 for i := uint8(0); i < availableDataSets; i++ { - legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3]) - legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4]) + for j := uint8(0); j < numAddrs; j++ { + data[j] = d.txRead8(j + APDS9960_GFIFO_U_REG) + } + if d.txErr() != nil { + return false + } for j := uint8(0); j < 4; j++ { dataSets[i][j] = data[j] } @@ -315,9 +369,11 @@ func (d *Device) ReadGesture() (gesture int32) { // private functions -func (d *Device) configureDevice(cfg Configuration) { - d.DisableAll() // turn off everything - +func (d *Device) configureDevice(cfg Configuration) error { + err := d.DisableAll() // turn off everything + if err != nil { + return err + } // "default" settings if cfg.ProximityPulseLength == 0 { cfg.ProximityPulseLength = 16 @@ -350,69 +406,92 @@ func (d *Device) configureDevice(cfg Configuration) { d.gesture.sensitivity = 20 } - d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount) - d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount) - d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain) - d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles) - - if cfg.LEDBoost > 0 { - d.LEDBoost(cfg.LEDBoost) - } -} - -func (d *Device) enable(cfg enableConfig) { - var gen, pien, aien, wen, pen, aen, pon uint8 - - if cfg.GEN { - gen = 1 + err = d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount) + if err != nil { + return err } - if cfg.PIEN { - pien = 1 + err = d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount) + if err != nil { + return err } - if cfg.AIEN { - aien = 1 + err = d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain) + if err != nil { + return err } - if cfg.WEN { - wen = 1 + err = d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles) + if err == nil && cfg.LEDBoost > 0 { + err = d.LEDBoost(cfg.LEDBoost) } - if cfg.PEN { - pen = 1 - } - if cfg.AEN { - aen = 1 - } - if cfg.PON { - pon = 1 + return err +} + +func (d *Device) enable(cfg encfg) error { + d.txNew() + cfg.write7bits(d.buf[:7]) + d.txWrite(APDS9960_ENABLE_REG, d.buf[:7]) + err := d.txErr() + if err == nil && cfg&enPON != 0 { + time.Sleep(time.Millisecond * 10) } + return err +} - 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) +func (d *Device) txErr() error { return d._txerr } - if cfg.PON { - time.Sleep(time.Millisecond * 10) +func (d *Device) txNew() { d._txerr = nil } + +func (d *Device) txRead8(addr uint8) uint8 { + if d._txerr != nil { + return 0 } + 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) readStatus(param string) bool { - data := []byte{0} - legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data) - - switch param { - case "CPSAT": - return data[0]>>7&0x01 == 1 - case "PGSAT": - return data[0]>>6&0x01 == 1 - 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 +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 { diff --git a/examples/apds9960/proximity/main.go b/examples/apds9960/proximity/main.go index 8428a573a..06a6e0f56 100644 --- a/examples/apds9960/proximity/main.go +++ b/examples/apds9960/proximity/main.go @@ -8,33 +8,42 @@ import ( ) func main() { - + // Sleep to catch any errors through the serial monitor. + time.Sleep(1000 * time.Millisecond) + bus := machine.I2C0 // use Nano 33 BLE Sense's internal I2C bus - machine.I2C1.Configure(machine.I2CConfig{ - SCL: machine.SCL1_PIN, - SDA: machine.SDA1_PIN, - Frequency: machine.TWI_FREQ_400KHZ, + err := bus.Configure(machine.I2CConfig{ + SCL: machine.GP1, + SDA: machine.GP0, + Frequency: 400 * machine.KHz, }) + if err != nil { + panic(err.Error()) + } - sensor := apds9960.New(machine.I2C1) + sensor := apds9960.New(bus) // use default settings sensor.Configure(apds9960.Configuration{}) if !sensor.Connected() { println("APDS-9960 not connected!") + println("err:", sensor.Err()) return } - - sensor.EnableProximity() // enable proximity engine - + println("APDS connected!") + err = sensor.EnableProximity() // enable proximity engine + if err != nil { + panic(err.Error()) + } for { - if sensor.ProximityAvailable() { p := sensor.ReadProximity() println("Proximity:", p) } + if err := sensor.Err(); err != nil { + println(err.Error()) + } time.Sleep(time.Millisecond * 100) } - }