Skip to content

Commit 9ea31f3

Browse files
committed
Ready for release
1 parent 74b617c commit 9ea31f3

File tree

10 files changed

+125
-217
lines changed

10 files changed

+125
-217
lines changed

README.rst

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ This driver depends on:
2222

2323
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
2424
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
25-
* `Register <https://github.com/adafruit/Adafruit_CircuitPython_Register>`_
2625

2726
Please ensure all dependencies are available on the CircuitPython filesystem.
2827
This is easily achieved by downloading
@@ -56,7 +55,23 @@ To install in a virtual environment in your current project:
5655
Usage Example
5756
=============
5857

59-
.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst.
58+
.. code-block:: python3
59+
60+
import board
61+
import busio
62+
import adafruit_mcp4728
63+
64+
i2c = busio.I2C(board.SCL, board.SDA)
65+
mcp4728 = adafruit_mcp4728.MCP4728(i2c)
66+
67+
mcp4728.channel_a.value = 65535 # Voltage = VDD
68+
mcp4728.channel_b.value = int(65535/2) # VDD/2
69+
mcp4728.channel_c.value = int(65535/4) # VDD/4
70+
71+
mcp4728.channel_d.value = int(65535/4) # Vref.INTERNAL/4 * 2 => 2.048/4 * 2 = 2.048/2 = ~ 1.024V
72+
73+
74+
mcp4728.save_settings() # save the current values to the eeprom,making them the default on power up
6075
6176
Contributing
6277
============

adafruit_mcp4728.py

Lines changed: 55 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,9 @@
4848
from struct import pack_into
4949
from time import sleep
5050
import adafruit_bus_device.i2c_device as i2c_device
51-
from adafruit_register.i2c_struct import UnaryStruct
52-
import traceback
5351

5452
_MCP4728_DEFAULT_ADDRESS = 0x60
5553

56-
_MCP4728_CH_A_MULTI_IB = 0x40
57-
_MCP4728_CH_B_MULTI_IB = 0x42
58-
_MCP4728_CH_C_MULTI_IB = 0x44
59-
_MCP4728_CH_D_MULTI_IB = 0x46
60-
61-
_MCP4728_CH_A_SINGLE_EEPROM = 0x58
62-
_MCP4728_CH_B_SINGLE_EEPROM = 0x5A
63-
_MCP4728_CH_C_SINGLE_EEPROM = 0x5C
64-
_MCP4728_CH_D_SINGLE_EEPROM = 0x5E
65-
6654
_MCP4728_CH_A_MULTI_EEPROM = 0x50
6755

