diff --git a/apds9930/apds9930.go b/apds9930/apds9930.go index 1e59d79..2f12f2e 100644 --- a/apds9930/apds9930.go +++ b/apds9930/apds9930.go @@ -1,59 +1,229 @@ package apds9930 -import "tinygo.org/x/drivers" +import ( + "errors" + + "tinygo.org/x/drivers" +) + +var errInvalidParam = errors.New("apds9930: invalid param") type Dev struct { bus drivers.I2C _txerr error addr uint16 - buf [8]byte + buf [3]byte } func New(bus drivers.I2C, addr uint8) Dev { return Dev{bus: bus, addr: uint16(addr)} } -func (d *Dev) Init() error { +// Status contains info on: +// +// AVALID: Indicates that the ALS Ch0/Ch1 channels have completed an integration cycle. +// PSValid. Indicates that the PS has completed an integration cycle. +// AINTL ALS Interrupt. Indicates that the device is asserting an ALS interrupt +// PINT: Proximity Interrupt. Indicates that the device is asserting a proximity interrupt. +// PSAT: Proximity Saturation. Indicates that the proximity measurement is saturated +type Status uint8 + +func (s Status) ALSAvailable() bool { return s&(1<<0) != 0 } // AVALID +func (s Status) ProximityAvailable() bool { return s&(1<<1) != 0 } // PVALID +func (s Status) HasALSInterrupt() bool { return s&(1<<4) != 0 } // AINT +func (s Status) HasProxInterrupt() bool { return s&(1<<5) != 0 } // PINT +func (s Status) IsProximitySaturated() bool { return s&(1<<6) != 0 } // PSAT + +type Enable uint8 + +const ( + EnPower Enable = 1 << iota + EnALS + EnProx + EnWait + EnALSInt + EnProxInt + EnSleepAfterInt +) + +// Luminic control gain. +type ALSGain uint8 + +const ( + AGain1 ALSGain = iota + AGain8 + AGain16 + AGain120 +) + +type ProxGain uint8 + +const ( + PGain1 ProxGain = iota + PGain2 + PGain4 + PGain8 +) + +type Drive uint8 + +const ( + _ Drive = iota + Drive12_5mA + Drive25mA + Drive50mA + Drive100mA +) + +type Config struct { + StartMode Enable + LEDDrive Drive + // ALSGain ALSGain + // ProximityGain ProxGain +} + +func (d *Dev) Init(cfg Config) error { + if cfg.LEDDrive > Drive100mA { + return errInvalidParam + } d.txNew() d.txWrite8(regENABLE, 0x00) // disable all features. d.txWrite8(regATIME, 0xee) // set default integration time. d.txWrite8(regPPULSE, 0x04) d.txWrite8(regWTIME, 0xee) // set default wait time. d.txWrite8(regPTIME, 0xff) // set default pulse count. - d.txWrite8(regCONTROL, 0x20) + if d.txErr() != nil { + return d.txErr() + } + if cfg.LEDDrive != 0 { + err := d.SetLEDDrive(cfg.LEDDrive) + if err != nil { + return err + } + } + return nil +} + +func (d *Dev) Status() (Status, error) { + d.txNew() + v := d.txRead8(regSTATUS) + return Status(v), d.txErr() +} + +// Enable sets the ENABLE register used primarily to +// power the APDS-9930 device on/off, enable functions, and interrupts. +// Arguments must be ORed, i.e: d.Enable(EnPower|EnProx); to enable proximity. +func (d *Dev) Enable(en Enable) error { + en &= 0b01111111 // Seventh bit reserved. + d.txNew() + d.txWrite8(regENABLE, uint8(en)) return d.txErr() } +func (d *Dev) enableLightSensor(withInterrupts bool) error { + return nil +} + +func (d *Dev) setAmbientLightGain() { + +} + func (d *Dev) EnableProximity() error { + return d.Enable(EnPower | EnALS | EnProx | EnWait) +} + +func (d *Dev) proxIntLowThresh() (uint16, error) { d.txNew() - d.txWrite8(regENABLE, 8|4|2|1) + return d.txRead16(regPILTL), d.txErr() +} + +func (d *Dev) setProxIntLowThresh(loThresh uint16) error { + d.txNew() + d.txWrite16(regPILTL, loThresh) return d.txErr() } -func (d *Dev) ProximityAvailable() bool { +func (d *Dev) proxIntHighThresh() (uint16, error) { d.txNew() - return d.txRead8(regSTATUS)&0x20 == 1 + val := d.txRead16(regPIHTL) + return val, d.txErr() } -func (d *Dev) ReadProximity() uint8 { +func (d *Dev) setProxIntHighThresh(hiThresh uint16) error { d.txNew() - h := d.txRead8(regPDATAH) - l := d.txRead8(regPDATAL) + d.txWrite16(regPIHTL, hiThresh) + return d.txErr() +} + +func (d *Dev) LEDDrive() (Drive, error) { + d.txNew() + val := (d.txRead8(regCONTROL) >> 6) & 0b11 + return 3 - Drive(val), d.txErr() +} + +// SetLEDDrive drive strength for proximity and ALS +// +// Value LED Current +// 3 100 mA +// 2 50 mA +// 1 25 mA +// 0 12.5 mA +func (d *Dev) SetLEDDrive(drive Drive) error { + if drive > 3 { + return errInvalidParam + } + drive = 3 - drive + current, err := d.LEDDrive() + if err != nil { + return err + } + // Replace LED bits in Control register. + current &= 0b00111111 + current |= drive << 6 + d.txNew() + d.txWrite8(regCONTROL, uint8(current)) + return d.txErr() +} + +func (d *Dev) proxGain() (uint8, error) { + val := d.txRead8(regCONTROL) + return (val >> 2) & 0b11, d.txErr() +} + +// ReadProximity returns a 10-bit value (0..1023), the higher the value the closer the object +func (d *Dev) ReadProximity() uint16 { + d.txNew() + v := d.txRead16(regPDATAL) if d.txErr() != nil { return 0 } - return (h << 8) | l + return v } -func (d *Dev) txRead8(reg uint8) uint8 { +func (d *Dev) txRead16(addr uint8) uint16 { if d.txErr() != nil { return 0 } - d.buf[0] = reg | 0xa0 + d.buf[0] = addr | protoAutoInc + d._txerr = d.bus.Tx(d.addr, d.buf[:1], d.buf[1:3]) + return uint16(d.buf[1]) | uint16(d.buf[2])<<8 + +} + +func (d *Dev) txRead8(addr uint8) uint8 { + if d.txErr() != nil { + return 0 + } + d.buf[0] = addr | protoAutoInc d._txerr = d.bus.Tx(d.addr, d.buf[:1], d.buf[1:2]) return d.buf[1] } +func (d *Dev) txWrite16(addr uint8, val uint16) { + d.txWrite8(addr, uint8(val)) + d.txWrite8(addr+1, uint8(val>>8)) +} + func (d *Dev) txWrite8(reg uint8, val uint8) { if d.txErr() != nil { return diff --git a/apds9930/registers.go b/apds9930/registers.go index 79587e5..5772c7e 100644 --- a/apds9930/registers.go +++ b/apds9930/registers.go @@ -1,5 +1,9 @@ package apds9930 +const ( + protoAutoInc = 0xA0 +) + const ( regENABLE = 0x00 regATIME = 0x01 diff --git a/examples/apds9930/proximity/proximity.go b/examples/apds9930/proximity/proximity.go index e7861b1..901abc3 100644 --- a/examples/apds9930/proximity/proximity.go +++ b/examples/apds9930/proximity/proximity.go @@ -22,7 +22,7 @@ func main() { } sensor := apds9930.New(bus, 0x39) - err = sensor.Init() + err = sensor.Init(apds9930.Config{}) if err != nil { panic(err) } @@ -32,7 +32,11 @@ func main() { } println("proximity enabled!") for { - time.Sleep(50 * time.Millisecond) + stat, _ := sensor.Status() + if !stat.ProximityAvailable() { + time.Sleep(5 * time.Millisecond) + continue + } prox := sensor.ReadProximity() println("proximity:", prox) }