32
32
from time import sleep
33
33
from adafruit_bus_device import i2c_device
34
34
35
+ try :
36
+ from typing import Dict , Iterable , Iterator , List , Optional , Tuple
37
+ from typing_extensions import Literal
38
+ from busio import I2C
39
+ except ImportError :
40
+ pass
41
+
35
42
MCP4728_DEFAULT_ADDRESS = 0x60
36
43
37
44
MCP4728A4_DEFAULT_ADDRESS = 0x64
@@ -51,7 +58,9 @@ class CV:
51
58
"""struct helper"""
52
59
53
60
@classmethod
54
- def add_values (cls , value_tuples ):
61
+ def add_values (
62
+ cls , value_tuples : Iterable [Tuple [str , int , str , Optional [float ]]]
63
+ ) -> None :
55
64
"""creates CV entries"""
56
65
cls .string = {}
57
66
cls .lsb = {}
@@ -63,16 +72,14 @@ def add_values(cls, value_tuples):
63
72
cls .lsb [value ] = lsb
64
73
65
74
@classmethod
66
- def is_valid (cls , value ) :
75
+ def is_valid (cls , value : int ) -> bool :
67
76
"""Returns true if the given value is a member of the CV"""
68
77
return value in cls .string
69
78
70
79
71
80
class Vref (CV ):
72
81
"""Options for ``vref``"""
73
82
74
- pass # pylint: disable=unnecessary-pass
75
-
76
83
77
84
Vref .add_values (
78
85
(
@@ -117,7 +124,7 @@ class MCP4728:
117
124
118
125
"""
119
126
120
- def __init__ (self , i2c_bus , address : int = MCP4728_DEFAULT_ADDRESS ):
127
+ def __init__ (self , i2c_bus : I2C , address : int = MCP4728_DEFAULT_ADDRESS ) -> None :
121
128
122
129
self .i2c_device = i2c_device .I2CDevice (i2c_bus , address )
123
130
@@ -129,17 +136,19 @@ def __init__(self, i2c_bus, address: int = MCP4728_DEFAULT_ADDRESS):
129
136
self .channel_d = Channel (self , self ._cache_page (* raw_registers [3 ]), 3 )
130
137
131
138
@staticmethod
132
- def _get_flags (high_byte ) :
139
+ def _get_flags (high_byte : int ) -> Tuple [ int , int , int ] :
133
140
vref = (high_byte & 1 << 7 ) > 0
134
141
gain = (high_byte & 1 << 4 ) > 0
135
142
power_state = (high_byte & 0b011 << 5 ) >> 5
136
143
return (vref , gain , power_state )
137
144
138
145
@staticmethod
139
- def _cache_page (value , vref , gain , power_state ):
146
+ def _cache_page (
147
+ value : int , vref : int , gain : int , power_state : int
148
+ ) -> Dict [str , int ]:
140
149
return {"value" : value , "vref" : vref , "gain" : gain , "power_state" : power_state }
141
150
142
- def _read_registers (self ):
151
+ def _read_registers (self ) -> List [ Tuple [ int , int , int , int ]] :
143
152
buf = bytearray (24 )
144
153
145
154
with self .i2c_device as i2c :
@@ -158,7 +167,7 @@ def _read_registers(self):
158
167
159
168
return current_values
160
169
161
- def save_settings (self ):
170
+ def save_settings (self ) -> None :
162
171
"""Saves the currently selected values, Vref, and gain selections for each channel
163
172
to the EEPROM, setting them as defaults on power up"""
164
173
byte_list = []
@@ -169,7 +178,7 @@ def save_settings(self):
169
178
self ._write_multi_eeprom (byte_list )
170
179
171
180
# TODO: add the ability to set an offset
172
- def _write_multi_eeprom (self , byte_list ) :
181
+ def _write_multi_eeprom (self , byte_list : List [ int ]) -> None :
173
182
buffer_list = [_MCP4728_CH_A_MULTI_EEPROM ]
174
183
buffer_list += byte_list
175
184
@@ -180,7 +189,7 @@ def _write_multi_eeprom(self, byte_list):
180
189
181
190
sleep (0.015 ) # the better to write you with
182
191
183
- def sync_vrefs (self ):
192
+ def sync_vrefs (self ) -> None :
184
193
"""Syncs the driver's vref state with the DAC"""
185
194
gain_setter_command = 0b10000000
186
195
gain_setter_command |= self .channel_a .vref << 3
@@ -193,7 +202,7 @@ def sync_vrefs(self):
193
202
with self .i2c_device as i2c :
194
203
i2c .write (buf )
195
204
196
- def sync_gains (self ):
205
+ def sync_gains (self ) -> None :
197
206
"""Syncs the driver's gain state with the DAC"""
198
207
199
208
sync_setter_command = 0b11000000
@@ -208,7 +217,7 @@ def sync_gains(self):
208
217
with self .i2c_device as i2c :
209
218
i2c .write (buf )
210
219
211
- def _set_value (self , channel ) :
220
+ def _set_value (self , channel : "Channel" ) -> None :
212
221
213
222
channel_bytes = self ._generate_bytes_with_flags (channel )
214
223
@@ -222,7 +231,7 @@ def _set_value(self, channel):
222
231
i2c .write (output_buffer )
223
232
224
233
@staticmethod
225
- def _generate_bytes_with_flags (channel ) :
234
+ def _generate_bytes_with_flags (channel : "Channel" ) -> bytearray :
226
235
buf = bytearray (2 )
227
236
pack_into (">H" , buf , 0 , channel .raw_value )
228
237
@@ -232,12 +241,12 @@ def _generate_bytes_with_flags(channel):
232
241
return buf
233
242
234
243
@staticmethod
235
- def _chunk (big_list , chunk_size ) :
244
+ def _chunk (big_list : bytearray , chunk_size : int ) -> Iterator [ bytearray ] :
236
245
"""Divides a given list into `chunk_size` sized chunks"""
237
246
for i in range (0 , len (big_list ), chunk_size ):
238
247
yield big_list [i : i + chunk_size ]
239
248
240
- def _general_call (self , byte_command ) :
249
+ def _general_call (self , byte_command : int ) -> None :
241
250
buffer_list = [_MCP4728_GENERAL_CALL_ADDRESS ]
242
251
buffer_list += [byte_command ]
243
252
@@ -246,20 +255,20 @@ def _general_call(self, byte_command):
246
255
with self .i2c_device as i2c :
247
256
i2c .write (buf )
248
257
249
- def reset (self ):
258
+ def reset (self ) -> None :
250
259
"""Internal Reset similar to a Power-on Reset (POR).
251
260
The contents of the EEPROM are loaded into each DAC input
252
261
and output registers immediately"""
253
262
254
263
self ._general_call (_MCP4728_GENERAL_CALL_RESET_COMMAND )
255
264
256
- def wakeup (self ):
265
+ def wakeup (self ) -> None :
257
266
"""Reset the Power-Down bits (PD1, PD0 = 0,0) and
258
267
Resumes Normal Operation mode"""
259
268
260
269
self ._general_call (_MCP4728_GENERAL_CALL_WAKEUP_COMMAND )
261
270
262
- def soft_update (self ):
271
+ def soft_update (self ) -> None :
263
272
"""Updates all DAC analog outputs (VOUT) at the same time."""
264
273
265
274
self ._general_call (_MCP4728_GENERAL_CALL_SOFTWARE_UPDATE_COMMAND )
@@ -281,59 +290,64 @@ class Channel:
281
290
282
291
"""
283
292
284
- def __init__ (self , dac_instance , cache_page , index ):
293
+ def __init__ (
294
+ self ,
295
+ dac_instance : Literal [0 , 1 , 2 , 3 ],
296
+ cache_page : Dict [str , int ],
297
+ index : int ,
298
+ ) -> None :
285
299
self ._vref = cache_page ["vref" ]
286
300
self ._gain = cache_page ["gain" ]
287
301
self ._raw_value = cache_page ["value" ]
288
302
self ._dac = dac_instance
289
303
self .channel_index = index
290
304
291
305
@property
292
- def normalized_value (self ):
306
+ def normalized_value (self ) -> float :
293
307
"""The DAC value as a floating point number in the range 0.0 to 1.0."""
294
308
return self .raw_value / (2 ** 12 - 1 )
295
309
296
310
@normalized_value .setter
297
- def normalized_value (self , value ) :
311
+ def normalized_value (self , value : float ) -> None :
298
312
if value < 0.0 or value > 1.0 :
299
313
raise AttributeError ("`normalized_value` must be between 0.0 and 1.0" )
300
314
301
315
self .raw_value = int (value * 4095.0 )
302
316
303
317
@property
304
- def value (self ):
318
+ def value (self ) -> int :
305
319
"""The 16-bit scaled current value for the channel. Note that the MCP4728 is a 12-bit piece
306
320
so quantization errors will occur"""
307
321
return self .normalized_value * (2 ** 16 - 1 )
308
322
309
323
@value .setter
310
- def value (self , value ) :
324
+ def value (self , value : int ) -> None :
311
325
if value < 0 or value > (2 ** 16 - 1 ):
312
326
raise AttributeError (
313
- "`value` must be a 16-bit integer between 0 and %s" % (2 ** 16 - 1 )
327
+ f "`value` must be a 16-bit integer between 0 and { (2 ** 16 - 1 )} "
314
328
)
315
329
316
330
# Scale from 16-bit to 12-bit value (quantization errors will occur!).
317
331
self .raw_value = value >> 4
318
332
319
333
@property
320
- def raw_value (self ):
334
+ def raw_value (self ) -> int :
321
335
"""The native 12-bit value used by the DAC"""
322
336
return self ._raw_value
323
337
324
338
@raw_value .setter
325
- def raw_value (self , value ) :
339
+ def raw_value (self , value : int ) -> None :
326
340
if value < 0 or value > (2 ** 12 - 1 ):
327
341
raise AttributeError (
328
- "`raw_value` must be a 12-bit integer between 0 and %s" % (2 ** 12 - 1 )
342
+ f "`raw_value` must be a 12-bit integer between 0 and { (2 ** 12 - 1 )} "
329
343
)
330
344
self ._raw_value = value
331
345
# disabling the protected access warning here because making it public would be
332
346
# more confusing
333
347
self ._dac ._set_value (self ) # pylint:disable=protected-access
334
348
335
349
@property
336
- def gain (self ):
350
+ def gain (self ) -> Literal [ 1 , 2 ] :
337
351
"""Sets the gain of the channel if the Vref for the channel is ``Vref.INTERNAL``.
338
352
**The gain setting has no effect if the Vref for the channel is `Vref.VDD`**.
339
353
@@ -342,19 +356,19 @@ def gain(self):
342
356
return self ._gain
343
357
344
358
@gain .setter
345
- def gain (self , value ) :
359
+ def gain (self , value : Literal [ 1 , 2 ]) -> None :
346
360
if not value in (1 , 2 ):
347
361
raise AttributeError ("`gain` must be 1 or 2" )
348
362
self ._gain = value - 1
349
363
self ._dac .sync_gains ()
350
364
351
365
@property
352
- def vref (self ):
366
+ def vref (self ) -> Literal [ 0 , 1 ] :
353
367
"""Sets the DAC's voltage reference source. Must be a ``VREF``"""
354
368
return self ._vref
355
369
356
370
@vref .setter
357
- def vref (self , value ) :
371
+ def vref (self , value : Literal [ 0 , 1 ]) -> None :
358
372
if not Vref .is_valid (value ):
359
373
raise AttributeError ("range must be a `Vref`" )
360
374
self ._vref = value
0 commit comments