6856
class CV:
@@ -101,68 +89,49 @@ class MCP4728:
10189
:param address: The I2C slave address of the sensor
10290
10391
"""
104-
_channel_a_single_write_eeprom = UnaryStruct(_MCP4728_CH_A_SINGLE_EEPROM, ">H")
105-
_channel_b_single_write_eeprom = UnaryStruct(_MCP4728_CH_B_SINGLE_EEPROM, ">H")
106-
_channel_c_single_write_eeprom = UnaryStruct(_MCP4728_CH_C_SINGLE_EEPROM, ">H")
107-
_channel_d_single_write_eeprom = UnaryStruct(_MCP4728_CH_D_SINGLE_EEPROM, ">H")
108-
109-
_channel_a_multi_write = UnaryStruct(_MCP4728_CH_A_MULTI_IB, ">H")
110-
_channel_b_multi_write = UnaryStruct(_MCP4728_CH_B_MULTI_IB, ">H")
111-
_channel_c_multi_write = UnaryStruct(_MCP4728_CH_C_MULTI_IB, ">H")
112-
_channel_d_multi_write = UnaryStruct(_MCP4728_CH_D_MULTI_IB, ">H")
113-
114-
_multi_write_channel_a_start = UnaryStruct(_MCP4728_CH_A_MULTI_EEPROM, ">HHHH")
11592

11693
def __init__(self, i2c_bus, address=_MCP4728_DEFAULT_ADDRESS):
11794

11895
self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
119-
96+
12097
raw_registers = self._read_registers()
12198

12299
self.channel_a = Channel(self, self._cache_page(*raw_registers[0]), 0)
123100
self.channel_b = Channel(self, self._cache_page(*raw_registers[1]), 1)
124101
self.channel_c = Channel(self, self._cache_page(*raw_registers[2]), 2)
125102
self.channel_d = Channel(self, self._cache_page(*raw_registers[3]), 3)
126103

127-
def _get_flags(self, high_byte):
104+
@staticmethod
105+
def _get_flags(high_byte):
128106
vref = (high_byte & 1<<7) > 0
129107
gain = (high_byte & 1<<4) > 0
130-
pd = (high_byte & 0b011<<5)>>5
131-
return (vref, gain, pd)
108+
power_state = (high_byte & 0b011<<5)>>5
109+
return (vref, gain, power_state)
132110

133-
def _cache_page(self, value, vref, gain, pd):
134-
return {"value": value, "vref": vref, "gain": gain, "pd": pd}
111+
@staticmethod
112+
def _cache_page(value, vref, gain, power_state):
113+
return {"value": value, "vref": vref, "gain": gain, "power_state": power_state}
135114

136115
def _read_registers(self):
137116
buf = bytearray(24)
138117

139118
with self.i2c_device as i2c:
140119
i2c.readinto(buf)
141120

142-
self._print_buffer(buf)
143-
144121
# stride is 6 because we get 6 bytes for each channel; 3 for the output regs
145-
# and 3 for the eeprom. here we only care about the output buffer so we throw out
122+
# and 3 for the eeprom. Here we only care about the output regoster so we throw out
146123
# the eeprom values as 'n/a'
147124
current_values = []
148-
for header, high_byte, low_byte, na_1, na_2, na_3 in self._chunk(buf, 6):
125+
for header, high_byte, low_byte, na_1, na_2, na_3 in self._chunk(buf, 6):#pylint:disable=unused-variable
149126
value = (high_byte & 0b00001111) << 8 | low_byte
150-
vref, gain, pd = self._get_flags(high_byte)
151-
current_values.append((value, vref, gain, pd))
127+
vref, gain, power_state = self._get_flags(high_byte)
128+
current_values.append((value, vref, gain, power_state))
152129

153130
return current_values
154131

155-
def _print_buffer(self, buf):
156-
print()
157-
for index, value in enumerate(buf):
158-
if index %3 == 0:
159-
print("\n%4s\t"%index, end="")
160-
print("%s %s "%(format(value, '#010b'), hex(value)), end="")
161-
print()
162-
163-
164132
def save_settings(self):
165-
"""Saves the currently selected values, Vref, and gain selections for each channel to the EEPROM, setting them as defaults on power up"""
133+
"""Saves the currently selected values, Vref, and gain selections for each channel
134+
to the EEPROM, setting them as defaults on power up"""
166135
byte_list = []
167136
byte_list += self._generate_bytes_with_flags(self.channel_a)
168137
byte_list += self._generate_bytes_with_flags(self.channel_b)
@@ -176,48 +145,36 @@ def _write_multi_eeprom(self, byte_list):
176145
buffer_list += byte_list
177146

178147
buf = bytearray(buffer_list)
179-
self._print_buffer(buf)
148+
180149
with self.i2c_device as i2c:
181150
i2c.write(buf)
182151

183152
sleep(0.015) # the better to write you with
184153

185-
def _sync_vrefs(self):
186-
base_values = 0b10000000
187-
base_values |= (self.channel_a.vref<<3)
188-
base_values |= (self.channel_b.vref<<2)
189-
base_values |= (self.channel_c.vref<<1)
190-
base_values |= (self.channel_d.vref)
191-
192-
print("vref values:")
193-
print(base_values)
194-
print(self._lzb(base_values))
154+
def sync_vrefs(self):
155+
"""Syncs the driver's vref state with the DAC"""
156+
gain_setter_command = 0b10000000
157+
gain_setter_command |= (self.channel_a.vref<<3)
158+
gain_setter_command |= (self.channel_b.vref<<2)
159+
gain_setter_command |= (self.channel_c.vref<<1)
160+
gain_setter_command |= (self.channel_d.vref)
195161

196162
buf = bytearray(1)
197-
pack_into(">B", buf, 0, base_values)
198-
print("buf bytes:")
199-
self._print_buffer(buf)
163+
pack_into(">B", buf, 0, gain_setter_command)
200164
with self.i2c_device as i2c:
201165
i2c.write(buf)
202166

