Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APDS9930 #632

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions apds9930/apds9930.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package apds9930

import (
"errors"

"tinygo.org/x/drivers"
)

var errInvalidParam = errors.New("apds9930: invalid param")

type Dev struct {
bus drivers.I2C
_txerr error
addr uint16
buf [3]byte
}

func New(bus drivers.I2C, addr uint8) Dev {
return Dev{bus: bus, addr: uint16(addr)}
}

// 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 (
Drive100mA Drive = iota
Drive50mA
Drive25mA
Drive12_5mA
)

type Config struct {
ProxGain ProxGain
ALSGain ALSGain
LEDDrive Drive
}

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.

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) {
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()
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) LEDDrive() (Drive, error) {
d.txNew()
val := (d.txRead8(regCONTROL) >> 6) & 0b11
return 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
}
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 v
}

func (d *Dev) txRead16(addr uint8) uint16 {
if d.txErr() != nil {
return 0
}
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
}
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 }
23 changes: 23 additions & 0 deletions apds9930/registers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package apds9930

const (
protoAutoInc = 0xA0
)

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
)
43 changes: 43 additions & 0 deletions examples/apds9930/proximity/proximity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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(apds9930.Config{})
if err != nil {
panic(err)
}
err = sensor.EnableProximity()
if err != nil {
panic(err)
}
println("proximity enabled!")
for {
stat, _ := sensor.Status()
if !stat.ProximityAvailable() {
time.Sleep(5 * time.Millisecond)
continue
}
prox := sensor.ReadProximity()
println("proximity:", prox)
}
}