Skip to content

Commit

Permalink
Merge pull request #331 from RPi-Distro/fix-329
Browse files Browse the repository at this point in the history
Add ADC.voltage property, fix #329
  • Loading branch information
waveform80 committed Feb 19, 2018
2 parents 7d5892e + a386e6b commit 339fb1b
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 31 deletions.
90 changes: 59 additions & 31 deletions gpiozero/spi_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,21 @@ class AnalogInputDevice(SPIDevice):
led = PWMLED(17)
led.source = pot.values
The :attr:`voltage` attribute reports values between 0.0 and *max_voltage*
(which defaults to 3.3, the logic level of the GPIO pins).
.. _analog to digital converters: https://en.wikipedia.org/wiki/Analog-to-digital_converter
"""

def __init__(self, bits, **spi_args):
def __init__(self, bits, max_voltage=3.3, **spi_args):
if bits is None:
raise InputDeviceError('you must specify the bit resolution of the device')
self._bits = bits
self._min_value = -(2 ** bits)
self._range = 2 ** (bits + 1) - 1
if max_voltage <= 0:
raise InputDeviceError('max_voltage must be positive')
self._max_voltage = float(max_voltage)
super(AnalogInputDevice, self).__init__(shared=True, **spi_args)

@property
Expand Down Expand Up @@ -144,17 +152,33 @@ def raw_value(self):
"""
return self._read()

@property
def max_voltage(self):
"""
The voltage required to set the device's value to 1.
"""
return self._max_voltage

@property
def voltage(self):
"""
The current voltage read from the device. This will be a value between
0 and the *max_voltage* parameter specified in the constructor.
"""
return self.value * self._max_voltage


class MCP3xxx(AnalogInputDevice):
"""
Extends :class:`AnalogInputDevice` to implement an interface for all ADC
chips with a protocol similar to the Microchip MCP3xxx series of devices.
"""

def __init__(self, channel=0, bits=10, differential=False, **spi_args):
def __init__(self, channel=0, bits=10, differential=False, max_voltage=3.3,
**spi_args):
self._channel = channel
self._differential = bool(differential)
super(MCP3xxx, self).__init__(bits, **spi_args)
super(MCP3xxx, self).__init__(bits, max_voltage, **spi_args)

@property
def channel(self):
Expand Down Expand Up @@ -256,8 +280,10 @@ class MCP30xx(MCP3xxx):
chips with a protocol similar to the Microchip MCP30xx series of devices.
"""

def __init__(self, channel=0, differential=False, **spi_args):
super(MCP30xx, self).__init__(channel, 10, differential, **spi_args)
def __init__(self, channel=0, differential=False, max_voltage=3.3,
**spi_args):
super(MCP30xx, self).__init__(channel, 10, differential, max_voltage,
**spi_args)


class MCP32xx(MCP3xxx):
Expand All @@ -266,8 +292,9 @@ class MCP32xx(MCP3xxx):
chips with a protocol similar to the Microchip MCP32xx series of devices.
"""

def __init__(self, channel=0, differential=False, **spi_args):
super(MCP32xx, self).__init__(channel, 12, differential, **spi_args)
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
super(MCP32xx, self).__init__(channel, 12, differential, max_voltage,
**spi_args)


class MCP33xx(MCP3xxx):
Expand All @@ -277,8 +304,9 @@ class MCP33xx(MCP3xxx):
these chips supporting the full 13-bit signed range of output values.
"""

def __init__(self, channel=0, differential=False, **spi_args):
super(MCP33xx, self).__init__(channel, 12, differential, **spi_args)
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
super(MCP33xx, self).__init__(channel, 12, differential, max_voltage,
**spi_args)

def _read(self):
if self.differential:
Expand Down Expand Up @@ -351,8 +379,8 @@ class MCP3001(MCP30xx):
.. _MCP3001: http://www.farnell.com/datasheets/630400.pdf
"""
def __init__(self, **spi_args):
super(MCP3001, self).__init__(0, differential=True, **spi_args)
def __init__(self, max_voltage=3.3, **spi_args):
super(MCP3001, self).__init__(0, True, max_voltage, **spi_args)