203-
def _sync_gains(self):
204-
# [S] 1 1 0 0 A2 A1 A0 0
205-
# [A] 1 1 0 X GXA GXB GXC GXD [A] [P]
206-
base_values = 0b11000000
207-
base_values |= (self.channel_a.gain<<3)
208-
base_values |= (self.channel_b.gain<<2)
209-
base_values |= (self.channel_c.gain<<1)
210-
base_values |= (self.channel_d.gain)
211-
167+
def sync_gains(self):
168+
"""Syncs the driver's gain state with the DAC"""
212169

213-
print("gain values:")
214-
print(base_values)
215-
print(self._lzb(base_values))
170+
sync_setter_command = 0b11000000
171+
sync_setter_command |= (self.channel_a.gain<<3)
172+
sync_setter_command |= (self.channel_b.gain<<2)
173+
sync_setter_command |= (self.channel_c.gain<<1)
174+
sync_setter_command |= (self.channel_d.gain)
216175

217176
buf = bytearray(1)
218-
pack_into(">B", buf, 0, base_values)
219-
print("buf bytes:")
220-
self._print_buffer(buf)
177+
pack_into(">B", buf, 0, sync_setter_command)
221178

222179
with self.i2c_device as i2c:
223180
i2c.write(buf)
@@ -227,14 +184,15 @@ def _set_value(self, channel):
227184
channel_bytes = self._generate_bytes_with_flags(channel)
228185

229186
write_command_byte = 0b01000000 # 0 1 0 0 0 DAC1 DAC0 UDAC
230-
write_command_byte |= (channel._channel_index<<1)
187+
write_command_byte |= (channel.channel_index<<1)
231188

232189
channel_bytes.insert(0, write_command_byte)
233190

234191
with self.i2c_device as i2c:
235192
i2c.write(bytearray(channel_bytes))
236193

237-
def _generate_bytes_with_flags(self, channel):
194+
@staticmethod
195+
def _generate_bytes_with_flags(channel):
238196
buf = bytearray(2)
239197
pack_into(">H", buf, 0, channel.raw_value)
240198

@@ -243,25 +201,22 @@ def _generate_bytes_with_flags(self, channel):
243201

244202
return buf
245203

246-
def _chunk(self, l, n):
247-
# For item i in a range that is a length of l,
248-
for i in range(0, len(l), n):
249-
# Create an index range for l of n items:
250-
yield l[i:i+n]
251-
252-
def _lzb(self, byte_val): # leading zero bin
253-
return format(byte_val, '#010b')
254-
def _bt(self):
255-
for line in traceback.format_stack():
256-
print(line.strip())
204+
@staticmethod
205+
def _chunk(big_list, chunk_size):
206+
"""Divides a given list into `chunk_size` sized chunks"""
207+
for i in range(0, len(big_list), chunk_size):
208+
yield big_list[i:i+chunk_size]
209+
257210
class Channel:
258-
"""An instance of a single channel for a multi-channel DAC"""
211+
"""An instance of a single channel for a multi-channel DAC.
212+
213+
**All available channels are created automatically and should not be created by the user**"""
259214
def __init__(self, dac_instance, cache_page, index):
260215
self._vref = cache_page['vref']
261216
self._gain = cache_page['gain']
262217
self._raw_value = cache_page['value']
263218
self._dac = dac_instance
264-
self._channel_index = index
219+
self.channel_index = index
265220

266221
@property
267222
def normalized_value(self):
@@ -299,19 +254,25 @@ def raw_value(self, value):
299254
if value < 0 or value > (2**12-1):
300255
raise AttributeError("`raw_value` must be a 12-bit integer between 0 and %s"%(2**12-1))
301256
self._raw_value = value
302-
self._dac._set_value(self)
257+
# disabling the protected access warning here because making it public would be
258+
# more confusing
259+
self._dac._set_value(self) #pylint:disable=protected-access
303260

304261
@property
305262
def gain(self):
306-
"""Sets the gain of the channel if the Vref for the channel is ``Vref.INTERNAL``. *The gain setting has no effect if the Vref for the channel is `Vref.VDD`*. With gain set to 1, the output voltage goes from 0v to 2.048V. If a channe's gain is set to 2, the voltage goes from 0v to 4.096V. `gain` Must be 1 or 2"""
263+
"""Sets the gain of the channel if the Vref for the channel is ``Vref.INTERNAL``.
264+
**The gain setting has no effect if the Vref for the channel is `Vref.VDD`**.
265+
266+
With gain set to 1, the output voltage goes from 0v to 2.048V. If a channe's gain is set
267+
to 2, the voltage goes from 0v to 4.096V. `gain` Must be 1 or 2"""
307268
return self._gain
308269

