Skip to main content

variegated_fdc1004/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs)]
4
5use core::fmt;
6#[cfg(feature = "defmt")]
7use defmt::Format;
8use embedded_hal_async::delay::DelayNs;
9use embedded_hal_async::i2c::I2c;
10use ux::i24;
11
12/// Errors that can occur when communicating with the FDC1004 chip.
13#[derive(Debug, Clone, Copy)]
14#[cfg_attr(feature = "defmt", derive(Format))]
15pub enum FDC1004Error<E>{
16    /// Failed to find a suitable CAPDAC setting for the measurement range.
17    UnableToFindCapdacSetting,
18    /// Measurement has not completed yet.
19    MeasurementNotComplete,
20    /// Invalid channel for single-ended measurement.
21    InvalidMeasurementChannel,
22    /// I2C communication error.
23    I2CError(E)
24}
25
26/// Output data rate configuration for FDC1004 measurements.
27/// 
28/// The FDC1004 supports configurable sample rates for capacitance measurements.
29/// Higher rates provide faster measurements but may have reduced accuracy.
30#[derive(Default, Copy, Clone, Debug)]
31pub enum OutputRate {
32    /// 100 samples per second (default).
33    #[default]
34    SPS100,
35    /// 200 samples per second.
36    SPS200,
37    /// 400 samples per second.
38    SPS400,
39}
40
41impl OutputRate {
42    fn delay_ns(&self) -> u32 {
43        match self {
44            OutputRate::SPS100 => 11_000_000, // 11 milliseconds in nanoseconds
45            OutputRate::SPS200 => 6_000_000,  // 6 milliseconds in nanoseconds
46            OutputRate::SPS400 => 3_000_000,  // 3 milliseconds in nanoseconds
47        }
48    }
49}
50
51static CAPDAC_MAX: u8 = 0x1F;
52
53/// FDC1004 input channel selection.
54/// 
55/// The FDC1004 has 4 capacitive input channels (CIN1-CIN4) that can be 
56/// configured for single-ended or differential measurements.
57#[derive(Copy, Clone, Debug)]
58pub enum Channel {
59    /// Capacitive input channel 1.
60    CIN1,
61    /// Capacitive input channel 2.
62    CIN2,
63    /// Capacitive input channel 3.
64    CIN3,
65    /// Capacitive input channel 4.
66    CIN4,
67    /// Internal CAPDAC reference for offset compensation.
68    CAPDAC,
69    /// Disabled channel (no connection).
70    DISABLED
71}
72
73/// FDC1004 measurement channel identifier.
74/// 
75/// The FDC1004 supports up to 4 simultaneous measurements using different
76/// measurement configurations. Each measurement can be independently configured
77/// with different input channels and CAPDAC settings.
78#[derive(Copy, Clone)]
79pub enum Measurement {
80    /// Measurement channel 1.
81    Measurement1,
82    /// Measurement channel 2.
83    Measurement2,
84    /// Measurement channel 3.
85    Measurement3,
86    /// Measurement channel 4.
87    Measurement4,
88}
89
90impl Measurement {
91    fn msb_register(&self) -> RegisterAddress {
92        match self {
93            Measurement::Measurement1 => RegisterAddress::Measurement1MSB,
94            Measurement::Measurement2 => RegisterAddress::Measurement2MSB,
95            Measurement::Measurement3 => RegisterAddress::Measurement3MSB,
96            Measurement::Measurement4 => RegisterAddress::Measurement4MSB,
97        }
98    }
99
100    fn lsb_register(&self) -> RegisterAddress {
101        match self {
102            Measurement::Measurement1 => RegisterAddress::Measurement1LSB,
103            Measurement::Measurement2 => RegisterAddress::Measurement2LSB,
104            Measurement::Measurement3 => RegisterAddress::Measurement3LSB,
105            Measurement::Measurement4 => RegisterAddress::Measurement4LSB,
106        }
107    }
108
109    fn config_register(&self) -> RegisterAddress {
110        match self {
111            Measurement::Measurement1 => RegisterAddress::Measurement1Config,
112            Measurement::Measurement2 => RegisterAddress::Measurement2Config,
113            Measurement::Measurement3 => RegisterAddress::Measurement3Config,
114            Measurement::Measurement4 => RegisterAddress::Measurement4Config,
115        }
116    }
117
118    fn ready_according_to_config(&self, config: &FDCConfiguration) -> bool {
119        match self {
120            Measurement::Measurement1 => config.measurement1_done,
121            Measurement::Measurement2 => config.measurement2_done,
122            Measurement::Measurement3 => config.measurement3_done,
123            Measurement::Measurement4 => config.measurement4_done,
124        }
125    }
126}
127
128/// FDC1004 register addresses.
129/// 
130/// These addresses correspond to the internal registers of the FDC1004 chip
131/// as specified in the datasheet. Each register serves a specific function
132/// for configuration, measurement data, calibration, or device identification.
133#[derive(Copy, Clone, Debug)]
134pub enum RegisterAddress {
135    /// Measurement 1 most significant byte.
136    Measurement1MSB,
137    /// Measurement 1 least significant byte.
138    Measurement1LSB,
139    /// Measurement 2 most significant byte.
140    Measurement2MSB,
141    /// Measurement 2 least significant byte.
142    Measurement2LSB,
143    /// Measurement 3 most significant byte.
144    Measurement3MSB,
145    /// Measurement 3 least significant byte.
146    Measurement3LSB,
147    /// Measurement 4 most significant byte.
148    Measurement4MSB,
149    /// Measurement 4 least significant byte.
150    Measurement4LSB,
151    /// Measurement 1 configuration register.
152    Measurement1Config,
153    /// Measurement 2 configuration register.
154    Measurement2Config,
155    /// Measurement 3 configuration register.
156    Measurement3Config,
157    /// Measurement 4 configuration register.
158    Measurement4Config,
159    /// FDC configuration register.
160    FdcConf,
161    /// Offset calibration for CIN1.
162    OffsetCalCIN1,
163    /// Offset calibration for CIN2.
164    OffsetCalCIN2,
165    /// Offset calibration for CIN3.
166    OffsetCalCIN3,
167    /// Offset calibration for CIN4.
168    OffsetCalCIN4,
169    /// Gain calibration for CIN1.
170    GainCalCIN1,
171    /// Gain calibration for CIN2.
172    GainCalCIN2,
173    /// Gain calibration for CIN3.
174    GainCalCIN3,
175    /// Gain calibration for CIN4.
176    GainCalCIN4,
177    /// Manufacturer ID register.
178    ManufacturerId,
179    /// Device ID register.
180    DeviceId,
181}
182
183impl RegisterAddress {
184    pub(crate) fn to_u8(&self) -> u8 {
185        match self {
186            RegisterAddress::Measurement1MSB => 0x00,
187            RegisterAddress::Measurement1LSB => 0x01,
188            RegisterAddress::Measurement2MSB => 0x02,
189            RegisterAddress::Measurement2LSB => 0x03,
190            RegisterAddress::Measurement3MSB => 0x04,
191            RegisterAddress::Measurement3LSB => 0x05,
192            RegisterAddress::Measurement4MSB => 0x06,
193            RegisterAddress::Measurement4LSB => 0x07,
194            RegisterAddress::Measurement1Config => 0x08,
195            RegisterAddress::Measurement2Config => 0x09,
196            RegisterAddress::Measurement3Config => 0x0A,
197            RegisterAddress::Measurement4Config => 0x0B,
198            RegisterAddress::FdcConf => 0x0C,
199            RegisterAddress::OffsetCalCIN1 => 0x0D,
200            RegisterAddress::OffsetCalCIN2 => 0x0E,
201            RegisterAddress::OffsetCalCIN3 => 0x0F,
202            RegisterAddress::OffsetCalCIN4 => 0x10,
203            RegisterAddress::GainCalCIN1 => 0x11,
204            RegisterAddress::GainCalCIN2 => 0x12,
205            RegisterAddress::GainCalCIN3 => 0x13,
206            RegisterAddress::GainCalCIN4 => 0x14,
207            RegisterAddress::ManufacturerId => 0xFE,
208            RegisterAddress::DeviceId => 0xFF,
209        }
210    }
211}
212
213static PICOFARADS_PER_CAPDAC: f32 = 3.125;
214
215/// Result of a capacitance measurement operation.
216/// 
217/// The FDC1004 can measure capacitances within a certain range. If the measured
218/// capacitance is outside this range, the result will indicate overflow or underflow.
219#[derive(Debug, Clone, Copy)]
220#[cfg_attr(feature = "defmt", derive(Format))]
221pub enum SuccessfulMeasurement {
222    /// Measurement completed successfully and is within the measurable range.
223    MeasurementInRange(MeasuredCapacitance),
224    /// Capacitance is too small to measure accurately (below measurement range).
225    Underflow,
226    /// Capacitance is too large to measure accurately (above measurement range).
227    Overflow,
228}
229
230/// A capacitance measurement result with associated CAPDAC offset.
231/// 
232/// Contains the raw measurement value from the FDC1004 along with the 
233/// CAPDAC setting used during measurement. The CAPDAC provides offset
234/// compensation to extend the measurement range.
235#[derive(Debug, Clone, Copy)]
236pub struct MeasuredCapacitance {
237    pub(crate) value: i24,
238    pub(crate) capdac: u8,
239}
240
241#[cfg(feature = "defmt")]
242impl Format for MeasuredCapacitance {
243    fn format(&self, f: defmt::Formatter) {
244        defmt::write!(f, "MeasuredCapacitance {} pF", self.to_pf());
245    }
246}
247
248impl MeasuredCapacitance {
249    pub(crate) fn new(value: i24, capdac: u8) -> Self {
250        MeasuredCapacitance {
251            value,
252            capdac,
253        }
254    }
255
256    /// Convert the measured capacitance value to picofarads.
257    pub fn to_pf(&self) -> f32 {
258        let vali32 : i32 = self.value.into();
259        let val: f32 = vali32 as f32;
260
261        let mut pf = val / 524_288f32;
262
263        pf += PICOFARADS_PER_CAPDAC * (self.capdac as f32);
264        pf
265    }
266}
267
268#[derive(Debug)]
269struct MeasurementConfiguration {
270    channel_a: Channel,
271    channel_b: Channel,
272    offset_capacitance: u8,
273}
274
275impl MeasurementConfiguration {
276    pub(crate) fn new(channel_a: Channel, channel_b: Channel, offset_capacitance: u8) -> Self {
277        MeasurementConfiguration {
278            channel_a,
279            channel_b,
280            offset_capacitance,
281        }
282    }
283
284    pub(crate) fn to_u16(&self) -> u16 {
285        let mut val = 0;
286
287        val |= match self.channel_a {
288            Channel::CIN1 => 0x0000,
289            Channel::CIN2 => 0x2000,
290            Channel::CIN3 => 0x4000,
291            Channel::CIN4 => 0x6000,
292            _ => 0x0000,
293        };
294
295        val |= match self.channel_b {
296            Channel::CIN1 => 0x0000,
297            Channel::CIN2 => 0x0400,
298            Channel::CIN3 => 0x0800,
299            Channel::CIN4 => 0x0C00,
300            Channel::CAPDAC => 0x1000,
301            Channel::DISABLED => 0x1C00,
302        };
303
304        val |= (self.offset_capacitance as u16) << 5;
305
306        val
307    }
308}
309
310#[derive(Default, Debug)]
311struct FDCConfiguration {
312    reset: bool,
313    rate: OutputRate,
314    repeat: bool,
315    initiate_measurement1: bool,
316    initiate_measurement2: bool,
317    initiate_measurement3: bool,
318    initiate_measurement4: bool,
319    measurement1_done: bool,
320    measurement2_done: bool,
321    measurement3_done: bool,
322    measurement4_done: bool,
323}
324
325impl FDCConfiguration {
326    pub(crate) fn from_u16(d: u16) -> Self {
327        let mut config = FDCConfiguration::default();
328
329        config.reset = d & (1u16 << 15) != 0;
330        config.rate = match d & 0x0C00 {
331            0x0400 => OutputRate::SPS100,
332            0x0800 => OutputRate::SPS200,
333            0x0C00 => OutputRate::SPS400,
334            _ => OutputRate::SPS100,
335        };
336        config.repeat = d & (1u16 << 8) != 0;
337        config.initiate_measurement1 = d & (1u16 << 7) != 0;
338        config.initiate_measurement2 = d & (1u16 << 6) != 0;
339        config.initiate_measurement3 = d & (1u16 << 5) != 0;
340        config.initiate_measurement4 = d & (1u16 << 4) != 0;
341        config.measurement1_done = d & (1u16 << 3) != 0;
342        config.measurement2_done = d & (1u16 << 2) != 0;
343        config.measurement3_done = d & (1u16 << 1) != 0;
344        config.measurement4_done = d & (1u16 << 0) != 0;
345
346        config
347    }
348
349    pub(crate) fn rate(&mut self, rate: OutputRate) -> &mut Self {
350        self.rate = rate;
351        self
352    }
353
354    pub(crate) fn initiate_measurement1(&mut self, initiate: bool) -> &mut Self {
355        self.initiate_measurement1 = initiate;
356        self
357    }
358
359    pub(crate) fn initiate_measurement2(&mut self, initiate: bool) -> &mut Self {
360        self.initiate_measurement2 = initiate;
361        self
362    }
363
364    pub(crate) fn initiate_measurement3(&mut self, initiate: bool) -> &mut Self {
365        self.initiate_measurement3 = initiate;
366        self
367    }
368
369    pub(crate) fn initiate_measurement4(&mut self, initiate: bool) -> &mut Self {
370        self.initiate_measurement4 = initiate;
371        self
372    }
373
374    #[allow(unused)]
375    pub(crate) fn reset(&mut self, reset: bool) -> &mut Self {
376        self.reset = reset;
377        self
378    }
379
380    #[allow(unused)]
381    pub(crate) fn repeat(&mut self, repeat: bool) -> &mut Self {
382        self.repeat = repeat;
383        self
384    }
385
386    pub(crate) fn to_u16(&self) -> u16 {
387        let mut val = 0;
388
389        val |= if self.reset { 1u16 << 15 } else { 0x0000 };
390        val |= match self.rate {
391            OutputRate::SPS100 => 0x0400,
392            OutputRate::SPS200 => 0x0800,
393            OutputRate::SPS400 => 0x0C00,
394        };
395        val |= if self.repeat { 1u16 << 8 } else { 0x0000 };
396        val |= if self.initiate_measurement1 { 1u16 << 7 } else { 0x0000 };
397        val |= if self.initiate_measurement2 { 1u16 << 6 } else { 0x0000 };
398        val |= if self.initiate_measurement3 { 1u16 << 5 } else { 0x0000 };
399        val |= if self.initiate_measurement4 { 1u16 << 4 } else { 0x0000 };
400        val |= if self.measurement1_done { 1u16 << 3 } else { 0x0000 };
401        val |= if self.measurement2_done { 1u16 << 2 } else { 0x0000 };
402        val |= if self.measurement3_done { 1u16 << 1 } else { 0x0000 };
403        val |= if self.measurement4_done { 1u16 << 0 } else { 0x0000 };
404
405        val
406    }
407}
408
409/// FDC1004 Capacitance-to-Digital Converter driver.
410/// 
411/// This driver provides an async interface to the Texas Instruments FDC1004
412/// 4-channel capacitance-to-digital converter. The FDC1004 can measure
413/// capacitances from femtofarads to picofarads with high resolution.
414/// 
415/// # Examples
416/// 
417/// ```no_run
418/// use variegated_fdc1004::{FDC1004, OutputRate, Channel};
419/// 
420/// // Create a new FDC1004 driver instance
421/// let mut fdc = FDC1004::new(i2c, 0x50, OutputRate::SPS100, delay);
422/// 
423/// // Read capacitance from channel 1
424/// let result = fdc.read_capacitance(Channel::CIN1).await?;
425/// ```
426pub struct FDC1004<I2C: I2c, D: DelayNs> {
427    i2c: I2C,
428    address: u8,
429    output_rate: OutputRate,
430    delay: D,
431}
432
433impl<I2C: I2c, D: DelayNs> FDC1004<I2C, D> 
434where 
435    I2C::Error: fmt::Debug,
436{
437    /// Create a new FDC1004 driver instance.
438    /// 
439    /// # Arguments
440    /// 
441    /// * `i2c` - I2C peripheral for communication with the FDC1004
442    /// * `address` - I2C address of the FDC1004 device (typically 0x50)
443    /// * `output_rate` - Desired measurement sample rate
444    /// * `delay` - Delay provider for timing operations
445    pub fn new(i2c: I2C, address: u8, output_rate: OutputRate, delay: D) -> Self {
446        FDC1004 {
447            i2c,
448            address,
449            output_rate,
450            delay,
451        }
452    }
453
454    /// Read capacitance from the specified channel with automatic CAPDAC adjustment.
455    /// 
456    /// This method automatically adjusts the CAPDAC setting to find the optimal
457    /// measurement range for the target capacitor. It will iterate through different
458    /// CAPDAC values until a valid measurement is obtained or the limits are reached.
459    /// 
460    /// # Arguments
461    /// 
462    /// * `channel` - The input channel to measure (CIN1-CIN4)
463    /// 
464    /// # Returns
465    /// 
466    /// * `Ok(SuccessfulMeasurement::MeasurementInRange)` - Valid measurement with capacitance data
467    /// * `Ok(SuccessfulMeasurement::Underflow)` - Capacitance below measurable range
468    /// * `Ok(SuccessfulMeasurement::Overflow)` - Capacitance above measurable range
469    /// * `Err` - Communication or configuration error
470    pub async fn read_capacitance(&mut self, channel: Channel) -> Result<SuccessfulMeasurement, FDC1004Error<I2C::Error>> {
471        let mut capdac: u8 = 0x00;
472
473        for _ in 0..33 {
474            let m = self.measure_channel(channel, capdac).await?;
475            if m < i24::max_value() && m > i24::min_value() {
476                return Ok(SuccessfulMeasurement::MeasurementInRange(MeasuredCapacitance::new(m, capdac)));
477            }
478
479            if m == i24::max_value() && capdac < CAPDAC_MAX {
480                capdac += 1;
481            } else if m == i24::min_value() && capdac > 0 {
482                capdac -= 1;
483            } else {
484                return match capdac {
485                    0 => Ok(SuccessfulMeasurement::Underflow),
486                    _ => Ok(SuccessfulMeasurement::Overflow)
487                };
488            }
489        }
490
491        Err(FDC1004Error::UnableToFindCapdacSetting)
492    }
493
494    /// Measure capacitance on a specific channel with a given CAPDAC setting.
495    /// 
496    /// This is a lower-level method that performs a single measurement with
497    /// a specific CAPDAC value. Use `read_capacitance` for automatic CAPDAC
498    /// adjustment instead.
499    /// 
500    /// # Arguments
501    /// 
502    /// * `channel` - The input channel to measure (CIN1-CIN4)
503    /// * `capdac` - CAPDAC offset value (0-31) for measurement range adjustment
504    /// 
505    /// # Returns
506    /// 
507    /// Raw 24-bit measurement value from the FDC1004.
508    pub async fn measure_channel(&mut self, channel: Channel, capdac: u8) -> Result<i24, FDC1004Error<I2C::Error>> {
509        // Map input channels to measurement slots, with proper error handling for invalid channels
510        let measurement = match channel {
511            Channel::CIN1 => Measurement::Measurement1,
512            Channel::CIN2 => Measurement::Measurement2,
513            Channel::CIN3 => Measurement::Measurement3,
514            Channel::CIN4 => Measurement::Measurement4,
515            // CAPDAC and DISABLED channels cannot be measured directly
516            Channel::CAPDAC | Channel::DISABLED => return Err(FDC1004Error::InvalidMeasurementChannel),
517        };
518
519        self.configure_single_measurement(channel, measurement.clone(), capdac).await?;
520        self.trigger_single_measurement(measurement.clone()).await?;
521        self.delay.delay_ns(self.output_rate.delay_ns()).await;
522
523        return self.read_measurement(measurement).await;
524    }
525
526    /// Configure a measurement channel with specific input and CAPDAC settings.
527    /// 
528    /// Sets up one of the four measurement channels with the specified input
529    /// channel and CAPDAC offset value.
530    /// 
531    /// # Arguments
532    /// 
533    /// * `channel` - Input channel to connect to this measurement
534    /// * `measurement` - Which measurement slot (1-4) to configure
535    /// * `capdac` - CAPDAC offset value for measurement range adjustment
536    pub async fn configure_single_measurement(&mut self, channel: Channel, measurement: Measurement, capdac: u8) -> Result<(), FDC1004Error<I2C::Error>> {
537        let config = MeasurementConfiguration::new(channel, Channel::CAPDAC, capdac);
538
539        self.write_u16(measurement.config_register(), config.to_u16()).await
540    }
541
542    /// Trigger a measurement on the specified measurement channel.
543    /// 
544    /// Initiates a capacitance measurement on one of the pre-configured
545    /// measurement channels. The measurement must be configured first using
546    /// `configure_single_measurement`.
547    /// 
548    /// # Arguments
549    /// 
550    /// * `measurement` - Which measurement slot (1-4) to trigger
551    pub async fn trigger_single_measurement(&mut self, measurement: Measurement) -> Result<(), FDC1004Error<I2C::Error>> {
552        let mut config = FDCConfiguration::default();
553        let config = config.rate(self.output_rate);
554        let config = match measurement {
555            Measurement::Measurement1 => config.initiate_measurement1(true),
556            Measurement::Measurement2 => config.initiate_measurement2(true),
557            Measurement::Measurement3 => config.initiate_measurement3(true),
558            Measurement::Measurement4 => config.initiate_measurement4(true),
559        };
560
561        self.write_u16(RegisterAddress::FdcConf, config.to_u16()).await
562    }
563
564    /// Read the result of a completed measurement.
565    /// 
566    /// Retrieves the measurement data from the specified measurement channel.
567    /// The measurement must be completed before calling this method, otherwise
568    /// a `MeasurementNotComplete` error will be returned.
569    /// 
570    /// # Arguments
571    /// 
572    /// * `measurement` - Which measurement slot (1-4) to read from
573    /// 
574    /// # Returns
575    /// 
576    /// Raw 24-bit measurement value from the FDC1004.
577    pub async fn read_measurement(&mut self, measurement: Measurement) -> Result<i24, FDC1004Error<I2C::Error>> {
578        // Wait for measurement to complete with timeout
579        const MAX_WAIT_ATTEMPTS: u8 = 10;
580        let wait_delay_ns = self.output_rate.delay_ns();
581        
582        for _ in 0..MAX_WAIT_ATTEMPTS {
583            let config = FDCConfiguration::from_u16(self.read_u16(RegisterAddress::FdcConf).await?);
584            
585            if measurement.ready_according_to_config(&config) {
586                break;
587            }
588            
589            // Wait for one sample period before checking again
590            self.delay.delay_ns(wait_delay_ns).await;
591        }
592        
593        // Final check - if still not ready, return error
594        let config = FDCConfiguration::from_u16(self.read_u16(RegisterAddress::FdcConf).await?);
595        if !measurement.ready_according_to_config(&config) {
596            return Err(FDC1004Error::MeasurementNotComplete);
597        }
598
599        let msb = self.read_u16(measurement.msb_register()).await? as i32;
600        let lsb = self.read_u16(measurement.lsb_register()).await? as i32;
601
602        let mut val24 = i24::default();
603        val24 |= i24::new(msb) << 8;
604        val24 |= i24::new(lsb) >> 8;
605
606        Ok(val24)
607    }
608
609    pub(crate) async fn write_u16(&mut self, reg: RegisterAddress, data: u16) -> Result<(), FDC1004Error<I2C::Error>> {
610        let data = data.to_be_bytes();
611        self.i2c.write(self.address, &[reg.to_u8(), data[0], data[1]]).await.map_err(|e| FDC1004Error::I2CError(e))
612    }
613
614    pub(crate) async fn read_u16(&mut self, reg: RegisterAddress) -> Result<u16, FDC1004Error<I2C::Error>> {
615        let mut data: [u8; 2] = [0,0];
616        self.i2c.write_read(self.address, &[reg.to_u8()], &mut data).await.map_err(|e| FDC1004Error::I2CError(e))?;
617
618        let be = u16::from_be_bytes(data);
619
620        return Ok(be);
621    }
622}
623