Files
python-gpiozero/tests/test_spi_devices.py
Dave Jones d2a7c1434a Fix SPI tests when spidev is installed
Doh! SPI tests fail when the spidev package is installed (which it
normally isn't in my virtualenv) because hardware SPI always takes
precedence and the mock SPI stuff only replaces software SPI.
2017-06-21 14:40:52 +01:00

348 lines
12 KiB
Python

from __future__ import (
unicode_literals,
absolute_import,
print_function,
division,
)
str = type('')
import sys
import pytest
from mock import patch
from collections import namedtuple
try:
from math import isclose
except ImportError:
from gpiozero.compat import isclose
from gpiozero.pins.mock import MockSPIDevice, MockPin
from gpiozero import *
def teardown_function(function):
Device._pin_factory.reset()
def clamp(v, min_value, max_value):
return min(max_value, max(min_value, v))
def scale(v, ref, bits):
v /= ref
vmin = -(2 ** bits)
vmax = -vmin - 1
vrange = vmax - vmin
return int(((v + 1) / 2.0) * vrange + vmin)
class MockMCP3xxx(MockSPIDevice):
def __init__(
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
channels=8, bits=10):
super(MockMCP3xxx, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin)
self.vref = 3.3
self.channels = [0.0] * channels
self.channel_bits = 3
self.bits = bits
self.state = 'idle'
def on_start(self):
super(MockMCP3xxx, self).on_start()
self.state = 'idle'
def on_bit(self):
if self.state == 'idle':
if self.rx_buf[-1]:
self.state = 'mode'
self.rx_buf = []
elif self.state == 'mode':
if self.rx_buf[-1]:
self.state = 'single'
else:
self.state = 'diff'
self.rx_buf = []
elif self.state in ('single', 'diff'):
if len(self.rx_buf) == self.channel_bits:
self.on_result(self.state == 'diff', self.rx_word())
self.state = 'result'
elif self.state == 'result':
if not self.tx_buf:
self.state = 'idle'
self.rx_buf = []
else:
assert False
def on_result(self, differential, channel):
if differential:
pos_channel = channel
neg_channel = pos_channel ^ 1
result = self.channels[pos_channel] - self.channels[neg_channel]
result = clamp(result, 0, self.vref)
else:
result = clamp(self.channels[channel], 0, self.vref)
result = scale(result, self.vref, self.bits)
self.tx_word(result, self.bits + 2)
class MockMCP3xx1(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None, bits=10):
super(MockMCP3xx1, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
def on_start(self):
super(MockMCP3xx1, self).on_start()
result = self.channels[0] - self.channels[1]
result = clamp(result, 0, self.vref)
result = scale(result, self.vref, self.bits)
self.tx_word(result, self.bits + 3)
def on_bit(self):
pass
class MockMCP3xx2(MockMCP3xxx):
def __init__(
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
bits=10):
super(MockMCP3xx2, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
self.channel_bits = 1
class MockMCP33xx(MockMCP3xxx):
def __init__(
self, clock_pin, mosi_pin, miso_pin, select_pin=None,
channels=8):
super(MockMCP33xx, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels, 12)
def on_result(self, differential, channel):
if differential:
pos_channel = channel
neg_channel = pos_channel ^ 1
result = self.channels[pos_channel] - self.channels[neg_channel]
result = clamp(result, -self.vref, self.vref)
else:
result = clamp(self.channels[channel], 0, self.vref)
result = scale(result, self.vref, self.bits)
if result < 0:
result += 8192
self.tx_word(result, self.bits + 3)
class MockMCP3001(MockMCP3xx1):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3001, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
class MockMCP3002(MockMCP3xx2):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3002, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
class MockMCP3004(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3004, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=10)
class MockMCP3008(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3008, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=10)
class MockMCP3201(MockMCP3xx1):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3201, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
class MockMCP3202(MockMCP3xx2):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3202, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
class MockMCP3204(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3204, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=12)
class MockMCP3208(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3208, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=12)
class MockMCP3301(MockMCP3xxx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3301, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=12)
def on_start(self):
super(MockMCP3301, self).on_start()
result = self.channels[0] - self.channels[1]
result = clamp(result, -self.vref, self.vref)
result = scale(result, self.vref, self.bits)
if result < 0:
result += 8192
self.tx_word(result, self.bits + 4)
class MockMCP3302(MockMCP33xx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3302, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=4)
class MockMCP3304(MockMCP33xx):
def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
super(MockMCP3304, self).__init__(
clock_pin, mosi_pin, miso_pin, select_pin, channels=8)
def single_mcp_test(mock, pot, channel, bits):
scale = 2**bits
tolerance = 1 / scale
mock.channels[channel] = 0.0
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=tolerance)
mock.channels[channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
mock.channels[channel] = mock.vref
assert pot.raw_value == scale - 1
assert isclose(pot.value, 1.0, abs_tol=tolerance)
def differential_mcp_test(mock, pot, pos_channel, neg_channel, bits, full=False):
scale = 2**bits
tolerance = 1 / 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)
mock.channels[pos_channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
mock.channels[pos_channel] = mock.vref
assert pot.raw_value == scale - 1
assert isclose(pot.value, 1.0, abs_tol=tolerance)
mock.channels[neg_channel] = mock.vref / 2
assert pot.raw_value == (scale / 2) - 1
assert isclose(pot.value, 0.5, abs_tol=tolerance)
mock.channels[pos_channel] = mock.vref / 2
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=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)
else:
assert pot.raw_value == 0
assert isclose(pot.value, 0.0, abs_tol=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)
def test_MCP3002():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3002(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3002(channel=5)
with MCP3002(channel=1) 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)
def test_MCP3004():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3004(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3004(channel=5)
with MCP3004(channel=3) 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)
def test_MCP3008():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3008(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3008(channel=9)
with MCP3008(channel=0) as pot:
single_mcp_test(mock, pot, 0, 10)
with MCP3008(channel=0, differential=True) as pot:
differential_mcp_test(mock, pot, 0, 1, 10)
def test_MCP3201():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3201(11, 10, 9, 8)
with MCP3201() as pot:
differential_mcp_test(mock, pot, 0, 1, 12)
def test_MCP3202():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3202(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3202(channel=5)
with MCP3202(channel=1) 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)
def test_MCP3204():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3204(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3204(channel=5)
with MCP3204(channel=1) 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)
def test_MCP3208():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3208(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3208(channel=9)
with MCP3208(channel=7) 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)
def test_MCP3301():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3301(11, 10, 9, 8)
with MCP3301() as pot:
differential_mcp_test(mock, pot, 0, 1, 12, full=True)
def test_MCP3302():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3302(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3302(channel=4)
with MCP3302(channel=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)
def test_MCP3304():
with patch('gpiozero.pins.local.SpiDev', None):
mock = MockMCP3304(11, 10, 9, 8)
with pytest.raises(ValueError):
MCP3304(channel=9)
with MCP3304(channel=5) 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)