309270
@gain.setter
310271
def gain(self, value):
311-
if value != 1 and value != 2:
272+
if not value in (1, 2):
312273
raise AttributeError("`gain` must be 1 or 2")
313274
self._gain = value-1
314-
self._dac._sync_gains()
275+
self._dac.sync_gains()
315276

316277
@property
317278
def vref(self):
@@ -323,4 +284,4 @@ def vref(self, value):
323284
if not Vref.is_valid(value):
324285
raise AttributeError("range must be a `Vref`")
325286
self._vref = value
326-
self._dac._sync_vrefs()
287+
self._dac.sync_vrefs()

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# Uncomment the below if you use native CircuitPython modules such as
2121
# digitalio, micropython and busio. List the modules you use. Without it, the
2222
# autodoc module docs will fail to generate with a warning.
23-
autodoc_mock_imports = ["adafruit_register", "adafruit_bus_device"]
23+
autodoc_mock_imports = ["adafruit_bus_device"]
2424

2525

2626

docs/index.rst

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,11 @@ Table of Contents
2323
.. toctree::
2424
:caption: Tutorials
2525

26-
.. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave
27-
the toctree above for use later.
28-
2926
.. toctree::
3027
:caption: Related Products
3128

32-
.. todo:: Add any product links here. If there are none, then simply delete this todo and leave
33-
the toctree above for use later.
29+
Adafruit MCP4728 Breakout <https://www.adafruit.com/product/44xx>
30+
3431

3532
.. toctree::
3633
:caption: Other Links

examples/mcp4728_full_test.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import board
2+
import busio
3+
import adafruit_mcp4728
4+
5+
i2c = busio.I2C(board.SCL, board.SDA)
6+
mcp4728 = adafruit_mcp4728.MCP4728(i2c)
7+
8+
#pylint:disable=no-member
9+
mcp4728.channel_a.vref = adafruit_mcp4728.Vref.VDD # sets the channel to scale between 0v and VDD
10+
mcp4728.channel_b.vref = adafruit_mcp4728.Vref.VDD
11+
mcp4728.channel_c.vref = adafruit_mcp4728.Vref.VDD
12+
mcp4728.channel_d.vref = adafruit_mcp4728.Vref.INTERNAL # scales between 0v and 2.048v
13+
14+
mcp4728.channel_a.gain = 1
15+
mcp4728.channel_b.gain = 1
16+
mcp4728.channel_c.gain = 1
17+
18+
# changes the gain to 2 so the channel now scales between 0v and 4.098V (2 x Internal Vref)
19+
mcp4728.channel_d.gain = 2
20+
21+
22+
mcp4728.channel_a.value = 65535 # Voltage = VDD
23+
mcp4728.channel_b.value = int(65535/2) # VDD/2
24+
mcp4728.channel_c.value = int(65535/4) # VDD/4
25+
26+
mcp4728.channel_d.value = int(65535/4) # Vref.INTERNAL/4 * 2 => 2.048/4 * 2 = 2.048/2 = ~ 1.024V
27+
28+
print("cha value:", mcp4728.channel_a.value)
29+
print("chb value:", mcp4728.channel_b.value)
30+
print("chc value:", mcp4728.channel_c.value)
31+
print("chd value:", mcp4728.channel_d.value)
32+
print()
33+
34+
print("cha gain:", mcp4728.channel_a.gain)
35+
print("chb gain:", mcp4728.channel_b.gain)
36+
print("chc gain:", mcp4728.channel_c.gain)
37+
print("chd gain:", mcp4728.channel_d.gain)
38+
print()
39+
print("cha vref:", mcp4728.channel_a.vref)
40+
print("chb vref:", mcp4728.channel_b.vref)
41+
print("chc vref:", mcp4728.channel_c.vref)
42+
print("chd vref:", mcp4728.channel_d.vref)
43+
44+
45+
mcp4728.save_settings()

0 commit comments

Comments
 (0)