Skip to main content

stm_32/peripherals/
ist8308.rs

1// ******************************************************************************
2// * File     : platforms/stm_32/src/peripherals/ist8308.rs
3// * Date     : June 28, 2026
4// ******************************************************************************
5// *
6// * Copyright (c) 2023, AeroVironment, Inc.
7// * All rights reserved.
8// *
9// * Redistribution and use in source and binary forms, with or without
10// * modification, are permitted provided that the following conditions are met:
11// *
12// * 1.Redistributions of source code must retain the above copyright notice, this
13// * list of conditions and the following disclaimer.
14// *
15// * 2.Redistributions in binary form must reproduce the above copyright notice,
16// * this list of conditions and the following disclaimer in the documentation
17// * and/or other materials provided with the distribution.
18// *
19// * 3.Neither the name of the copyright holder nor the names of its
20// * contributors may be used to endorse or promote products derived from
21// * this software without specific prior written permission.
22// *
23// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24// * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25// * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26// * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27// * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28// * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29// * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30// * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31// * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33// *
34// ******************************************************************************
35
36pub use embassy_stm32::mode::Async;
37pub use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
38pub use embassy_sync::signal::Signal;
39use veloxity_core::{errors, packets};
40
41// I2C Specific
42use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
43use embassy_stm32::i2c::I2c;
44use embedded_hal_async::i2c::I2c as _;
45
46// Polled Sensors
47use crate::synch_at;
48use embassy_time::Duration;
49use embassy_time::Timer;
50
51// Other
52
53pub static MAG_SIGNAL: Signal<
54    CriticalSectionRawMutex,
55    Result<packets::MagPacket, errors::SensorError>,
56> = Signal::<CriticalSectionRawMutex, Result<packets::MagPacket, errors::SensorError>>::new();
57
58pub struct Ist8308Sensor {
59    pub dev: I2cDevice<
60        'static,
61        CriticalSectionRawMutex,
62        I2c<'static, Async, embassy_stm32::i2c::mode::Master>,
63    >,
64}
65
66impl Ist8308Sensor {
67    async fn write_read(
68        &mut self,
69        address: u8,
70        register: &[u8],
71        data: &mut [u8],
72    ) -> Result<(), ()> {
73        match self.dev.write(address, register).await {
74            Err(_e) => return Err(()),
75            Ok(_) => {}
76        }
77
78        Timer::after(Duration::from_micros(0)).await;
79
80        // Read register
81        match self.dev.read(address, data).await {
82            Err(_e) => return Err(()),
83            Ok(_) => {}
84        }
85
86        Ok(())
87    }
88
89    pub async fn run(&mut self) {
90        const ADDRESS: u8 = 0x0C;
91
92        // Check device ID
93
94        const WAI_REG: u8 = 0x00;
95        let mut device_id = [0u8; 1];
96        if self
97            .write_read(ADDRESS, &[WAI_REG], &mut device_id)
98            .await
99            .is_err()
100        {
101            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
102                "IST8308 Mag failed: reading WAI_REG",
103            )));
104            return;
105        }
106        const DEVICE_ID: u8 = 0x08;
107        if device_id[0] != DEVICE_ID {
108            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
109                "IST8308 Mag failed: bad device ID",
110            )));
111            return;
112        }
113
114        // Reset
115
116        const CNTL3_REG: u8 = 0x32;
117        const CNTL3_VAL_SRST: u8 = 1;
118        if self
119            .dev
120            .write(ADDRESS, &[CNTL3_REG, CNTL3_VAL_SRST])
121            .await
122            .is_err()
123        {
124            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
125                "IST8308 Mag failed: writing CNTL3_REG",
126            )));
127            return;
128        }
129        Timer::after(Duration::from_millis(20)).await; // allow 20 ms to reset
130
131        //  Check status
132        let mut cntrl3 = [0u8; 1];
133        if self
134            .write_read(ADDRESS, &[CNTL3_REG], &mut cntrl3)
135            .await
136            .is_err()
137        {
138            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
139                "IST8308 Mag failed: reading CNTL3_REG",
140            )));
141            return;
142        }
143        if (cntrl3[0] & 0x01) != 0 {
144            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
145                "IST8308 Mag failed: bad status CNTL3_REG",
146            )));
147            return;
148        }
149
150        // Configure
151
152        // Enable DRDY (None Connected)
153        const CNTL3_VAL_DRDY_EN: u8 = 1 << 3;
154
155        if self
156            .dev
157            .write(ADDRESS, &[CNTL3_REG, CNTL3_VAL_DRDY_EN])
158            .await
159            .is_err()
160        {
161            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
162                "IST8308 Mag failed: writing CNTL4_REG",
163            )));
164            return;
165        }
166
167        const CNTL4_REG: u8 = 0x34;
168        const CNTL4_VAL_DYNAMIC_RANGE_500: u8 = 0;
169
170        if self
171            .dev
172            .write(ADDRESS, &[CNTL4_REG, CNTL4_VAL_DYNAMIC_RANGE_500])
173            .await
174            .is_err()
175        {
176            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
177                "IST8308 Mag failed: writing CNTL4_REG",
178            )));
179            return;
180        }
181
182        const OSRCNTL_REG: u8 = 0x41;
183        const OSRCNTL_VAL_Y_16: u8 = 4 << 3;
184        const OSRCNTL_VAL_XZ_16: u8 = 4;
185
186        if self
187            .dev
188            .write(
189                ADDRESS,
190                &[OSRCNTL_REG, OSRCNTL_VAL_Y_16 | OSRCNTL_VAL_XZ_16],
191            )
192            .await
193            .is_err()
194        {
195            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
196                "IST8308 Mag failed: writing OSRCNTL_REG",
197            )));
198            return;
199        }
200
201        // Set ODR
202        const CNTL2_REG: u8 = 0x31;
203        const CNTL2_VAL_CONT_ODR100_MODE: u8 = 0x08; //Continuous (100Hz) mode
204        if self
205            .dev
206            .write(ADDRESS, &[CNTL2_REG, CNTL2_VAL_CONT_ODR100_MODE])
207            .await
208            .is_err()
209        {
210            MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
211                "IST8308 Mag failed: writing CNTL2_REG",
212            )));
213            return;
214        }
215
216        let loop_period = Duration::from_hz(100);
217        loop {
218            let timestamp = synch_at(loop_period) + Duration::from_micros(900);
219            Timer::at(timestamp).await;
220
221            // Read Data
222            const STAT1_REG: u8 = 0x10;
223            let mut data = [0u8; 7];
224            if self
225                .write_read(ADDRESS, &[STAT1_REG], &mut data)
226                .await
227                .is_err()
228            {
229                MAG_SIGNAL.signal(Err(errors::SensorError::GenericSensorError(
230                    "IST8308 Mag failed: reading STAT1_REG",
231                )));
232                continue;
233            }
234
235            const STAT1_VAL_DRDY: u8 = 0x01;
236            let status = data[0];
237            let data_ready = (status & STAT1_VAL_DRDY) != 0;
238            if data_ready {
239                let flux = [
240                    f32::from((((data[2] as u16) << 8) | (data[1] as u16)) as i16) * 1.5e-7,
241                    f32::from((((data[4] as u16) << 8) | (data[3] as u16)) as i16) * 1.5e-7,
242                    f32::from((((data[6] as u16) << 8) | (data[5] as u16)) as i16) * 1.5e-7,
243                ]; // Units of Tesla
244
245                let timestamp_us = timestamp.as_micros();
246
247                let header = packets::RosflightPacketHeader {
248                    timestamp: timestamp_us,
249                    status: status as u16,
250                };
251
252                let mag_packet = packets::MagPacket {
253                    header,
254                    flux,
255                    temperature: 0.0f32,
256                };
257                MAG_SIGNAL.signal(Ok(mag_packet)); // make data available for other tasks
258                // previous_timestamp_us = timestamp_us;
259            }
260        }
261    }
262}
263
264#[embassy_executor::task]
265pub async fn task(mut ist: Ist8308Sensor) {
266    ist.run().await;
267}