Skip to main content

ads111x_rs/
lib.rs

1#![no_std]
2
3#[cfg(feature = "async")]
4use embedded_hal_async::i2c::I2c as AsyncI2c;
5
6use embedded_hal::i2c::I2c;
7
8pub struct ADS111x<I2C, const MODEL: u8> {
9    address: Address,
10    i2c: I2C,
11    pub config: Config,
12}
13
14pub type ADS1115<I2C> = ADS111x<I2C, 5>;
15pub type ADS1114<I2C> = ADS111x<I2C, 4>;
16pub type ADS1113<I2C> = ADS111x<I2C, 3>;
17
18#[cfg(feature = "async")]
19impl<I2C> ADS1113<I2C> {
20    pub fn new(i2c: I2C, config: Config) -> Self {
21        Self {
22            address: Address::Ground,
23            i2c,
24            config,
25        }
26    }
27}
28
29impl<I2C: I2c> ADS1113<I2C> {
30    fn read_channel_raw(&mut self) -> Result<i16, I2C::Error> {
31        let mut config = [(1 << 7) | 1, (self.config.data_rate as u8)];
32        let mut data = [0u8; 2];
33
34        self.i2c
35            .write(self.address as u8, &[0x01, config[0], config[1]])?;
36
37        loop {
38            self.i2c
39                .write_read(self.address as u8, &[0x01], &mut config)?;
40            if (config[0] & 0x80) != 0 {
41                break;
42            }
43        }
44
45        self.i2c
46            .write_read(self.address as u8, &[0x00], &mut data)?;
47        Ok(i16::from_be_bytes(data))
48    }
49
50    pub fn read_adc(&mut self) -> Result<i16, I2C::Error> {
51        self.read_channel_raw()
52    }
53}
54
55#[cfg(feature = "async")]
56impl<I2C: AsyncI2c> ADS1113<I2C> {
57    async fn read_channel_asyncraw(&mut self) -> Result<i16, I2C::Error> {
58        let mut config = [(1 << 7) | 1, (self.config.data_rate as u8)];
59        let mut data = [0u8; 2];
60
61        self.i2c
62            .write(self.address as u8, &[0x01, config[0], config[1]])
63            .await?;
64
65        loop {
66            self.i2c
67                .write_read(self.address as u8, &[0x01], &mut config)
68                .await?;
69            if (config[0] & 0x80) != 0 {
70                break;
71            }
72        }
73
74        self.i2c
75            .write_read(self.address as u8, &[0x00], &mut data)
76            .await?;
77        Ok(i16::from_be_bytes(data))
78    }
79
80    pub async fn read_asyncadc(&mut self) -> Result<i16, I2C::Error> {
81        self.read_channel_asyncraw().await
82    }
83}
84
85impl<I2C> ADS1114<I2C> {
86    pub fn new(i2c: I2C, config: Config) -> Self {
87        Self {
88            address: Address::Ground,
89            i2c,
90            config,
91        }
92    }
93}
94
95impl<I2C: I2c> ADS1114<I2C> {
96    /// Configures the ALERT/RDY pin as a conversion ready pin.
97    /// Note: `config.comp_que` must NOT be `DisableComparator` (11b) for this to work.
98    pub fn enable_conversion_ready_pin(&mut self) -> Result<(), I2C::Error> {
99        // Set Hi_thresh MSB to 1 (0x8000)
100        self.i2c.write(self.address as u8, &[0x03, 0x80, 0x00])?;
101        // Set Lo_thresh MSB to 0 (0x0000)
102        self.i2c.write(self.address as u8, &[0x02, 0x00, 0x00])?;
103        Ok(())
104    }
105
106    fn read_channel_raw(&mut self) -> Result<i16, I2C::Error> {
107        let mut config = [
108            (self.config.gain as u8) | (1 << 7) | 1,
109            (self.config.comp_que as u8)
110                | (self.config.comp_lat as u8)
111                | (self.config.comp_pol as u8)
112                | (self.config.comp_mode as u8)
113                | (self.config.data_rate as u8),
114        ];
115        let mut data = [0u8; 2];
116
117        self.i2c
118            .write(self.address as u8, &[0x01, config[0], config[1]])?;
119
120        loop {
121            self.i2c
122                .write_read(self.address as u8, &[0x01], &mut config)?;
123            if (config[0] & 0x80) != 0 {
124                break;
125            }
126        }
127
128        self.i2c
129            .write_read(self.address as u8, &[0x00], &mut data)?;
130        Ok(i16::from_be_bytes(data))
131    }
132
133    pub fn read_adc(&mut self) -> Result<i16, I2C::Error> {
134        self.read_channel_raw()
135    }
136}
137
138#[cfg(feature = "async")]
139impl<I2C: AsyncI2c> ADS1114<I2C> {
140    /// Configures the ALERT/RDY pin as a conversion ready pin.
141    /// Note: `config.comp_que` must NOT be `DisableComparator` (11b) for this to work.
142    pub async fn enable_async_conversion_ready_pin(&mut self) -> Result<(), I2C::Error> {
143        // Set Hi_thresh MSB to 1 (0x8000)
144        self.i2c
145            .write(self.address as u8, &[0x03, 0x80, 0x00])
146            .await?;
147        // Set Lo_thresh MSB to 0 (0x0000)
148        self.i2c
149            .write(self.address as u8, &[0x02, 0x00, 0x00])
150            .await?;
151        Ok(())
152    }
153
154    async fn read_channel_asyncraw(&mut self) -> Result<i16, I2C::Error> {
155        let mut config = [
156            (self.config.gain as u8) | (1 << 7) | 1,
157            (self.config.comp_que as u8)
158                | (self.config.comp_lat as u8)
159                | (self.config.comp_pol as u8)
160                | (self.config.comp_mode as u8)
161                | (self.config.data_rate as u8),
162        ];
163        let mut data = [0u8; 2];
164
165        self.i2c
166            .write(self.address as u8, &[0x01, config[0], config[1]])
167            .await?;
168
169        loop {
170            self.i2c
171                .write_read(self.address as u8, &[0x01], &mut config)
172                .await?;
173            if (config[0] & 0x80) != 0 {
174                break;
175            }
176        }
177
178        self.i2c
179            .write_read(self.address as u8, &[0x00], &mut data)
180            .await?;
181        Ok(i16::from_be_bytes(data))
182    }
183
184    pub async fn read_asyncadc(&mut self) -> Result<i16, I2C::Error> {
185        self.read_channel_asyncraw().await
186    }
187}
188
189impl<I2C> ADS1115<I2C> {
190    pub fn new(address: Address, i2c: I2C, config: Config) -> Self {
191        Self {
192            address,
193            i2c,
194            config,
195        }
196    }
197}
198
199impl<I2C: I2c> ADS1115<I2C> {
200    /// Configures the ALERT/RDY pin as a conversion ready pin.
201    /// Note: `config.comp_que` must NOT be `DisableComparator` (11b) for this to work.
202    pub fn enable_conversion_ready_pin(&mut self) -> Result<(), I2C::Error> {
203        // Set Hi_thresh MSB to 1 (0x8000)
204        self.i2c.write(self.address as u8, &[0x03, 0x80, 0x00])?;
205        // Set Lo_thresh MSB to 0 (0x0000)
206        self.i2c.write(self.address as u8, &[0x02, 0x00, 0x00])?;
207        Ok(())
208    }
209
210    fn read_channel_raw(&mut self, mux: u8) -> Result<i16, I2C::Error> {
211        let mut config = [
212            (self.config.gain as u8) | (1 << 7) | (mux << 4) | 1,
213            (self.config.comp_que as u8)
214                | (self.config.comp_lat as u8)
215                | (self.config.comp_pol as u8)
216                | (self.config.comp_mode as u8)
217                | (self.config.data_rate as u8),
218        ];
219        let mut data = [0u8; 2];
220
221        self.i2c
222            .write(self.address as u8, &[0x01, config[0], config[1]])?;
223
224        loop {
225            self.i2c
226                .write_read(self.address as u8, &[0x01], &mut config)?;
227            if (config[0] & 0x80) != 0 {
228                break;
229            }
230        }
231
232        self.i2c
233            .write_read(self.address as u8, &[0x00], &mut data)?;
234        Ok(i16::from_be_bytes(data))
235    }
236
237    pub fn read_adc_a0(&mut self) -> Result<i16, I2C::Error> {
238        self.read_channel_raw(0b100)
239    }
240
241    pub fn read_adc_a1(&mut self) -> Result<i16, I2C::Error> {
242        self.read_channel_raw(0b101)
243    }
244
245    pub fn read_adc_a2(&mut self) -> Result<i16, I2C::Error> {
246        self.read_channel_raw(0b110)
247    }
248
249    pub fn read_adc_a3(&mut self) -> Result<i16, I2C::Error> {
250        self.read_channel_raw(0b111)
251    }
252
253    pub fn read_adc_a0n1(&mut self) -> Result<i16, I2C::Error> {
254        self.read_channel_raw(0b000)
255    }
256
257    pub fn read_adc_a0n3(&mut self) -> Result<i16, I2C::Error> {
258        self.read_channel_raw(0b001)
259    }
260
261    pub fn read_adc_a1n3(&mut self) -> Result<i16, I2C::Error> {
262        self.read_channel_raw(0b010)
263    }
264
265    pub fn read_adc_a2n3(&mut self) -> Result<i16, I2C::Error> {
266        self.read_channel_raw(0b011)
267    }
268
269    pub fn read_4adc(&mut self) -> Result<[i16; 4], I2C::Error> {
270        let mut values = [0i16; 4];
271        for (i, val) in values.iter_mut().enumerate() {
272            *val = self.read_channel_raw(0b100 + i as u8)?;
273        }
274        Ok(values)
275    }
276}
277
278#[cfg(feature = "async")]
279impl<I2C: AsyncI2c> ADS1115<I2C> {
280    /// Configures the ALERT/RDY pin as a conversion ready pin.
281    /// Note: `config.comp_que` must NOT be `DisableComparator` (11b) for this to work.
282    pub async fn enable_async_conversion_ready_pin(&mut self) -> Result<(), I2C::Error> {
283        // Set Hi_thresh MSB to 1 (0x8000)
284        self.i2c
285            .write(self.address as u8, &[0x03, 0x80, 0x00])
286            .await?;
287        // Set Lo_thresh MSB to 0 (0x0000)
288        self.i2c
289            .write(self.address as u8, &[0x02, 0x00, 0x00])
290            .await?;
291        Ok(())
292    }
293
294    async fn read_channel_asyncraw(&mut self, mux: u8) -> Result<i16, I2C::Error> {
295        let mut config = [
296            (self.config.gain as u8) | (1 << 7) | (mux << 4) | 1,
297            (self.config.comp_que as u8)
298                | (self.config.comp_lat as u8)
299                | (self.config.comp_pol as u8)
300                | (self.config.comp_mode as u8)
301                | (self.config.data_rate as u8),
302        ];
303        let mut data = [0u8; 2];
304
305        self.i2c
306            .write(self.address as u8, &[0x01, config[0], config[1]])
307            .await?;
308
309        loop {
310            self.i2c
311                .write_read(self.address as u8, &[0x01], &mut config)
312                .await?;
313            if (config[0] & 0x80) != 0 {
314                break;
315            }
316        }
317
318        self.i2c
319            .write_read(self.address as u8, &[0x00], &mut data)
320            .await?;
321        Ok(i16::from_be_bytes(data))
322    }
323
324    pub async fn read_async_adc_a0(&mut self) -> Result<i16, I2C::Error> {
325        self.read_channel_asyncraw(0b100).await
326    }
327
328    pub async fn read_async_adc_a1(&mut self) -> Result<i16, I2C::Error> {
329        self.read_channel_asyncraw(0b101).await
330    }
331
332    pub async fn read_async_adc_a2(&mut self) -> Result<i16, I2C::Error> {
333        self.read_channel_asyncraw(0b110).await
334    }
335
336    pub async fn read_async_adc_a3(&mut self) -> Result<i16, I2C::Error> {
337        self.read_channel_asyncraw(0b111).await
338    }
339
340    pub async fn read_async_adc_a0n1(&mut self) -> Result<i16, I2C::Error> {
341        self.read_channel_asyncraw(0b000).await
342    }
343
344    pub async fn read_async_adc_a0n3(&mut self) -> Result<i16, I2C::Error> {
345        self.read_channel_asyncraw(0b001).await
346    }
347
348    pub async fn read_async_adc_a1n3(&mut self) -> Result<i16, I2C::Error> {
349        self.read_channel_asyncraw(0b010).await
350    }
351
352    pub async fn read_async_adc_a2n3(&mut self) -> Result<i16, I2C::Error> {
353        self.read_channel_asyncraw(0b011).await
354    }
355
356    pub async fn read_async_4adc(&mut self) -> Result<[i16; 4], I2C::Error> {
357        let mut values = [0i16; 4];
358        for (i, val) in values.iter_mut().enumerate() {
359            *val = self.read_channel_asyncraw(0b100 + i as u8).await?;
360        }
361        Ok(values)
362    }
363}
364
365/**
366
367## Config
368
369Send the configuration to the device. But some config can't be send due to some model limitation.
370
371*/
372#[derive(Debug, Clone, Copy, Default)]
373pub struct Config {
374    pub gain: Gain,
375    pub data_rate: DataRate,
376    pub comp_mode: CompMode,
377    pub comp_pol: CompPol,
378    pub comp_lat: CompLat,
379    pub comp_que: CompQue,
380}
381
382impl Config {
383    pub fn with_gain(self, gain: Gain) -> Self {
384        Self { gain, ..self }
385    }
386
387    pub fn with_data_rate(self, data_rate: DataRate) -> Self {
388        Self { data_rate, ..self }
389    }
390
391    pub fn with_comp_mode(self, comp_mode: CompMode) -> Self {
392        Self { comp_mode, ..self }
393    }
394
395    pub fn with_comp_pol(self, comp_pol: CompPol) -> Self {
396        Self { comp_pol, ..self }
397    }
398
399    pub fn with_comp_lat(self, comp_lat: CompLat) -> Self {
400        Self { comp_lat, ..self }
401    }
402
403    pub fn with_comp_que(self, comp_que: CompQue) -> Self {
404        Self { comp_que, ..self }
405    }
406}
407
408// NOTE: First Bit configuration
409
410/// PGA[11:9]: Programmable gain amplifier
411/// Only for ADS1114/5
412#[derive(Debug, Clone, Copy, Default)]
413#[repr(u8)]
414// 15 14 13 12 [Gain] 8
415pub enum Gain {
416    #[default]
417    V6_144 = 0,
418    V4_096 = 1 << 1,
419    V2_048 = 2 << 1,
420    V1_024 = 3 << 1,
421    V0_512 = 4 << 1,
422    V0_256 = 5 << 1,
423}
424
425// NOTE: Second Bit configuration
426
427/// DR[7:5]: Data rate
428#[derive(Debug, Clone, Copy, Default)]
429#[repr(u8)]
430// [DR] 4 3 2 1 0
431pub enum DataRate {
432    SPS8 = 0,
433    SPS16 = 1 << 5,
434    SPS32 = 2 << 5,
435    SPS64 = 3 << 5,
436    SPS128 = 4 << 5,
437    SPS250 = 5 << 5,
438    SPS475 = 6 << 5,
439    #[default]
440    SPS860 = 7 << 5,
441}
442
443/// COMP_MODE [4]:  Comparator mode
444/// Only for ADS1114/5
445#[derive(Debug, Clone, Copy, Default)]
446#[repr(u8)]
447// 7 6 5 [COMP_MODE] 3 2 1 0
448pub enum CompMode {
449    #[default]
450    Traditional = 0,
451    Window = 1 << 4,
452}
453
454/// COMP_POL [3]:  Comparator polarity
455/// Only for ADS1114/5
456#[derive(Debug, Clone, Copy, Default)]
457#[repr(u8)]
458// 7 6 5 4 [COMP_POL] 2 1 0
459pub enum CompPol {
460    #[default]
461    ActiveLow = 0,
462    ActiveHigh = 1 << 3,
463}
464
465/// COMP_LAT [2]: Latching Comparator
466/// Only for ADS1114/5
467#[derive(Debug, Clone, Copy, Default)]
468#[repr(u8)]
469// 7 6 5 4 3 [COMP_LAT] 1 0
470pub enum CompLat {
471    #[default]
472    NonLatching = 0,
473    Latching = 1 << 2,
474}
475
476/// COMP_QUE [1:0]: Comparator queue and disable
477/// Only for ADS1114/5
478#[derive(Debug, Clone, Copy, Default)]
479#[repr(u8)]
480// 7 6 5 4 3 2 [COMP_QUE]
481pub enum CompQue {
482    OneConversion = 0,
483    TwoConversions = 1,
484    FourConversions = 2,
485    #[default]
486    DisableComparator = 3,
487}
488
489#[derive(Debug, Clone, Copy, Default)]
490#[repr(u8)]
491pub enum Address {
492    #[default]
493    Ground = 0b1001000,
494    VDD = 0b1001001,
495    SDA = 0b1001010,
496    SCL = 0b1001011,
497}