capsules_extra/apds9960.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Proximity SyscallDriver for the Adafruit APDS9960 gesture/ambient
6//! light/proximity sensor.
7//!
8//! Datasheet:
9//! <https://content.arduino.cc/assets/Nano_BLE_Sense_av02-4191en_ds_apds-9960.pdf>
10//!
11//! > The APDS-9960 device features advanced Gesture detection, Proximity
12//! > detection, Digital Ambient Light Sense (ALS) and Color Sense (RGBC). The
13//! > slim modular package, L 3.94 x W 2.36 x H 1.35 mm, incorporates an IR LED
14//! > and factory calibrated LED driver for drop-in compatibility with existing
15//! > footprints
16//!
17//! Usage
18//! -----
19//!
20//! ```rust,ignore
21//! let apds9960_i2c = static_init!(
22//! capsules::virtual_i2c::I2CDevice,
23//! capsules::virtual_i2c::I2CDevice::new(sensors_i2c_bus, 0x39)
24//! );
25//!
26//! let apds9960 = static_init!(
27//! capsules::apds9960::APDS9960<'static>,
28//! capsules::apds9960::APDS9960::new(
29//! apds9960_i2c,
30//! &nrf52840::gpio::PORT[APDS9960_PIN],
31//! &mut capsules::apds9960::BUFFER
32//! )
33//! );
34//! apds9960_i2c.set_client(apds9960);
35//! nrf52840::gpio::PORT[APDS9960_PIN].set_client(apds9960);
36//!
37//! let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
38//!
39//! let proximity = static_init!(
40//! capsules::proximity::ProximitySensor<'static>,
41//! capsules::proximity::ProximitySensor::new(apds9960 , board_kernel.create_grant(&grant_cap)));
42//!
43//! kernel::hil::sensors::ProximityDriver::set_client(apds9960, proximity);
44//! ```
45
46use core::cell::Cell;
47use kernel::hil::gpio;
48use kernel::hil::i2c;
49use kernel::utilities::cells::{OptionalCell, TakeCell};
50use kernel::ErrorCode;
51
52// I2C Buffer of 16 bytes
53pub const BUF_LEN: usize = 16;
54
55// BUFFER Layout: [0,... , 12 , 13 , 14 , 15]
56// ^take_meas() callback stored ^take_meas_int callback stored ^low thresh ^high thresh
57
58// Common Register Masks
59const PON: u8 = 1 << 0; // Power-On
60const SAI: u8 = 1 << 4; // Sleep after Interrupt
61const PEN: u8 = 1 << 2; // Proximity Sensor Enable
62const PIEN: u8 = 1 << 5; // Proximity Sensor Enable
63const PVALID: u8 = 1 << 1; // Proximity Reading Valid Bit
64
65// Default Proximity Int Persistence (amount of times a prox reading can be within the interrupt-generating range before an int is actually fired;
66// this is to prevent false triggers)
67static PERS: u8 = 4;
68
69// Device Registers
70#[repr(u8)]
71enum Registers {
72 ENABLE = 0x80,
73 ID = 0x92,
74 PILT = 0x89,
75 PIHT = 0x8B,
76 CONFIG3 = 0x9f,
77 PICCLR = 0xe5,
78 PERS = 0x8c,
79 PDATA = 0x9c,
80 CONTROLREG1 = 0x8f,
81 PROXPULSEREG = 0x8e,
82 STATUS = 0x93,
83}
84
85// States
86#[derive(Clone, Copy, PartialEq)]
87enum State {
88 ReadId,
89
90 /// States visited in take_measurement_on_interrupt() function
91 StartingProximity,
92 ConfiguringProximity1,
93 ConfiguringProximity2,
94 ConfiguringProximity3,
95 SendSAI, // Send sleep-after-interrupt bit to Config3 reg
96 PowerOn, // Send sensor activation and power on info to device
97 Idle, // Waiting for Data (interrupt)
98 PowerOff, // Sending power off command to device (to latch values in device data registers)
99 ReadData, // Read data from reg
100
101 /// States visited in take_measurement() function
102 TakeMeasurement1,
103 TakeMeasurement2,
104 TakeMeasurement3,
105 TakeMeasurement4,
106
107 /// States for optional chip functionality
108 SetPulse, // Set proximity pulse
109 SetLdrive, // Set LED Current for Prox and ALS sensors
110 Done, // Final state for take_measurement() state sequence
111}
112
113pub struct APDS9960<'a, I: i2c::I2CDevice> {
114 i2c: &'a I,
115 interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
116 prox_callback: OptionalCell<&'a dyn kernel::hil::sensors::ProximityClient>,
117 state: Cell<State>,
118 buffer: TakeCell<'static, [u8]>,
119}
120
121impl<'a, I: i2c::I2CDevice> APDS9960<'a, I> {
122 pub fn new(
123 i2c: &'a I,
124 interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
125 buffer: &'static mut [u8],
126 ) -> APDS9960<'a, I> {
127 // setup and return struct
128 APDS9960 {
129 i2c,
130 interrupt_pin,
131 prox_callback: OptionalCell::empty(),
132 state: Cell::new(State::Idle),
133 buffer: TakeCell::new(buffer),
134 }
135 }
136
137 // Read I2C-based ID of device (should be 0xAB)
138 pub fn read_id(&self) -> Result<(), ErrorCode> {
139 if self.state.get() == State::Idle {
140 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
141 self.i2c.enable();
142
143 buffer[0] = Registers::ID as u8;
144
145 match self.i2c.write_read(buffer, 1, 1) {
146 Ok(()) => {
147 self.state.set(State::ReadId); // Reading ID
148 Ok(())
149 }
150 Err((err, buffer)) => {
151 self.buffer.replace(buffer);
152 self.i2c.disable();
153 Err(err.into())
154 }
155 }
156 })
157 } else {
158 Err(ErrorCode::BUSY)
159 }
160 }
161
162 // Set Proximity Pulse Count and Length(1 = default)
163 pub fn set_proximity_pulse(&self, mut length: u8, mut count: u8) -> Result<(), ErrorCode> {
164 if self.state.get() == State::Idle {
165 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
166 self.i2c.enable();
167
168 if length > 3 {
169 length = 3;
170 }
171 if count > 63 {
172 count = 63;
173 }
174
175 buffer[0] = Registers::PROXPULSEREG as u8;
176 buffer[1] = length << 6 | count;
177
178 match self.i2c.write(buffer, 2) {
179 Ok(()) => {
180 self.state.set(State::SetPulse); // Send pulse control command to device
181 Ok(())
182 }
183 Err((err, buffer)) => {
184 self.buffer.replace(buffer);
185 self.i2c.disable();
186 Err(err.into())
187 }
188 }
189 })
190 } else {
191 Err(ErrorCode::BUSY)
192 }
193 }
194
195 // Set LED Current Strength (0 -> 100 mA , 3 --> 12.5 mA)
196 pub fn set_ldrive(&self, mut ldrive: u8) -> Result<(), ErrorCode> {
197 if self.state.get() == State::Idle {
198 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
199 self.i2c.enable();
200
201 if ldrive > 3 {
202 ldrive = 3;
203 }
204
205 buffer[0] = Registers::CONTROLREG1 as u8;
206 buffer[1] = ldrive << 6;
207
208 match self.i2c.write(buffer, 2) {
209 Ok(()) => {
210 self.state.set(State::SetLdrive); // Send LED Current Control gain
211 Ok(())
212 }
213 Err((err, buffer)) => {
214 self.buffer.replace(buffer);
215 self.i2c.disable();
216 Err(err.into())
217 }
218 }
219 })
220 } else {
221 Err(ErrorCode::BUSY)
222 }
223 }
224
225 // Take measurement immediately
226 pub fn take_measurement(&self) -> Result<(), ErrorCode> {
227 if self.state.get() == State::Idle {
228 // Enable power and proximity sensor
229 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
230 self.i2c.enable();
231
232 buffer[0] = Registers::ENABLE as u8;
233 buffer[1] = PON | PEN;
234
235 match self.i2c.write(buffer, 2) {
236 Ok(()) => {
237 self.state.set(State::TakeMeasurement1);
238 Ok(())
239 }
240 Err((err, buffer)) => {
241 self.buffer.replace(buffer);
242 self.i2c.disable();
243 Err(err.into())
244 }
245 }
246 })
247 } else {
248 Err(ErrorCode::BUSY)
249 }
250 }
251
252 // Take Simple proximity measurement with interrupt persistence set to 4; `low` and `high` indicate upper interrupt threshold values
253 // IC fires interrupt when (prox_reading < low) || (prox_reading > high)
254 pub fn take_measurement_on_interrupt(&self, low: u8, high: u8) -> Result<(), ErrorCode> {
255 if self.state.get() == State::Idle {
256 // Set threshold values
257 self.buffer.take().map(|buffer| {
258 // Save proximity thresholds to buffer unused space
259 buffer[14] = low;
260 buffer[15] = high;
261
262 self.buffer.replace(buffer);
263 });
264
265 // Configure interrupt pin
266 self.interrupt_pin.make_input();
267 self.interrupt_pin
268 .set_floating_state(gpio::FloatingState::PullUp);
269 self.interrupt_pin.disable_interrupts();
270 self.interrupt_pin
271 .enable_interrupts(gpio::InterruptEdge::FallingEdge);
272
273 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
274 // Set the device to Sleep-After-Interrupt Mode
275 self.i2c.enable();
276
277 buffer[0] = Registers::CONFIG3 as u8;
278 buffer[1] = SAI;
279
280 match self.i2c.write(buffer, 2) {
281 Ok(()) => {
282 self.state.set(State::SendSAI);
283 Ok(())
284 }
285 Err((err, buffer)) => {
286 self.buffer.replace(buffer);
287 self.i2c.disable();
288 Err(err.into())
289 }
290 }
291 })
292 } else {
293 Err(ErrorCode::BUSY)
294 }
295 }
296}
297
298impl<I: i2c::I2CDevice> i2c::I2CClient for APDS9960<'_, I> {
299 fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), i2c::Error>) {
300 match self.state.get() {
301 State::ReadId => {
302 // The ID is in `buffer[0]`, and should be 0xAB.
303 self.buffer.replace(buffer);
304 self.i2c.disable();
305 self.state.set(State::Idle);
306 }
307 State::SendSAI => {
308 // Set persistence to 4
309 buffer[0] = Registers::PERS as u8;
310 buffer[1] = (PERS) << 4;
311
312 match self.i2c.write(buffer, 2) {
313 Ok(()) => {
314 self.state.set(State::StartingProximity);
315 }
316 Err((_err, buffer)) => {
317 self.buffer.replace(buffer);
318 self.state.set(State::Idle);
319 self.i2c.disable();
320 }
321 }
322 }
323 State::StartingProximity => {
324 // Set low prox thresh to value in buffer
325 buffer[0] = Registers::PILT as u8;
326 buffer[1] = buffer[14];
327
328 match self.i2c.write(buffer, 2) {
329 Ok(()) => {
330 self.state.set(State::ConfiguringProximity1);
331 }
332 Err((_err, buffer)) => {
333 self.buffer.replace(buffer);
334 self.state.set(State::Idle);
335 self.i2c.disable();
336 }
337 }
338 }
339 State::ConfiguringProximity1 => {
340 // Set high prox thresh to value in buffer
341 buffer[0] = Registers::PIHT as u8;
342 buffer[1] = buffer[15];
343
344 match self.i2c.write(buffer, 2) {
345 Ok(()) => {
346 self.state.set(State::ConfiguringProximity2);
347 }
348 Err((_err, buffer)) => {
349 self.buffer.replace(buffer);
350 self.state.set(State::Idle);
351 self.i2c.disable();
352 }
353 }
354 }
355 State::ConfiguringProximity2 => {
356 // Clear proximity interrupt.
357 buffer[0] = Registers::PICCLR as u8;
358
359 match self.i2c.write(buffer, 1) {
360 Ok(()) => {
361 self.state.set(State::ConfiguringProximity3);
362 }
363 Err((_err, buffer)) => {
364 self.buffer.replace(buffer);
365 self.state.set(State::Idle);
366 self.i2c.disable();
367 }
368 }
369 }
370 State::ConfiguringProximity3 => {
371 // Enable Device
372 buffer[0] = Registers::ENABLE as u8;
373 buffer[1] = PON | PEN | PIEN;
374
375 match self.i2c.write(buffer, 2) {
376 Ok(()) => {
377 self.state.set(State::PowerOn);
378 }
379 Err((_err, buffer)) => {
380 self.buffer.replace(buffer);
381 self.state.set(State::Idle);
382 self.i2c.disable();
383 }
384 }
385 }
386 State::PowerOn => {
387 // Go into idle state and wait for interrupt for data
388 self.buffer.replace(buffer);
389 self.i2c.disable();
390 self.state.set(State::Idle);
391 }
392 State::ReadData => {
393 // read prox_data from buffer and return it in callback
394 buffer[13] = buffer[0]; // save callback to an unused place in buffer
395
396 // Clear proximity interrupt
397 buffer[0] = Registers::PICCLR as u8;
398
399 match self.i2c.write(buffer, 1) {
400 Ok(()) => {
401 self.interrupt_pin.disable_interrupts();
402 self.state.set(State::PowerOff);
403 }
404 Err((_err, buffer)) => {
405 self.buffer.replace(buffer);
406 self.state.set(State::Idle);
407 self.i2c.disable();
408 }
409 }
410 }
411 State::PowerOff => {
412 // Deactivate the device
413
414 buffer[0] = Registers::ENABLE as u8;
415 buffer[1] = 0_u8;
416
417 match self.i2c.write(buffer, 2) {
418 Ok(()) => {
419 self.state.set(State::Done);
420 }
421 Err((_err, buffer)) => {
422 self.buffer.replace(buffer);
423 self.state.set(State::Idle);
424 self.i2c.disable();
425 }
426 }
427 }
428 State::Done => {
429 // Return to IDLE and perform callback
430 let prox_data: u8 = buffer[13];
431
432 self.buffer.replace(buffer);
433 self.i2c.disable();
434 self.state.set(State::Idle);
435
436 self.prox_callback.map(|cb| cb.callback(prox_data));
437 }
438 State::TakeMeasurement1 => {
439 // Read status reg
440 buffer[0] = Registers::STATUS as u8;
441
442 match self.i2c.write_read(buffer, 1, 1) {
443 Ok(()) => {
444 self.state.set(State::TakeMeasurement2);
445 }
446 Err((_err, buffer)) => {
447 self.buffer.replace(buffer);
448 self.state.set(State::Idle);
449 self.i2c.disable();
450 }
451 }
452 }
453 State::TakeMeasurement2 => {
454 // Determine if prox data is valid by checking PVALID bit in status reg
455
456 let status_reg: u8 = buffer[0];
457
458 if status_reg & PVALID > 0 {
459 buffer[0] = Registers::PDATA as u8;
460
461 match self.i2c.write_read(buffer, 1, 1) {
462 Ok(()) => {
463 self.state.set(State::TakeMeasurement3);
464 }
465 Err((_err, buffer)) => {
466 self.buffer.replace(buffer);
467 self.state.set(State::Idle);
468 self.i2c.disable();
469 }
470 }
471 } else {
472 // If not valid then keep rechecking status reg
473 buffer[0] = Registers::STATUS as u8;
474
475 match self.i2c.write_read(buffer, 1, 1) {
476 Ok(()) => {
477 self.state.set(State::TakeMeasurement2);
478 }
479 Err((_err, buffer)) => {
480 self.buffer.replace(buffer);
481 self.state.set(State::Idle);
482 self.i2c.disable();
483 }
484 }
485 }
486 }
487 State::TakeMeasurement3 => {
488 buffer[12] = buffer[0]; // Save callback value
489
490 // Reset callback value
491 buffer[0] = Registers::ENABLE as u8;
492 buffer[1] = 0;
493
494 match self.i2c.write(buffer, 2) {
495 Ok(()) => {
496 self.state.set(State::TakeMeasurement4);
497 }
498 Err((_err, buffer)) => {
499 self.buffer.replace(buffer);
500 self.state.set(State::Idle);
501 self.i2c.disable();
502 }
503 }
504 }
505 State::TakeMeasurement4 => {
506 // Return to IDLE and perform callback
507
508 let prox_data: u8 = buffer[12]; // Get callback value
509 self.buffer.replace(buffer);
510 self.i2c.disable();
511 self.state.set(State::Idle);
512
513 self.prox_callback.map(|cb| cb.callback(prox_data));
514 }
515
516 State::SetPulse => {
517 // Return to IDLE
518 self.buffer.replace(buffer);
519 self.i2c.disable();
520 self.state.set(State::Idle);
521 }
522 State::SetLdrive => {
523 // Return to IDLE
524 self.buffer.replace(buffer);
525 self.i2c.disable();
526 self.state.set(State::Idle);
527 }
528
529 _ => {}
530 }
531 }
532}
533
534/// Interrupt Service Routine
535impl<I: i2c::I2CDevice> gpio::Client for APDS9960<'_, I> {
536 fn fired(&self) {
537 self.buffer.take().map(|buffer| {
538 // Read value in PDATA reg
539 self.i2c.enable();
540
541 buffer[0] = Registers::PDATA as u8;
542
543 match self.i2c.write_read(buffer, 1, 1) {
544 Ok(()) => {
545 self.state.set(State::ReadData);
546 }
547 Err((_err, buffer)) => {
548 self.buffer.replace(buffer);
549 self.i2c.disable();
550 }
551 }
552 });
553 }
554}
555
556/// Proximity Driver Trait Implementation
557impl<'a, I: i2c::I2CDevice> kernel::hil::sensors::ProximityDriver<'a> for APDS9960<'a, I> {
558 fn read_proximity(&self) -> Result<(), ErrorCode> {
559 self.take_measurement()
560 }
561
562 fn read_proximity_on_interrupt(&self, low: u8, high: u8) -> Result<(), ErrorCode> {
563 self.take_measurement_on_interrupt(low, high)
564 }
565
566 fn set_client(&self, client: &'a dyn kernel::hil::sensors::ProximityClient) {
567 self.prox_callback.set(client);
568 }
569}