capsules_extra/
tsl2561.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 Taos TSL2561 light sensor.
6//!
7//! <http://www.digikey.com/product-detail/en/ams-taos-usa-inc/TSL2561FN/TSL2561-FNCT-ND/3095298>
8//!
9//! > The TSL2560 and TSL2561 are light-to-digital converters that transform
10//! > light intensity to a digital signal output capable of direct I2C
11//! > interface. Each device combines one broadband photodiode (visible plus
12//! > infrared) and one infrared-responding photodiode on a single CMOS
13//! > integrated circuit capable of providing a near-photopic response over an
14//! > effective 20-bit dynamic range (16-bit resolution). Two integrating ADCs
15//! > convert the photodiode currents to a digital output that represents the
16//! > irradiance measured on each channel. This digital output can be input to a
17//! > microprocessor where illuminance (ambient light level) in lux is derived
18//! > using an empirical formula to approximate the human eye response.
19
20use core::cell::Cell;
21
22use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
23use kernel::hil::gpio;
24use kernel::hil::i2c;
25use kernel::syscall::{CommandReturn, SyscallDriver};
26use kernel::utilities::cells::{OptionalCell, TakeCell};
27use kernel::{ErrorCode, ProcessId};
28
29/// Syscall driver number.
30use capsules_core::driver;
31pub const DRIVER_NUM: usize = driver::NUM::Tsl2561 as usize;
32
33// Buffer to use for I2C messages
34pub const BUFFER_LENGTH: usize = 4;
35
36/// Command register defines
37const COMMAND_REG: u8 = 0x80;
38const WORD_PROTOCOL: u8 = 0x20;
39
40/// Control_Reg defines
41const POWER_ON: u8 = 0x03;
42const POWER_OFF: u8 = 0x00;
43
44/// Timing_Reg defines
45const INTEGRATE_TIME_101_MS: u8 = 0x01;
46const LOW_GAIN_MODE: u8 = 0x00;
47
48// Interrupt_Control_Reg defines
49const INTERRUPT_CONTROL_LEVEL: u8 = 0x10;
50const INTERRUPT_ON_ADC_DONE: u8 = 0x0;
51
52// ADC counts to Lux value conversion copied from TSL2561 manual
53// −−−−------------------------------
54// Value scaling factors
55// −−−−−−−−−−−−−−−-------------------
56const LUX_SCALE: u16 = 14; // scale by 2^14
57const RATIO_SCALE: u16 = 9; // scale ratio by 2^9
58
59// −−−−−−−−−−−−−−−−−−−−−−−-----------
60// Integration time scaling factors
61// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
62const CH_SCALE: u16 = 10; // scale channel values by 2^10
63#[allow(dead_code)]
64const CHSCALE_TINT0: u16 = 0x7517; // 322/11 * 2^CH_SCALE
65const CHSCALE_TINT1: u16 = 0x0fe7; // 322/81 * 2^CH_SCALE
66
67// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
68// T, FN, and CL Package coefficients
69// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
70// For Ch1/Ch0=0.00 to 0.50
71// Lux/Ch0=0.0304−0.062*((Ch1/Ch0)^1.4)
72// piecewise approximation
73// For Ch1/Ch0=0.00 to 0.125:
74// Lux/Ch0=0.0304−0.0272*(Ch1/Ch0)
75//
76// For Ch1/Ch0=0.125 to 0.250:
77// Lux/Ch0=0.0325−0.0440*(Ch1/Ch0)
78//
79// For Ch1/Ch0=0.250 to 0.375:
80// Lux/Ch0=0.0351−0.0544*(Ch1/Ch0)
81//
82// For Ch1/Ch0=0.375 to 0.50:
83// Lux/Ch0=0.0381−0.0624*(Ch1/Ch0)
84//
85// For Ch1/Ch0=0.50 to 0.61:
86// Lux/Ch0=0.0224−0.031*(Ch1/Ch0)
87//
88// For Ch1/Ch0=0.61 to 0.80:
89// Lux/Ch0=0.0128−0.0153*(Ch1/Ch0)
90//
91// For Ch1/Ch0=0.80 to 1.30:
92// Lux/Ch0=0.00146−0.00112*(Ch1/Ch0)
93//
94// For Ch1/Ch0>1.3:
95// Lux/Ch0=0
96// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
97const K1T: usize = 0x0040; // 0.125 * 2^RATIO_SCALE
98const B1T: usize = 0x01f2; // 0.0304 * 2^LUX_SCALE
99const M1T: usize = 0x01be; // 0.0272 * 2^LUX_SCALE
100const K2T: usize = 0x0080; // 0.250 * 2^RATIO_SCALE
101const B2T: usize = 0x0214; // 0.0325 * 2^LUX_SCALE
102const M2T: usize = 0x02d1; // 0.0440 * 2^LUX_SCALE
103const K3T: usize = 0x00c0; // 0.375 * 2^RATIO_SCALE
104const B3T: usize = 0x023f; // 0.0351 * 2^LUX_SCALE
105const M3T: usize = 0x037b; // 0.0544 * 2^LUX_SCALE
106const K4T: usize = 0x0100; // 0.50 * 2^RATIO_SCALE
107const B4T: usize = 0x0270; // 0.0381 * 2^LUX_SCALE
108const M4T: usize = 0x03fe; // 0.0624 * 2^LUX_SCALE
109const K5T: usize = 0x0138; // 0.61 * 2^RATIO_SCALE
110const B5T: usize = 0x016f; // 0.0224 * 2^LUX_SCALE
111const M5T: usize = 0x01fc; // 0.0310 * 2^LUX_SCALE
112const K6T: usize = 0x019a; // 0.80 * 2^RATIO_SCALE
113const B6T: usize = 0x00d2; // 0.0128 * 2^LUX_SCALE
114const M6T: usize = 0x00fb; // 0.0153 * 2^LUX_SCALE
115const K7T: usize = 0x029a; // 1.3 * 2^RATIO_SCALE
116const B7T: usize = 0x0018; // 0.00146 * 2^LUX_SCALE
117const M7T: usize = 0x0012; // 0.00112 * 2^LUX_SCALE
118const K8T: usize = 0x029a; // 1.3 * 2^RATIO_SCALE
119const B8T: usize = 0x0000; // 0.000 * 2^LUX_SCALE
120const M8T: usize = 0x0000; // 0.000 * 2^LUX_SCALE
121
122// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
123// CS package coefficients
124// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
125// For 0 <= Ch1/Ch0 <= 0.52
126// Lux/Ch0 = 0.0315−0.0593*((Ch1/Ch0)^1.4)
127// piecewise approximation
128// For 0 <= Ch1/Ch0 <= 0.13
129// Lux/Ch0 = 0.0315−0.0262*(Ch1/Ch0)
130// For 0.13 <= Ch1/Ch0 <= 0.26
131// Lux/Ch0 = 0.0337−0.0430*(Ch1/Ch0)
132// For 0.26 <= Ch1/Ch0 <= 0.39
133// Lux/Ch0 = 0.0363−0.0529*(Ch1/Ch0)
134// For 0.39 <= Ch1/Ch0 <= 0.52
135// Lux/Ch0 = 0.0392−0.0605*(Ch1/Ch0)
136// For 0.52 < Ch1/Ch0 <= 0.65
137// Lux/Ch0 = 0.0229−0.0291*(Ch1/Ch0)
138// For 0.65 < Ch1/Ch0 <= 0.80
139// Lux/Ch0 = 0.00157−0.00180*(Ch1/Ch0)
140// For 0.80 < Ch1/Ch0 <= 1.30
141// Lux/Ch0 = 0.00338−0.00260*(Ch1/Ch0)
142// For Ch1/Ch0 > 1.30
143// Lux = 0
144// −−−−−−−−−−−−−−−−−−−−−−−−−−−−------
145// const K1C: usize = 0x0043; // 0.130 * 2^RATIO_SCALE
146// const B1C: usize = 0x0204; // 0.0315 * 2^LUX_SCALE
147// const M1C: usize = 0x01ad; // 0.0262 * 2^LUX_SCALE
148// const K2C: usize = 0x0085; // 0.260 * 2^RATIO_SCALE
149// const B2C: usize = 0x0228; // 0.0337 * 2^LUX_SCALE
150// const M2C: usize = 0x02c1; // 0.0430 * 2^LUX_SCALE
151// const K3C: usize = 0x00c8; // 0.390 * 2^RATIO_SCALE
152// const B3C: usize = 0x0253; // 0.0363 * 2^LUX_SCALE
153// const M3C: usize = 0x0363; // 0.0529 * 2^LUX_SCALE
154// const K4C: usize = 0x010a; // 0.520 * 2^RATIO_SCALE
155// const B4C: usize = 0x0282; // 0.0392 * 2^LUX_SCALE
156// const M4C: usize = 0x03df; // 0.0605 * 2^LUX_SCALE
157// const K5C: usize = 0x014d; // 0.65 * 2^RATIO_SCALE
158// const B5C: usize = 0x0177; // 0.0229 * 2^LUX_SCALE
159// const M5C: usize = 0x01dd; // 0.0291 * 2^LUX_SCALE
160// const K6C: usize = 0x019a; // 0.80 * 2^RATIO_SCALE
161// const B6C: usize = 0x0101; // 0.0157 * 2^LUX_SCALE
162// const M6C: usize = 0x0127; // 0.0180 * 2^LUX_SCALE
163// const K7C: usize = 0x029a; // 1.3 * 2^RATIO_SCALE
164// const B7C: usize = 0x0037; // 0.00338 * 2^LUX_SCALE
165// const M7C: usize = 0x002b; // 0.00260 * 2^LUX_SCALE
166// const K8C: usize = 0x029a; // 1.3 * 2^RATIO_SCALE
167// const B8C: usize = 0x0000; // 0.000 * 2^LUX_SCALE
168// const M8C: usize = 0x0000; // 0.000 * 2^LUX_SCALE
169
170#[allow(dead_code)]
171enum Registers {
172    Control = 0x00,
173    Timing = 0x01,
174    ThresholdLowLow = 0x02,
175    ThresholdLowHigh = 0x03,
176    ThresholdHighLow = 0x04,
177    ThresholdHighHigh = 0x05,
178    Interrupt = 0x06,
179    Id = 0x0a,
180    Data0Low = 0x0c,
181    Data0High = 0x0d,
182    Data1Low = 0x0e,
183    Data1High = 0x0f,
184}
185
186#[derive(Clone, Copy, PartialEq)]
187enum State {
188    Idle,
189
190    /// Read the Id register.
191    SelectId,
192    ReadingId,
193
194    /// Process of taking a light measurement.
195    TakeMeasurementTurnOn,
196    TakeMeasurementConfigMeasurement,
197    TakeMeasurementReset1,
198    TakeMeasurementReset2,
199
200    /// Read the ADC registers.
201    ReadMeasurement1,
202    ReadMeasurement2,
203    ReadMeasurement3,
204    /// Calculate light and call the callback with the value.
205    GotMeasurement,
206
207    /// Disable I2C and release buffer
208    Done,
209}
210
211#[derive(Default)]
212pub struct App {}
213
214pub struct TSL2561<'a, I: i2c::I2CDevice> {
215    i2c: &'a I,
216    interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
217    state: Cell<State>,
218    buffer: TakeCell<'static, [u8]>,
219    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
220    owning_process: OptionalCell<ProcessId>,
221}
222
223impl<'a, I: i2c::I2CDevice> TSL2561<'a, I> {
224    pub fn new(
225        i2c: &'a I,
226        interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
227        buffer: &'static mut [u8],
228        apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
229    ) -> Self {
230        // setup and return struct
231        Self {
232            i2c,
233            interrupt_pin,
234            state: Cell::new(State::Idle),
235            buffer: TakeCell::new(buffer),
236            apps,
237            owning_process: OptionalCell::empty(),
238        }
239    }
240
241    pub fn read_id(&self) {
242        self.buffer.take().map(|buffer| {
243            // turn on i2c to send commands
244            self.i2c.enable();
245
246            buffer[0] = Registers::Id as u8 | COMMAND_REG;
247            // buffer[0] = Registers::Id as u8;
248            // TODO verify errors
249            let _ = self.i2c.write(buffer, 1);
250            self.state.set(State::SelectId);
251        });
252    }
253
254    pub fn take_measurement(&self) {
255        // Need pull up on interrupt pin
256        self.interrupt_pin.make_input();
257        self.interrupt_pin
258            .enable_interrupts(gpio::InterruptEdge::FallingEdge);
259
260        self.buffer.take().map(|buf| {
261            // Turn on i2c to send commands
262            self.i2c.enable();
263
264            buf[0] = Registers::Control as u8 | COMMAND_REG;
265            buf[1] = POWER_ON;
266            // TODO verify errors
267            let _ = self.i2c.write(buf, 2);
268            self.state.set(State::TakeMeasurementTurnOn);
269        });
270    }
271
272    fn calculate_lux(&self, chan0: u16, chan1: u16) -> usize {
273        // First, scale the channel values depending on the gain and integration
274        // time. 16X, 402mS is nominal. Scale if integration time is NOT 402 msec.
275        // let mut ch_scale = CHSCALE_TINT0 as usize; // 13.7ms
276        let mut ch_scale = CHSCALE_TINT1 as usize; // 101ms
277                                                   // let mut ch_scale: usize = 1 << CH_SCALE; // Default
278
279        // Scale if gain is NOT 16X
280        ch_scale <<= 4; // scale 1X to 16X
281
282        // scale the channel values
283        let channel0 = (chan0 as usize * ch_scale) >> CH_SCALE;
284        let channel1 = (chan1 as usize * ch_scale) >> CH_SCALE;
285
286        // Find the ratio of the channel values (Channel1/Channel0).
287        // Protect against divide by zero.
288        let mut ratio1 = 0;
289        if channel0 != 0 {
290            ratio1 = (channel1 << (RATIO_SCALE + 1)) / channel0;
291        }
292
293        // round the ratio value
294        let ratio = (ratio1 + 1) >> 1;
295
296        // is ratio <= eachBreak ?
297        let mut b = 0;
298        let mut m = 0;
299        // T, FN, and CL package
300        if ratio <= K1T {
301            b = B1T;
302            m = M1T;
303        } else if ratio <= K2T {
304            b = B2T;
305            m = M2T;
306        } else if ratio <= K3T {
307            b = B3T;
308            m = M3T;
309        } else if ratio <= K4T {
310            b = B4T;
311            m = M4T;
312        } else if ratio <= K5T {
313            b = B5T;
314            m = M5T;
315        } else if ratio <= K6T {
316            b = B6T;
317            m = M6T;
318        } else if ratio <= K7T {
319            b = B7T;
320            m = M7T;
321        } else if ratio > K8T {
322            b = B8T;
323            m = M8T;
324        }
325        // CS package
326        // if ratio <= K1C {
327        //     b=B1C; m=M1C;
328        // } else if ratio <= K2C {
329        //     b=B2C; m=M2C;
330        // } else if ratio <= K3C {
331        //     b=B3C; m=M3C;
332        // } else if ratio <= K4C {
333        //     b=B4C; m=M4C;
334        // } else if ratio <= K5C {
335        //     b=B5C; m=M5C;
336        // } else if ratio <= K6C {
337        //     b=B6C; m=M6C;
338        // } else if ratio <= K7C {
339        //     b=B7C; m=M7C;
340        // } else if ratio > K8C {
341        //     b=B8C; m=M8C;
342        // }
343
344        // Calculate actual lux value
345        let mut val = ((channel0 * b) as isize) - ((channel1 * m) as isize);
346
347        // Do not allow negative lux value
348        if val < 0 {
349            val = 0;
350        }
351
352        // round lsb (2^(LUX_SCALE−1))
353        // val += (1 << (LUX_SCALE−1));
354        val += 1 << (LUX_SCALE - 1);
355
356        // strip off fractional portion and return lux
357        let lux = val >> LUX_SCALE;
358
359        lux as usize
360    }
361}
362
363impl<I: i2c::I2CDevice> i2c::I2CClient for TSL2561<'_, I> {
364    fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), i2c::Error>) {
365        match self.state.get() {
366            State::SelectId => {
367                // TODO verify errors
368                let _ = self.i2c.read(buffer, 1);
369                self.state.set(State::ReadingId);
370            }
371            State::ReadingId => {
372                self.buffer.replace(buffer);
373                self.i2c.disable();
374                self.state.set(State::Idle);
375            }
376            State::TakeMeasurementTurnOn => {
377                buffer[0] = Registers::Timing as u8 | COMMAND_REG;
378                buffer[1] = INTEGRATE_TIME_101_MS | LOW_GAIN_MODE;
379                // TODO verify errors
380                let _ = self.i2c.write(buffer, 2);
381                self.state.set(State::TakeMeasurementConfigMeasurement);
382            }
383            State::TakeMeasurementConfigMeasurement => {
384                buffer[0] = Registers::Interrupt as u8 | COMMAND_REG;
385                buffer[1] = INTERRUPT_CONTROL_LEVEL | INTERRUPT_ON_ADC_DONE;
386                // TODO verify errors
387                let _ = self.i2c.write(buffer, 2);
388                self.state.set(State::TakeMeasurementReset1);
389            }
390            State::TakeMeasurementReset1 => {
391                buffer[0] = Registers::Control as u8 | COMMAND_REG;
392                buffer[1] = POWER_OFF;
393                // TODO verify errors
394                let _ = self.i2c.write(buffer, 2);
395                self.state.set(State::TakeMeasurementReset2);
396            }
397            State::TakeMeasurementReset2 => {
398                buffer[0] = Registers::Control as u8 | COMMAND_REG;
399                buffer[1] = POWER_ON;
400                // TODO verify errors
401                let _ = self.i2c.write(buffer, 2);
402                self.state.set(State::Done);
403            }
404            State::ReadMeasurement1 => {
405                // TODO verify errors
406                let _ = self.i2c.read(buffer, 2);
407                self.state.set(State::ReadMeasurement2);
408            }
409            State::ReadMeasurement2 => {
410                // Store the previous readings in the buffer where they
411                // won't get overwritten.
412                buffer[2] = buffer[0];
413                buffer[3] = buffer[1];
414                buffer[0] = Registers::Data0Low as u8 | COMMAND_REG | WORD_PROTOCOL;
415                // TODO verify errors
416                let _ = self.i2c.write(buffer, 2);
417                self.state.set(State::ReadMeasurement3);
418            }
419            State::ReadMeasurement3 => {
420                // TODO verify errors
421                let _ = self.i2c.read(buffer, 2);
422                self.state.set(State::GotMeasurement);
423            }
424            State::GotMeasurement => {
425                let chan0 = ((buffer[1] as u16) << 8) | (buffer[0] as u16);
426                let chan1 = ((buffer[3] as u16) << 8) | (buffer[2] as u16);
427
428                let lux = self.calculate_lux(chan0, chan1);
429
430                self.owning_process.map(|pid| {
431                    let _ = self.apps.enter(pid, |_, upcalls| {
432                        upcalls.schedule_upcall(0, (0, lux, 0)).ok();
433                    });
434                });
435
436                buffer[0] = Registers::Control as u8 | COMMAND_REG;
437                buffer[1] = POWER_OFF;
438                // TODO verify errors
439                let _ = self.i2c.write(buffer, 2);
440                self.interrupt_pin.disable_interrupts();
441                self.state.set(State::Done);
442            }
443            State::Done => {
444                self.buffer.replace(buffer);
445                self.i2c.disable();
446                self.state.set(State::Idle);
447            }
448            _ => {}
449        }
450    }
451}
452
453impl<I: i2c::I2CDevice> gpio::Client for TSL2561<'_, I> {
454    fn fired(&self) {
455        self.buffer.take().map(|buffer| {
456            // turn on i2c to send commands
457            self.i2c.enable();
458
459            // Read the first of the ADC registers.
460            buffer[0] = Registers::Data1Low as u8 | COMMAND_REG | WORD_PROTOCOL;
461            // TODO verify errors
462            let _ = self.i2c.write(buffer, 1);
463            self.state.set(State::ReadMeasurement1);
464        });
465    }
466}
467
468impl<I: i2c::I2CDevice> SyscallDriver for TSL2561<'_, I> {
469    fn command(
470        &self,
471        command_num: usize,
472        _: usize,
473        _: usize,
474        process_id: ProcessId,
475    ) -> CommandReturn {
476        if command_num == 0 {
477            // Handle this first as it should be returned
478            // unconditionally
479            return CommandReturn::success();
480        }
481        // Check if this non-virtualized driver is already in use by
482        // some (alive) process
483        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
484            self.apps
485                .enter(current_process, |_, _| current_process == process_id)
486                .unwrap_or(true)
487        });
488        if match_or_empty_or_nonexistant {
489            self.owning_process.set(process_id);
490        } else {
491            return CommandReturn::failure(ErrorCode::NOMEM);
492        }
493        match command_num {
494            // Take a measurement
495            1 => {
496                self.take_measurement();
497                CommandReturn::success()
498            }
499            // default
500            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
501        }
502    }
503
504    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
505        self.apps.enter(processid, |_, _| {})
506    }
507}