8
8
Brushless fan controller: extended functionality
9
9
10
10
11
- * Author(s): Bryan Siepert
11
+ * Author(s): Bryan Siepert, Ryan Pavlik
12
12
13
13
Implementation Notes
14
14
--------------------
22
22
* Adafruit CircuitPython firmware for the supported boards:
23
23
https://github.com/adafruit/circuitpython/releases
24
24
25
- # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
26
- # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
25
+ * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
26
+ * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
27
27
28
- The class defined here may be used instead of adafruit_emc2101.EMC2101,
28
+ The class defined here may be used instead of :class:` adafruit_emc2101.EMC2101` ,
29
29
if your device has enough RAM to support it. This class adds LUT control
30
30
and PWM frequency control to the base feature set.
31
31
"""
32
32
33
33
from micropython import const
34
+ from adafruit_register .i2c_struct_array import StructArray
34
35
from adafruit_register .i2c_struct import UnaryStruct
35
36
from adafruit_register .i2c_bit import RWBit
36
37
from adafruit_register .i2c_bits import RWBits
43
44
_PWM_FREQ = const (0x4D )
44
45
_PWM_DIV = const (0x4E )
45
46
_LUT_HYSTERESIS = const (0x4F )
47
+ _LUT_BASE = const (0x50 )
46
48
47
49
MAX_LUT_SPEED = 0x3F # 6-bit value
48
50
MAX_LUT_TEMP = 0x7F # 7-bit
@@ -54,32 +56,16 @@ def _speed_to_lsb(percentage):
54
56
55
57
class FanSpeedLUT :
56
58
"""A class used to provide a dict-like interface to the EMC2101's Temperature to Fan speed
57
- Look Up Table"""
59
+ Look Up Table.
58
60
59
- # seems like a pain but ¯\_(ツ)_/¯
60
- _fan_lut_t1 = UnaryStruct (0x50 , "<B" )
61
- _fan_lut_s1 = UnaryStruct (0x51 , "<B" )
61
+ Keys are integer temperatures, values are fan duty cycles between 0 and 100.
62
+ A max of 8 values may be stored.
62
63
63
- _fan_lut_t2 = UnaryStruct (0x52 , "<B" )
64
- _fan_lut_s2 = UnaryStruct (0x53 , "<B" )
65
-
66
- _fan_lut_t3 = UnaryStruct (0x54 , "<B" )
67
- _fan_lut_s3 = UnaryStruct (0x55 , "<B" )
68
-
69
- _fan_lut_t4 = UnaryStruct (0x56 , "<B" )
70
- _fan_lut_s4 = UnaryStruct (0x57 , "<B" )
71
-
72
- _fan_lut_t5 = UnaryStruct (0x58 , "<B" )
73
- _fan_lut_s5 = UnaryStruct (0x59 , "<B" )
74
-
75
- _fan_lut_t6 = UnaryStruct (0x5A , "<B" )
76
- _fan_lut_s6 = UnaryStruct (0x5B , "<B" )
77
-
78
- _fan_lut_t7 = UnaryStruct (0x5C , "<B" )
79
- _fan_lut_s7 = UnaryStruct (0x5D , "<B" )
64
+ To remove a single stored point in the LUT, assign it as `None`.
65
+ """
80
66
81
- _fan_lut_t8 = UnaryStruct ( 0x5E , "<B" )
82
- _fan_lut_s8 = UnaryStruct ( 0x5F , "<B" )
67
+ # 8 (Temperature, Speed) pairs in increasing order
68
+ _fan_lut = StructArray ( _LUT_BASE , "<B" , 16 )
83
69
84
70
def __init__ (self , fan_obj ):
85
71
self .emc_fan = fan_obj
@@ -96,9 +82,14 @@ def __getitem__(self, index):
96
82
def __setitem__ (self , index , value ):
97
83
if not isinstance (index , int ):
98
84
raise IndexError
99
- if value > 100.0 or value < 0 :
85
+ if value is None :
86
+ # Assign None to remove this entry
87
+ del self .lut_values [index ]
88
+ elif value > 100.0 or value < 0 :
89
+ # Range check
100
90
raise AttributeError ("LUT values must be a fan speed from 0-100%" )
101
- self .lut_values [index ] = value
91
+ else :
92
+ self .lut_values [index ] = value
102
93
self ._update_lut ()
103
94
104
95
def __repr__ (self ):
@@ -108,7 +99,7 @@ def __repr__(self):
108
99
def __str__ (self ):
109
100
"""return the official string representation of the LUT"""
110
101
value_strs = []
111
- lut_keys = list (sorted (self .lut_values .keys ()))
102
+ lut_keys = tuple (sorted (self .lut_values .keys ()))
112
103
for temp in lut_keys :
113
104
fan_drive = self .lut_values [temp ]
114
105
value_strs .append ("%d deg C => %.1f%% duty cycle" % (temp , fan_drive ))
@@ -137,18 +128,23 @@ def _update_lut(self):
137
128
# get and sort the new lut keys so that we can assign them in order
138
129
for idx , current_temp in enumerate (sorted (self .lut_values .keys ())):
139
130
current_speed = _speed_to_lsb (self .lut_values [current_temp ])
140
- setattr (self , "_fan_lut_t%d" % (idx + 1 ), current_temp )
141
- setattr (self , "_fan_lut_s%d" % (idx + 1 ), current_speed )
131
+ self ._set_lut_entry (idx , current_temp , current_speed )
142
132
143
133
# Set the remaining LUT entries to the default (Temp/Speed = max value)
144
- for idx in range (8 )[len (self .lut_values ) :]:
145
- setattr (self , "_fan_lut_t%d" % (idx + 1 ), MAX_LUT_TEMP )
146
- setattr (self , "_fan_lut_s%d" % (idx + 1 ), MAX_LUT_SPEED )
134
+ for idx in range (len (self .lut_values ), 8 ):
135
+ self ._set_lut_entry (idx , MAX_LUT_TEMP , MAX_LUT_SPEED )
147
136
self .emc_fan .lut_enabled = current_mode
148
137
138
+ def _set_lut_entry (self , idx , temp , speed ):
139
+ self ._fan_lut [idx * 2 ] = bytearray ((temp ,))
140
+ self ._fan_lut [idx * 2 + 1 ] = bytearray ((speed ,))
141
+
149
142
150
143
class EMC2101_LUT (EMC2101 ): # pylint: disable=too-many-instance-attributes
151
- """Driver for the EMC2101 Fan Controller.
144
+ """Driver for the EMC2101 Fan Controller, with PWM frequency and LUT control.
145
+
146
+ See :class:`adafruit_emc2101.EMC2101` for the base/common functionality.
147
+
152
148
:param ~busio.I2C i2c_bus: The I2C bus the EMC is connected to.
153
149
"""
154
150
@@ -234,5 +230,5 @@ def lut_enabled(self, enable_lut):
234
230
235
231
@property
236
232
def lut (self ):
237
- """The dict-like representation of the LUT"""
233
+ """The dict-like representation of the LUT, actually of type :class:`FanSpeedLUT` """
238
234
return self ._lut
0 commit comments