capsules_extra/
fxos8700cq.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 FXOS8700CQ accelerometer.
6//!
7//! <https://www.nxp.com/docs/en/data-sheet/FXOS8700CQ.pdf>
8//!
9//! The driver provides x, y, and z acceleration data to a callback function.
10//! It implements the `hil::sensors::NineDof` trait.
11//!
12//! Usage
13//! -----
14//!
15//! ```rust,ignore
16//! # use kernel::static_init;
17//!
18//! let fxos8700_i2c = static_init!(I2CDevice, I2CDevice::new(i2c_bus, 0x1e));
19//! let fxos8700 = static_init!(
20//!     capsules::fxos8700cq::Fxos8700cq<'static>,
21//!     capsules::fxos8700cq::Fxos8700cq::new(fxos8700_i2c,
22//!                                           &sam4l::gpio::PA[9], // Interrupt pin
23//!                                           &mut capsules::fxos8700cq::BUF));
24//! fxos8700_i2c.set_client(fxos8700);
25//! sam4l::gpio::PA[9].set_client(fxos8700);
26//! ```
27
28use core::cell::Cell;
29use kernel::hil;
30use kernel::hil::gpio;
31use kernel::hil::i2c::{Error, I2CClient, I2CDevice};
32use kernel::utilities::cells::{OptionalCell, TakeCell};
33use kernel::ErrorCode;
34
35/// Recommended buffer length for this driver.
36pub const BUF_LEN: usize = 6;
37
38#[allow(dead_code)]
39enum Registers {
40    Status = 0x00,
41    OutXMsb = 0x01,
42    OutXLsb = 0x02,
43    OutYMsb = 0x03,
44    OutYLsb = 0x04,
45    OutZMsb = 0x05,
46    OutZLsb = 0x06,
47    FSetup = 0x09,
48    TrigCfg = 0x0a,
49    Sysmod = 0x0b,
50    IntSource = 0x0c,
51    WhoAmI = 0x0d,
52    XyzDataCfg = 0x0e,
53    HpFilterCutoff = 0x0f,
54    PlStatus = 0x10,
55    PlCfg = 0x11,
56    PlCount = 0x12,
57    PlBfZcomp = 0x13,
58    PlThsReg = 0x14,
59    AFfmtCfg = 0x15,
60    AFfmtSrc = 0x16,
61    AFfmtThs = 0x17,
62    AFfmtCount = 0x18,
63    TransientCfg = 0x1d,
64    TransientSrc = 0x1e,
65    TransientThs = 0x1f,
66    TransientCount = 0x20,
67    PulseCfg = 0x21,
68    PulseSrc = 0x22,
69    PulseThsx = 0x23,
70    PulseThsy = 0x24,
71    PulseThsz = 0x25,
72    PulseTmlt = 0x26,
73    PulseLtcy = 0x27,
74    PulseWind = 0x28,
75    AslpCount = 0x29,
76    CtrlReg1 = 0x2a,
77    CtrlReg2 = 0x2b,
78    CtrlReg3 = 0x2c,
79    CtrlReg4 = 0x2d,
80    CtrlReg5 = 0x2e,
81    OffX = 0x2f,
82    OffY = 0x30,
83    OffZ = 0x31,
84    MDrStatus = 0x32,
85    MOutXMsb = 0x33,
86    MOutXLsb = 0x34,
87    MOutYMsb = 0x35,
88    MOutYLsb = 0x36,
89    MOutZMsb = 0x37,
90    MOutZLsb = 0x38,
91    CmpXMsb = 0x39,
92    CmpXLsb = 0x3a,
93    CmpYMsb = 0x3b,
94    CmpYLsb = 0x3c,
95    CmpZMsb = 0x3d,
96    CmpZLsb = 0x3e,
97    MOffXMsb = 0x3f,
98    MOffXLsb = 0x40,
99    MOffYMsb = 0x41,
100    MOffYLsb = 0x42,
101    MOffZMsb = 0x43,
102    MOffZLsb = 0x44,
103    MaxXMsb = 0x45,
104    MaxXLsb = 0x46,
105    MaxYMsb = 0x47,
106    MaxYLsb = 0x48,
107    MaxZMsb = 0x49,
108    MaxZLsb = 0x4a,
109    MinXMsb = 0x4b,
110    MinXLsb = 0x4c,
111    MinYMsb = 0x4d,
112    MinYLsb = 0x4e,
113    MinZMsb = 0x4f,
114    MinZLsb = 0x50,
115    Temp = 0x51,
116    MThsCfg = 0x52,
117    MThsSrc = 0x53,
118    MThsXMsb = 0x54,
119    MThsXLsb = 0x55,
120    MThsYMsb = 0x56,
121    MThsYLsb = 0x57,
122    MThsZMsb = 0x58,
123    MThsZLsb = 0x59,
124    MThsCount = 0x5a,
125    MCtrlReg1 = 0x5b,
126    MCtrlReg2 = 0x5c,
127    MCtrlReg3 = 0x5d,
128    MIntSrc = 0x5e,
129    AVecmCfg = 0x5f,
130    AVecmThsMsb = 0x60,
131    AVecmThsLsb = 0x61,
132    AVecmCnt = 0x62,
133    AVecmInitxMsb = 0x63,
134    AVecmInitxLsb = 0x64,
135    AVecmInityMsb = 0x65,
136    AVecmInityLsb = 0x66,
137    AVecmInitzMsb = 0x67,
138    AVecmInitzLsb = 0x68,
139    MVecmCfg = 0x69,
140    MVecmThsMsb = 0x6a,
141    MVecmThsLsb = 0x6b,
142    MVecmCnt = 0x6c,
143    MVecmInitxMsb = 0x6d,
144    MVecmInitxLsb = 0x6e,
145    MVecmInityMsb = 0x6f,
146    MVecmInityLsb = 0x70,
147    MVecmInitzMsb = 0x71,
148    MVecmInitzLsb = 0x72,
149    AFfmtThsXMsb = 0x73,
150    AFfmtThsXLsb = 0x74,
151    AFfmtThsYMsb = 0x75,
152    AFfmtThsYLsb = 0x76,
153    AFfmtThsZMsb = 0x77,
154    AFfmtThsZLsb = 0x78,
155}
156
157#[derive(Clone, Copy, PartialEq)]
158enum State {
159    /// Sensor is in standby mode
160    Disabled,
161
162    /// Activate the accelerometer to take a reading
163    ReadAccelSetup,
164
165    /// Wait for the acceleration sample to be ready
166    ReadAccelWait,
167
168    /// Activate sensor to take readings
169    ReadAccelWaiting,
170
171    /// Reading accelerometer data
172    ReadAccelReading,
173
174    /// Deactivate sensor
175    ReadAccelDeactivating(i16, i16, i16),
176
177    /// Configuring reading the magnetometer
178    ReadMagStart,
179
180    /// Have the magnetometer values and sending them to application
181    ReadMagValues,
182}
183
184pub struct Fxos8700cq<'a> {
185    i2c: &'a dyn I2CDevice,
186    interrupt_pin1: &'a dyn gpio::InterruptPin<'a>,
187    state: Cell<State>,
188    buffer: TakeCell<'static, [u8]>,
189    callback: OptionalCell<&'a dyn hil::sensors::NineDofClient>,
190}
191
192impl<'a> Fxos8700cq<'a> {
193    pub fn new(
194        i2c: &'a dyn I2CDevice,
195        interrupt_pin1: &'a dyn gpio::InterruptPin<'a>,
196        buffer: &'static mut [u8],
197    ) -> Fxos8700cq<'a> {
198        Fxos8700cq {
199            i2c,
200            interrupt_pin1,
201            state: Cell::new(State::Disabled),
202            buffer: TakeCell::new(buffer),
203            callback: OptionalCell::empty(),
204        }
205    }
206
207    fn start_read_accel(&self) -> Result<(), ErrorCode> {
208        if self.state.get() == State::Disabled {
209            self.interrupt_pin1.make_input(); // Need an interrupt pin
210            self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buf| {
211                self.i2c.enable();
212                // Configure the data ready interrupt.
213                buf[0] = Registers::CtrlReg4 as u8;
214                buf[1] = 1; // CtrlReg4 data ready interrupt
215                buf[2] = 1; // CtrlReg5 drdy on pin 1
216
217                if let Err((error, buf)) = self.i2c.write(buf, 3) {
218                    self.buffer.replace(buf);
219                    self.i2c.disable();
220                    Err(error.into())
221                } else {
222                    self.state.set(State::ReadAccelSetup);
223                    Ok(())
224                }
225            })
226        } else {
227            Err(ErrorCode::BUSY)
228        }
229    }
230
231    fn start_read_magnetometer(&self) -> Result<(), ErrorCode> {
232        if self.state.get() == State::Disabled {
233            self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buf| {
234                self.i2c.enable();
235                // Configure the magnetometer.
236                buf[0] = Registers::MCtrlReg1 as u8;
237                // Enable both accelerometer and magnetometer, and set one-shot read.
238                buf[1] = 0b00100011;
239
240                if let Err((error, buf)) = self.i2c.write(buf, 2) {
241                    self.buffer.replace(buf);
242                    self.i2c.disable();
243                    Err(error.into())
244                } else {
245                    self.state.set(State::ReadMagStart);
246                    Ok(())
247                }
248            })
249        } else {
250            Err(ErrorCode::BUSY)
251        }
252    }
253}
254
255impl gpio::Client for Fxos8700cq<'_> {
256    fn fired(&self) {
257        self.buffer.take().map(|buffer| {
258            self.interrupt_pin1.disable_interrupts();
259
260            // When we get this interrupt we can read the sample.
261            self.i2c.enable();
262            buffer[0] = Registers::OutXMsb as u8;
263
264            // Upon success, this will trigger an upcall.
265            // As this particular upcall does not have any field
266            // for the status, we can ignore the error, as this
267            // yields to not scheduling the upcall.
268            if let Err((_error, buffer)) = self.i2c.write_read(buffer, 1, 6) {
269                self.buffer.replace(buffer);
270                self.i2c.disable();
271            } else {
272                self.state.set(State::ReadAccelReading);
273            }
274        });
275    }
276}
277
278impl I2CClient for Fxos8700cq<'_> {
279    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>) {
280        // If there's an I2C error, just reset and issue a callback
281        // with all 0s. Otherwise, if there's no sensor attached,
282        // it's possible to have nondeterministic behavior, where
283        // sometimes you get callbacks and sometimes you don't, based
284        // on whether a floating interrupt line triggers. -pal 3/19/21
285        if status != Ok(()) {
286            self.state.set(State::Disabled);
287            self.buffer.replace(buffer);
288            self.callback.map(|cb| {
289                cb.callback(0, 0, 0);
290            });
291            return;
292        }
293        match self.state.get() {
294            State::ReadAccelSetup => {
295                // Setup the interrupt so we know when the sample is ready
296                self.interrupt_pin1
297                    .enable_interrupts(gpio::InterruptEdge::FallingEdge);
298
299                // Enable the accelerometer.
300                buffer[0] = Registers::CtrlReg1 as u8;
301                buffer[1] = 1;
302
303                // The callback function has no error field,
304                // we can safely ignore the error value.
305                if let Err((_error, buffer)) = self.i2c.write(buffer, 2) {
306                    self.state.set(State::Disabled);
307                    self.buffer.replace(buffer);
308                    self.callback.map(|cb| {
309                        cb.callback(0, 0, 0);
310                    });
311                } else {
312                    self.state.set(State::ReadAccelWait);
313                }
314            }
315            State::ReadAccelWait => {
316                if !self.interrupt_pin1.read() {
317                    // Sample is already ready.
318                    self.interrupt_pin1.disable_interrupts();
319                    buffer[0] = Registers::OutXMsb as u8;
320
321                    // The callback function has no error field,
322                    // we can safely ignore the error value.
323                    if let Err((_error, buffer)) = self.i2c.write_read(buffer, 1, 6) {
324                        self.state.set(State::Disabled);
325                        self.buffer.replace(buffer);
326                        self.callback.map(|cb| {
327                            cb.callback(0, 0, 0);
328                        });
329                    } else {
330                        self.state.set(State::ReadAccelReading);
331                    }
332                } else {
333                    // Wait for the interrupt to trigger
334                    self.buffer.replace(buffer);
335                    self.i2c.disable();
336                    self.state.set(State::ReadAccelWaiting);
337                }
338            }
339            State::ReadAccelReading => {
340                let x = (((buffer[0] as i16) << 8) | buffer[1] as i16) >> 2;
341                let y = (((buffer[2] as i16) << 8) | buffer[3] as i16) >> 2;
342                let z = (((buffer[4] as i16) << 8) | buffer[5] as i16) >> 2;
343
344                let x = ((x as isize) * 244) / 1000;
345                let y = ((y as isize) * 244) / 1000;
346                let z = ((z as isize) * 244) / 1000;
347
348                // Now put the chip into standby mode.
349                buffer[0] = Registers::CtrlReg1 as u8;
350                buffer[1] = 0; // Set the active bit to 0.
351
352                // The callback function has no error field,
353                // we can safely ignore the error value.
354                if let Err((_error, buffer)) = self.i2c.write(buffer, 2) {
355                    self.state.set(State::Disabled);
356                    self.buffer.replace(buffer);
357                    self.callback.map(|cb| {
358                        cb.callback(0, 0, 0);
359                    });
360                } else {
361                    self.state
362                        .set(State::ReadAccelDeactivating(x as i16, y as i16, z as i16));
363                }
364            }
365            State::ReadAccelDeactivating(x, y, z) => {
366                self.i2c.disable();
367                self.state.set(State::Disabled);
368                self.buffer.replace(buffer);
369                self.callback.map(|cb| {
370                    cb.callback(x as usize, y as usize, z as usize);
371                });
372            }
373            State::ReadMagStart => {
374                // One shot measurement taken, now read result.
375                buffer[0] = Registers::MOutXMsb as u8;
376                self.state.set(State::ReadMagValues);
377
378                // The callback function has no error field,
379                // we can safely ignore the error value.
380                if let Err((_error, buffer)) = self.i2c.write_read(buffer, 1, 6) {
381                    self.state.set(State::Disabled);
382                    self.buffer.replace(buffer);
383                    self.callback.map(|cb| {
384                        cb.callback(0, 0, 0);
385                    });
386                }
387            }
388            State::ReadMagValues => {
389                let x = (((buffer[0] as u16) << 8) | buffer[1] as u16) as i16;
390                let y = (((buffer[2] as u16) << 8) | buffer[3] as u16) as i16;
391                let z = (((buffer[4] as u16) << 8) | buffer[5] as u16) as i16;
392
393                // Can immediately return values as the one-shot mode automatically
394                // disables the fxo after taking the measurement.
395                self.i2c.disable();
396                self.state.set(State::Disabled);
397                self.buffer.replace(buffer);
398
399                self.callback
400                    .map(|cb| cb.callback(x as usize, y as usize, z as usize));
401            }
402            _ => {}
403        }
404    }
405}
406
407impl<'a> hil::sensors::NineDof<'a> for Fxos8700cq<'a> {
408    fn set_client(&self, client: &'a dyn hil::sensors::NineDofClient) {
409        self.callback.set(client);
410    }
411
412    fn read_accelerometer(&self) -> Result<(), ErrorCode> {
413        self.start_read_accel()
414    }
415
416    fn read_magnetometer(&self) -> Result<(), ErrorCode> {
417        self.start_read_magnetometer()
418    }
419}