Skip to main content

stm_32/peripherals/
dps310.rs

1// ******************************************************************************
2// * File     : platforms/stm_32/src/peripherals/dps310.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
36use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
37use embassy_stm32::exti::ExtiInput;
38use embassy_stm32::gpio::Output;
39use embassy_stm32::mode::Async;
40use embassy_stm32::spi;
41use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
42use embassy_sync::signal::Signal;
43use embassy_time::Duration;
44use embassy_time::Timer;
45use embassy_time::with_timeout;
46use embedded_hal_async::spi::SpiDevice as _;
47
48use crate::synch_at;
49use veloxity_core::errors;
50use veloxity_core::packets;
51
52// Device dependent
53const SPI_READ: u8 = 0x80;
54const SPI_WRITE: u8 = 0x00;
55
56pub static BARO_SIGNAL: Signal<
57    CriticalSectionRawMutex,
58    Result<packets::BaroPacket, errors::SensorError>,
59> = Signal::<CriticalSectionRawMutex, Result<packets::BaroPacket, errors::SensorError>>::new();
60
61pub struct Dps310Sensor {
62    pub dev: SpiDevice<
63        'static,
64        CriticalSectionRawMutex,
65        spi::Spi<'static, Async, spi::mode::Master>,
66        Output<'static>,
67    >,
68    pub drdy: ExtiInput<'static, Async>,
69    pub three_wire: bool,
70}
71
72fn compliment(x: u32, bits: u32) -> f64 {
73    let mut x = x as i32;
74    if (x & (1i32 << (bits - 1))) != 0 {
75        x -= 1i32 << bits;
76    }
77    f64::from(x)
78}
79
80const MEAS_CFG_REG: u8 = 0x08;
81const ISR_REG: u8 = 0x0A;
82const DPS310_READ_P_CMD: u8 = 0x00;
83const DPS310_READ_T_CMD: u8 = 0x03;
84
85const K1: f64 = 524288.0;
86const K8: f64 = 7864320.0; //
87
88impl Dps310Sensor {
89    async fn read_register(&mut self, reg_addr: u8) -> Result<u8, errors::SensorError> {
90        let tx = [reg_addr | SPI_READ, 0x00];
91        let mut rx = [0u8; 2];
92        self.dev.transfer(&mut rx, &tx).await.map_err(|e| match e {
93            _ => errors::SensorError::GenericSensorError("SPI failed: read_register"),
94        })?;
95        Ok(rx[1])
96    }
97
98    async fn write_register(&mut self, reg_addr: u8, value: u8) -> Result<(), errors::SensorError> {
99        let tx = [reg_addr | SPI_WRITE, value];
100        // Soft Reset
101        self.dev.write(&tx).await.map_err(|e| match e {
102            _ => errors::SensorError::GenericSensorError("SPI failed: write_register"),
103        })?;
104        Ok(())
105    }
106
107    async fn initialize_sensor(&mut self) -> Result<[f64; 9], errors::SensorError> {
108        // SOFT RESET
109        const RESET_REG: u8 = 0x0C;
110        self.write_register(RESET_REG, 0x09).await?;
111        Timer::after_millis(52).await; // Wait reset (12ms) and for Coefficients to be ready (40ms).
112
113        // 3-WIRE MODE & DRDY interrupts
114        // Set to 3-wire or 4-wire SPI mode so we can read registers.
115        // Interrupt and FIFO Config 0x09
116        // 7 - 	1, DRDY active high
117        // 6 - 	0, Disable FIFO full interrupt
118        // 5 - 	1, Int on temp
119        // 4 - 	1, Int on pressure
120        // 3 - 	0, no Temp data shift
121        // 2 - 	0, no Press data shift
122        // 1 - 	0, Disable FIFO
123        // 0 - 	1, 3-wire SPI interface
124        const CFG_REG: u8 = 0x09;
125        let three_wire_mode: u8 = if self.three_wire { 0x01 } else { 0x00 };
126        self.write_register(CFG_REG, three_wire_mode | 0xB0).await?;
127
128        // CHECK PRODUCT ID
129        // there's a more concise way to do the if else, but I'm leaving it for now...
130        const PRODUCT_ID_REG: u8 = 0x0D;
131        const PRODUCT_ID: u8 = 0x10;
132        let id = self.read_register(PRODUCT_ID_REG).await?;
133        if id != PRODUCT_ID {
134            //    "Failure: ID = {:#02x} failure. Should be {:#02x}",
135            //    id,
136            //    PRODUCT_ID
137            //);
138            return Err(errors::SensorError::GenericSensorError("ID mismatch"));
139        }
140
141        // CHECK IF CALIBRATION COEFFICIENTS ARE READY
142        // again, better way to do if else is for future work
143        const COEF_READY: u8 = 0x80;
144        let coef_rdy = self.read_register(MEAS_CFG_REG).await?;
145        if (coef_rdy & COEF_READY) == 0x00 {
146            return Err(errors::SensorError::GenericSensorError(
147                "Calibration coefficients not ready",
148            ));
149        }
150
151        let cal = self.read_calibration_coefficients().await?;
152        Ok(cal)
153    }
154
155    async fn read_calibration_coefficients(&mut self) -> Result<[f64; 9], errors::SensorError> {
156        const COEF_REG: u8 = 0x10;
157        let mut tx = [0u8; 19];
158        tx[0] = COEF_REG | SPI_READ;
159
160        let mut rx = [0u8; 19];
161        self.dev.transfer(&mut rx, &tx).await.map_err(|e| match e {
162            _ => {
163                errors::SensorError::GenericSensorError("SPI failed: read_calibration_coefficients")
164            }
165        })?;
166
167        // move u8 date into u32 data for bit manipulation
168        let buf = rx.map(|x| x as u32);
169        // compute coefficint values in f64
170        let mut cal = [0f64; 9];
171        cal[0] = compliment((buf[1] << 4) | ((buf[2] >> 4) & 0x0F), 12); // C0
172        cal[1] = compliment(((buf[2] & 0x0F) << 8) | buf[3], 12); // C1
173        cal[2] = compliment((buf[4] << 12) | (buf[5] << 4) | ((buf[6] >> 4) & 0x0F), 20); // C00
174        cal[3] = compliment(((buf[6] & 0x0F) << 16) | (buf[7] << 8) | buf[8], 20); // C10
175        cal[6] = compliment((buf[9] << 8) | buf[10], 16); // C01
176        cal[7] = compliment((buf[11] << 8) | buf[12], 16); // C11
177        cal[4] = compliment((buf[13] << 8) | buf[14], 16); // C20
178        cal[8] = compliment((buf[15] << 8) | buf[16], 16); // C21
179        cal[5] = compliment((buf[17] << 8) | buf[18], 16); // C30
180
181        Ok(cal)
182    }
183
184    async fn pressure_config(&mut self) -> Result<(), errors::SensorError> {
185        // PRESSURE CONFIG
186        const PRS_CFG_REG: u8 = 0x06;
187        self.write_register(PRS_CFG_REG, 0x03).await?; // 8x oversampling
188
189        Ok(())
190    }
191
192    async fn temperature_config(&mut self) -> Result<(), errors::SensorError> {
193        // CHECK TEMPERATURE SOURCE
194        const COEF_SRCE_REG: u8 = 0x28;
195        let temp_source = self.read_register(COEF_SRCE_REG).await? & 0x80;
196
197        // TEMPERATURE CONFIG
198        const TMP_CFG_REG: u8 = 0x07;
199        self.write_register(TMP_CFG_REG, temp_source | 0x00).await?; //no oversampling
200
201        Ok(())
202    }
203
204    async fn measurement_configuration(&mut self) -> Result<(), errors::SensorError> {
205        // Measurement Configuration
206        // 7 - 	0, read only
207        // 6 - 	0, read only
208        // 5 - 	0, read only
209        // 4 - 	0, read only
210        // 3 - 	0, reserved
211        // 2:0 - 	111, pressure and temperature continuous mode
212        // Set to idle
213        self.write_register(MEAS_CFG_REG, 0x00).await?;
214
215        Ok(())
216    }
217
218    async fn get_sensor_data(&mut self, cmd: u8) -> Result<i32, errors::SensorError> {
219        let mut rx = [0u8; 4];
220        self.dev
221            .transfer(&mut rx, &[cmd | SPI_READ, 0, 0, 0])
222            .await
223            .map_err(|e| match e {
224                _ => errors::SensorError::GenericSensorError("SPI failed: get_sensor_data"),
225            })?;
226
227        // Clear the ISR
228        self.read_register(ISR_REG).await?;
229
230        let raw = (((rx[1] as u32) << 24 | (rx[2] as u32) << 16 | (rx[3] as u32) << 8) as i32) >> 8;
231
232        Ok(raw)
233    }
234
235    async fn get_pressure_data(&mut self) -> Result<(i32, u16), errors::SensorError> {
236        // Start the Pressure read
237        self.write_register(MEAS_CFG_REG, 0x01).await?;
238
239        // wait for data ready...
240        // Use DRDY signal for better robustness? otherwise, timeout at 14ms.
241        let _drdy_result = with_timeout(
242            Duration::from_micros(14_000),
243            self.drdy.wait_for_rising_edge(),
244        )
245        .await
246        .is_ok();
247        Timer::after_micros(20).await; // We need at least 14us delay here if running at 2 MHz, maybe because of the messy harness?
248
249        // read status (highest 8 bits)
250        let status = (self.read_register(MEAS_CFG_REG).await? as u16) << 8;
251
252        // read Pressure data
253        let raw_p = self.get_sensor_data(DPS310_READ_P_CMD).await?;
254        Ok((raw_p, status))
255    }
256
257    async fn get_temperature_data(&mut self) -> Result<(i32, u16), errors::SensorError> {
258        // Start Temperature read
259        self.write_register(MEAS_CFG_REG, 0x02).await?;
260
261        // wait for data ready...
262        // Use DRDY signal if available, otherwise let it timeout
263        let _drdy_result = with_timeout(
264            Duration::from_micros(3_000),
265            self.drdy.wait_for_rising_edge(),
266        )
267        .await
268        .is_ok();
269
270        // read status (modify lowest 8 bits)
271        let status_low = self.read_register(MEAS_CFG_REG).await? as u16;
272
273        // read Temperature data
274        let raw_t = self.get_sensor_data(DPS310_READ_T_CMD).await?;
275        Ok((raw_t, status_low))
276    }
277
278    fn process_temperature_data(
279        &mut self,
280        raw_t: i32,
281        raw_t_previous: &mut i32,
282        cal: &[f64; 9],
283    ) -> (f64, f64) {
284        *raw_t_previous += (raw_t - *raw_t_previous) / 16; // filter temperature a bit (1/127 is cutoff frequenc of 100Hz * (1/16)/(2*pi) around 1 sec to 1/e)
285        let raw_t_f64 = f64::from(*raw_t_previous) / K1;
286        let temperature = cal[0] * 0.5 + cal[1] * raw_t_f64; // K
287
288        (raw_t_f64, temperature)
289    }
290
291    fn process_pressure_data(&mut self, raw_p: i32, raw_t_f64: f64, cal: &[f64; 9]) -> (f64, f64) {
292        let raw_p_f64 = f64::from(raw_p) / K8;
293        let pressure = cal[2]
294            + raw_p_f64 * (cal[3] + raw_p_f64 * (cal[4] + raw_p_f64 * cal[5]))
295            + raw_t_f64 * (cal[6] + raw_p_f64 * (cal[7] + raw_p_f64 * cal[8])); // Pa
296        (raw_p_f64, pressure)
297    }
298
299    pub async fn run(&mut self) {
300        // initialize the sensor
301        let mut cal = match self.initialize_sensor().await {
302            Ok(cal) => cal,
303            Err(e) => {
304                BARO_SIGNAL.signal(Err(e));
305                return;
306            }
307        };
308        if let Err(e) = self.pressure_config().await {
309            BARO_SIGNAL.signal(Err(e));
310            return;
311        }
312        if let Err(e) = self.temperature_config().await {
313            BARO_SIGNAL.signal(Err(e));
314            return;
315        }
316        if let Err(e) = self.measurement_configuration().await {
317            BARO_SIGNAL.signal(Err(e));
318            return;
319        }
320        //////////////////////////////////////////////////////////////////////////////////////
321        // Periodic Data Acquisition
322        let mut raw_t_previous = 0_i32;
323        let sample_period = Duration::from_hz(50);
324
325        loop {
326            let timestamp = synch_at(sample_period);
327            Timer::at(timestamp).await;
328
329            // process pressure data
330            let (raw_p, status_high) = match self.get_pressure_data().await {
331                Ok(data) => data,
332                Err(e) => {
333                    BARO_SIGNAL.signal(Err(e));
334                    continue;
335                }
336            };
337            let (raw_t, status_low) = match self.get_temperature_data().await {
338                Ok(data) => data,
339                Err(e) => {
340                    BARO_SIGNAL.signal(Err(e));
341                    continue;
342                }
343            };
344
345            // combine status bits
346            let status_combined = (status_high & 0xFF00) | (status_low & 0x00FF);
347
348            let (raw_t_f64, temperature) =
349                self.process_temperature_data(raw_t, &mut raw_t_previous, &mut cal);
350            let (_raw_p_f64, pressure) = self.process_pressure_data(raw_p, raw_t_f64, &mut cal);
351
352            if status_combined == 0xD0E0 {
353                let header = packets::RosflightPacketHeader {
354                    timestamp: timestamp.as_micros(),
355                    status: status_combined,
356                };
357                let baro_packet = packets::BaroPacket {
358                    header,
359                    pressure: pressure as f32,
360                    temperature: temperature as f32,
361                    ..Default::default()
362                };
363                BARO_SIGNAL.signal(Ok(baro_packet)); // make data available for other tasks.
364            } else {
365                BARO_SIGNAL.signal(Err(errors::SensorError::GenericSensorError("Bad status")));
366            }
367        }
368    }
369}
370
371#[embassy_executor::task]
372pub async fn task(mut dps: Dps310Sensor) {
373    dps.run().await;
374}