adafruit-circuitpython-ads7128 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adafruit_ads7128/ads7128.py +595 -0
- adafruit_ads7128/analog_in.py +167 -0
- adafruit_ads7128/digital_inout.py +188 -0
- adafruit_circuitpython_ads7128-1.0.0.dist-info/METADATA +147 -0
- adafruit_circuitpython_ads7128-1.0.0.dist-info/RECORD +8 -0
- adafruit_circuitpython_ads7128-1.0.0.dist-info/WHEEL +5 -0
- adafruit_circuitpython_ads7128-1.0.0.dist-info/licenses/LICENSE +21 -0
- adafruit_circuitpython_ads7128-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""
|
|
5
|
+
:py:class:`~adafruit_ads7128.ads7128.ADS7128`
|
|
6
|
+
================================================================================
|
|
7
|
+
|
|
8
|
+
CircuitPython driver for the Adafruit ADS7128 8-Channel ADC and GPIO Expander
|
|
9
|
+
|
|
10
|
+
* Author(s): Liz Clark
|
|
11
|
+
|
|
12
|
+
Implementation Notes
|
|
13
|
+
--------------------
|
|
14
|
+
|
|
15
|
+
**Hardware:**
|
|
16
|
+
|
|
17
|
+
* `Adafruit ADS7128 8-Channel ADC and GPIO Expander <https://www.adafruit.com/product/6494>`_
|
|
18
|
+
|
|
19
|
+
**Software and Dependencies:**
|
|
20
|
+
|
|
21
|
+
* Adafruit CircuitPython firmware for the supported boards:
|
|
22
|
+
https://circuitpython.org/downloads
|
|
23
|
+
|
|
24
|
+
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import time
|
|
28
|
+
|
|
29
|
+
from adafruit_bus_device import i2c_device
|
|
30
|
+
from micropython import const
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from typing import Tuple
|
|
34
|
+
|
|
35
|
+
from busio import I2C
|
|
36
|
+
except ImportError:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
__version__ = "1.0.0"
|
|
40
|
+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS7128.git"
|
|
41
|
+
|
|
42
|
+
# Oversampling ratios
|
|
43
|
+
OSR_NONE = 0
|
|
44
|
+
"""No oversampling (12-bit result)."""
|
|
45
|
+
OSR_2 = 1
|
|
46
|
+
"""2x oversampling (13-bit result)."""
|
|
47
|
+
OSR_4 = 2
|
|
48
|
+
"""4x oversampling (14-bit result)."""
|
|
49
|
+
OSR_8 = 3
|
|
50
|
+
"""8x oversampling (15-bit result)."""
|
|
51
|
+
OSR_16 = 4
|
|
52
|
+
"""16x oversampling (16-bit result)."""
|
|
53
|
+
OSR_32 = 5
|
|
54
|
+
"""32x oversampling (16-bit result)."""
|
|
55
|
+
OSR_64 = 6
|
|
56
|
+
"""64x oversampling (16-bit result)."""
|
|
57
|
+
OSR_128 = 7
|
|
58
|
+
"""128x oversampling (16-bit result)."""
|
|
59
|
+
|
|
60
|
+
_DEFAULT_ADDR = const(0x10)
|
|
61
|
+
|
|
62
|
+
# Opcodes
|
|
63
|
+
_OP_READ = const(0x10)
|
|
64
|
+
_OP_WRITE = const(0x08)
|
|
65
|
+
_OP_SET_BIT = const(0x18)
|
|
66
|
+
_OP_CLEAR_BIT = const(0x20)
|
|
67
|
+
|
|
68
|
+
# Registers
|
|
69
|
+
_STATUS = const(0x00)
|
|
70
|
+
_GENERAL_CFG = const(0x01)
|
|
71
|
+
_DATA_CFG = const(0x02)
|
|
72
|
+
_OSR_CFG = const(0x03)
|
|
73
|
+
_OPMODE_CFG = const(0x04)
|
|
74
|
+
_PIN_CFG = const(0x05)
|
|
75
|
+
_GPIO_CFG = const(0x07)
|
|
76
|
+
_GPO_DRIVE_CFG = const(0x09)
|
|
77
|
+
_GPO_VALUE = const(0x0B)
|
|
78
|
+
_GPI_VALUE = const(0x0D)
|
|
79
|
+
_ZCD_BLANKING_CFG = const(0x0F)
|
|
80
|
+
_SEQUENCE_CFG = const(0x10)
|
|
81
|
+
_CHANNEL_SEL = const(0x11)
|
|
82
|
+
_AUTO_SEQ_CH_SEL = const(0x12)
|
|
83
|
+
_ALERT_CH_SEL = const(0x14)
|
|
84
|
+
_ALERT_PIN_CFG = const(0x17)
|
|
85
|
+
_EVENT_FLAG = const(0x18)
|
|
86
|
+
_EVENT_HIGH_FLAG = const(0x1A)
|
|
87
|
+
_EVENT_LOW_FLAG = const(0x1C)
|
|
88
|
+
_RECENT_LSB_CH0 = const(0xA0) # +2 per channel
|
|
89
|
+
_RMS_CFG = const(0xC0)
|
|
90
|
+
_RMS_LSB = const(0xC1)
|
|
91
|
+
_RMS_MSB = const(0xC2)
|
|
92
|
+
_GPO_ZCD_UPDATE_EN = const(0xE7)
|
|
93
|
+
|
|
94
|
+
# SYSTEM_STATUS bits
|
|
95
|
+
_BOR = const(0x01)
|
|
96
|
+
_CRC_ERR_IN = const(0x02)
|
|
97
|
+
_RMS_DONE = const(0x10)
|
|
98
|
+
|
|
99
|
+
# GENERAL_CFG bits
|
|
100
|
+
_RST = const(0x01)
|
|
101
|
+
_CAL = const(0x02)
|
|
102
|
+
_CNVST = const(0x08)
|
|
103
|
+
_DWC_EN = const(0x10)
|
|
104
|
+
_STATS_EN = const(0x20)
|
|
105
|
+
_CRC_EN = const(0x40)
|
|
106
|
+
_RMS_EN = const(0x80)
|
|
107
|
+
|
|
108
|
+
# SEQUENCE_CFG bits
|
|
109
|
+
_SEQ_MODE = const(0x01)
|
|
110
|
+
_SEQ_START = const(0x10)
|
|
111
|
+
|
|
112
|
+
# OPMODE_CFG bits
|
|
113
|
+
_OSC_SEL = const(0x10)
|
|
114
|
+
_CONV_MODE = const(0x20)
|
|
115
|
+
|
|
116
|
+
# DATA_CFG bits
|
|
117
|
+
_APPEND_CHID = const(0x10)
|
|
118
|
+
|
|
119
|
+
# ALERT_PIN_CFG bits
|
|
120
|
+
_ALERT_DRIVE = const(0x04)
|
|
121
|
+
|
|
122
|
+
# ZCD_BLANKING_CFG bits
|
|
123
|
+
_ZCD_MULT = const(0x80)
|
|
124
|
+
|
|
125
|
+
# RMS_CFG bits
|
|
126
|
+
_RMS_DC_SUB = const(0x04)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _crc8(value: int) -> int:
|
|
130
|
+
# CRC-8-CCITT, polynomial 0x07, over a single byte
|
|
131
|
+
crc = value
|
|
132
|
+
for _ in range(8):
|
|
133
|
+
if crc & 0x80:
|
|
134
|
+
crc = ((crc << 1) ^ 0x07) & 0xFF
|
|
135
|
+
else:
|
|
136
|
+
crc = (crc << 1) & 0xFF
|
|
137
|
+
return crc
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ADS7128: # noqa: PLR0904
|
|
141
|
+
"""Driver for the ADS7128 8-channel 12-bit ADC with GPIOs.
|
|
142
|
+
|
|
143
|
+
:param ~busio.I2C i2c: The I2C bus the ADS7128 is connected to.
|
|
144
|
+
:param int address: The I2C address of the device. Defaults to :const:`0x10`.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __init__(self, i2c: "I2C", address: int = _DEFAULT_ADDR) -> None:
|
|
148
|
+
self.i2c_device = i2c_device.I2CDevice(i2c, address)
|
|
149
|
+
self._crc_enabled = False
|
|
150
|
+
self._crc_error = False
|
|
151
|
+
self._in_buf = bytearray(1)
|
|
152
|
+
self._out_buf = bytearray(2)
|
|
153
|
+
self._wbuf = bytearray(3)
|
|
154
|
+
self._reset()
|
|
155
|
+
|
|
156
|
+
def _read_register(self, reg: int) -> int:
|
|
157
|
+
if self._crc_enabled:
|
|
158
|
+
out = bytes((_OP_READ, _crc8(_OP_READ), reg, _crc8(reg)))
|
|
159
|
+
in_buf = bytearray(2)
|
|
160
|
+
with self.i2c_device as i2c:
|
|
161
|
+
i2c.write_then_readinto(out, in_buf)
|
|
162
|
+
if in_buf[1] != _crc8(in_buf[0]):
|
|
163
|
+
self._crc_error = True
|
|
164
|
+
return in_buf[0]
|
|
165
|
+
self._out_buf[0] = _OP_READ
|
|
166
|
+
self._out_buf[1] = reg
|
|
167
|
+
with self.i2c_device as i2c:
|
|
168
|
+
i2c.write_then_readinto(self._out_buf, self._in_buf)
|
|
169
|
+
return self._in_buf[0]
|
|
170
|
+
|
|
171
|
+
def _write_register(self, reg: int, data: int) -> None:
|
|
172
|
+
if self._crc_enabled:
|
|
173
|
+
out = bytes((_OP_WRITE, _crc8(_OP_WRITE), reg, _crc8(reg), data, _crc8(data)))
|
|
174
|
+
with self.i2c_device as i2c:
|
|
175
|
+
i2c.write(out)
|
|
176
|
+
return
|
|
177
|
+
self._wbuf[0] = _OP_WRITE
|
|
178
|
+
self._wbuf[1] = reg
|
|
179
|
+
self._wbuf[2] = data
|
|
180
|
+
with self.i2c_device as i2c:
|
|
181
|
+
i2c.write(self._wbuf)
|
|
182
|
+
|
|
183
|
+
def _set_bits(self, reg: int, mask: int) -> None:
|
|
184
|
+
if self._crc_enabled:
|
|
185
|
+
out = bytes((_OP_SET_BIT, _crc8(_OP_SET_BIT), reg, _crc8(reg), mask, _crc8(mask)))
|
|
186
|
+
with self.i2c_device as i2c:
|
|
187
|
+
i2c.write(out)
|
|
188
|
+
return
|
|
189
|
+
self._wbuf[0] = _OP_SET_BIT
|
|
190
|
+
self._wbuf[1] = reg
|
|
191
|
+
self._wbuf[2] = mask
|
|
192
|
+
with self.i2c_device as i2c:
|
|
193
|
+
i2c.write(self._wbuf)
|
|
194
|
+
|
|
195
|
+
def _clear_bits(self, reg: int, mask: int) -> None:
|
|
196
|
+
if self._crc_enabled:
|
|
197
|
+
out = bytes(
|
|
198
|
+
(
|
|
199
|
+
_OP_CLEAR_BIT,
|
|
200
|
+
_crc8(_OP_CLEAR_BIT),
|
|
201
|
+
reg,
|
|
202
|
+
_crc8(reg),
|
|
203
|
+
mask,
|
|
204
|
+
_crc8(mask),
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
with self.i2c_device as i2c:
|
|
208
|
+
i2c.write(out)
|
|
209
|
+
return
|
|
210
|
+
self._wbuf[0] = _OP_CLEAR_BIT
|
|
211
|
+
self._wbuf[1] = reg
|
|
212
|
+
self._wbuf[2] = mask
|
|
213
|
+
with self.i2c_device as i2c:
|
|
214
|
+
i2c.write(self._wbuf)
|
|
215
|
+
|
|
216
|
+
def _read_12bit(self, lsb_reg: int) -> int:
|
|
217
|
+
# 12-bit value is MSB-aligned across the register pair:
|
|
218
|
+
# MSB register = [D11:D4], LSB register = [D3:D0, 0, 0, 0, 0]
|
|
219
|
+
lsb = self._read_register(lsb_reg)
|
|
220
|
+
msb = self._read_register(lsb_reg + 1)
|
|
221
|
+
return (msb << 4) | (lsb >> 4)
|
|
222
|
+
|
|
223
|
+
def _reset(self) -> None:
|
|
224
|
+
self._write_register(_GENERAL_CFG, _RST)
|
|
225
|
+
time.sleep(0.005)
|
|
226
|
+
self._write_register(_GENERAL_CFG, _CAL)
|
|
227
|
+
for _ in range(1000):
|
|
228
|
+
if not self._read_register(_GENERAL_CFG) & _CAL:
|
|
229
|
+
break
|
|
230
|
+
time.sleep(0.001)
|
|
231
|
+
else:
|
|
232
|
+
raise RuntimeError("ADS7128 calibration timed out")
|
|
233
|
+
self._write_register(_STATUS, _BOR)
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def _check_channel(channel: int) -> None:
|
|
237
|
+
if not 0 <= channel <= 7:
|
|
238
|
+
raise ValueError("channel must be 0-7")
|
|
239
|
+
|
|
240
|
+
def _read(self, channel: int) -> int:
|
|
241
|
+
"""Read a single 12-bit conversion (0-4095) from a channel in manual mode.
|
|
242
|
+
|
|
243
|
+
:param int channel: Channel number, 0-7.
|
|
244
|
+
:return: The 12-bit conversion result.
|
|
245
|
+
:rtype: int
|
|
246
|
+
"""
|
|
247
|
+
self._check_channel(channel)
|
|
248
|
+
self._set_bits(_GENERAL_CFG, _STATS_EN)
|
|
249
|
+
chan_sel = self._read_register(_CHANNEL_SEL)
|
|
250
|
+
chan_sel = (chan_sel & 0xF0) | (channel & 0x0F)
|
|
251
|
+
self._write_register(_CHANNEL_SEL, chan_sel)
|
|
252
|
+
self._set_bits(_GENERAL_CFG, _CNVST)
|
|
253
|
+
time.sleep(0.00001)
|
|
254
|
+
return self._read_12bit(_RECENT_LSB_CH0 + channel * 2)
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def sequence_channels(self) -> int:
|
|
258
|
+
"""Bitmask of channels included in the auto-sequence (bit 0 = CH0)."""
|
|
259
|
+
return self._read_register(_AUTO_SEQ_CH_SEL)
|
|
260
|
+
|
|
261
|
+
@sequence_channels.setter
|
|
262
|
+
def sequence_channels(self, channel_mask: int) -> None:
|
|
263
|
+
self._write_register(_AUTO_SEQ_CH_SEL, channel_mask & 0xFF)
|
|
264
|
+
|
|
265
|
+
def start_sequence(self) -> None:
|
|
266
|
+
"""Start autonomous auto-sequence conversions on the selected channels."""
|
|
267
|
+
self._set_bits(_GENERAL_CFG, _STATS_EN)
|
|
268
|
+
self._set_bits(_DATA_CFG, _APPEND_CHID)
|
|
269
|
+
self._set_bits(_OPMODE_CFG, _CONV_MODE)
|
|
270
|
+
self._write_register(_SEQUENCE_CFG, _SEQ_MODE | _SEQ_START)
|
|
271
|
+
|
|
272
|
+
def stop_sequence(self) -> None:
|
|
273
|
+
"""Stop the auto-sequence."""
|
|
274
|
+
self._clear_bits(_SEQUENCE_CFG, _SEQ_START)
|
|
275
|
+
|
|
276
|
+
def read_sequence_result(self) -> "Tuple[int, int]":
|
|
277
|
+
"""Read the next conversion result from the running sequence.
|
|
278
|
+
|
|
279
|
+
:return: A ``(value, channel)`` tuple where ``value`` is the 12-bit
|
|
280
|
+
conversion result and ``channel`` is the channel it came from.
|
|
281
|
+
:rtype: tuple
|
|
282
|
+
"""
|
|
283
|
+
out = bytes((_OP_READ, 0x00))
|
|
284
|
+
in_buf = bytearray(2)
|
|
285
|
+
with self.i2c_device as i2c:
|
|
286
|
+
i2c.write_then_readinto(out, in_buf)
|
|
287
|
+
value = (in_buf[0] << 4) | ((in_buf[1] >> 4) & 0x0F)
|
|
288
|
+
channel = in_buf[1] & 0x0F
|
|
289
|
+
return value, channel
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def oversampling(self) -> int:
|
|
293
|
+
"""Oversampling ratio (0-7). See the ``OSR_*`` module constants."""
|
|
294
|
+
return self._read_register(_OSR_CFG) & 0x07
|
|
295
|
+
|
|
296
|
+
@oversampling.setter
|
|
297
|
+
def oversampling(self, osr: int) -> None:
|
|
298
|
+
if not 0 <= osr <= 7:
|
|
299
|
+
raise ValueError("oversampling must be 0-7")
|
|
300
|
+
self._write_register(_OSR_CFG, osr & 0x07)
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def pin_cfg(self) -> int:
|
|
304
|
+
"""Per-channel analog (bit clear) / digital (bit set) selection bitmask."""
|
|
305
|
+
return self._read_register(_PIN_CFG)
|
|
306
|
+
|
|
307
|
+
@pin_cfg.setter
|
|
308
|
+
def pin_cfg(self, value: int) -> None:
|
|
309
|
+
self._write_register(_PIN_CFG, value & 0xFF)
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def gpio_cfg(self) -> int:
|
|
313
|
+
"""Per-channel digital direction bitmask (bit set = output, clear = input)."""
|
|
314
|
+
return self._read_register(_GPIO_CFG)
|
|
315
|
+
|
|
316
|
+
@gpio_cfg.setter
|
|
317
|
+
def gpio_cfg(self, value: int) -> None:
|
|
318
|
+
self._write_register(_GPIO_CFG, value & 0xFF)
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def gpo_drive_cfg(self) -> int:
|
|
322
|
+
"""Per-channel output drive bitmask (bit set = push-pull, clear = open-drain)."""
|
|
323
|
+
return self._read_register(_GPO_DRIVE_CFG)
|
|
324
|
+
|
|
325
|
+
@gpo_drive_cfg.setter
|
|
326
|
+
def gpo_drive_cfg(self, value: int) -> None:
|
|
327
|
+
self._write_register(_GPO_DRIVE_CFG, value & 0xFF)
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def gpo_value(self) -> int:
|
|
331
|
+
"""Digital output level bitmask, one bit per channel."""
|
|
332
|
+
return self._read_register(_GPO_VALUE)
|
|
333
|
+
|
|
334
|
+
@gpo_value.setter
|
|
335
|
+
def gpo_value(self, value: int) -> None:
|
|
336
|
+
self._write_register(_GPO_VALUE, value & 0xFF)
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def gpi_value(self) -> int:
|
|
340
|
+
"""Digital input level bitmask, one bit per channel. (read-only)"""
|
|
341
|
+
return self._read_register(_GPI_VALUE)
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def gpo_zcd_update_en(self) -> int:
|
|
345
|
+
"""Per-channel zero-crossing-to-GPO update enable bitmask."""
|
|
346
|
+
return self._read_register(_GPO_ZCD_UPDATE_EN)
|
|
347
|
+
|
|
348
|
+
@gpo_zcd_update_en.setter
|
|
349
|
+
def gpo_zcd_update_en(self, value: int) -> None:
|
|
350
|
+
self._write_register(_GPO_ZCD_UPDATE_EN, value & 0xFF)
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def crc_enabled(self) -> bool:
|
|
354
|
+
"""Whether CRC validation of I2C transactions is enabled."""
|
|
355
|
+
return self._crc_enabled
|
|
356
|
+
|
|
357
|
+
@crc_enabled.setter
|
|
358
|
+
def crc_enabled(self, enable: bool) -> None:
|
|
359
|
+
if enable:
|
|
360
|
+
self._set_bits(_GENERAL_CFG, _CRC_EN)
|
|
361
|
+
self._crc_enabled = True
|
|
362
|
+
else:
|
|
363
|
+
self._clear_bits(_GENERAL_CFG, _CRC_EN)
|
|
364
|
+
self._crc_enabled = False
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def crc_error(self) -> bool:
|
|
368
|
+
"""``True`` if a CRC error has been detected on the I2C interface. (read-only)"""
|
|
369
|
+
if self._read_register(_STATUS) & _CRC_ERR_IN:
|
|
370
|
+
return True
|
|
371
|
+
return self._crc_error
|
|
372
|
+
|
|
373
|
+
def clear_crc_error(self) -> None:
|
|
374
|
+
"""Clear the CRC error flag."""
|
|
375
|
+
self._crc_error = False
|
|
376
|
+
self._write_register(_STATUS, _CRC_ERR_IN)
|
|
377
|
+
|
|
378
|
+
@property
|
|
379
|
+
def statistics_enabled(self) -> bool:
|
|
380
|
+
"""Whether min/max/recent statistics tracking is enabled."""
|
|
381
|
+
return bool(self._read_register(_GENERAL_CFG) & _STATS_EN)
|
|
382
|
+
|
|
383
|
+
@statistics_enabled.setter
|
|
384
|
+
def statistics_enabled(self, enable: bool) -> None:
|
|
385
|
+
if enable:
|
|
386
|
+
self._set_bits(_GENERAL_CFG, _STATS_EN)
|
|
387
|
+
else:
|
|
388
|
+
self._clear_bits(_GENERAL_CFG, _STATS_EN)
|
|
389
|
+
|
|
390
|
+
def reset_statistics(self) -> None:
|
|
391
|
+
"""Clear all recorded statistics and restart recording."""
|
|
392
|
+
self._clear_bits(_GENERAL_CFG, _STATS_EN)
|
|
393
|
+
self._set_bits(_GENERAL_CFG, _STATS_EN)
|
|
394
|
+
|
|
395
|
+
@property
|
|
396
|
+
def dwc_enabled(self) -> bool:
|
|
397
|
+
"""Whether the digital window comparator is enabled."""
|
|
398
|
+
return bool(self._read_register(_GENERAL_CFG) & _DWC_EN)
|
|
399
|
+
|
|
400
|
+
@dwc_enabled.setter
|
|
401
|
+
def dwc_enabled(self, enable: bool) -> None:
|
|
402
|
+
if enable:
|
|
403
|
+
self._set_bits(_GENERAL_CFG, _DWC_EN)
|
|
404
|
+
else:
|
|
405
|
+
self._clear_bits(_GENERAL_CFG, _DWC_EN)
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def event_flags(self) -> int:
|
|
409
|
+
"""Combined high/low threshold event flags, one bit per channel. (read-only)"""
|
|
410
|
+
return self._read_register(_EVENT_FLAG)
|
|
411
|
+
|
|
412
|
+
@property
|
|
413
|
+
def event_high_flags(self) -> int:
|
|
414
|
+
"""High threshold event flags, one bit per channel. (read-only)"""
|
|
415
|
+
return self._read_register(_EVENT_HIGH_FLAG)
|
|
416
|
+
|
|
417
|
+
@property
|
|
418
|
+
def event_low_flags(self) -> int:
|
|
419
|
+
"""Low threshold event flags, one bit per channel. (read-only)"""
|
|
420
|
+
return self._read_register(_EVENT_LOW_FLAG)
|
|
421
|
+
|
|
422
|
+
def clear_event_flags(self) -> None:
|
|
423
|
+
"""Clear all high and low threshold event flags."""
|
|
424
|
+
self._write_register(_EVENT_HIGH_FLAG, 0xFF)
|
|
425
|
+
self._write_register(_EVENT_LOW_FLAG, 0xFF)
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def alert_push_pull(self) -> bool:
|
|
429
|
+
"""ALERT pin drive: ``True`` for push-pull, ``False`` for open-drain."""
|
|
430
|
+
return bool(self._read_register(_ALERT_PIN_CFG) & _ALERT_DRIVE)
|
|
431
|
+
|
|
432
|
+
@alert_push_pull.setter
|
|
433
|
+
def alert_push_pull(self, value: bool) -> None:
|
|
434
|
+
cfg = self._read_register(_ALERT_PIN_CFG)
|
|
435
|
+
if value:
|
|
436
|
+
cfg |= _ALERT_DRIVE
|
|
437
|
+
else:
|
|
438
|
+
cfg &= ~_ALERT_DRIVE & 0xFF
|
|
439
|
+
self._write_register(_ALERT_PIN_CFG, cfg)
|
|
440
|
+
|
|
441
|
+
@property
|
|
442
|
+
def alert_logic(self) -> int:
|
|
443
|
+
"""ALERT pin logic: 0=active low, 1=active high, 2=pulsed low, 3=pulsed high."""
|
|
444
|
+
return self._read_register(_ALERT_PIN_CFG) & 0x03
|
|
445
|
+
|
|
446
|
+
@alert_logic.setter
|
|
447
|
+
def alert_logic(self, value: int) -> None:
|
|
448
|
+
if not 0 <= value <= 3:
|
|
449
|
+
raise ValueError("alert_logic must be 0-3")
|
|
450
|
+
cfg = self._read_register(_ALERT_PIN_CFG)
|
|
451
|
+
cfg = (cfg & ~0x03 & 0xFF) | value
|
|
452
|
+
self._write_register(_ALERT_PIN_CFG, cfg)
|
|
453
|
+
|
|
454
|
+
@property
|
|
455
|
+
def alert_channels(self) -> int:
|
|
456
|
+
"""Bitmask of channels that can trigger the ALERT pin (bit 0 = CH0)."""
|
|
457
|
+
return self._read_register(_ALERT_CH_SEL)
|
|
458
|
+
|
|
459
|
+
@alert_channels.setter
|
|
460
|
+
def alert_channels(self, channel_mask: int) -> None:
|
|
461
|
+
self._write_register(_ALERT_CH_SEL, channel_mask & 0xFF)
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def low_power_oscillator(self) -> bool:
|
|
465
|
+
"""Use the low-power oscillator (``True``) or high-speed oscillator (``False``)."""
|
|
466
|
+
return bool(self._read_register(_OPMODE_CFG) & _OSC_SEL)
|
|
467
|
+
|
|
468
|
+
@low_power_oscillator.setter
|
|
469
|
+
def low_power_oscillator(self, value: bool) -> None:
|
|
470
|
+
cfg = self._read_register(_OPMODE_CFG)
|
|
471
|
+
if value:
|
|
472
|
+
cfg |= _OSC_SEL
|
|
473
|
+
else:
|
|
474
|
+
cfg &= ~_OSC_SEL & 0xFF
|
|
475
|
+
self._write_register(_OPMODE_CFG, cfg)
|
|
476
|
+
|
|
477
|
+
@property
|
|
478
|
+
def clock_divider(self) -> int:
|
|
479
|
+
"""Autonomous-mode sampling clock divider, 0-15."""
|
|
480
|
+
return self._read_register(_OPMODE_CFG) & 0x0F
|
|
481
|
+
|
|
482
|
+
@clock_divider.setter
|
|
483
|
+
def clock_divider(self, divider: int) -> None:
|
|
484
|
+
if not 0 <= divider <= 15:
|
|
485
|
+
raise ValueError("clock_divider must be 0-15")
|
|
486
|
+
cfg = self._read_register(_OPMODE_CFG)
|
|
487
|
+
cfg = (cfg & 0xF0) | (divider & 0x0F)
|
|
488
|
+
self._write_register(_OPMODE_CFG, cfg)
|
|
489
|
+
|
|
490
|
+
@property
|
|
491
|
+
def zcd_channel(self) -> int:
|
|
492
|
+
"""Analog channel monitored by the zero-crossing detector, 0-7."""
|
|
493
|
+
return (self._read_register(_CHANNEL_SEL) >> 4) & 0x0F
|
|
494
|
+
|
|
495
|
+
@zcd_channel.setter
|
|
496
|
+
def zcd_channel(self, channel: int) -> None:
|
|
497
|
+
self._check_channel(channel)
|
|
498
|
+
reg = self._read_register(_CHANNEL_SEL)
|
|
499
|
+
reg = (reg & 0x0F) | (channel << 4)
|
|
500
|
+
self._write_register(_CHANNEL_SEL, reg)
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def zcd_blanking(self) -> int:
|
|
504
|
+
"""Zero-crossing blanking count, 0-127 (conversions skipped after an event)."""
|
|
505
|
+
return self._read_register(_ZCD_BLANKING_CFG) & 0x7F
|
|
506
|
+
|
|
507
|
+
@zcd_blanking.setter
|
|
508
|
+
def zcd_blanking(self, count: int) -> None:
|
|
509
|
+
if not 0 <= count <= 127:
|
|
510
|
+
raise ValueError("zcd_blanking must be 0-127")
|
|
511
|
+
reg = self._read_register(_ZCD_BLANKING_CFG)
|
|
512
|
+
reg = (reg & _ZCD_MULT) | (count & 0x7F)
|
|
513
|
+
self._write_register(_ZCD_BLANKING_CFG, reg)
|
|
514
|
+
|
|
515
|
+
@property
|
|
516
|
+
def zcd_blanking_multiply(self) -> bool:
|
|
517
|
+
"""When ``True``, the zero-crossing blanking count is multiplied by 8."""
|
|
518
|
+
return bool(self._read_register(_ZCD_BLANKING_CFG) & _ZCD_MULT)
|
|
519
|
+
|
|
520
|
+
@zcd_blanking_multiply.setter
|
|
521
|
+
def zcd_blanking_multiply(self, value: bool) -> None:
|
|
522
|
+
reg = self._read_register(_ZCD_BLANKING_CFG)
|
|
523
|
+
if value:
|
|
524
|
+
reg |= _ZCD_MULT
|
|
525
|
+
else:
|
|
526
|
+
reg &= ~_ZCD_MULT & 0xFF
|
|
527
|
+
self._write_register(_ZCD_BLANKING_CFG, reg)
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def rms_enabled(self) -> bool:
|
|
531
|
+
"""Whether the RMS computation module is enabled.
|
|
532
|
+
|
|
533
|
+
Setting this to ``True`` clears any previous result and starts a new
|
|
534
|
+
computation using samples from autonomous conversion mode.
|
|
535
|
+
"""
|
|
536
|
+
return bool(self._read_register(_GENERAL_CFG) & _RMS_EN)
|
|
537
|
+
|
|
538
|
+
@rms_enabled.setter
|
|
539
|
+
def rms_enabled(self, enable: bool) -> None:
|
|
540
|
+
if enable:
|
|
541
|
+
self._set_bits(_GENERAL_CFG, _RMS_EN)
|
|
542
|
+
else:
|
|
543
|
+
self._clear_bits(_GENERAL_CFG, _RMS_EN)
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def rms_channel(self) -> int:
|
|
547
|
+
"""Analog channel the RMS module monitors, 0-7."""
|
|
548
|
+
return (self._read_register(_RMS_CFG) >> 4) & 0x0F
|
|
549
|
+
|
|
550
|
+
@rms_channel.setter
|
|
551
|
+
def rms_channel(self, channel: int) -> None:
|
|
552
|
+
self._check_channel(channel)
|
|
553
|
+
reg = self._read_register(_RMS_CFG)
|
|
554
|
+
reg = (reg & 0x0F) | (channel << 4)
|
|
555
|
+
self._write_register(_RMS_CFG, reg)
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
def rms_samples(self) -> int:
|
|
559
|
+
"""RMS sample-count setting: 0=1024, 1=4096, 2=16384, 3=65536 samples."""
|
|
560
|
+
return self._read_register(_RMS_CFG) & 0x03
|
|
561
|
+
|
|
562
|
+
@rms_samples.setter
|
|
563
|
+
def rms_samples(self, setting: int) -> None:
|
|
564
|
+
if not 0 <= setting <= 3:
|
|
565
|
+
raise ValueError("rms_samples must be 0-3")
|
|
566
|
+
reg = self._read_register(_RMS_CFG)
|
|
567
|
+
reg = (reg & 0xFC) | (setting & 0x03)
|
|
568
|
+
self._write_register(_RMS_CFG, reg)
|
|
569
|
+
|
|
570
|
+
@property
|
|
571
|
+
def rms_dc_subtract(self) -> bool:
|
|
572
|
+
"""Whether the DC component is subtracted before the RMS calculation."""
|
|
573
|
+
return bool(self._read_register(_RMS_CFG) & _RMS_DC_SUB)
|
|
574
|
+
|
|
575
|
+
@rms_dc_subtract.setter
|
|
576
|
+
def rms_dc_subtract(self, enable: bool) -> None:
|
|
577
|
+
if enable:
|
|
578
|
+
self._set_bits(_RMS_CFG, _RMS_DC_SUB)
|
|
579
|
+
else:
|
|
580
|
+
self._clear_bits(_RMS_CFG, _RMS_DC_SUB)
|
|
581
|
+
|
|
582
|
+
@property
|
|
583
|
+
def rms(self) -> int:
|
|
584
|
+
"""The 16-bit RMS result over the configured number of samples. (read-only)"""
|
|
585
|
+
lsb = self._read_register(_RMS_LSB)
|
|
586
|
+
msb = self._read_register(_RMS_MSB)
|
|
587
|
+
return (msb << 8) | lsb
|
|
588
|
+
|
|
589
|
+
@property
|
|
590
|
+
def rms_done(self) -> bool:
|
|
591
|
+
"""``True`` if the RMS computation is complete. Reading clears the flag. (read-only)"""
|
|
592
|
+
if self._read_register(_STATUS) & _RMS_DONE:
|
|
593
|
+
self._set_bits(_STATUS, _RMS_DONE)
|
|
594
|
+
return True
|
|
595
|
+
return False
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
:py:class:`~adafruit_ads7128.analog_in.AnalogIn`
|
|
7
|
+
======================================================
|
|
8
|
+
AnalogIn for ADC readings.
|
|
9
|
+
|
|
10
|
+
* Author(s): Liz Clark
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from micropython import const
|
|
15
|
+
|
|
16
|
+
from adafruit_ads7128.ads7128 import ADS7128
|
|
17
|
+
|
|
18
|
+
__version__ = "1.0.0"
|
|
19
|
+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS7128.git"
|
|
20
|
+
|
|
21
|
+
_EVENT_RGN = const(0x1E)
|
|
22
|
+
_HYSTERESIS_CH0 = const(0x20) # per-channel block base, +4 per channel
|
|
23
|
+
_MAX_LSB_CH0 = const(0x60) # +2 per channel
|
|
24
|
+
_MIN_LSB_CH0 = const(0x80)
|
|
25
|
+
_RECENT_LSB_CH0 = const(0xA0) # +2 per channel
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AnalogIn:
|
|
29
|
+
"""AnalogIn-compatible wrapper for ADS7128 single-ended readings.
|
|
30
|
+
|
|
31
|
+
Provides a :attr:`value` property scaled to a 16-bit unsigned range
|
|
32
|
+
``[0, 65535]`` and a :attr:`voltage` property in volts, matching the
|
|
33
|
+
CircuitPython ``analogio.AnalogIn`` API.
|
|
34
|
+
|
|
35
|
+
:param ADS7128 adc: The ADS7128 driver instance.
|
|
36
|
+
:param int pin: Channel number (0-7).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, adc: ADS7128, pin: int) -> None:
|
|
40
|
+
if not isinstance(adc, ADS7128):
|
|
41
|
+
raise ValueError("adc must be an ADS7128 instance")
|
|
42
|
+
if pin < 0 or pin > 7:
|
|
43
|
+
raise ValueError("pin must be 0-7")
|
|
44
|
+
self._adc = adc
|
|
45
|
+
self._pin = pin
|
|
46
|
+
adc.pin_cfg = adc.pin_cfg & ~(1 << pin) & 0xFF
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def value(self) -> int:
|
|
50
|
+
"""ADC reading as an unsigned 16-bit integer in the range ``[0, 65535]``.
|
|
51
|
+
|
|
52
|
+
The 12-bit conversion result is scaled to 16 bits to match the
|
|
53
|
+
``analogio.AnalogIn`` API.
|
|
54
|
+
"""
|
|
55
|
+
raw = self._adc._read(self._pin)
|
|
56
|
+
return min((raw * 65535) // 4095, 65535)
|
|
57
|
+
|
|
58
|
+
def voltage(self, reference_voltage: float = 5.0) -> float:
|
|
59
|
+
"""Read the channel and convert the result to volts.
|
|
60
|
+
|
|
61
|
+
:param float reference_voltage: ADC reference voltage in volts.
|
|
62
|
+
Defaults to :const:`5.0` to match the device's default reference.
|
|
63
|
+
:return: The measured voltage in volts.
|
|
64
|
+
:rtype: float
|
|
65
|
+
"""
|
|
66
|
+
return (self._adc._read(self._pin) / 4096.0) * reference_voltage
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def max(self) -> int:
|
|
70
|
+
"""Maximum value recorded on this channel, in 12-bit counts. (read-only)
|
|
71
|
+
|
|
72
|
+
Requires :attr:`ADS7128.statistics_enabled` to be ``True``.
|
|
73
|
+
"""
|
|
74
|
+
return self._adc._read_12bit(_MAX_LSB_CH0 + self._pin * 2)
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def min(self) -> int:
|
|
78
|
+
"""Minimum value recorded on this channel, in 12-bit counts. (read-only)
|
|
79
|
+
|
|
80
|
+
Requires :attr:`ADS7128.statistics_enabled` to be ``True``.
|
|
81
|
+
"""
|
|
82
|
+
return self._adc._read_12bit(_MIN_LSB_CH0 + self._pin * 2)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def recent(self) -> int:
|
|
86
|
+
"""Most recent conversion recorded on this channel, in 12-bit counts. (read-only)
|
|
87
|
+
|
|
88
|
+
Requires :attr:`ADS7128.statistics_enabled` to be ``True``.
|
|
89
|
+
"""
|
|
90
|
+
return self._adc._read_12bit(_RECENT_LSB_CH0 + self._pin * 2)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def high_threshold(self) -> int:
|
|
94
|
+
"""Window-comparator high threshold for this channel, in 12-bit counts (0-4095)."""
|
|
95
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
96
|
+
msb = self._adc._read_register(base + 1)
|
|
97
|
+
hyst = self._adc._read_register(base)
|
|
98
|
+
return (msb << 4) | ((hyst >> 4) & 0x0F)
|
|
99
|
+
|
|
100
|
+
@high_threshold.setter
|
|
101
|
+
def high_threshold(self, value: int) -> None:
|
|
102
|
+
if not 0 <= value <= 0x0FFF:
|
|
103
|
+
raise ValueError("high_threshold must be 0-4095")
|
|
104
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
105
|
+
self._adc._write_register(base + 1, (value >> 4) & 0xFF)
|
|
106
|
+
hyst = self._adc._read_register(base)
|
|
107
|
+
hyst = (hyst & 0x0F) | ((value & 0x0F) << 4)
|
|
108
|
+
self._adc._write_register(base, hyst)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def low_threshold(self) -> int:
|
|
112
|
+
"""Window-comparator low threshold for this channel, in 12-bit counts (0-4095)."""
|
|
113
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
114
|
+
msb = self._adc._read_register(base + 3)
|
|
115
|
+
evt = self._adc._read_register(base + 2)
|
|
116
|
+
return (msb << 4) | ((evt >> 4) & 0x0F)
|
|
117
|
+
|
|
118
|
+
@low_threshold.setter
|
|
119
|
+
def low_threshold(self, value: int) -> None:
|
|
120
|
+
if not 0 <= value <= 0x0FFF:
|
|
121
|
+
raise ValueError("low_threshold must be 0-4095")
|
|
122
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
123
|
+
self._adc._write_register(base + 3, (value >> 4) & 0xFF)
|
|
124
|
+
evt = self._adc._read_register(base + 2)
|
|
125
|
+
evt = (evt & 0x0F) | ((value & 0x0F) << 4)
|
|
126
|
+
self._adc._write_register(base + 2, evt)
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def hysteresis(self) -> int:
|
|
130
|
+
"""Window-comparator hysteresis for this channel, 0-15."""
|
|
131
|
+
return self._adc._read_register(_HYSTERESIS_CH0 + self._pin * 4) & 0x0F
|
|
132
|
+
|
|
133
|
+
@hysteresis.setter
|
|
134
|
+
def hysteresis(self, value: int) -> None:
|
|
135
|
+
if not 0 <= value <= 15:
|
|
136
|
+
raise ValueError("hysteresis must be 0-15")
|
|
137
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
138
|
+
reg = self._adc._read_register(base)
|
|
139
|
+
reg = (reg & 0xF0) | (value & 0x0F)
|
|
140
|
+
self._adc._write_register(base, reg)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def event_region(self) -> bool:
|
|
144
|
+
"""Comparator event region: ``True`` for in-band, ``False`` for out-of-window."""
|
|
145
|
+
return bool(self._adc._read_register(_EVENT_RGN) & (1 << self._pin))
|
|
146
|
+
|
|
147
|
+
@event_region.setter
|
|
148
|
+
def event_region(self, in_band: bool) -> None:
|
|
149
|
+
if in_band:
|
|
150
|
+
self._adc._set_bits(_EVENT_RGN, 1 << self._pin)
|
|
151
|
+
else:
|
|
152
|
+
self._adc._clear_bits(_EVENT_RGN, 1 << self._pin)
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def event_count(self) -> int:
|
|
156
|
+
"""Consecutive samples before an alert triggers, 0-15 (alert after count + 1)."""
|
|
157
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
158
|
+
return self._adc._read_register(base + 2) & 0x0F
|
|
159
|
+
|
|
160
|
+
@event_count.setter
|
|
161
|
+
def event_count(self, count: int) -> None:
|
|
162
|
+
if not 0 <= count <= 15:
|
|
163
|
+
raise ValueError("event_count must be 0-15")
|
|
164
|
+
base = _HYSTERESIS_CH0 + self._pin * 4
|
|
165
|
+
evt = self._adc._read_register(base + 2)
|
|
166
|
+
evt = (evt & 0xF0) | (count & 0x0F)
|
|
167
|
+
self._adc._write_register(base + 2, evt)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Liz Clark for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
:py:class:`~adafruit_ads7128.digital_inout.DigitalInOut`
|
|
7
|
+
==============================================================
|
|
8
|
+
|
|
9
|
+
Digital input/output of the ADS7128.
|
|
10
|
+
|
|
11
|
+
* Author(s): Liz Clark
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import digitalio
|
|
16
|
+
from micropython import const
|
|
17
|
+
|
|
18
|
+
from adafruit_ads7128.ads7128 import ADS7128
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
from digitalio import Direction, DriveMode, Pull
|
|
24
|
+
except ImportError:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
__version__ = "1.0.0"
|
|
28
|
+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS7128.git"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_ZCD_CFG_CH0_3 = const(0xE3)
|
|
32
|
+
_ZCD_CFG_CH4_7 = const(0xE4)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_bit(val: int, bit: int) -> bool:
|
|
36
|
+
return val & (1 << bit) > 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _enable_bit(val: int, bit: int) -> int:
|
|
40
|
+
return val | (1 << bit)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _clear_bit(val: int, bit: int) -> int:
|
|
44
|
+
return val & ~(1 << bit) & 0xFF
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DigitalInOut:
|
|
48
|
+
"""Digital input/output of the ADS7128. The interface is the same as the
|
|
49
|
+
``digitalio.DigitalInOut`` class, however:
|
|
50
|
+
|
|
51
|
+
* the ADS7128 does not support pull-up or pull-down resistors;
|
|
52
|
+
* a channel used here is taken out of analog mode.
|
|
53
|
+
|
|
54
|
+
Open-drain outputs are supported through :attr:`drive_mode`. A
|
|
55
|
+
:exc:`ValueError` is raised when attempting to set an unsupported pull
|
|
56
|
+
configuration.
|
|
57
|
+
|
|
58
|
+
:param ADS7128 adc: The ADS7128 driver instance.
|
|
59
|
+
:param int pin: Channel number (0-7).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, adc: ADS7128, pin: int) -> None:
|
|
63
|
+
if not isinstance(adc, ADS7128):
|
|
64
|
+
raise ValueError("adc must be an ADS7128 instance")
|
|
65
|
+
if pin < 0 or pin > 7:
|
|
66
|
+
raise ValueError("pin must be 0-7")
|
|
67
|
+
self._adc = adc
|
|
68
|
+
self._pin = pin
|
|
69
|
+
|
|
70
|
+
def switch_to_output(
|
|
71
|
+
self,
|
|
72
|
+
value: bool = False,
|
|
73
|
+
drive_mode: "DriveMode" = digitalio.DriveMode.PUSH_PULL,
|
|
74
|
+
**kwargs,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Switch the pin to a digital output with the given starting value and
|
|
77
|
+
drive mode (push-pull by default).
|
|
78
|
+
|
|
79
|
+
:param bool value: Initial output level. Defaults to ``False`` (low).
|
|
80
|
+
:param ~digitalio.DriveMode drive_mode: Output drive mode.
|
|
81
|
+
"""
|
|
82
|
+
self.direction = digitalio.Direction.OUTPUT
|
|
83
|
+
self.drive_mode = drive_mode
|
|
84
|
+
self.value = value
|
|
85
|
+
|
|
86
|
+
def switch_to_input(self, pull: "Pull" = None, **kwargs) -> None:
|
|
87
|
+
"""Switch the pin to a digital input.
|
|
88
|
+
|
|
89
|
+
:param ~digitalio.Pull pull: Must be ``None``; pull resistors are not
|
|
90
|
+
supported and any other value raises :exc:`ValueError`.
|
|
91
|
+
"""
|
|
92
|
+
self.direction = digitalio.Direction.INPUT
|
|
93
|
+
self.pull = pull
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def value(self) -> bool:
|
|
97
|
+
"""The value of the pin, either ``True`` for high or ``False`` for low.
|
|
98
|
+
|
|
99
|
+
Configure the pin as an output or input before writing or reading this.
|
|
100
|
+
"""
|
|
101
|
+
if _get_bit(self._adc.gpio_cfg, self._pin):
|
|
102
|
+
return _get_bit(self._adc.gpo_value, self._pin)
|
|
103
|
+
return _get_bit(self._adc.gpi_value, self._pin)
|
|
104
|
+
|
|
105
|
+
@value.setter
|
|
106
|
+
def value(self, val: bool) -> None:
|
|
107
|
+
if val:
|
|
108
|
+
self._adc.gpo_value = _enable_bit(self._adc.gpo_value, self._pin)
|
|
109
|
+
else:
|
|
110
|
+
self._adc.gpo_value = _clear_bit(self._adc.gpo_value, self._pin)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def direction(self) -> "Direction":
|
|
114
|
+
"""The direction of the pin, either ``digitalio.Direction.INPUT`` or
|
|
115
|
+
``digitalio.Direction.OUTPUT``.
|
|
116
|
+
"""
|
|
117
|
+
if _get_bit(self._adc.gpio_cfg, self._pin):
|
|
118
|
+
return digitalio.Direction.OUTPUT
|
|
119
|
+
return digitalio.Direction.INPUT
|
|
120
|
+
|
|
121
|
+
@direction.setter
|
|
122
|
+
def direction(self, val: "Direction") -> None:
|
|
123
|
+
self._adc.pin_cfg = _enable_bit(self._adc.pin_cfg, self._pin)
|
|
124
|
+
if val == digitalio.Direction.INPUT:
|
|
125
|
+
self._adc.gpio_cfg = _clear_bit(self._adc.gpio_cfg, self._pin)
|
|
126
|
+
elif val == digitalio.Direction.OUTPUT:
|
|
127
|
+
self._adc.gpio_cfg = _enable_bit(self._adc.gpio_cfg, self._pin)
|
|
128
|
+
else:
|
|
129
|
+
raise ValueError("Expected INPUT or OUTPUT direction!")
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def drive_mode(self) -> "DriveMode":
|
|
133
|
+
"""The output drive mode, either ``digitalio.DriveMode.PUSH_PULL`` or
|
|
134
|
+
``digitalio.DriveMode.OPEN_DRAIN``.
|
|
135
|
+
"""
|
|
136
|
+
if _get_bit(self._adc.gpo_drive_cfg, self._pin):
|
|
137
|
+
return digitalio.DriveMode.PUSH_PULL
|
|
138
|
+
return digitalio.DriveMode.OPEN_DRAIN
|
|
139
|
+
|
|
140
|
+
@drive_mode.setter
|
|
141
|
+
def drive_mode(self, val: "DriveMode") -> None:
|
|
142
|
+
if val == digitalio.DriveMode.PUSH_PULL:
|
|
143
|
+
self._adc.gpo_drive_cfg = _enable_bit(self._adc.gpo_drive_cfg, self._pin)
|
|
144
|
+
elif val == digitalio.DriveMode.OPEN_DRAIN:
|
|
145
|
+
self._adc.gpo_drive_cfg = _clear_bit(self._adc.gpo_drive_cfg, self._pin)
|
|
146
|
+
else:
|
|
147
|
+
raise ValueError("Expected PUSH_PULL or OPEN_DRAIN drive mode!")
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def zcd_output(self) -> int:
|
|
151
|
+
"""Zero-crossing-detector output mode mapped onto this GPO pin.
|
|
152
|
+
|
|
153
|
+
0=low, 1=high, 2=ZCD signal, 3=inverted ZCD. Use with
|
|
154
|
+
:attr:`zcd_output_enabled`.
|
|
155
|
+
"""
|
|
156
|
+
if self._pin < 4:
|
|
157
|
+
reg = _ZCD_CFG_CH0_3
|
|
158
|
+
shift = self._pin * 2
|
|
159
|
+
else:
|
|
160
|
+
reg = _ZCD_CFG_CH4_7
|
|
161
|
+
shift = (self._pin - 4) * 2
|
|
162
|
+
return (self._adc._read_register(reg) >> shift) & 0x03
|
|
163
|
+
|
|
164
|
+
@zcd_output.setter
|
|
165
|
+
def zcd_output(self, mode: int) -> None:
|
|
166
|
+
if not 0 <= mode <= 3:
|
|
167
|
+
raise ValueError("zcd_output must be 0-3")
|
|
168
|
+
if self._pin < 4:
|
|
169
|
+
reg = _ZCD_CFG_CH0_3
|
|
170
|
+
shift = self._pin * 2
|
|
171
|
+
else:
|
|
172
|
+
reg = _ZCD_CFG_CH4_7
|
|
173
|
+
shift = (self._pin - 4) * 2
|
|
174
|
+
val = self._adc._read_register(reg)
|
|
175
|
+
val = (val & ~(0x03 << shift) & 0xFF) | (mode << shift)
|
|
176
|
+
self._adc._write_register(reg, val)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def zcd_output_enabled(self) -> bool:
|
|
180
|
+
"""Whether the zero-crossing detector drives this GPO pin."""
|
|
181
|
+
return _get_bit(self._adc.gpo_zcd_update_en, self._pin)
|
|
182
|
+
|
|
183
|
+
@zcd_output_enabled.setter
|
|
184
|
+
def zcd_output_enabled(self, value: bool) -> None:
|
|
185
|
+
if value:
|
|
186
|
+
self._adc.gpo_zcd_update_en = _enable_bit(self._adc.gpo_zcd_update_en, self._pin)
|
|
187
|
+
else:
|
|
188
|
+
self._adc.gpo_zcd_update_en = _clear_bit(self._adc.gpo_zcd_update_en, self._pin)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adafruit-circuitpython-ads7128
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: CircuitPython driver for the Adafruit ADS7128 8-Channel ADC and GPIO Expander
|
|
5
|
+
Author-email: Adafruit Industries <circuitpython@adafruit.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adafruit/Adafruit_CircuitPython_ADS7128
|
|
8
|
+
Keywords: adafruit,blinka,circuitpython,micropython,ads7128,ADC,,GPIO,expander
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
11
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
12
|
+
Classifier: Topic :: System :: Hardware
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Description-Content-Type: text/x-rst
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: Adafruit-Blinka
|
|
17
|
+
Requires-Dist: adafruit-circuitpython-busdevice
|
|
18
|
+
Requires-Dist: adafruit-circuitpython-register
|
|
19
|
+
Provides-Extra: optional
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
Introduction
|
|
23
|
+
============
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-ads7128/badge/?version=latest
|
|
27
|
+
:target: https://docs.circuitpython.org/projects/ads7128/en/latest/
|
|
28
|
+
:alt: Documentation Status
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
|
|
32
|
+
:target: https://adafru.it/discord
|
|
33
|
+
:alt: Discord
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
.. image:: https://github.com/adafruit/Adafruit_CircuitPython_ADS7128/workflows/Build%20CI/badge.svg
|
|
37
|
+
:target: https://github.com/adafruit/Adafruit_CircuitPython_ADS7128/actions
|
|
38
|
+
:alt: Build Status
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
42
|
+
:target: https://github.com/astral-sh/ruff
|
|
43
|
+
:alt: Code Style: Ruff
|
|
44
|
+
|
|
45
|
+
CircuitPython driver for the Adafruit ADS7128 8-Channel ADC and GPIO Expander
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Dependencies
|
|
49
|
+
=============
|
|
50
|
+
This driver depends on:
|
|
51
|
+
|
|
52
|
+
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
|
|
53
|
+
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
|
|
54
|
+
* `Register <https://github.com/adafruit/Adafruit_CircuitPython_Register>`_
|
|
55
|
+
|
|
56
|
+
Please ensure all dependencies are available on the CircuitPython filesystem.
|
|
57
|
+
This is easily achieved by downloading
|
|
58
|
+
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
|
|
59
|
+
or individual libraries can be installed using
|
|
60
|
+
`circup <https://github.com/adafruit/circup>`_.
|
|
61
|
+
|
|
62
|
+
`Purchase one from the Adafruit shop <http://www.adafruit.com/products/6494>`_
|
|
63
|
+
|
|
64
|
+
Installing from PyPI
|
|
65
|
+
=====================
|
|
66
|
+
|
|
67
|
+
On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
|
|
68
|
+
PyPI <https://pypi.org/project/adafruit-circuitpython-ads7128/>`_.
|
|
69
|
+
To install for current user:
|
|
70
|
+
|
|
71
|
+
.. code-block:: shell
|
|
72
|
+
|
|
73
|
+
pip3 install adafruit-circuitpython-ads7128
|
|
74
|
+
|
|
75
|
+
To install system-wide (this may be required in some cases):
|
|
76
|
+
|
|
77
|
+
.. code-block:: shell
|
|
78
|
+
|
|
79
|
+
sudo pip3 install adafruit-circuitpython-ads7128
|
|
80
|
+
|
|
81
|
+
To install in a virtual environment in your current project:
|
|
82
|
+
|
|
83
|
+
.. code-block:: shell
|
|
84
|
+
|
|
85
|
+
mkdir project-name && cd project-name
|
|
86
|
+
python3 -m venv .venv
|
|
87
|
+
source .env/bin/activate
|
|
88
|
+
pip3 install adafruit-circuitpython-ads7128
|
|
89
|
+
|
|
90
|
+
Installing to a Connected CircuitPython Device with Circup
|
|
91
|
+
==========================================================
|
|
92
|
+
|
|
93
|
+
Make sure that you have ``circup`` installed in your Python environment.
|
|
94
|
+
Install it with the following command if necessary:
|
|
95
|
+
|
|
96
|
+
.. code-block:: shell
|
|
97
|
+
|
|
98
|
+
pip3 install circup
|
|
99
|
+
|
|
100
|
+
With ``circup`` installed and your CircuitPython device connected use the
|
|
101
|
+
following command to install:
|
|
102
|
+
|
|
103
|
+
.. code-block:: shell
|
|
104
|
+
|
|
105
|
+
circup install adafruit_ads7128
|
|
106
|
+
|
|
107
|
+
Or the following command to update an existing version:
|
|
108
|
+
|
|
109
|
+
.. code-block:: shell
|
|
110
|
+
|
|
111
|
+
circup update
|
|
112
|
+
|
|
113
|
+
Usage Example
|
|
114
|
+
=============
|
|
115
|
+
|
|
116
|
+
.. code-block:: python
|
|
117
|
+
|
|
118
|
+
import time
|
|
119
|
+
|
|
120
|
+
import board
|
|
121
|
+
|
|
122
|
+
from adafruit_ads7128.ads7128 import ADS7128
|
|
123
|
+
from adafruit_ads7128.analog_in import AnalogIn
|
|
124
|
+
|
|
125
|
+
REFERENCE_VOLTAGE = 3.3
|
|
126
|
+
|
|
127
|
+
i2c = board.I2C()
|
|
128
|
+
adc = ADS7128(i2c)
|
|
129
|
+
channel = AnalogIn(adc, 0)
|
|
130
|
+
|
|
131
|
+
while True:
|
|
132
|
+
print(f"value: {channel.value} voltage: {channel.voltage(REFERENCE_VOLTAGE):.3f} V")
|
|
133
|
+
time.sleep(1.0)
|
|
134
|
+
|
|
135
|
+
Documentation
|
|
136
|
+
=============
|
|
137
|
+
API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/ads7128/en/latest/>`_.
|
|
138
|
+
|
|
139
|
+
For information on building library documentation, please check out
|
|
140
|
+
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
141
|
+
|
|
142
|
+
Contributing
|
|
143
|
+
============
|
|
144
|
+
|
|
145
|
+
Contributions are welcome! Please read our `Code of Conduct
|
|
146
|
+
<https://github.com/adafruit/Adafruit_CircuitPython_ADS7128/blob/HEAD/CODE_OF_CONDUCT.md>`_
|
|
147
|
+
before contributing to help this project stay welcoming.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
adafruit_ads7128/ads7128.py,sha256=Gqra7hlDzWPXs1MGMyerz8VzygFLVvB7tCB6h-DmG-0,19505
|
|
2
|
+
adafruit_ads7128/analog_in.py,sha256=bhJRRHnISrIAOjEparXZjFOCvxac7r2p2I5AaWWBNU0,6094
|
|
3
|
+
adafruit_ads7128/digital_inout.py,sha256=qndR0Sygo3rDyN9N2AOXq13kfRdiOjPsSgaBS1mCI-U,6301
|
|
4
|
+
adafruit_circuitpython_ads7128-1.0.0.dist-info/licenses/LICENSE,sha256=0JsO0yQOspYeKuhk2Y_s1wan9NO0DYeWSRJBrYJdeko,1100
|
|
5
|
+
adafruit_circuitpython_ads7128-1.0.0.dist-info/METADATA,sha256=Kh0jl5eh5LDYsvLqQRrR_S8IG4Akc5TtlmnTRczbpVw,4695
|
|
6
|
+
adafruit_circuitpython_ads7128-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
adafruit_circuitpython_ads7128-1.0.0.dist-info/top_level.txt,sha256=lYnOESNlST-F5z_4I2LlQ-n58vkuWz4UhKIkFpbyohI,17
|
|
8
|
+
adafruit_circuitpython_ads7128-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Liz Clark for Adafruit Industries
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adafruit_ads7128
|