capsules_extra/
bmm150.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 2023.
4
5//! SyscallDriver for the Bosch BMM150 geomagnetic sensor.
6//!
7//! <https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmm150-ds001.pdf>
8//!
9//! > The BMM150 is a standalone geomagnetic sensor for consumer
10//! > market applications. It allows measurements of the magnetic
11//! > field in three perpendicular axes. Based on Bosch’s proprietary
12//! > FlipCore technology, performance and features of BMM150 are
13//! > carefully tuned and perfectly match the demanding requirements of
14//! > all 3-axis mobile applications such as electronic compass, navigation
15//! > or augmented reality.
16//!
17//! //! Driver Semantics
18//! ----------------
19//!
20//! This driver exposes the BMM150's functionality via the [NineDof] and
21//! [NineDofClient] HIL interfaces. If gyroscope or accelerometer data is
22//! requested, the driver will return a ErrorCode.
23//!
24//! //! Usage
25//! -----
26//!
27//! ```rust,ignore
28//! # use kernel::static_init;
29//!
30//! let bmm150_i2c = static_init!(
31//!     capsules::virtual_i2c::I2CDevice,
32//!     capsules::virtual_i2c::I2CDevice::new(i2c_bus, 0x10));
33//! let bmm150 = static_init!(
34//!     capsules::bmm150::BMM150<'static>,
35//!     capsules::bmm150::BMM150::new(bmm150_i2c,
36//!         &mut capsules::BMM150::BUFFER));
37//! bmm150_i2c.set_client(bmm150);
38//! ```
39
40use core::cell::Cell;
41use kernel::hil::i2c::{self, I2CClient, I2CDevice};
42use kernel::hil::sensors::{NineDof, NineDofClient};
43use kernel::utilities::cells::{OptionalCell, TakeCell};
44use kernel::ErrorCode;
45
46#[allow(dead_code)]
47enum Registers {
48    ChipID = 0x40,
49    DATAxLsb = 0x42,
50    DATAxMsb = 0x43,
51    DATAyLsb = 0x44,
52    DATAyMsb = 0x45,
53    DATAzLsb = 0x46,
54    DATAzMsb = 0x47,
55    RHALLlsb = 0x48,
56    RHALLmsb = 0x49,
57    INTST = 0x4A,
58    CTRL1 = 0x4B,
59    CTRL2 = 0x4C,
60    CTRL3 = 0x4D,
61    CTRL4 = 0x4E,
62    LoThres = 0x4F,
63    HiThres = 0x50,
64    REPXY = 0x51,
65    REPZ = 0x52,
66}
67
68pub struct BMM150<'a, I: I2CDevice> {
69    buffer: TakeCell<'static, [u8]>,
70    i2c: &'a I,
71    ninedof_client: OptionalCell<&'a dyn NineDofClient>,
72    state: Cell<State>,
73}
74
75impl<'a, I: I2CDevice> BMM150<'a, I> {
76    pub fn new(buffer: &'static mut [u8], i2c: &'a I) -> BMM150<'a, I> {
77        BMM150 {
78            buffer: TakeCell::new(buffer),
79            i2c,
80            ninedof_client: OptionalCell::empty(),
81            state: Cell::new(State::Suspend),
82        }
83    }
84
85    pub fn start_measurement(&self) -> Result<(), ErrorCode> {
86        self.buffer
87            .take()
88            .map(|buffer| {
89                self.i2c.enable();
90                match self.state.get() {
91                    State::Suspend => {
92                        buffer[0] = Registers::CTRL1 as u8;
93                        buffer[1] = 0x1_u8;
94
95                        if let Err((_error, buffer)) = self.i2c.write(buffer, 2) {
96                            self.buffer.replace(buffer);
97                            self.i2c.disable();
98                        } else {
99                            self.state.set(State::PowerOn);
100                        }
101                    }
102                    State::Sleep => {
103                        buffer[0] = Registers::CTRL2 as u8;
104                        buffer[1] = 0x3A_u8;
105
106                        if let Err((_error, buffer)) = self.i2c.write(buffer, 2) {
107                            self.buffer.replace(buffer);
108                            self.i2c.disable();
109                        } else {
110                            self.state.set(State::InitializeReading);
111                        }
112                    }
113                    _ => {}
114                }
115            })
116            .ok_or(ErrorCode::FAIL)
117    }
118}
119
120impl<'a, I: i2c::I2CDevice> NineDof<'a> for BMM150<'a, I> {
121    fn set_client(&self, client: &'a dyn NineDofClient) {
122        self.ninedof_client.set(client);
123    }
124
125    fn read_magnetometer(&self) -> Result<(), ErrorCode> {
126        self.start_measurement()
127    }
128}
129
130#[derive(Clone, Copy, Debug)]
131enum State {
132    Suspend,
133    Sleep,
134    PowerOn,
135    InitializeReading,
136    ReadMeasurement,
137    Read,
138}
139
140impl<I: I2CDevice> I2CClient for BMM150<'_, I> {
141    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
142        if let Err(i2c_err) = status {
143            self.state.set(State::Sleep);
144            self.buffer.replace(buffer);
145            self.ninedof_client
146                .map(|client| client.callback(i2c_err as usize, 0, 0));
147            return;
148        }
149
150        match self.state.get() {
151            State::PowerOn => {
152                buffer[0] = Registers::CTRL2 as u8;
153                buffer[1] = 0x3A_u8;
154
155                if let Err((error, buffer)) = self.i2c.write(buffer, 2) {
156                    self.buffer.replace(buffer);
157                    self.i2c.disable();
158                    self.ninedof_client
159                        .map(|client| client.callback(error as usize, 0, 0));
160                } else {
161                    self.state.set(State::InitializeReading);
162                }
163            }
164            State::InitializeReading => {
165                buffer[0] = Registers::DATAxLsb as u8;
166
167                if let Err((i2c_err, buffer)) = self.i2c.write(buffer, 1) {
168                    self.state.set(State::Sleep);
169                    self.buffer.replace(buffer);
170                    self.ninedof_client
171                        .map(|client| client.callback(i2c_err as usize, 0, 0));
172                } else {
173                    self.state.set(State::ReadMeasurement);
174                }
175            }
176            State::ReadMeasurement => {
177                if let Err((i2c_err, buffer)) = self.i2c.read(buffer, 8) {
178                    self.state.set(State::Sleep);
179                    self.buffer.replace(buffer);
180                    self.ninedof_client
181                        .map(|client| client.callback(i2c_err as usize, 0, 0));
182                } else {
183                    self.state.set(State::Read);
184                }
185            }
186            State::Read => {
187                let x_axis = ((buffer[1] as i16) << 5) | ((buffer[0] as i16) >> 3);
188                let y_axis = ((buffer[3] as i16) << 5) | ((buffer[2] as i16) >> 3);
189                let z_axis = ((buffer[5] as i16) << 7) | ((buffer[4] as i16) >> 1);
190
191                self.state.set(State::Sleep);
192                self.buffer.replace(buffer);
193                self.i2c.disable();
194                self.ninedof_client.map(|client| {
195                    client.callback(x_axis as usize, y_axis as usize, z_axis as usize)
196                });
197            }
198            State::Sleep => {}   // should never happen
199            State::Suspend => {} // should never happen
200        }
201    }
202}