Skip to content

Commit 1847b54

Browse files
authored
Merge pull request #7 from rpavlik/slim-lut
Slim lut
2 parents ba9c879 + 7b27d9a commit 1847b54

File tree

4 files changed

+58
-46
lines changed

4 files changed

+58
-46
lines changed

adafruit_emc2101/__init__.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121
2222
* Adafruit CircuitPython firmware for the supported boards:
2323
https://github.com/adafruit/circuitpython/releases
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
24+
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
25+
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
2726
"""
2827

2928
from micropython import const
@@ -64,7 +63,7 @@ class CV:
6463

6564
@classmethod
6665
def add_values(cls, value_tuples):
67-
"creates CV entires"
66+
"creates CV entries"
6867
cls.string = {}
6968
cls.lsb = {}
7069

@@ -133,20 +132,28 @@ class SpinupTime(CV):
133132

134133

135134
class EMC2101: # pylint: disable=too-many-instance-attributes
136-
"""Driver for the EMC2101 Fan Controller.
135+
"""Basic driver for the EMC2101 Fan Controller.
136+
137137
:param ~busio.I2C i2c_bus: The I2C bus the EMC is connected to.
138+
139+
If you need control over PWM frequency and the controller's built in temperature/speed
140+
look-up table (LUT), you will need :class:`emc2101_lut.EMC2101_LUT` which extends this
141+
class to add those features, at the cost of increased memory usage.
138142
"""
139143

140144
_part_id = ROUnaryStruct(_REG_PARTID, "<B")
141145
_mfg_id = ROUnaryStruct(_REG_MFGID, "<B")
142146
_int_temp = ROUnaryStruct(_INTERNAL_TEMP, "<b")
143147

148+
# Some of these registers are defined as two halves because
149+
# the chip does not support multi-byte reads or writes, and there
150+
# is currently no way to tell Struct to do a transaction for each byte.
151+
144152
# IMPORTANT! the sign bit for the external temp is in the msbyte so mark it as signed
145153
# and lsb as unsigned
146154
_ext_temp_msb = ROUnaryStruct(_EXTERNAL_TEMP_MSB, "<b")
147155
_ext_temp_lsb = ROUnaryStruct(_EXTERNAL_TEMP_LSB, "<B")
148156

149-
# _tach_read = ROUnaryStruct(_TACH_LSB, "<H")
150157
_tach_read_lsb = ROUnaryStruct(_TACH_LSB, "<B")
151158
_tach_read_msb = ROUnaryStruct(_TACH_MSB, "<B")
152159
_tach_mode_enable = RWBit(_REG_CONFIG, 2)
@@ -225,7 +232,7 @@ def manual_fan_speed(self):
225232
@manual_fan_speed.setter
226233
def manual_fan_speed(self, fan_speed):
227234
if fan_speed not in range(0, 101):
228-
raise AttributeError("manual_fan_speed must be from 0-100 ")
235+
raise AttributeError("manual_fan_speed must be from 0-100")
229236

230237
# convert from a percentage to an lsb value
231238
percentage = fan_speed / 100.0
@@ -238,7 +245,12 @@ def manual_fan_speed(self, fan_speed):
238245
@property
239246
def lut_enabled(self):
240247
"""Enable or disable the internal look up table used to map a given temperature
241-
to a fan speed. When the LUT is disabled fan speed can be changed with `manual_fan_speed`"""
248+
to a fan speed.
249+
250+
When the LUT is disabled (the default), fan speed can be changed with `manual_fan_speed`.
251+
To actually set this to True and modify the LUT, you need to use the extended version of
252+
this driver, :class:`emc2101_lut.EMC2101_LUT`
253+
"""
242254
return not self._fan_lut_prog
243255

244256
@property

adafruit_emc2101/emc2101_lut.py

+33-37
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Brushless fan controller: extended functionality
99
1010
11-
* Author(s): Bryan Siepert
11+
* Author(s): Bryan Siepert, Ryan Pavlik
1212
1313
Implementation Notes
1414
--------------------
@@ -22,15 +22,16 @@
2222
* Adafruit CircuitPython firmware for the supported boards:
2323
https://github.com/adafruit/circuitpython/releases
2424
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
2727
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`,
2929
if your device has enough RAM to support it. This class adds LUT control
3030
and PWM frequency control to the base feature set.
3131
"""
3232

