From 4bd873a82dd8f7a5154ce26f9cb951b9e98bad5a Mon Sep 17 00:00:00 2001 From: soypat Date: Tue, 26 Dec 2023 15:00:56 -0800 Subject: [PATCH 1/3] add draft apds9930 --- apds9930/apds9930.go | 68 ++++++++++++++++++++++++ apds9930/registers.go | 19 +++++++ examples/apds9930/proximity/proximity.go | 39 ++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 apds9930/apds9930.go create mode 100644 apds9930/registers.go create mode 100644 examples/apds9930/proximity/proximity.go diff --git a/apds9930/apds9930.go b/apds9930/apds9930.go new file mode 100644 index 000000000..1e59d7974 --- /dev/null +++ b/apds9930/apds9930.go @@ -0,0 +1,68 @@ +package apds9930 + +import "tinygo.org/x/drivers" + +type Dev struct { + bus drivers.I2C + _txerr error + addr uint16 + buf [8]byte +} + +func New(bus drivers.I2C, addr uint8) Dev { + return Dev{bus: bus, addr: uint16(addr)} +} + +func (d *Dev) Init() error { + 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) + return d.txErr() +} + +func (d *Dev) EnableProximity() error { + d.txNew() + d.txWrite8(regENABLE, 8|4|2|1) + return d.txErr() +} + +func (d *Dev) ProximityAvailable() bool { + d.txNew() + return d.txRead8(regSTATUS)&0x20 == 1 +} + +func (d *Dev) ReadProximity() uint8 { + d.txNew() + h := d.txRead8(regPDATAH) + l := d.txRead8(regPDATAL) + if d.txErr() != nil { + return 0 + } + return (h << 8) | l +} + +func (d *Dev) txRead8(reg uint8) uint8 { + if d.txErr() != nil { + return 0 + } + d.buf[0] = reg | 0xa0 + d._txerr = d.bus.Tx(d.addr, d.buf[:1], d.buf[1:2]) + return d.buf[1] +} + +func (d *Dev) txWrite8(reg uint8, val uint8) { + if d.txErr() != nil { + return + } + d.buf[0] = reg | 0x80 + d.buf[1] = val + d._txerr = d.bus.Tx(d.addr, d.buf[:2], nil) +} + +func (d *Dev) txNew() { d._txerr = nil } + +func (d *Dev) txErr() error { return d._txerr } diff --git a/apds9930/registers.go b/apds9930/registers.go new file mode 100644 index 000000000..79587e51b --- /dev/null +++ b/apds9930/registers.go @@ -0,0 +1,19 @@ +package apds9930 + +const ( + regENABLE = 0x00 + regATIME = 0x01 + regPTIME = 0x02 + regWTIME = 0x03 + regPILTL = 0x08 + regPILTH = 0x09 + regPIHTL = 0x0A + regPIHTH = 0x0B + regCONFIG = 0x0D + regPPULSE = 0x0E + regCONTROL = 0x0F + regSTATUS = 0x13 + regPDATAL = 0x18 + regPDATAH = 0x19 + regPOFFSET = 0x1E +) diff --git a/examples/apds9930/proximity/proximity.go b/examples/apds9930/proximity/proximity.go new file mode 100644 index 000000000..e7861b1e5 --- /dev/null +++ b/examples/apds9930/proximity/proximity.go @@ -0,0 +1,39 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/apds9930" +) + +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 + err := bus.Configure(machine.I2CConfig{ + SCL: machine.GP1, + SDA: machine.GP0, + Frequency: 100 * machine.KHz, + }) + if err != nil { + panic(err.Error()) + } + sensor := apds9930.New(bus, 0x39) + + err = sensor.Init() + if err != nil { + panic(err) + } + err = sensor.EnableProximity() + if err != nil { + panic(err) + } + println("proximity enabled!") + for { + time.Sleep(50 * time.Millisecond) + prox := sensor.ReadProximity() + println("proximity:", prox) + } +} From 09b7b249fe10702f6d4694b9aa088edfb6305899 Mon Sep 17 00:00:00 2001 From: soypat Date: Tue, 26 Dec 2023 22:35:48 -0300 Subject: [PATCH 2/3] working more or less --- apds9930/apds9930.go | 196 +++++++++++++++++++++-- apds9930/registers.go | 4 + examples/apds9930/proximity/proximity.go | 8 +- 3 files changed, 193 insertions(+), 15 deletions(-) diff --git a/apds9930/apds9930.go b/apds9930/apds9930.go index 1e59d7974..2f12f2e98 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() + 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) proxIntHighThresh() (uint16, error) { d.txNew() - d.txWrite8(regENABLE, 8|4|2|1) + val := d.txRead16(regPIHTL) + return val, d.txErr() +} + +func (d *Dev) setProxIntHighThresh(hiThresh uint16) error { + d.txNew() + d.txWrite16(regPIHTL, hiThresh) return d.txErr() } -func (d *Dev) ProximityAvailable() bool { +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() - return d.txRead8(regSTATUS)&0x20 == 1 + d.txWrite8(regCONTROL, uint8(current)) + return d.txErr() } -func (d *Dev) ReadProximity() uint8 { +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() - h := d.txRead8(regPDATAH) - l := d.txRead8(regPDATAL) + v := d.txRead16(regPDATAL) + if d.txErr() != nil { + return 0 + } + return v +} + +func (d *Dev) txRead16(addr uint8) uint16 { if d.txErr() != nil { return 0 } - return (h << 8) | l + 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(reg uint8) uint8 { +func (d *Dev) txRead8(addr uint8) uint8 { 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: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 79587e51b..5772c7e52 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 e7861b1e5..901abc377 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) } From 540584281507b20d2fc9955c4e47a5082e86aefd Mon Sep 17 00:00:00 2001 From: soypat Date: Tue, 26 Dec 2023 22:44:25 -0300 Subject: [PATCH 3/3] rewrite enum implementations --- apds9930/apds9930.go | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/apds9930/apds9930.go b/apds9930/apds9930.go index 2f12f2e98..8d26bd256 100644 --- a/apds9930/apds9930.go +++ b/apds9930/apds9930.go @@ -68,18 +68,16 @@ const ( type Drive uint8 const ( - _ Drive = iota - Drive12_5mA - Drive25mA + Drive100mA Drive = iota Drive50mA - Drive100mA + Drive25mA + Drive12_5mA ) type Config struct { - StartMode Enable - LEDDrive Drive - // ALSGain ALSGain - // ProximityGain ProxGain + ProxGain ProxGain + ALSGain ALSGain + LEDDrive Drive } func (d *Dev) Init(cfg Config) error { @@ -92,16 +90,13 @@ func (d *Dev) Init(cfg Config) error { d.txWrite8(regPPULSE, 0x04) d.txWrite8(regWTIME, 0xee) // set default wait time. d.txWrite8(regPTIME, 0xff) // set default pulse count. - if d.txErr() != nil { - return d.txErr() - } - if cfg.LEDDrive != 0 { - err := d.SetLEDDrive(cfg.LEDDrive) - if err != nil { - return err - } - } - return nil + + var ctlval uint8 = 0b10 << 4 // Use Channel 1 diode. + ctlval |= uint8(cfg.LEDDrive&0b11) << 6 + ctlval |= uint8(cfg.ProxGain&0b11) << 2 + ctlval |= uint8(cfg.ALSGain & 0b11) + d.txWrite8(regCONTROL, ctlval) + return d.txErr() } func (d *Dev) Status() (Status, error) { @@ -125,7 +120,6 @@ func (d *Dev) enableLightSensor(withInterrupts bool) error { } func (d *Dev) setAmbientLightGain() { - } func (d *Dev) EnableProximity() error { @@ -158,7 +152,7 @@ func (d *Dev) setProxIntHighThresh(hiThresh uint16) error { func (d *Dev) LEDDrive() (Drive, error) { d.txNew() val := (d.txRead8(regCONTROL) >> 6) & 0b11 - return 3 - Drive(val), d.txErr() + return Drive(val), d.txErr() } // SetLEDDrive drive strength for proximity and ALS @@ -172,7 +166,6 @@ func (d *Dev) SetLEDDrive(drive Drive) error { if drive > 3 { return errInvalidParam } - drive = 3 - drive current, err := d.LEDDrive() if err != nil { return err