emc2101/
lib.rs

1//! A platform agnostic Rust driver for EMC2101, based on the
2//! [`embedded-hal`](https://github.com/rust-embedded/embedded-hal) traits.
3
4#![no_std]
5#![macro_use]
6pub(crate) mod fmt;
7
8mod error;
9use bounded_integer::BoundedU8;
10pub use error::{Error, Result};
11
12#[cfg(not(any(feature = "sync", feature = "async")))]
13compile_error!("You should probably choose at least one of `sync` and `async` features.");
14
15#[cfg(feature = "sync")]
16use embedded_hal::i2c::ErrorType;
17#[cfg(feature = "sync")]
18use embedded_hal::i2c::I2c;
19#[cfg(feature = "async")]
20use embedded_hal_async::i2c::ErrorType as AsyncErrorType;
21#[cfg(feature = "async")]
22use embedded_hal_async::i2c::I2c as AsyncI2c;
23
24use fugit::HertzU32;
25use heapless::Vec;
26
27/// EMC2101 sensor's I2C address.
28pub const DEFAULT_ADDRESS: u8 = 0b1001100; // This is I2C address 0x4C
29
30const EMC2101_PRODUCT_ID: u8 = 0x16;
31const EMC2101R_PRODUCT_ID: u8 = 0x28;
32
33/// EMC2101 sensor's Product.
34#[derive(Debug, Clone, Copy)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum Product {
37    EMC2101,
38    EMC2101R,
39}
40
41/// ADC Conversion Rates.
42#[derive(Debug, Clone, Copy)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44#[repr(u8)]
45pub enum ConversionRate {
46    Rate1_16Hz = 0,
47    Rate1_8Hz = 1,
48    Rate1_4Hz = 2,
49    Rate1_2Hz = 3,
50    Rate1Hz = 4,
51    Rate2Hz = 5,
52    Rate4Hz = 6,
53    Rate8Hz = 7,
54    Rate16Hz = 8,
55    Rate32Hz = 9,
56}
57
58/// ADC Filter Levels.
59#[derive(Debug, Clone, Copy)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61#[repr(u8)]
62pub enum FilterLevel {
63    Disabled = 0,
64    Level1 = 1,
65    Level2 = 3,
66}
67
68/// Registers of the EMC2101 sensor.
69#[cfg(any(feature = "async", feature = "sync"))]
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71enum Register {
72    InternalTemperature = 0x00,
73    ExternalTemperatureMSB = 0x01,
74    Status = 0x02,
75    Configuration = 0x03,
76    ConversionRate = 0x04,
77    InternalTempLimit = 0x05,
78    ExternalTempLimitHigh = 0x07,
79    ExternalTempLimitLow = 0x08,
80    ExternalTemperatureForce = 0x0C,
81    ExternalTemperatureLSB = 0x10,
82    AlertMask = 0x16,
83    ExternalTempCriticalLimit = 0x19,
84    ExternalTempCriticalHysteresis = 0x21,
85    TachLSB = 0x46,
86    TachMSB = 0x47,
87    FanConfig = 0x4A,
88    FanSetting = 0x4C,
89    PWMFrequency = 0x4D,
90    PWMFrequencyDivide = 0x4E,
91    FanControlLUTHysteresis = 0x4F,
92    FanControlLUTT1 = 0x50,
93    FanControlLUTS1 = 0x51,
94    AveragingFilter = 0xBF,
95    ProductID = 0xFD,
96}
97
98#[cfg(any(feature = "async", feature = "sync"))]
99impl From<Register> for u8 {
100    fn from(r: Register) -> u8 {
101        r as u8
102    }
103}
104
105/// Device Satuts.
106#[derive(Debug, Clone, Copy, Default)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub struct Status {
109    pub eeprom_error: bool,
110    pub ext_diode_fault: bool,
111    pub adc_busy: bool,
112    pub temp_int_high: bool,
113    pub temp_ext_high: bool,
114    pub temp_ext_low: bool,
115    pub temp_ext_critical: bool,
116    pub tack_limit: bool,
117}
118
119impl From<u8> for Status {
120    fn from(s: u8) -> Self {
121        Self {
122            eeprom_error: s & 0x20 == 0x20,
123            ext_diode_fault: s & 0x04 == 0x04,
124            adc_busy: s & 0x80 == 0x80,
125            temp_int_high: s & 0x40 == 0x40,
126            temp_ext_high: s & 0x10 == 0x10,
127            temp_ext_low: s & 0x08 == 0x08,
128            temp_ext_critical: s & 0x02 == 0x02,
129            tack_limit: s & 0x01 == 0x01,
130        }
131    }
132}
133
134/// Look-up Table Level
135#[derive(Debug, Clone, Copy, Default)]
136pub struct Level {
137    pub temp: BoundedU8<0, 127>,
138    pub step: BoundedU8<0, 63>,
139}
140
141/// Manual implementation of defmt 'Format' trait, since BoundedU8 does not implement it.
142#[cfg(feature = "defmt")]
143impl defmt::Format for Level {
144    fn format(&self, fmt: defmt::Formatter) {
145        defmt::write!(
146            fmt,
147            "Level {{ temp: {=u8}, step: {=u8} }}",
148            self.temp.get(),
149            self.step.get(),
150        );
151    }
152}
153
154/// An EMC2101 sensor on the I2C bus `I`.
155///
156/// The address of the sensor will be `DEFAULT_ADDRESS` from this package,
157/// unless there is some kind of special address translating hardware in use.
158#[maybe_async_cfg::maybe(
159    sync(feature = "sync", self = "EMC2101"),
160    async(feature = "async", keep_self)
161)]
162pub struct AsyncEMC2101<I> {
163    i2c: I,
164    address: u8,
165    variant: Option<Product>,
166}
167
168#[maybe_async_cfg::maybe(
169    sync(
170        feature = "sync",
171        self = "EMC2101",
172        idents(AsyncI2c(sync = "I2c"), AsyncErrorType(sync = "ErrorType"))
173    ),
174    async(feature = "async", keep_self)
175)]
176impl<I: AsyncI2c + AsyncErrorType> AsyncEMC2101<I> {
177    /// Initializes the EMC2101 driver.
178    ///
179    /// This consumes the I2C bus `I`. The address will almost always
180    /// be `DEFAULT_ADDRESS` from this crate.
181    pub async fn with_address(i2c: I, address: u8) -> Result<Self, I::Error> {
182        let mut emc2101 = AsyncEMC2101 {
183            i2c,
184            address,
185            variant: None,
186        };
187        trace!("new");
188        emc2101.variant = Some(emc2101.check_id().await?);
189        // Disable all alerts interrupt, will be enable one by one calling monitor_xxx() functions.
190        emc2101.write_reg(Register::AlertMask, 0xFF).await?;
191        Ok(emc2101)
192    }
193    pub async fn new(i2c: I) -> Result<Self, I::Error> {
194        AsyncEMC2101::with_address(i2c, DEFAULT_ADDRESS).await
195    }
196
197    /// check_id asks the EMC2101 sensor to report its Product.
198    async fn check_id(&mut self) -> Result<Product, I::Error> {
199        trace!("check_id");
200        match self.read_reg(Register::ProductID).await? {
201            EMC2101_PRODUCT_ID => Ok(Product::EMC2101),
202            EMC2101R_PRODUCT_ID => Ok(Product::EMC2101R),
203            _ => Err(Error::InvalidID),
204        }
205    }
206
207    /// status gives the device current status.
208    pub async fn status(&mut self) -> Result<Status, I::Error> {
209        trace!("status");
210        Ok(self.read_reg(Register::Status).await?.into())
211    }
212
213    /// configure_adc set the conversion rate in Hertz and the filter level.
214    pub async fn configure_adc(
215        &mut self,
216        rate: ConversionRate,
217        filter: FilterLevel,
218    ) -> Result<&mut Self, I::Error> {
219        trace!("configure_adc");
220        // ConversionRate[3:0] : ADC conversion rate.
221        self.write_reg(Register::ConversionRate, rate as u8).await?;
222        // AveragingFilter[2:1] FILTER[1:0] : control the level of digital filtering
223        // that is applied to the External Diode temperature measurements.
224        let f_set: u8 = (filter as u8) << 1;
225        let f_clr: u8 = !f_set & 0x06;
226        self.update_reg(Register::AveragingFilter, f_set, f_clr)
227            .await?;
228        Ok(self)
229    }
230
231    /// force_temp_external force the external temperature value to a virtual value.
232    /// When determining the position of the Fan Control Look-up Table, the contents
233    /// of the ExternalTemperatureForce Register will be used instead of the measured
234    /// External Diode temperature as normal.
235    pub async fn force_temp_external(&mut self, value: i8) -> Result<&mut Self, I::Error> {
236        trace!("force_temp_external");
237        self.write_reg(Register::ExternalTemperatureForce, value as u8)
238            .await?;
239        // Set FanConfig[6] FORCE : the ExternalTemperatureForce Register is used.
240        self.update_reg(Register::FanConfig, 0b0100_0000, 0).await?;
241        Ok(self)
242    }
243
244    /// real_temp_external let the measured External Diode temperature be used to
245    /// determine the position in the Fan Control Look-up Table.
246    pub async fn real_temp_external(&mut self) -> Result<&mut Self, I::Error> {
247        trace!("real_temp_external");
248        // Clear FanConfig[6] FORCE : the ExternalTemperatureForce Register is not used.
249        self.update_reg(Register::FanConfig, 0, 0b0100_0000).await?;
250        Ok(self)
251    }
252
253    /// temp_conversion_rate get the current conversion rate in Hertz.
254    pub async fn temp_conversion_rate(&mut self) -> Result<ConversionRate, I::Error> {
255        trace!("temp_conversion_rate");
256        let rate: ConversionRate = match self.read_reg(Register::ConversionRate).await? {
257            0 => ConversionRate::Rate1_16Hz,
258            1 => ConversionRate::Rate1_8Hz,
259            2 => ConversionRate::Rate1_4Hz,
260            3 => ConversionRate::Rate1_2Hz,
261            4 => ConversionRate::Rate1Hz,
262            5 => ConversionRate::Rate2Hz,
263            6 => ConversionRate::Rate4Hz,
264            7 => ConversionRate::Rate8Hz,
265            8 => ConversionRate::Rate16Hz,
266            9..=15 => ConversionRate::Rate32Hz,
267            _ => return Err(Error::InvalidValue),
268        };
269        Ok(rate)
270    }
271
272    /// temp_internal read the internal temperature value in degree Celsius.
273    pub async fn temp_internal(&mut self) -> Result<i8, I::Error> {
274        trace!("temp_internal");
275        Ok(self.read_reg(Register::InternalTemperature).await? as i8)
276    }
277
278    /// temp_external read the external temperature value in degree Celsius.
279    pub async fn temp_external(&mut self) -> Result<i8, I::Error> {
280        trace!("temp_external");
281        Ok(self.read_reg(Register::ExternalTemperatureMSB).await? as i8)
282    }
283
284    /// temp_external_precise read the external temperature value in degree Celsius.
285    pub async fn temp_external_precise(&mut self) -> Result<f32, I::Error> {
286        trace!("temp_external_precise");
287        let msb = self.read_reg(Register::ExternalTemperatureMSB).await?;
288        let lsb = self.read_reg(Register::ExternalTemperatureLSB).await?;
289        let raw: i16 = (((msb as u16) << 8) + lsb as u16) as i16;
290        let ret: f32 = (raw >> 5) as f32 * 0.125;
291        Ok(ret)
292    }
293
294    /// monitor_temp_internal_high start monitoring the internal temperature and will create
295    /// an alert when the temperature exceeds the limit.
296    /// The temp_int_high will be true in Status until the internal temperature drops below the high limit.
297    pub async fn monitor_temp_internal_high(&mut self, limit: i8) -> Result<&mut Self, I::Error> {
298        trace!("monitor_temp_internal_high");
299        // If the measured temperature for the internal diode exceeds the Internal Temperature limit,
300        // then the INT_HIGH bit is set in the Status Register. It remains set until the internal
301        // temperature drops below the high limit.
302        self.write_reg(Register::InternalTempLimit, limit as u8)
303            .await?;
304        // Clear AlertMask[6] INT_MSK : The Internal Diode will generate an interrupt if measured temperature
305        // exceeds the Internal Diode high limit.
306        self.update_reg(Register::AlertMask, 0, 0b0100_0000).await?;
307        Ok(self)
308    }
309
310    /// monitor_temp_external_critical start monitoring the external temperature and will create
311    /// an alert when the temperature exceeds the critical limit.
312    /// The temp_ext_critical will be true in Status until the external temperature drops below the critical
313    /// limit minus critical hysteresis.
314    pub async fn monitor_temp_external_critical(
315        &mut self,
316        limit: i8,
317        hysteresis: u8,
318    ) -> Result<&mut Self, I::Error> {
319        trace!("monitor_temp_external_critical");
320        // If the external diode exceeds the TCRIT Temp limit (even if it does not exceeds the External Diode
321        // Temperature Limit), the TCRIT bit is set in the Status Register. It remains set until the external
322        // temperature drops below the Critical Limit minus the Critical Hysteresis.
323        self.write_reg(Register::ExternalTempCriticalLimit, limit as u8)
324            .await?;
325        self.write_reg(Register::ExternalTempCriticalHysteresis, hysteresis)
326            .await?;
327        // Clear AlertMask[4] HIGH_MSK : The External Diode will generate an interrupt if measured temperature
328        // exceeds the External Diode high limit.
329        self.update_reg(Register::AlertMask, 0, 0b0001_0000).await?;
330        Ok(self)
331    }
332
333    /// monitor_temp_external_high start monitoring the external temperature and will create
334    /// an alert when the temperature exceeds the high limit.
335    /// The temp_ext_high will be true in Status until the external temperature drops below the high limit.
336    pub async fn monitor_temp_external_high(&mut self, limit: i8) -> Result<&mut Self, I::Error> {
337        trace!("monitor_temp_external_high");
338        // If the measured temperature for the external diode exceeds the External Temperature High limit,
339        // then the EXT_HIGH bit is set in the Status Register. It remains set until the external
340        // temperature drops below the high limit.
341        self.write_reg(Register::ExternalTempLimitHigh, limit as u8)
342            .await?;
343        // Clear AlertMask[4] HIGH_MSK : The External Diode will generate an interrupt if measured temperature
344        // exceeds the External Diode high limit.
345        self.update_reg(Register::AlertMask, 0, 0b0001_0000).await?;
346        Ok(self)
347    }
348
349    /// monitor_temp_external_low start monitoring the external temperature and will create
350    /// an alert when the temperature drops below the low limit.
351    /// The temp_ext_low will be true in Status until the external temperature exceeds the low limit.
352    pub async fn monitor_temp_external_low(&mut self, limit: i8) -> Result<&mut Self, I::Error> {
353        trace!("monitor_temp_external_low");
354        // If the measured temperature for the external diode drops below the External Temperature Low limit,
355        // then the EXT_LOW bit is set in the Status Register. It remains set until the external
356        // temperature exceeds the low limit.
357        self.write_reg(Register::ExternalTempLimitLow, limit as u8)
358            .await?;
359        // Clear AlertMask[3] LOW_MSK : The External Diode will generate an interrupt if measured temperature
360        // drops below the External Diode low limit.
361        self.update_reg(Register::AlertMask, 0, 0b0000_1000).await?;
362        Ok(self)
363    }
364
365    /// enable_alert_output configure ALERT#/TACH pin as open drain active low ALERT# interrupt.
366    /// This may require an external pull-up resistor to set the proper signaling levels.
367    pub async fn enable_alert_output(&mut self) -> Result<&mut Self, I::Error> {
368        trace!("enable_alert_output");
369        // Clear Configuration[2] ALT_TCH : The ALERT#/TACH pin will function as open drain,
370        // active low interrupt.
371        // Clear Configuration[7] MASK : The ALERT#/TACH pin will be asserted if any bit is set in the
372        // Status Register. Once the pin is asserted, it remains asserted.
373        self.update_reg(Register::Configuration, 0, 0b1000_0100)
374            .await?;
375        Ok(self)
376    }
377
378    /// enable_tach_input configure ALERT#/TACH pin as high impedance TACH input.
379    pub async fn enable_tach_input(&mut self) -> Result<&mut Self, I::Error> {
380        trace!("enable_tach_input");
381        // Set Configuration[2] ALT_TCH : The ALERT#/TACH pin will function as high impedance TACH input.
382        self.update_reg(Register::Configuration, 0b0000_0100, 0)
383            .await?;
384        Ok(self)
385    }
386
387    /// set_fan_pwm set FAN in PWM mode and configure it's base frequency.
388    pub async fn set_fan_pwm(
389        &mut self,
390        frequency: HertzU32,
391        inverted: bool,
392    ) -> Result<&mut Self, I::Error> {
393        trace!("set_fan_pwm");
394        match frequency.raw() {
395            1_400 => {
396                // Set FanConfig[3] CLK_SEL : The base clock that is used to determine the PWM
397                // frequency is 1.4kHz.
398                // Clear FanConfig[2] CLK_OVR : The base clock frequency is determined by the
399                // CLK_SEL bit.
400                if inverted {
401                    // Set FanConfig[4] POLARITY : The polarity of the Fan output driver is inverted.
402                    // A 0x00 setting will correspond to a 100% duty cycle or maximum DAC output voltage.
403                    self.update_reg(Register::FanConfig, 0b0001_1000, 0b0000_0100)
404                        .await?;
405                } else {
406                    // Clear FanConfig[4] POLARITY : The polarity of the Fan output driver is non-inverted.
407                    // A 0x00 setting will correspond to a 0% duty cycle or minimum DAC output voltage.
408                    self.update_reg(Register::FanConfig, 0b0000_1000, 0b0001_0100)
409                        .await?;
410                }
411            }
412            360_000 => {
413                // Clear FanConfig[3] CLK_SEL : The base clock that is used to determine the PWM
414                // frequency is 360kHz.
415                // Clear FanConfig[2] CLK_OVR : The base clock frequency is determined by the
416                // CLK_SEL bit.
417                if inverted {
418                    // Set FanConfig[4] POLARITY : The polarity of the Fan output driver is inverted.
419                    // A 0x00 setting will correspond to a 100% duty cycle or maximum DAC output voltage.
420                    self.update_reg(Register::FanConfig, 0b0001_0000, 0b0000_1100)
421                        .await?;
422                } else {
423                    // Clear FanConfig[4] POLARITY : The polarity of the Fan output driver is non-inverted.
424                    // A 0x00 setting will correspond to a 0% duty cycle or minimum DAC output voltage.
425                    self.update_reg(Register::FanConfig, 0, 0b0001_1100).await?;
426                }
427            }
428            23..=160_000 => {
429                // Set FanConfig[2] CLK_OVR : The base clock that is used to determine the PWM frequency
430                // is set by the Frequency Divide Register.
431                if inverted {
432                    // Set FanConfig[4] POLARITY : The polarity of the Fan output driver is inverted.
433                    // A 0x00 setting will correspond to a 100% duty cycle or maximum DAC output voltage.
434                    self.update_reg(Register::FanConfig, 0b0001_0010, 0b0000_1000)
435                        .await?;
436                } else {
437                    // Clear FanConfig[4] POLARITY : The polarity of the Fan output driver is non-inverted.
438                    // A 0x00 setting will correspond to a 0% duty cycle or minimum DAC output voltage.
439                    self.update_reg(Register::FanConfig, 0b0000_0010, 0b0001_1000)
440                        .await?;
441                }
442                // The PWM frequency when the PWMFrequencyDivide Register is used is shown in equation :
443                // PWM_D = (360k / (2 * PWM_F * FREQ))
444                let div: u16 = (160_000u32 / frequency.raw()) as u16;
445                let pwm_f: u8 = (div >> 8) as u8 & 0x1F;
446                let pwm_d: u8 = (div & 0xFF) as u8;
447                // The PWMFrequency Register determines the final PWM frequency and "effective resolution"
448                // of the PWM driver.
449                self.write_reg(Register::PWMFrequency, pwm_f).await?;
450                // When the CLK_OVR bit is set to a logic '1', the PWMFrequencyDivide Register is used in
451                // conjunction with the PWMFrequency Register to determine the final PWM frequency that the
452                // load will see.
453                self.write_reg(Register::PWMFrequencyDivide, pwm_d).await?;
454            }
455            _ => {
456                error!("Invalid PWM Frequency.");
457                return Err(Error::InvalidValue);
458            }
459        }
460        // Clear Configuration[4] DAC : PWM output enabled at FAN pin.
461        self.update_reg(Register::Configuration, 0, 0b0001_0000)
462            .await?;
463        Ok(self)
464    }
465
466    /// set_fan_dac set FAN in DAC mode.
467    pub async fn set_fan_dac(&mut self, inverted: bool) -> Result<&mut Self, I::Error> {
468        trace!("set_fan_dac");
469        // Set Configuration[4] DAC : DAC output enabled at FAN pin.
470        self.update_reg(Register::Configuration, 0b0001_0000, 0)
471            .await?;
472        if inverted {
473            // Set FanConfig[4] POLARITY : The polarity of the Fan output driver is inverted.
474            // A 0x00 setting will correspond to a 100% duty cycle or maximum DAC output voltage.
475            self.update_reg(Register::FanConfig, 0b0001_0000, 0).await?;
476        } else {
477            // Clear FanConfig[4] POLARITY : The polarity of the Fan output driver is non-inverted.
478            // A 0x00 setting will correspond to a 0% duty cycle or minimum DAC output voltage.
479            self.update_reg(Register::FanConfig, 0, 0b0001_0000).await?;
480        }
481        Ok(self)
482    }
483
484    /// set_fan_power set the FAN power (for both modes PWM/DAC).
485    /// The 'step' must be between 0 and 63.
486    /// If Look-up Table was enabled, it will be disabled and the fixed power value will be used.
487    pub async fn set_fan_power(&mut self, step: BoundedU8<0, 63>) -> Result<&mut Self, I::Error> {
488        trace!("set_fan_power");
489        let fan_config: u8 = self.read_reg(Register::FanConfig).await?;
490        // FanConfig[5] PROG == 0 :
491        // the FanSetting Register and Fan Control Look-Up Table Registers are read-only.
492        if fan_config & 0x20 == 0x00 {
493            // Set FanConfig[5] PROG : the FanSetting Register and Fan Control Look-Up
494            // Table Registers can be written and the Fan Control Look-Up Table Registers
495            // will not be used.
496            self.write_reg(Register::FanConfig, fan_config | 0x20)
497                .await?;
498        }
499        // The FanSetting Register drives the fan driver when the Fan Control Look-Up
500        // Table is not used.
501        // Any data written to the FanSetting register is applied immediately to the
502        // fan driver (PWM or DAC).
503        self.write_reg(Register::FanSetting, step.get()).await?;
504        Ok(self)
505    }
506
507    /// set_fan_lut set the FAN according to a Look-up Table with hysteresis.
508    /// The 'lut' must be 8 or less levels and be ordered with lower temperature value first.
509    /// Each level temperature must be between 0 and 127 degrees Celsius.
510    /// Each level step must be between 0 and 63.
511    pub async fn set_fan_lut(
512        &mut self,
513        lut: Vec<Level, 8>,
514        hysteresis: BoundedU8<0, 31>,
515    ) -> Result<&mut Self, I::Error> {
516        trace!("set_fan_lut");
517        if lut.is_empty() {
518            return Err(Error::InvalidSize);
519        }
520        let fan_config: u8 = self.read_reg(Register::FanConfig).await?;
521        // FanConfig[5] PROG == 0 :
522        // the FanSetting Register and Fan Control Look-Up Table Registers are read-only
523        // and the Fan Control Look-Up Table Registers will be used.
524        if fan_config & 0x20 == 0x00 {
525            // Set FanConfig[5] PROG : the FanSetting Register and Fan Control Look-Up
526            // Table Registers can be written and the Fan Control Look-Up Table Registers
527            // will not be used.
528            self.write_reg(Register::FanConfig, fan_config | 0x20)
529                .await?;
530        }
531        let mut last_temp = <BoundedU8<0, 127>>::new(0).unwrap();
532        for (index, level) in (0_u8..).zip(lut.iter()) {
533            if level.temp <= last_temp {
534                return Err(Error::InvalidSorting);
535            }
536            last_temp = level.temp;
537            self.write_reg(
538                (Register::FanControlLUTT1 as u8) + 2 * index,
539                level.temp.get(),
540            )
541            .await?;
542            self.write_reg(
543                (Register::FanControlLUTS1 as u8) + 2 * index,
544                level.step.get(),
545            )
546            .await?;
547        }
548        // FanControlLUTHysteresis determines the amount of hysteresis applied to the temperature
549        // inputs of the fan control Fan Control Look-Up Table.
550        self.write_reg(Register::FanControlLUTHysteresis, hysteresis.get())
551            .await?;
552        // Clear FanConfig[5] PROG : the FanSetting Register and Fan Control Look-Up Table
553        // Registers are read-only and the Fan Control Look-Up Table Registers will be used.
554        self.write_reg(Register::FanConfig, fan_config | 0x20)
555            .await?;
556        Ok(self)
557    }
558
559    /// fan_rpm gives the Fan speed in RPM.
560    pub async fn fan_rpm(&mut self) -> Result<u16, I::Error> {
561        trace!("fan_rpm");
562        let msb = self.read_reg(Register::TachMSB).await?;
563        let lsb = self.read_reg(Register::TachLSB).await?;
564        let raw: u16 = ((msb as u16) << 8) + (lsb as u16);
565        Ok(if raw == 0xFFFF {
566            0
567        } else {
568            (5_400_000u32 / (raw as u32)) as u16
569        })
570    }
571
572    /// read_reg read a register value.
573    async fn read_reg<R: Into<u8>>(&mut self, reg: R) -> Result<u8, I::Error> {
574        trace!("read_reg");
575        let mut buf = [0x00];
576        let reg = reg.into();
577        self.i2c
578            .write_read(self.address, &[reg], &mut buf)
579            .await
580            .map_err(Error::I2c)?;
581        debug!("R @0x{:x}={:x}", reg, buf[0]);
582        Ok(buf[0])
583    }
584
585    /// write_reg blindly write a single register with a fixed value.
586    async fn write_reg<R: Into<u8>>(&mut self, reg: R, value: u8) -> Result<(), I::Error> {
587        trace!("write_reg");
588        let reg = reg.into();
589        debug!("W @0x{:x}={:x}", reg, value);
590        self.i2c
591            .write(self.address, &[reg, value])
592            .await
593            .map_err(Error::I2c)
594    }
595
596    /// update_reg first read the register value, apply a set mask, then a clear mask, and write the new value
597    /// only if different from the initial value.
598    async fn update_reg<R: Into<u8> + Clone>(
599        &mut self,
600        reg: R,
601        mask_set: u8,
602        mask_clear: u8,
603    ) -> Result<(), I::Error> {
604        trace!("update_reg");
605        let current = self.read_reg(reg.clone()).await?;
606        let updated = current | mask_set & !mask_clear;
607        if current != updated {
608            self.write_reg(reg, updated).await?;
609        }
610        Ok(())
611    }
612
613    /// Return the underlying I2C device
614    pub fn release(self) -> I {
615        self.i2c
616    }
617
618    /// Destroys this driver and releases the I2C bus `I`.
619    pub fn destroy(self) -> Self {
620        self
621    }
622}
623
624#[cfg(test)]
625mod test {
626    // extern crate alloc;
627    extern crate std;
628
629    use super::*;
630    use embedded_hal_mock::eh1::i2c;
631    use std::vec;
632
633    #[test]
634    fn new_emc2101() {
635        let expectations = [
636            i2c::Transaction::write_read(
637                DEFAULT_ADDRESS,
638                vec![Register::ProductID as u8],
639                vec![EMC2101_PRODUCT_ID],
640            ),
641            i2c::Transaction::write(DEFAULT_ADDRESS, vec![Register::AlertMask as u8, 0xFF]),
642        ];
643        let mock = i2c::Mock::new(&expectations);
644        let emc2101 = EMC2101::new(mock).unwrap();
645
646        let mut mock = emc2101.release();
647        mock.done();
648    }
649}