33
33
34
34
**Hardware:**
35
35
36
- .. todo:: Update the PID for the below and add links to any specific hardware product page(s), or category page(s)
37
36
* Adafruit's MCP4728 Breakout: https://adafruit.com/product/44XX
38
37
39
38
**Software and Dependencies:**
48
47
49
48
from time import sleep
50
49
import adafruit_bus_device .i2c_device as i2c_device
51
- from adafruit_register .i2c_struct import UnaryStruct , ROUnaryStruct
52
- from adafruit_register .i2c_struct_array import StructArray
53
- from adafruit_register .i2c_bit import RWBit
54
- from adafruit_register .i2c_bits import RWBits
50
+ from adafruit_register .i2c_struct import UnaryStruct
55
51
56
- _MCP4728_DEFAULT_ADDRESS = 0x60
52
+ _MCP4728_DEFAULT_ADDRESS = 0x60
57
53
58
54
"""
59
55
# DAC1, DAC0 DAC Channel Selection bits:
64
60
65
61
0 = slave addr(auto)
66
62
0 1 0 0 0 DAC1 DAC0 UDAC[A]
67
- 01000 + 00 + UDAC =
63
+ 01000 + 00 + UDAC =
68
64
0b01000000 = 0x40 (+2 for each successive)
69
65
VREF PD1 PD0 Gx D11 D10 D9 D8 [A]
70
66
D7 D6 D5 D4 D3 D2 D1 D0 [A]
78
74
1 0 1 0 0 Write Power-Down bits to Input Registers
79
75
"""
80
76
81
- '0b 010 00 000'
82
77
_MCP4728_CH_A_MULTI_IB = 0x40
83
78
_MCP4728_CH_B_MULTI_IB = 0x42
84
79
_MCP4728_CH_C_MULTI_IB = 0x44
85
80
_MCP4728_CH_D_MULTI_IB = 0x46
86
81
87
- '0b 010 11 000'
88
82
_MCP4728_CH_A_SINGLE_EEPROM = 0x58
89
83
_MCP4728_CH_B_SINGLE_EEPROM = 0x5A
90
84
_MCP4728_CH_C_SINGLE_EEPROM = 0x5C
91
85
_MCP4728_CH_D_SINGLE_EEPROM = 0x5E
92
86
93
- '0b 010 10 000'
94
87
_MCP4728_CH_A_MULTI_EEPROM = 0x50
95
88
89
+ #TODO: REMOVE THIS
90
+ #pylint: disable=unused-variable,no-self-use,invalid-name,too-few-public-methods
96
91
class MCP4728 :
97
92
"""Helper library for the Microchip MCP4728 I2C 12-bit Quad DAC.
98
93
@@ -103,7 +98,7 @@ class MCP4728:
103
98
_channel_a_single_write_eeprom = UnaryStruct (_MCP4728_CH_A_SINGLE_EEPROM , ">H" )
104
99
_channel_b_single_write_eeprom = UnaryStruct (_MCP4728_CH_B_SINGLE_EEPROM , ">H" )
105
100
_channel_c_single_write_eeprom = UnaryStruct (_MCP4728_CH_C_SINGLE_EEPROM , ">H" )
106
- _channel_d_single_write_eeprom = UnaryStruct (_MCP4728_CH_D_SINGLE_EEPROM , ">H" )
101
+ _channel_d_single_write_eeprom = UnaryStruct (_MCP4728_CH_D_SINGLE_EEPROM , ">H" )
107
102
108
103
_channel_a_multi_write = UnaryStruct (_MCP4728_CH_A_MULTI_IB , ">H" )
109
104
_channel_b_multi_write = UnaryStruct (_MCP4728_CH_B_MULTI_IB , ">H" )
@@ -113,145 +108,134 @@ class MCP4728:
113
108
_multi_write_channel_a_start = UnaryStruct (_MCP4728_CH_A_MULTI_EEPROM , ">HHHH" )
114
109
115
110
def __init__ (self , i2c_bus , address = _MCP4728_DEFAULT_ADDRESS ):
116
- self .i2c_device = i2c_device .I2CDevice (i2c_bus , address )
117
-
118
- @property
119
- def channel_a (self ):
120
- """channel a's current value"""
121
- return "hamsters"
122
-
123
- @channel_a .setter
124
- def channel_a (self , value ):
125
- self ._channel_a_multi = value
126
-
127
111
128
- @property
129
- def channel_b (self ):
130
- """channel b's current value"""
131
- return "elves"
132
-
133
- @channel_b .setter
134
- def channel_b (self , value ):
135
- self ._channel_b_multi = value
136
-
137
- @property
138
- def channel_c (self ):
139
- """channel c's current value"""
140
- return "pancakes"
141
-
142
- @channel_c .setter
143
- def channel_c (self , value ):
144
- self ._channel_c_multi = value
112
+ self .i2c_device = i2c_device .I2CDevice (i2c_bus , address )
113
+ self ._create_channels ()
145
114
146
- @property
147
- def channel_d (self ):
148
- """channel d's current value"""
149
- return "gummies"
150
-
151
- @channel_d .setter
152
- def channel_d (self , value ):
153
- self ._channel_d_multi = value
154
-
155
-
156
- def write_init (self , register_address , struct_format ):
157
- self .format = struct_format
158
- self .address = register_address
159
-
160
- # def read(self, obj):
161
- # buf = bytearray(1+struct.calcsize(self.format))
162
- # buf[0] = self.address
163
- # with self.i2c_device as i2c:
164
- # i2c.write_then_readinto(buf, buf, out_end=1, in_start=1)
165
- # return struct.unpack_from(self.format, buf, 1)[0]
166
-
167
- # def write(self, obj, value):
168
- # buf = bytearray(1+struct.calcsize(self.format))
169
- # buf[0] = self.address
170
- # struct.pack_into(self.format, buf, 1, value)
171
- # with self.i2c_device as i2c:
172
- # i2c.write(buf)
173
- def chunks (self , l , n ):
115
+ def _chunk (self , l , n ):
174
116
# For item i in a range that is a length of l,
175
117
for i in range (0 , len (l ), n ):
176
118
# Create an index range for l of n items:
177
119
yield l [i :i + n ]
178
120
179
- def get_flags (self , high_byte ):
121
+ def _lzb (self , byte_val ): # leading zero bin
122
+ return format (byte_val , '#010b' )
123
+
124
+ def _get_flags (self , high_byte ):
180
125
vref = (high_byte & 1 << 7 ) > 0
181
126
gain = (high_byte & 1 << 4 ) > 0
182
127
pd = (high_byte & 0b011 << 5 )>> 5
183
128
return (vref , gain , pd )
184
129
185
- def read_registers (self ):
130
+ def _cache_page (self , value , vref , gain , pd ):
131
+ return {"value" : value , "vref" : vref , "gain" : gain , "pd" : pd }
132
+
133
+ def _create_channels (self ):
134
+ raw_registers = self ._read_registers ()
135
+
136
+ self .channel_a = Channel (self ._cache_page (* raw_registers [0 ]))
137
+ self .channel_b = Channel (self ._cache_page (* raw_registers [1 ]))
138
+ self .channel_c = Channel (self ._cache_page (* raw_registers [2 ]))
139
+ self .channel_d = Channel (self ._cache_page (* raw_registers [3 ]))
140
+
141
+ def _read_registers (self ):
186
142
buf = bytearray (24 )
187
143
188
144
with self .i2c_device as i2c :
189
145
i2c .readinto (buf )
190
146
index = 0
191
147
for index , value in enumerate (buf ):
192
- if index % 3 is 0 :
148
+ if index % 3 == 0 :
193
149
print ("\n %4s\t " % index , end = "" )
194
- print ("%s %s " % ( format (value , '#010b' ), hex (value )), end = "" )
150
+ print ("%s %s " % (format (value , '#010b' ), hex (value )), end = "" )
195
151
print ()
196
- # stride is 6 because we get 6 bytes for each channel; 3 for the output regs
197
- # and 3 for the eeprom. here we only care about the output buffer
152
+
153
+ # stride is 6 because we get 6 bytes for each channel; 3 for the output regs
154
+ # and 3 for the eeprom. here we only care about the output buffer so we throw out
155
+ # the eeprom values as 'n/a'
198
156
current_values = []
199
- for header , high_byte , low_byte , na_1 , na_2 , na_3 in self .chunks (buf ,6 ):
157
+ for header , high_byte , low_byte , na_1 , na_2 , na_3 in self ._chunk (buf , 6 ):
200
158
value = (high_byte & 0b00001111 ) << 8 | low_byte
201
- vref , gain , pd = self .get_flags (high_byte )
202
- current_values << ( value , vref , pd , gain )
159
+ vref , gain , pd = self ._get_flags (high_byte )
160
+ current_values . append (( value , vref , gain , pd ) )
203
161
204
162
return current_values
205
- # ch_a_header, ch_a_hb, ch_a_lb = buf[0:3]
206
- # ch_aee_header, ch_aee_hb, ch_aee_lb = buf[3:6]
207
- # # ch_c_header, ch_c_hb, ch_c_lb = buf[6:9]
208
- # # ch_d_header, ch_d_hb, ch_d_lb = buf[9:12]
209
163
210
- # ch_a_val =
211
164
212
- # print("%s %s %s"%( self.b(ch_a_header),self.b(ch_a_hb), self.b(ch_a_lb) ))
213
- # print("%s %s %s"%( self.b(ch_b_header),self.b(ch_b_hb), self.b(ch_b_lb) ))
214
- # # print("%s %s %s"%( self.b(ch_c_header),self.b(ch_c_hb), self.b(ch_c_lb) ))
215
- # # print("%s %s %s"%( self.b(ch_d_header),self.b(ch_d_hb), self.b(ch_d_lb) ))
216
-
217
- def b (self , byte_val ):
218
- return format (byte_val , '#010b' )
219
-
220
- def write_multi_eeprom (self , byte_list , start = 0 ):
165
+ # TODO: add the ability to set an offset
166
+ def _write_multi_eeprom (self , byte_list ):
221
167
buffer_list = [_MCP4728_CH_A_MULTI_EEPROM ]
222
168
buffer_list += byte_list
223
- print ("Byte List:" )
224
- print (buffer_list )
169
+
225
170
buf = bytearray (buffer_list )
226
- # struct.pack_into(self.format, buf, 1, value)
227
171
with self .i2c_device as i2c :
228
172
i2c .write (buf )
229
173
174
+ sleep (0.015 ) # the better to write you with
230
175
231
176
232
-
177
+ class Channel :
178
+ """An instance of a single channel for a multi-channel DAC"""
179
+ def __init__ (self , cache_page ):
180
+ self ._vref = cache_page ['vref' ]
181
+ self ._gain = cache_page ['gain' ]
182
+ self ._raw_value = cache_page ['value' ]
233
183
184
+ @property
185
+ def normalized_value (self ):
186
+ """The DAC value as a floating point number in the range 0.0 to 1.0."""
187
+ return self ._raw_value / (2 ** 12 - 1 )
188
+
189
+ @normalized_value .setter
190
+ def normalized_value (self , value ):
191
+ if value < 0.0 or value > 1.0 :
192
+ raise AttributeError ("`normalized_value` must be between 0.0 and 1.0" )
193
+
194
+ self ._raw_value = int (value * 4095.0 )
234
195
235
196
@property
236
- def ch_a (self ):
237
- return "poo"
197
+ def value (self ):
198
+ """The 16-bit scaled current value for the channel. Note that the MCP4728 is a 12-bit piece
199
+ so quantization errors will occour"""
200
+ return self .normalized_value * (2 ** 16 - 1 )
238
201
239
- @ch_a .setter
240
- def ch_a (self , value ):
241
- self ._channel_a_multi = value
202
+ @value .setter
203
+ def value (self , value ):
204
+ if value < 0 or value > (2 ** 16 - 1 ):
205
+ raise AttributeError ("`value` must be a 16-bit integer between 0 and %s" % (2 ** 16 - 1 ))
242
206
243
- """
244
- # set the gain for a channel
245
- cache each, set all?
207
+ # Scale from 16-bit to 12-bit value (quantization errors will occur!).
208
+ self ._raw_value = value >> 4
246
209
247
- # set reference source for a channel
248
- cache each, set all?
210
+ @property
211
+ def raw_value (self ):
212
+ """The native 12-bit value used by the DAC"""
213
+ return self ._raw_value
249
214
250
- # save settings to eeprom
215
+ @raw_value .setter
216
+ def raw_value (self , value ):
217
+ if value < 0 or value > (2 ** 12 - 1 ):
218
+ raise AttributeError ("`raw_value` must be a 12-bit integer between 0 and %s" % (2 ** 12 - 1 ))
219
+ self ._raw_value = value
251
220
252
- # write a channel
221
+ @property
222
+ def gain (self ):
223
+ """Sets the gain of the channel. Must be 1 or 2"""
224
+ return self ._gain
253
225
254
- # write all channels
226
+ @gain .setter
227
+ def gain (self , value ):
228
+ if value < 1 or value > 2 :
229
+ raise AttributeError ("`gain` must be 1 or 2" )
230
+ self ._vref = value
255
231
256
- # latching?
257
- """
232
+ @property
233
+ def vref (self ):
234
+ """Sets the DAC's voltage reference source. Must be a ``VREF``"""
235
+ return self ._vref
236
+
237
+ @vref .setter
238
+ def vref (self , value ):
239
+ if value < 0 or value > 3 :
240
+ raise AttributeError ("`vref` must be a ``VREF``" )
241
+ self ._vref = value
0 commit comments