def _read(self):
# MCP3001 protocol looks like the following:
Expand All @@ -370,10 +398,10 @@ class MCP3002(MCP30xx, MCP3xx2):
.. _MCP3002: http://www.farnell.com/datasheets/1599363.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 2:
raise SPIBadChannel('channel must be 0 or 1')
super(MCP3002, self).__init__(channel, differential, **spi_args)
super(MCP3002, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3004(MCP30xx):
Expand All @@ -383,10 +411,10 @@ class MCP3004(MCP30xx):
.. _MCP3004: http://www.farnell.com/datasheets/808965.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 4:
raise SPIBadChannel('channel must be between 0 and 3')
super(MCP3004, self).__init__(channel, differential, **spi_args)
super(MCP3004, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3008(MCP30xx):
Expand All @@ -396,10 +424,10 @@ class MCP3008(MCP30xx):
.. _MCP3008: http://www.farnell.com/datasheets/808965.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 8:
raise SPIBadChannel('channel must be between 0 and 7')
super(MCP3008, self).__init__(channel, differential, **spi_args)
super(MCP3008, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3201(MCP32xx):
Expand All @@ -410,8 +438,8 @@ class MCP3201(MCP32xx):
.. _MCP3201: http://www.farnell.com/datasheets/1669366.pdf
"""
def __init__(self, **spi_args):
super(MCP3201, self).__init__(0, differential=True, **spi_args)
def __init__(self, max_voltage=3.3, **spi_args):
super(MCP3201, self).__init__(0, True, max_voltage, **spi_args)

def _read(self):
# MCP3201 protocol looks like the following:
Expand All @@ -429,10 +457,10 @@ class MCP3202(MCP32xx, MCP3xx2):
.. _MCP3202: http://www.farnell.com/datasheets/1669376.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 2:
raise SPIBadChannel('channel must be 0 or 1')
super(MCP3202, self).__init__(channel, differential, **spi_args)
super(MCP3202, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3204(MCP32xx):
Expand All @@ -442,10 +470,10 @@ class MCP3204(MCP32xx):
.. _MCP3204: http://www.farnell.com/datasheets/808967.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 4:
raise SPIBadChannel('channel must be between 0 and 3')
super(MCP3204, self).__init__(channel, differential, **spi_args)
super(MCP3204, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3208(MCP32xx):
Expand All @@ -455,10 +483,10 @@ class MCP3208(MCP32xx):
.. _MCP3208: http://www.farnell.com/datasheets/808967.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 8:
raise SPIBadChannel('channel must be between 0 and 7')
super(MCP3208, self).__init__(channel, differential, **spi_args)
super(MCP3208, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3301(MCP33xx):
Expand All @@ -469,8 +497,8 @@ class MCP3301(MCP33xx):
.. _MCP3301: http://www.farnell.com/datasheets/1669397.pdf
"""
def __init__(self, **spi_args):
super(MCP3301, self).__init__(0, differential=True, **spi_args)
def __init__(self, max_voltage=3.3, **spi_args):
super(MCP3301, self).__init__(0, True, max_voltage, **spi_args)

def _read(self):
# MCP3301 protocol looks like the following:
Expand All @@ -496,10 +524,10 @@ class MCP3302(MCP33xx):
.. _MCP3302: http://www.farnell.com/datasheets/1486116.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 4:
raise SPIBadChannel('channel must be between 0 and 4')
super(MCP3302, self).__init__(channel, differential, **spi_args)
super(MCP3302, self).__init__(channel, differential, max_voltage, **spi_args)


class MCP3304(MCP33xx):
Expand All @@ -512,8 +540,8 @@ class MCP3304(MCP33xx):
.. _MCP3304: http://www.farnell.com/datasheets/1486116.pdf
"""
def __init__(self, channel=0, differential=False, **spi_args):
def __init__(self, channel=0, differential=False, max_voltage=3.3, **spi_args):
if not 0 <= channel < 8:
raise SPIBadChannel('channel must be between 0 and 7')
super(MCP3304, self).__init__(channel, differential, **spi_args)
super(MCP3304, self).__init__(channel, differential, max_voltage, **spi_args)

34 changes: 34 additions & 0 deletions tests/test_spi_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,50 +208,64 @@ def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
def single_mcp_test(mock, pot, channel, bits):
scale = 2**bits
tolerance = 1 / scale
voltage_tolerance = pot.max_voltage / scale
mock.channels[channel] = 0.0
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=tolerance)
assert isclose(pot.voltage, 0.0, abs_tol=voltage_tolerance)
mock.channels[channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
assert isclose(pot.voltage, pot.max_voltage / 2, abs_tol=voltage_tolerance)
mock.channels[channel] = mock.vref
assert pot.raw_value == scale - 1
assert isclose(pot.value, 1.0, abs_tol=tolerance)
assert isclose(pot.voltage, pot.max_voltage, abs_tol=voltage_tolerance)

def differential_mcp_test(mock, pot, pos_channel, neg_channel, bits, full=False):
scale = 2**bits
tolerance = 1 / scale
voltage_tolerance = pot.max_voltage / scale
mock.channels[pos_channel] = 0.0
mock.channels[neg_channel] = 0.0
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=tolerance)
assert isclose(pot.voltage, 0.0, abs_tol=voltage_tolerance)
mock.channels[pos_channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
assert isclose(pot.voltage, pot.max_voltage / 2, abs_tol=voltage_tolerance)
mock.channels[pos_channel] = mock.vref
assert pot.raw_value == scale - 1
assert isclose(pot.value, 1.0, abs_tol=tolerance)
assert isclose(pot.voltage, pot.max_voltage, abs_tol=voltage_tolerance)
mock.channels[neg_channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
assert isclose(pot.voltage, pot.max_voltage / 2, abs_tol=voltage_tolerance)
mock.channels[pos_channel] = mock.vref / 2
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=tolerance)
assert isclose(pot.voltage, 0.0, abs_tol=voltage_tolerance)
mock.channels[pos_channel] = 0.0
mock.channels[neg_channel] = mock.vref
if full:
assert pot.raw_value == -scale
assert isclose(pot.value, -1.0, abs_tol=tolerance)
assert isclose(pot.voltage, -pot.max_voltage, abs_tol=voltage_tolerance)
else:
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=tolerance)
assert isclose(pot.voltage, 0.0, abs_tol=voltage_tolerance)


def test_MCP3001():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3001(11, 10, 9, 8)
with MCP3001() as pot:
differential_mcp_test(mock, pot, 0, 1, 10)
with MCP3001(max_voltage=5.0) as pot:
differential_mcp_test(mock, pot, 0, 1, 10)

def test_MCP3002():
with patch('gpiozero.pins.local.SpiDev', None):
Expand All @@ -260,6 +274,8 @@ def test_MCP3002():
MCP3002(channel=5)
with MCP3002(channel=1) as pot:
single_mcp_test(mock, pot, 1, 10)
with MCP3002(channel=1, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 1, 10)
with MCP3002(channel=1, differential=True) as pot:
differential_mcp_test(mock, pot, 1, 0, 10)

Expand All @@ -270,6 +286,8 @@ def test_MCP3004():
MCP3004(channel=5)
with MCP3004(channel=3) as pot:
single_mcp_test(mock, pot, 3, 10)
with MCP3004(channel=3, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 3, 10)
with MCP3004(channel=3, differential=True) as pot:
differential_mcp_test(mock, pot, 3, 2, 10)

Expand All @@ -280,6 +298,8 @@ def test_MCP3008():
MCP3008(channel=9)
with MCP3008(channel=0) as pot:
single_mcp_test(mock, pot, 0, 10)
with MCP3008(channel=1, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 1, 10)
with MCP3008(channel=0, differential=True) as pot:
differential_mcp_test(mock, pot, 0, 1, 10)

Expand All @@ -288,6 +308,8 @@ def test_MCP3201():
mock = MockMCP3201(11, 10, 9, 8)
with MCP3201() as pot:
differential_mcp_test(mock, pot, 0, 1, 12)
with MCP3201(max_voltage=5.0) as pot:
differential_mcp_test(mock, pot, 0, 1, 12)

def test_MCP3202():
with patch('gpiozero.pins.local.SpiDev', None):
Expand All @@ -296,6 +318,8 @@ def test_MCP3202():
MCP3202(channel=5)
with MCP3202(channel=1) as pot:
single_mcp_test(mock, pot, 1, 12)
with MCP3202(channel=1, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 1, 12)
with MCP3202(channel=1, differential=True) as pot:
differential_mcp_test(mock, pot, 1, 0, 12)

Expand All @@ -306,6 +330,8 @@ def test_MCP3204():
MCP3204(channel=5)
with MCP3204(channel=1) as pot:
single_mcp_test(mock, pot, 1, 12)
with MCP3204(channel=1, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 1, 12)
with MCP3204(channel=1, differential=True) as pot:
differential_mcp_test(mock, pot, 1, 0, 12)

Expand All @@ -316,6 +342,8 @@ def test_MCP3208():
MCP3208(channel=9)
with MCP3208(channel=7) as pot:
single_mcp_test(mock, pot, 7, 12)
with MCP3208(channel=7, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 7, 12)
with MCP3208(channel=7, differential=True) as pot:
differential_mcp_test(mock, pot, 7, 6, 12)

Expand All @@ -324,6 +352,8 @@ def test_MCP3301():
mock = MockMCP3301(11, 10, 9, 8)
with MCP3301() as pot:
differential_mcp_test(mock, pot, 0, 1, 12, full=True)
with MCP3301(max_voltage=5.0) as pot:
differential_mcp_test(mock, pot, 0, 1, 12, full=True)

def test_MCP3302():
with patch('gpiozero.pins.local.SpiDev', None):
Expand All @@ -332,6 +362,8 @@ def test_MCP3302():
MCP3302(channel=4)
with MCP3302(channel=0) as pot:
single_mcp_test(mock, pot, 0, 12)
with MCP3302(channel=0, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 0, 12)
with MCP3302(channel=0, differential=True) as pot:
differential_mcp_test(mock, pot, 0, 1, 12, full=True)

Expand All @@ -342,6 +374,8 @@ def test_MCP3304():
MCP3304(channel=9)
with MCP3304(channel=5) as pot:
single_mcp_test(mock, pot, 5, 12)
with MCP3304(channel=5, max_voltage=5.0) as pot:
single_mcp_test(mock, pot, 5, 12)
with MCP3304(channel=5, differential=True) as pot:
differential_mcp_test(mock, pot, 5, 4, 12, full=True)

0 comments on commit 339fb1b

Please sign in to comment.