capsules_extra/
isl29035.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//! SyscallDriver for the ISL29035 digital light sensor.
6//!
7//! <http://bit.ly/2rA00cH>
8//!
9//! > The ISL29035 is an integrated ambient and infrared light-to-digital
10//! > converter with I2C (SMBus compatible) Interface. Its advanced self-
11//! > calibrated photodiode array emulates human eye response with excellent IR
12//! > rejection. The on-chip ADC is capable of rejecting 50Hz and 60Hz flicker
13//! > caused by artificial light sources. The Lux range select feature allows
14//! > users to program the Lux range for optimized counts/Lux.
15//!
16//! Usage
17//! -----
18//!
19//! ```rust,ignore
20//! # use kernel::static_init;
21//! # use capsules::virtual_alarm::VirtualMuxAlarm;
22//!
23//! let isl29035_i2c = static_init!(I2CDevice, I2CDevice::new(i2c_bus, 0x44));
24//! let isl29035_virtual_alarm = static_init!(
25//!     VirtualMuxAlarm<'static, sam4l::ast::Ast>,
26//!     VirtualMuxAlarm::new(mux_alarm));
27//! isl29035_virtual_alarm.setup();
28//!
29//! let isl29035 = static_init!(
30//!     capsules::isl29035::Isl29035<'static, VirtualMuxAlarm<'static, sam4l::ast::Ast>>,
31//!     capsules::isl29035::Isl29035::new(isl29035_i2c, isl29035_virtual_alarm,
32//!                                       &mut capsules::isl29035::BUF));
33//! isl29035_i2c.set_client(isl29035);
34//! isl29035_virtual_alarm.set_client(isl29035);
35//! ```
36
37use core::cell::Cell;
38use kernel::hil::i2c::{Error, I2CClient, I2CDevice};
39use kernel::hil::sensors::{AmbientLight, AmbientLightClient};
40use kernel::hil::time::{self, ConvertTicks};
41use kernel::utilities::cells::{OptionalCell, TakeCell};
42use kernel::ErrorCode;
43
44/// Recommended buffer length.
45pub const BUF_LEN: usize = 3;
46
47#[derive(Copy, Clone, PartialEq)]
48enum State {
49    Disabled,
50    Enabling,
51    Integrating,
52    ReadingLI,
53    Disabling(usize),
54}
55
56pub struct Isl29035<'a, A: time::Alarm<'a>> {
57    i2c: &'a dyn I2CDevice,
58    alarm: &'a A,
59    state: Cell<State>,
60    buffer: TakeCell<'static, [u8]>,
61    client: OptionalCell<&'a dyn AmbientLightClient>,
62}
63
64impl<'a, A: time::Alarm<'a>> Isl29035<'a, A> {
65    pub fn new(i2c: &'a dyn I2CDevice, alarm: &'a A, buffer: &'static mut [u8]) -> Isl29035<'a, A> {
66        Isl29035 {
67            i2c,
68            alarm,
69            state: Cell::new(State::Disabled),
70            buffer: TakeCell::new(buffer),
71            client: OptionalCell::empty(),
72        }
73    }
74
75    pub fn start_read_lux(&self) -> Result<(), ErrorCode> {
76        if self.state.get() == State::Disabled {
77            self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buf| {
78                self.i2c.enable();
79                buf[0] = 0;
80                // CMD 1 Register:
81                // Interrupt persist for 1 integration cycle (bits 0 & 1)
82                // Measure ALS continuously (buts 5,6 & 7)
83                // Bit 2 is the interrupt bit
84                // Bits 3 & 4 are reserved
85                buf[1] = 0b10100000;
86
87                // CMD 2 Register:
88                // Range 4000 (bits 0, 1)
89                // ADC resolution 8-bit (bits 2,3)
90                // Other bits are reserved
91                buf[2] = 0b00001001;
92
93                if let Err((error, buf)) = self.i2c.write(buf, 3) {
94                    self.buffer.replace(buf);
95                    self.i2c.disable();
96                    Err(error.into())
97                } else {
98                    self.state.set(State::Enabling);
99                    Ok(())
100                }
101            })
102        } else {
103            Err(ErrorCode::BUSY)
104        }
105    }
106}
107
108impl<'a, A: time::Alarm<'a>> AmbientLight<'a> for Isl29035<'a, A> {
109    fn set_client(&self, client: &'a dyn AmbientLightClient) {
110        self.client.set(client)
111    }
112
113    fn read_light_intensity(&self) -> Result<(), ErrorCode> {
114        self.start_read_lux()
115    }
116}
117
118impl<'a, A: time::Alarm<'a>> time::AlarmClient for Isl29035<'a, A> {
119    fn alarm(&self) {
120        self.buffer.take().map(|buffer| {
121            // Turn on i2c to send commands.
122            self.i2c.enable();
123
124            buffer[0] = 0x02_u8;
125            if let Err((_error, buf)) = self.i2c.write_read(buffer, 1, 2) {
126                self.buffer.replace(buf);
127                self.i2c.disable();
128                self.state.set(State::Disabled);
129                self.client.map(|client| client.callback(0));
130            } else {
131                self.state.set(State::ReadingLI);
132            }
133        });
134    }
135}
136
137impl<'a, A: time::Alarm<'a>> I2CClient for Isl29035<'a, A> {
138    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>) {
139        if status.is_err() {
140            self.state.set(State::Disabled);
141            self.buffer.replace(buffer);
142            self.client.map(|client| client.callback(0));
143            return;
144        }
145        match self.state.get() {
146            State::Enabling => {
147                // Set a timer to wait for the conversion to be done.
148                // For 8 bits, thats 410 us (per Table 11 in the datasheet).
149                let interval = self.alarm.ticks_from_us(410);
150                self.alarm.set_alarm(self.alarm.now(), interval);
151
152                // Now wait for timer to expire
153                self.buffer.replace(buffer);
154                self.i2c.disable();
155                self.state.set(State::Integrating);
156            }
157            State::ReadingLI => {
158                // During configuration we set the ADC resolution to 8 bits and
159                // the range to 4000.
160                //
161                // Since it's only 8 bits, we ignore the second byte of output.
162                //
163                // For a given Range and n (-bits of ADC resolution):
164                // Lux = Data * (Range / 2^n)
165                let data = buffer[0] as usize; //((buffer[1] as usize) << 8) | buffer[0] as usize;
166                let lux = (data * 4000) >> 8;
167
168                buffer[0] = 0;
169
170                if let Err((_error, buffer)) = self.i2c.write(buffer, 2) {
171                    self.state.set(State::Disabled);
172                    self.buffer.replace(buffer);
173                    self.client.map(|client| client.callback(0));
174                } else {
175                    self.state.set(State::Disabling(lux));
176                }
177            }
178            State::Disabling(lux) => {
179                self.i2c.disable();
180                self.state.set(State::Disabled);
181                self.buffer.replace(buffer);
182                self.client.map(|client| client.callback(lux));
183            }
184            _ => {}
185        }
186    }
187}