From e346bad8936afd4d5d928425da394f78ed5359e8 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 29 Oct 2021 17:01:33 -0700 Subject: [PATCH 1/4] Fix copy/paste error reported in #3. This was previously fixed in #4, but it looks like the fix got clobbered when the LUT code was split out to a separate file. --- adafruit_emc2101/emc2101_lut.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_emc2101/emc2101_lut.py b/adafruit_emc2101/emc2101_lut.py index 5b6d919..7d3dc6e 100644 --- a/adafruit_emc2101/emc2101_lut.py +++ b/adafruit_emc2101/emc2101_lut.py @@ -209,7 +209,7 @@ def pwm_frequency(self): def pwm_frequency(self, value): if value < 0 or value > 0x1F: raise AttributeError("pwm_frequency must be from 0-31") - self._pwm_freq_div = value + self._pwm_freq = value @property def pwm_frequency_divisor(self): From 42ad1607f229f74379eb507c4094112688764250 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 29 Oct 2021 18:05:51 -0700 Subject: [PATCH 2/4] Move `_speed_to_lsb()` into the `EMC2101` base class as a method. This conversion is going to depend on the PWM_F and DAC register values, so it needs to be a method. Use this method in `manual_fan_speed()` as well as the LUT subclass. Move the `_pwm_freq` register definition into the base class too. It will be needed for the fixed fan speed conversions. Also import the `MAX_LUT_*` constants from the base module rather than redefining them. --- adafruit_emc2101/__init__.py | 11 ++++++++--- adafruit_emc2101/emc2101_lut.py | 16 ++++------------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/adafruit_emc2101/__init__.py b/adafruit_emc2101/__init__.py index 8381713..9b2fc66 100644 --- a/adafruit_emc2101/__init__.py +++ b/adafruit_emc2101/__init__.py @@ -53,6 +53,7 @@ _FAN_CONFIG = const(0x4A) _FAN_SPINUP = const(0x4B) _REG_FAN_SETTING = const(0x4C) +_PWM_FREQ = const(0x4D) _REG_PARTID = const(0xFD) # 0x16 _REG_MFGID = const(0xFE) # 0xFF16 @@ -202,6 +203,7 @@ class to add those features, at the cost of increased memory usage. `forced_ext_temp`""" _fan_setting = UnaryStruct(_REG_FAN_SETTING, " Date: Fri, 29 Oct 2021 20:19:34 -0700 Subject: [PATCH 3/4] Set fan speeds relative to PWM_F. - Add a `_full_speed_lsb` member variable which represents the (smallest) LSB value corresponding to a 100% fan setting. - Add a `_calculate_full_speed()` method that computes it. - Use `_full_speed_lsb` when converting percentages to fan speed settings and back. Fixes #19. --- adafruit_emc2101/__init__.py | 24 +++++++++++++++++++++--- adafruit_emc2101/emc2101_lut.py | 1 + examples/emc2101_set_pwm_freq.py | 4 +++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/adafruit_emc2101/__init__.py b/adafruit_emc2101/__init__.py index 9b2fc66..7c4b729 100644 --- a/adafruit_emc2101/__init__.py +++ b/adafruit_emc2101/__init__.py @@ -224,6 +224,7 @@ def __init__(self, i2c_bus): if not self._part_id in [0x16, 0x28] or self._mfg_id != 0x5D: raise AttributeError("Cannot find a EMC2101") + self._full_speed_lsb = None # See _calculate_full_speed(). self.initialize() def initialize(self): @@ -231,6 +232,7 @@ def initialize(self): self._tach_mode_enable = True self._enabled_forced_temp = False self._spin_tach_limit = False + self._calculate_full_speed() @property def internal_temperature(self): @@ -257,10 +259,26 @@ def fan_speed(self): val |= self._tach_read_msb << 8 return _FAN_RPM_DIVISOR / val - # pylint: disable=no-self-use + def _calculate_full_speed(self, pwm_f=None, dac=None): + """Determine the LSB value for a 100% fan setting""" + if dac is None: + dac = self.dac_output_enabled + + if dac: + # DAC mode is independent of PWM_F. + self._full_speed_lsb = float(MAX_LUT_SPEED) + return + + # PWM mode reaches 100% duty cycle at a 2*PWM_F setting. + if pwm_f is None: + pwm_f = self._pwm_freq + + # PWM_F=0 behaves like PWM_F=1. + self._full_speed_lsb = 2.0 * max(1, pwm_f) + def _speed_to_lsb(self, percentage): """Convert a fan speed percentage to a Fan Setting byte value""" - return round((percentage / 100.0) * MAX_LUT_SPEED) + return round((percentage / 100.0) * self._full_speed_lsb) @property def manual_fan_speed(self): @@ -268,7 +286,7 @@ def manual_fan_speed(self): given as the fan's PWM duty cycle represented as a float percentage. The value roughly approximates the percentage of the fan's maximum speed""" raw_setting = self._fan_setting & MAX_LUT_SPEED - return (raw_setting / MAX_LUT_SPEED) * 100 + return (raw_setting / self._full_speed_lsb) * 100 @manual_fan_speed.setter def manual_fan_speed(self, fan_speed): diff --git a/adafruit_emc2101/emc2101_lut.py b/adafruit_emc2101/emc2101_lut.py index 075d059..39e7bf6 100644 --- a/adafruit_emc2101/emc2101_lut.py +++ b/adafruit_emc2101/emc2101_lut.py @@ -202,6 +202,7 @@ def pwm_frequency(self, value): if value < 0 or value > 0x1F: raise AttributeError("pwm_frequency must be from 0-31") self._pwm_freq = value + self._calculate_full_speed(pwm_f=value) @property def pwm_frequency_divisor(self): diff --git a/examples/emc2101_set_pwm_freq.py b/examples/emc2101_set_pwm_freq.py index 454f850..cc61a6a 100644 --- a/examples/emc2101_set_pwm_freq.py +++ b/examples/emc2101_set_pwm_freq.py @@ -10,7 +10,9 @@ emc = EMC2101(i2c) emc.set_pwm_clock(use_preset=False) # Datasheet recommends using the maximum value of 31 (0x1F) -# to provide the highest effective resolution +# to provide the highest effective resolution. +# The PWM frequency must be set before changing `manual_fan_speed` or LUT entries. +# Otherwise the PWM duty cycle won't be configured correctly. emc.pwm_frequency = 14 # This divides the pwm frequency down to a smaller number From aea50b1b61a861319d43d7e91f22c3fc97271eef Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Sat, 30 Oct 2021 10:15:35 -0700 Subject: [PATCH 4/4] Change `dac_output_enabled` to a property. Recalculate the full speed LSB setting when this property is changed too. --- adafruit_emc2101/__init__.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/adafruit_emc2101/__init__.py b/adafruit_emc2101/__init__.py index 7c4b729..828cdd0 100644 --- a/adafruit_emc2101/__init__.py +++ b/adafruit_emc2101/__init__.py @@ -209,9 +209,7 @@ class to add those features, at the cost of increased memory usage. """When set to True, the magnitude of the fan output signal is inverted, making 0 the maximum value and 100 the minimum value""" - dac_output_enabled = RWBit(_REG_CONFIG, 4) - """When set, the fan control signal is output as a DC voltage instead of a PWM signal""" - + _dac_output_enabled = RWBit(_REG_CONFIG, 4) _conversion_rate = RWBits(4, 0x04, 0) # fan spin-up _spin_drive = RWBits(2, _FAN_SPINUP, 3) @@ -299,6 +297,16 @@ def manual_fan_speed(self, fan_speed): self._fan_setting = fan_speed_lsb self._fan_lut_prog = lut_disabled + @property + def dac_output_enabled(self): + """When set, the fan control signal is output as a DC voltage instead of a PWM signal""" + return self._dac_output_enabled + + @dac_output_enabled.setter + def dac_output_enabled(self, value): + self._dac_output_enabled = value + self._calculate_full_speed(dac=value) + @property def lut_enabled(self): """Enable or disable the internal look up table used to map a given temperature