5353_FAN_CONFIG = const (0x4A )
5454_FAN_SPINUP = const (0x4B )
5555_REG_FAN_SETTING = const (0x4C )
56+ _PWM_FREQ = const (0x4D )
5657
5758_REG_PARTID = const (0xFD ) # 0x16
5859_REG_MFGID = const (0xFE ) # 0xFF16
@@ -202,14 +203,13 @@ class to add those features, at the cost of increased memory usage.
202203 `forced_ext_temp`"""
203204
204205 _fan_setting = UnaryStruct (_REG_FAN_SETTING , "<B" )
206+ _pwm_freq = RWBits (5 , _PWM_FREQ , 0 )
205207 _fan_lut_prog = RWBit (_FAN_CONFIG , 5 )
206208 invert_fan_output = RWBit (_FAN_CONFIG , 4 )
207209 """When set to True, the magnitude of the fan output signal is inverted, making 0 the maximum
208210 value and 100 the minimum value"""
209211
210- dac_output_enabled = RWBit (_REG_CONFIG , 4 )
211- """When set, the fan control signal is output as a DC voltage instead of a PWM signal"""
212-
212+ _dac_output_enabled = RWBit (_REG_CONFIG , 4 )
213213 _conversion_rate = RWBits (4 , 0x04 , 0 )
214214 # fan spin-up
215215 _spin_drive = RWBits (2 , _FAN_SPINUP , 3 )
@@ -222,13 +222,15 @@ def __init__(self, i2c_bus):
222222 if not self ._part_id in [0x16 , 0x28 ] or self ._mfg_id != 0x5D :
223223 raise AttributeError ("Cannot find a EMC2101" )
224224
225+ self ._full_speed_lsb = None # See _calculate_full_speed().
225226 self .initialize ()
226227
227228 def initialize (self ):
228229 """Reset the controller to an initial default configuration"""
229230 self ._tach_mode_enable = True
230231 self ._enabled_forced_temp = False
231232 self ._spin_tach_limit = False
233+ self ._calculate_full_speed ()
232234
233235 @property
234236 def internal_temperature (self ):
@@ -255,27 +257,56 @@ def fan_speed(self):
255257 val |= self ._tach_read_msb << 8
256258 return _FAN_RPM_DIVISOR / val
257259
260+ def _calculate_full_speed (self , pwm_f = None , dac = None ):
261+ """Determine the LSB value for a 100% fan setting"""
262+ if dac is None :
263+ dac = self .dac_output_enabled
264+
265+ if dac :
266+ # DAC mode is independent of PWM_F.
267+ self ._full_speed_lsb = float (MAX_LUT_SPEED )
268+ return
269+
270+ # PWM mode reaches 100% duty cycle at a 2*PWM_F setting.
271+ if pwm_f is None :
272+ pwm_f = self ._pwm_freq
273+
274+ # PWM_F=0 behaves like PWM_F=1.
275+ self ._full_speed_lsb = 2.0 * max (1 , pwm_f )
276+
277+ def _speed_to_lsb (self , percentage ):
278+ """Convert a fan speed percentage to a Fan Setting byte value"""
279+ return round ((percentage / 100.0 ) * self ._full_speed_lsb )
280+
258281 @property
259282 def manual_fan_speed (self ):
260283 """The fan speed used while the LUT is being updated and is unavailable. The speed is
261284 given as the fan's PWM duty cycle represented as a float percentage.
262285 The value roughly approximates the percentage of the fan's maximum speed"""
263286 raw_setting = self ._fan_setting & MAX_LUT_SPEED
264- return (raw_setting / MAX_LUT_SPEED ) * 100
287+ return (raw_setting / self . _full_speed_lsb ) * 100
265288
266289 @manual_fan_speed .setter
267290 def manual_fan_speed (self , fan_speed ):
268291 if fan_speed not in range (0 , 101 ):
269292 raise AttributeError ("manual_fan_speed must be from 0-100" )
270293
271- # convert from a percentage to an lsb value
272- percentage = fan_speed / 100.0
273- fan_speed_lsb = round (percentage * MAX_LUT_SPEED )
294+ fan_speed_lsb = self ._speed_to_lsb (fan_speed )
274295 lut_disabled = self ._fan_lut_prog
275296 self ._fan_lut_prog = True
276297 self ._fan_setting = fan_speed_lsb
277298 self ._fan_lut_prog = lut_disabled
278299
300+ @property
301+ def dac_output_enabled (self ):
302+ """When set, the fan control signal is output as a DC voltage instead of a PWM signal"""
303+ return self ._dac_output_enabled
304+
305+ @dac_output_enabled .setter
306+ def dac_output_enabled (self , value ):
307+ self ._dac_output_enabled = value
308+ self ._calculate_full_speed (dac = value )
309+
279310 @property
280311 def lut_enabled (self ):
281312 """Enable or disable the internal look up table used to map a given temperature
0 commit comments