3333
from micropython import const
34+
from adafruit_register.i2c_struct_array import StructArray
3435
from adafruit_register.i2c_struct import UnaryStruct
3536
from adafruit_register.i2c_bit import RWBit
3637
from adafruit_register.i2c_bits import RWBits
@@ -43,6 +44,7 @@
4344
_PWM_FREQ = const(0x4D)
4445
_PWM_DIV = const(0x4E)
4546
_LUT_HYSTERESIS = const(0x4F)
47+
_LUT_BASE = const(0x50)
4648

4749
MAX_LUT_SPEED = 0x3F # 6-bit value
4850
MAX_LUT_TEMP = 0x7F # 7-bit
@@ -54,32 +56,16 @@ def _speed_to_lsb(percentage):
5456

5557
class FanSpeedLUT:
5658
"""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.
5860
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.
6263
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+
"""
8066

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)
8369

8470
def __init__(self, fan_obj):
8571
self.emc_fan = fan_obj
@@ -96,9 +82,14 @@ def __getitem__(self, index):
9682
def __setitem__(self, index, value):
9783
if not isinstance(index, int):
9884
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
10090
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
10293
self._update_lut()
10394

10495
def __repr__(self):
@@ -108,7 +99,7 @@ def __repr__(self):
10899
def __str__(self):
109100
"""return the official string representation of the LUT"""
110101
value_strs = []
111-
lut_keys = list(sorted(self.lut_values.keys()))
102+
lut_keys = tuple(sorted(self.lut_values.keys()))
112103
for temp in lut_keys:
113104
fan_drive = self.lut_values[temp]
114105
value_strs.append("%d deg C => %.1f%% duty cycle" % (temp, fan_drive))
@@ -137,18 +128,23 @@ def _update_lut(self):
137128
# get and sort the new lut keys so that we can assign them in order
138129
for idx, current_temp in enumerate(sorted(self.lut_values.keys())):
139130
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)
142132

143133
# 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)
147136
self.emc_fan.lut_enabled = current_mode
148137

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+
149142

150143
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+
152148
:param ~busio.I2C i2c_bus: The I2C bus the EMC is connected to.
153149
"""
154150

@@ -234,5 +230,5 @@ def lut_enabled(self, enable_lut):
234230

235231
@property
236232
def lut(self):
237-
"""The dict-like representation of the LUT"""
233+
"""The dict-like representation of the LUT, actually of type :class:`FanSpeedLUT`"""
238234
return self._lut

docs/examples.rst

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ LUT Usage Example
1111
-----------------
1212

1313
Use the temperature to fan speed Look Up Table to automatically control the fan speed.
14+
This example requires more memory than the first one because it needs to use the extended
15+
:class:`adafruit_emc2101.emc2101_lut.EMC2101_LUT` driver to access LUT functionality.
1416

1517
.. literalinclude:: ../examples/emc2101_lut_example.py
1618
:caption: examples/emc2101_lut_example.py
@@ -21,6 +23,8 @@ PWM Tuning
2123
-----------------
2224

2325
Adjust the EMC2101s PWM settings to fit your application.
26+
This example requires more memory than the first one because it needs to use the extended
27+
:class:`adafruit_emc2101.emc2101_lut.EMC2101_LUT` driver to access LUT functionality.
2428

2529
.. literalinclude:: ../examples/set_pwm_freq.py
2630
:caption: examples/set_pwm_freq.py

examples/emc2101_lut_example.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
time.sleep(3)
2828
print("50%% duty cycle is %f RPM:" % emc.fan_speed)
2929

30-
emc.forced_ext_temp = 43 # over 30, should be 50%
30+
emc.forced_ext_temp = 43 # over 42, should be 75%
3131
time.sleep(3)
3232
print("75%% duty cycle is %f RPM:" % emc.fan_speed)

0 commit comments

Comments
 (0)