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}