tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/apds9960/apds9960.go (about) 1 // Package apds9960 implements a driver for APDS-9960, 2 // a digital proximity, ambient light, RGB and gesture sensor. 3 // 4 // Datasheet: https://cdn.sparkfun.com/assets/learn_tutorials/3/2/1/Avago-APDS-9960-datasheet.pdf 5 package apds9960 6 7 import ( 8 "time" 9 10 "tinygo.org/x/drivers" 11 "tinygo.org/x/drivers/internal/legacy" 12 ) 13 14 // Device wraps an I2C connection to a APDS-9960 device. 15 type Device struct { 16 bus drivers.I2C 17 Address uint8 18 mode uint8 19 gesture gestureData 20 } 21 22 // Configuration for APDS-9960 device. 23 type Configuration struct { 24 ProximityPulseLength uint8 25 ProximityPulseCount uint8 26 GesturePulseLength uint8 27 GesturePulseCount uint8 28 ProximityGain uint8 29 GestureGain uint8 30 ColorGain uint8 31 ADCIntegrationCycles uint16 32 LEDBoost uint16 33 threshold uint8 34 sensitivity uint8 35 } 36 37 // for gesture-related data 38 type gestureData struct { 39 detected uint8 40 threshold uint8 41 sensitivity uint8 42 gXDelta int16 43 gYDelta int16 44 gXPrevDelta int16 45 gYPrevDelta int16 46 received bool 47 } 48 49 // for enabling various device function 50 type enableConfig struct { 51 GEN bool 52 PIEN bool 53 AIEN bool 54 WEN bool 55 PEN bool 56 AEN bool 57 PON bool 58 } 59 60 // New creates a new APDS-9960 connection. The I2C bus must already be 61 // configured. 62 // 63 // This function only creates the Device object, it does not touch the device. 64 func New(bus drivers.I2C) Device { 65 return Device{bus: bus, Address: ADPS9960_ADDRESS, mode: MODE_NONE} 66 } 67 68 // Connected returns whether APDS-9960 has been found. 69 // It does a "who am I" request and checks the response. 70 func (d *Device) Connected() bool { 71 data := []byte{0} 72 legacy.ReadRegister(d.bus, d.Address, APDS9960_ID_REG, data) 73 return data[0] == 0xAB 74 } 75 76 // GetMode returns current engine mode 77 func (d *Device) GetMode() uint8 { 78 return d.mode 79 } 80 81 // DisableAll turns off the device and all functions 82 func (d *Device) DisableAll() { 83 d.enable(enableConfig{}) 84 legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF4_REG, []byte{0x00}) 85 d.mode = MODE_NONE 86 d.gesture.detected = GESTURE_NONE 87 } 88 89 // SetProximityPulse sets proximity pulse length (4, 8, 16, 32) and count (1~64) 90 // default: 16, 64 91 func (d *Device) SetProximityPulse(length, count uint8) { 92 legacy.WriteRegister(d.bus, d.Address, APDS9960_PPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) 93 } 94 95 // SetGesturePulse sets gesture pulse length (4, 8, 16, 32) and count (1~64) 96 // default: 16, 64 97 func (d *Device) SetGesturePulse(length, count uint8) { 98 legacy.WriteRegister(d.bus, d.Address, APDS9960_GPULSE_REG, []byte{getPulseLength(length)<<6 | getPulseCount(count)}) 99 } 100 101 // SetADCIntegrationCycles sets ALS/color ADC internal integration cycles (1~256, 1 cycle = 2.78 ms) 102 // default: 4 (~10 ms) 103 func (d *Device) SetADCIntegrationCycles(cycles uint16) { 104 if cycles > 256 { 105 cycles = 256 106 } 107 legacy.WriteRegister(d.bus, d.Address, APDS9960_ATIME_REG, []byte{uint8(256 - cycles)}) 108 } 109 110 // SetGains sets proximity/gesture gain (1, 2, 4, 8x) and ALS/color gain (1, 4, 16, 64x) 111 // default: 1, 1, 4 112 func (d *Device) SetGains(proximityGain, gestureGain, colorGain uint8) { 113 legacy.WriteRegister(d.bus, d.Address, APDS9960_CONTROL_REG, []byte{getProximityGain(proximityGain)<<2 | getALSGain(colorGain)}) 114 legacy.WriteRegister(d.bus, d.Address, APDS9960_GCONF2_REG, []byte{getProximityGain(gestureGain) << 5}) 115 } 116 117 // LEDBoost sets proximity and gesture LED current level (100, 150, 200, 300 (%)) 118 // default: 100 119 func (d *Device) LEDBoost(percent uint16) { 120 var v uint8 121 switch percent { 122 case 100: 123 v = 0 124 case 150: 125 v = 1 126 case 200: 127 v = 2 128 case 300: 129 v = 3 130 } 131 legacy.WriteRegister(d.bus, d.Address, APDS9960_CONFIG2_REG, []byte{0x01 | v<<4}) 132 } 133 134 // Setthreshold sets threshold (0~255) for detecting gestures 135 // default: 30 136 func (d *Device) Setthreshold(t uint8) { 137 d.gesture.threshold = t 138 } 139 140 // Setsensitivity sets sensivity (0~100) for detecting gestures 141 // default: 20 142 func (d *Device) Setsensitivity(s uint8) { 143 if s > 100 { 144 s = 100 145 } 146 d.gesture.sensitivity = 100 - s 147 } 148 149 // EnableProximity starts the proximity engine 150 func (d *Device) EnableProximity() { 151 if d.mode != MODE_NONE { 152 d.DisableAll() 153 } 154 d.enable(enableConfig{PON: true, PEN: true, WEN: true}) 155 d.mode = MODE_PROXIMITY 156 } 157 158 // ProximityAvailable reports if proximity data is available 159 func (d *Device) ProximityAvailable() bool { 160 if d.mode == MODE_PROXIMITY && d.readStatus("PVALID") { 161 return true 162 } 163 return false 164 } 165 166 // ReadProximity reads proximity data (0~255) 167 func (d *Device) ReadProximity() (proximity int32) { 168 if d.mode != MODE_PROXIMITY { 169 return 0 170 } 171 data := []byte{0} 172 legacy.ReadRegister(d.bus, d.Address, APDS9960_PDATA_REG, data) 173 return 255 - int32(data[0]) 174 } 175 176 // EnableColor starts the color engine 177 func (d *Device) EnableColor() { 178 if d.mode != MODE_NONE { 179 d.DisableAll() 180 } 181 d.enable(enableConfig{PON: true, AEN: true, WEN: true}) 182 d.mode = MODE_COLOR 183 } 184 185 // ColorAvailable reports if color data is available 186 func (d *Device) ColorAvailable() bool { 187 if d.mode == MODE_COLOR && d.readStatus("AVALID") { 188 return true 189 } 190 return false 191 } 192 193 // ReadColor reads color data (red, green, blue, clear color/brightness) 194 func (d *Device) ReadColor() (r int32, g int32, b int32, clear int32) { 195 if d.mode != MODE_COLOR { 196 return 197 } 198 data := []byte{0, 0, 0, 0, 0, 0, 0, 0} 199 legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAL_REG, data[:1]) 200 legacy.ReadRegister(d.bus, d.Address, APDS9960_CDATAH_REG, data[1:2]) 201 legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAL_REG, data[2:3]) 202 legacy.ReadRegister(d.bus, d.Address, APDS9960_RDATAH_REG, data[3:4]) 203 legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAL_REG, data[4:5]) 204 legacy.ReadRegister(d.bus, d.Address, APDS9960_GDATAH_REG, data[5:6]) 205 legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAL_REG, data[6:7]) 206 legacy.ReadRegister(d.bus, d.Address, APDS9960_BDATAH_REG, data[7:]) 207 clear = int32(uint16(data[1])<<8 | uint16(data[0])) 208 r = int32(uint16(data[3])<<8 | uint16(data[2])) 209 g = int32(uint16(data[5])<<8 | uint16(data[4])) 210 b = int32(uint16(data[7])<<8 | uint16(data[6])) 211 return 212 } 213 214 // EnableGesture starts the gesture engine 215 func (d *Device) EnableGesture() { 216 if d.mode != MODE_NONE { 217 d.DisableAll() 218 } 219 d.enable(enableConfig{PON: true, PEN: true, GEN: true, WEN: true}) 220 d.mode = MODE_GESTURE 221 d.gesture.detected = GESTURE_NONE 222 d.gesture.gXDelta = 0 223 d.gesture.gYDelta = 0 224 d.gesture.gXPrevDelta = 0 225 d.gesture.gYPrevDelta = 0 226 d.gesture.received = false 227 } 228 229 // GestureAvailable reports if gesture data is available 230 func (d *Device) GestureAvailable() bool { 231 if d.mode != MODE_GESTURE { 232 return false 233 } 234 235 data := []byte{0, 0, 0, 0} 236 237 // check GVALID 238 legacy.ReadRegister(d.bus, d.Address, APDS9960_GSTATUS_REG, data[:1]) 239 if data[0]&0x01 == 0 { 240 return false 241 } 242 243 // get number of data sets available in FIFO 244 legacy.ReadRegister(d.bus, d.Address, APDS9960_GFLVL_REG, data[:1]) 245 availableDataSets := data[0] 246 if availableDataSets == 0 { 247 return false 248 } 249 250 // read up, down, left and right proximity data from FIFO 251 var dataSets [32][4]uint8 252 for i := uint8(0); i < availableDataSets; i++ { 253 legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_U_REG, data[:1]) 254 legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_D_REG, data[1:2]) 255 legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_L_REG, data[2:3]) 256 legacy.ReadRegister(d.bus, d.Address, APDS9960_GFIFO_R_REG, data[3:4]) 257 for j := uint8(0); j < 4; j++ { 258 dataSets[i][j] = data[j] 259 } 260 } 261 262 // gesture detection process 263 d.gesture.detected = GESTURE_NONE 264 for i := uint8(0); i < availableDataSets; i++ { 265 U := dataSets[i][0] 266 D := dataSets[i][1] 267 L := dataSets[i][2] 268 R := dataSets[i][3] 269 270 // if all readings fall below threshold, it's possible that 271 // a movement's just been made 272 if U < d.gesture.threshold && D < d.gesture.threshold && L < d.gesture.threshold && R < d.gesture.threshold { 273 d.gesture.received = true 274 // if there were movement in the previous step (including the last data sets) 275 if d.gesture.gXPrevDelta != 0 && d.gesture.gYPrevDelta != 0 { 276 totalX := d.gesture.gXPrevDelta - d.gesture.gXDelta 277 totalY := d.gesture.gYPrevDelta - d.gesture.gYDelta 278 // if previous and current movement are in opposite directions (pass through one led then next) 279 // and the difference is big enough, the gesture is recorded 280 switch { 281 case totalX < -int16(d.gesture.sensitivity): 282 d.gesture.detected = GESTURE_LEFT 283 case totalX > int16(d.gesture.sensitivity): 284 d.gesture.detected = GESTURE_RIGHT 285 case totalY > int16(d.gesture.sensitivity): 286 d.gesture.detected = GESTURE_DOWN 287 case totalY < -int16(d.gesture.sensitivity): 288 d.gesture.detected = GESTURE_UP 289 } 290 d.gesture.gXDelta = 0 291 d.gesture.gYDelta = 0 292 d.gesture.gXPrevDelta = 0 293 d.gesture.gYPrevDelta = 0 294 } 295 continue 296 } 297 298 // recording current movement 299 d.gesture.gXDelta = int16(R) - int16(L) 300 d.gesture.gYDelta = int16(D) - int16(U) 301 if d.gesture.received { 302 d.gesture.received = false 303 d.gesture.gXPrevDelta = d.gesture.gXDelta 304 d.gesture.gYPrevDelta = d.gesture.gYDelta 305 } 306 } 307 308 return d.gesture.detected != GESTURE_NONE 309 } 310 311 // ReadGesture reads last gesture data 312 func (d *Device) ReadGesture() (gesture int32) { 313 return int32(d.gesture.detected) 314 } 315 316 // private functions 317 318 func (d *Device) configureDevice(cfg Configuration) { 319 d.DisableAll() // turn off everything 320 321 // "default" settings 322 if cfg.ProximityPulseLength == 0 { 323 cfg.ProximityPulseLength = 16 324 } 325 if cfg.ProximityPulseCount == 0 { 326 cfg.ProximityPulseCount = 64 327 } 328 if cfg.GesturePulseLength == 0 { 329 cfg.GesturePulseLength = 16 330 } 331 if cfg.GesturePulseCount == 0 { 332 cfg.GesturePulseCount = 64 333 } 334 if cfg.ProximityGain == 0 { 335 cfg.ProximityGain = 1 336 } 337 if cfg.GestureGain == 0 { 338 cfg.GestureGain = 1 339 } 340 if cfg.ColorGain == 0 { 341 cfg.ColorGain = 4 342 } 343 if cfg.ADCIntegrationCycles == 0 { 344 cfg.ADCIntegrationCycles = 4 345 } 346 if cfg.threshold == 0 { 347 d.gesture.threshold = 30 348 } 349 if cfg.sensitivity == 0 { 350 d.gesture.sensitivity = 20 351 } 352 353 d.SetProximityPulse(cfg.ProximityPulseLength, cfg.ProximityPulseCount) 354 d.SetGesturePulse(cfg.GesturePulseLength, cfg.GesturePulseCount) 355 d.SetGains(cfg.ProximityGain, cfg.GestureGain, cfg.ColorGain) 356 d.SetADCIntegrationCycles(cfg.ADCIntegrationCycles) 357 358 if cfg.LEDBoost > 0 { 359 d.LEDBoost(cfg.LEDBoost) 360 } 361 } 362 363 func (d *Device) enable(cfg enableConfig) { 364 var gen, pien, aien, wen, pen, aen, pon uint8 365 366 if cfg.GEN { 367 gen = 1 368 } 369 if cfg.PIEN { 370 pien = 1 371 } 372 if cfg.AIEN { 373 aien = 1 374 } 375 if cfg.WEN { 376 wen = 1 377 } 378 if cfg.PEN { 379 pen = 1 380 } 381 if cfg.AEN { 382 aen = 1 383 } 384 if cfg.PON { 385 pon = 1 386 } 387 388 data := []byte{gen<<6 | pien<<5 | aien<<4 | wen<<3 | pen<<2 | aen<<1 | pon} 389 legacy.WriteRegister(d.bus, d.Address, APDS9960_ENABLE_REG, data) 390 391 if cfg.PON { 392 time.Sleep(time.Millisecond * 10) 393 } 394 } 395 396 func (d *Device) readStatus(param string) bool { 397 data := []byte{0} 398 legacy.ReadRegister(d.bus, d.Address, APDS9960_STATUS_REG, data) 399 400 switch param { 401 case "CPSAT": 402 return data[0]>>7&0x01 == 1 403 case "PGSAT": 404 return data[0]>>6&0x01 == 1 405 case "PINT": 406 return data[0]>>5&0x01 == 1 407 case "AINT": 408 return data[0]>>4&0x01 == 1 409 case "PVALID": 410 return data[0]>>1&0x01 == 1 411 case "AVALID": 412 return data[0]&0x01 == 1 413 default: 414 return false 415 } 416 } 417 418 func getPulseLength(l uint8) uint8 { 419 switch l { 420 case 4: 421 return 0 422 case 8: 423 return 1 424 case 16: 425 return 2 426 case 32: 427 return 3 428 default: 429 return 0 430 } 431 } 432 433 func getPulseCount(c uint8) uint8 { 434 if c < 1 && c > 64 { 435 return 0 436 } 437 return c - 1 438 } 439 440 func getProximityGain(g uint8) uint8 { 441 switch g { 442 case 1: 443 return 0 444 case 2: 445 return 1 446 case 4: 447 return 2 448 case 8: 449 return 3 450 default: 451 return 0 452 } 453 } 454 455 func getALSGain(g uint8) uint8 { 456 switch g { 457 case 1: 458 return 0 459 case 4: 460 return 1 461 case 16: 462 return 2 463 case 64: 464 return 3 465 default: 466 return 0 467 } 468 }