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}