tmp117/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![deny(missing_docs)]
4
5use device_register::{EditRegister, ReadRegister, WriteRegister};
6use embedded_hal::{
7    delay::DelayNs,
8    i2c::{I2c, SevenBitAddress},
9};
10pub use error::Error;
11use register::*;
12use tmp117_ll::Tmp117LL;
13
14pub mod asynchronous;
15pub mod error;
16pub mod register;
17pub mod tmp117_ll;
18
19/// Conversion factor used by the device. One lsb is this value
20pub const CELCIUS_CONVERSION: f32 = 0.0078125;
21
22/// The types of alerts possible
23pub enum Alert {
24    /// No alert were triggered
25    None,
26
27    /// A high alert was triggered
28    High,
29
30    /// A low alert was triggered
31    Low,
32
33    /// A high and a low alert was triggered
34    HighLow,
35}
36
37/// The continuous config
38#[derive(Default)]
39pub struct ContinuousConfig {
40    /// The average used, will use the one stored in the register if None
41    pub average: Average,
42
43    /// The convesion used, will use the one stored in the register if None
44    pub conversion: Conversion,
45
46    /// The high alert used, will use the one stored in the register if None
47    pub high: Option<f32>,
48
49    /// The low alert used, will use the one stored in the register if None
50    pub low: Option<f32>,
51
52    /// The temperature offset used, will use 0 if None
53    pub offset: Option<f32>,
54}
55/// Represents the ID of the device.
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57#[derive(Copy, Clone, PartialEq, Eq, Debug)]
58pub struct Id {
59    /// Should always be 0x117
60    pub device: u16,
61    /// Depends on the revision of the device
62    pub revision: u8,
63}
64
65/// The TMP117 driver. Note that the alert pin is not used in this driver,
66/// see the async implementation if you want the driver to use the alert pin in the drive
67pub struct Tmp117<T, E> {
68    tmp_ll: Tmp117LL<T, E>,
69}
70
71impl<T, E> Tmp117<T, E>
72where
73    T: I2c<SevenBitAddress, Error = E>,
74    E: embedded_hal::i2c::Error,
75{
76    /// Create a new tmp117 from a i2c bus
77    pub fn new(i2c: T, addr: u8) -> Self {
78        Tmp117::<T, E> {
79            tmp_ll: Tmp117LL::new(i2c, addr),
80        }
81    }
82
83    /// Create a new tmp117 from a low level tmp117 driver
84    pub fn new_from_ll(tmp_ll: Tmp117LL<T, E>) -> Self {
85        Tmp117::<T, E> { tmp_ll }
86    }
87
88    /// Returns the ID of the device
89    pub fn id(&mut self) -> Result<Id, Error<E>> {
90        let id: DeviceID = self.tmp_ll.read()?;
91        Ok(Id {
92            device: id.device_id().into(),
93            revision: id.revision().into(),
94        })
95    }
96
97    fn wait_eeprom(&mut self) -> Result<(), Error<E>> {
98        let mut configuration: Configuration = self.tmp_ll.read()?;
99        while configuration.eeprom_busy() {
100            configuration = self.tmp_ll.read()?;
101        }
102
103        Ok(())
104    }
105
106    fn read_temp_raw(&mut self) -> Result<f32, Error<E>> {
107        let temp: Temperature = self.tmp_ll.read()?;
108
109        // Convert to i16 for two complements
110        let val = (u16::from(temp) as i16) as f32 * CELCIUS_CONVERSION;
111        Ok(val)
112    }
113
114    fn check_alert(&mut self) -> Result<Alert, Error<E>> {
115        let config: Configuration = self.tmp_ll.read()?;
116        if config.high_alert() && config.low_alert() {
117            Ok(Alert::HighLow)
118        } else if config.high_alert() {
119            Ok(Alert::High)
120        } else if config.low_alert() {
121            Ok(Alert::Low)
122        } else {
123            Ok(Alert::None)
124        }
125    }
126
127    fn wait_for_data(&mut self) -> Result<(), Error<E>> {
128        // Loop while the data is not ok
129        loop {
130            let config: Configuration = self.tmp_ll.read()?;
131            if config.data_ready() {
132                break;
133            }
134        }
135        Ok(())
136    }
137
138    fn wait_for_alert(&mut self) -> Result<Alert, Error<E>> {
139        loop {
140            let alert = self.check_alert();
141            if let Ok(Alert::None) = alert {
142                continue;
143            } else {
144                return alert;
145            }
146        }
147    }
148
149    fn set_continuous(
150        &mut self,
151        config: ContinuousConfig,
152    ) -> Result<ContinuousHandler<'_, T, E>, Error<E>> {
153        if let Some(val) = config.high {
154            let high: HighLimit = ((val / CELCIUS_CONVERSION) as u16).into();
155            self.tmp_ll.write(high)?;
156        }
157        if let Some(val) = config.low {
158            let low: LowLimit = ((val / CELCIUS_CONVERSION) as u16).into();
159            self.tmp_ll.write(low)?;
160        }
161        if let Some(val) = config.offset {
162            let off: TemperatureOffset = ((val / CELCIUS_CONVERSION) as u16).into();
163            self.tmp_ll.write(off)?;
164        }
165
166        self.tmp_ll.edit(|r: &mut Configuration| {
167            r.set_mode(ConversionMode::Continuous);
168            r.set_polarity(Polarity::ActiveLow);
169            r.set_average(config.average);
170            r.set_conversion(config.conversion);
171        })?;
172
173        Ok(ContinuousHandler { tmp117: self })
174    }
175
176    fn set_oneshot(&mut self, average: Average) -> Result<(), Error<E>> {
177        self.tmp_ll.edit(|r: &mut Configuration| {
178            r.set_mode(ConversionMode::OneShot);
179            r.set_polarity(Polarity::ActiveLow);
180            r.set_average(average);
181        })?;
182        Ok(())
183    }
184
185    fn set_shutdown(&mut self) -> Result<(), Error<E>> {
186        self.tmp_ll.edit(|r: &mut Configuration| {
187            r.set_mode(ConversionMode::Shutdown);
188        })?;
189        Ok(())
190    }
191
192    /// Resets the device and put it in shutdown
193    pub fn reset<D>(&mut self, delay: &mut D) -> Result<(), Error<E>>
194    where
195        D: DelayNs,
196    {
197        self.tmp_ll.edit(|r: &mut Configuration| {
198            r.set_reset(true);
199        })?;
200        delay.delay_ms(2);
201        self.set_shutdown()?;
202        Ok(())
203    }
204
205    /// Write data to user eeprom. Note that this is blocking because we wait for write on the eeprom to complete
206    pub fn write_eeprom(&mut self, values: [u16; 3]) -> Result<(), Error<E>> {
207        self.wait_eeprom()?;
208        self.tmp_ll.write(UEEPROM1::from(values[0]))?;
209
210        self.wait_eeprom()?;
211        self.tmp_ll.write(UEEPROM2::from(values[1]))?;
212
213        self.wait_eeprom()?;
214        self.tmp_ll.write(UEEPROM3::from(values[2]))?;
215
216        Ok(())
217    }
218
219    /// Read the data from the eeprom
220    pub fn read_eeprom(&mut self) -> Result<[u16; 3], Error<E>> {
221        let u1: UEEPROM1 = self.tmp_ll.read()?;
222        let u2: UEEPROM2 = self.tmp_ll.read()?;
223        let u3: UEEPROM3 = self.tmp_ll.read()?;
224
225        Ok([u1.into(), u2.into(), u3.into()])
226    }
227
228    /// Wait for data and read the temperature in celsius and shutdown since it's a oneshot
229    pub fn oneshot(&mut self, average: Average) -> Result<f32, Error<E>> {
230        self.set_oneshot(average)?;
231        self.wait_for_data()?;
232        let data = self.read_temp_raw()?;
233        Ok(data)
234    }
235
236    /// Pass a config and closure for the continuous mode.
237    /// The device gets set to continuous, then the function is called with the handler
238    /// and finally the device is shutdown
239    pub fn continuous<F>(&mut self, config: ContinuousConfig, f: F) -> Result<(), Error<E>>
240    where
241        F: FnOnce(ContinuousHandler<'_, T, E>) -> Result<(), Error<E>>,
242    {
243        let handler = self.set_continuous(config)?;
244        f(handler)?;
245        self.set_shutdown()
246    }
247}
248
249/// Handler for the continuous mode
250pub struct ContinuousHandler<'a, T, E> {
251    tmp117: &'a mut Tmp117<T, E>,
252}
253
254impl<'a, T, E> ContinuousHandler<'a, T, E>
255where
256    T: I2c<SevenBitAddress, Error = E>,
257    E: embedded_hal::i2c::Error,
258{
259    /// Read the temperature in celsius, return an error if the value of the temperature is not ready
260    pub fn read_temp(&mut self) -> Result<f32, Error<E>> {
261        let config: Configuration = self.tmp117.tmp_ll.read()?;
262        if !config.data_ready() {
263            return Err(Error::DataNotReady);
264        }
265
266        let val = self.tmp117.read_temp_raw()?;
267        Ok(val)
268    }
269
270    /// Wait for the data to be ready and read the temperature in celsius
271    pub fn wait_temp(&mut self) -> Result<f32, Error<E>> {
272        self.tmp117.wait_for_data()?;
273        let val = self.tmp117.read_temp_raw()?;
274        Ok(val)
275    }
276
277    /// Check if an alert was triggered since the last calll
278    pub fn get_alert(&mut self) -> Result<Alert, Error<E>> {
279        let val = self.tmp117.check_alert()?;
280        Ok(val)
281    }
282
283    /// Wait for an alert to come and return it's value
284    pub fn wait_alert(&mut self) -> Result<Alert, Error<E>> {
285        let val = self.tmp117.wait_for_alert()?;
286        Ok(val)
287    }
288}