Skip to content
Browse files

starting work on MCP23017 GPIO extender API

  • Loading branch information...
1 parent aa105fb commit e3bf87155a87b89f22c418addf59a267b291cbe3 @npryce npryce committed Sep 19, 2012
View
4 Makefile
@@ -49,6 +49,10 @@ check-loopback:
PYTHONPATH=src:$(PYTHON_LIBDIR) $(PYTHON_ENV)/bin/py.test test -m loopback
.PHONY: check-loopback
+check-unit:
+ PYTHONPATH=src:$(PYTHON_LIBDIR) $(PYTHON_ENV)/bin/py.test test -m "not hardware and not loopback"
+.PHONY: check-unit
+
check-install:
$(MAKE) PYTHON_ENV=build/test-$(python)-$(ARCHITECTURE) env-again
build/test-$(python)-$(ARCHITECTURE)/bin/python$(python) setup.py install
View
20 src/quick2wire/parts/mcp23017.py
@@ -1,20 +1,26 @@
from quick2wire.i2c import writing_bytes, reading
-from quick2wire.parts.mcp23x17 import *
+import quick2wire.parts.mcp23x17 as mcp23x17
-class Chip(MCP23x17):
- """Raw access to the MCP23017 chip"""
+class Registers(mcp23x17.Registers):
+ """Low level access to the MCP23017 registers"""
- def __init__(self, master, address=0x20):
+ def __init__(self, master, address):
self.master = master
self.address = address
def write_register(self, reg, byte):
self.master.transaction(
- writing_bytes(reg, byte))
+ writing_bytes(address, reg, byte))
def read_register(self, reg):
return self.master.transaction(
- reading(reg))[0][0]
-
+ writing(address, reg),
+ reading(address, 1))[0][0]
+
+
+class MCP23017(mcp23x17.PinBanks):
+ def __init__(self, master, address=0x20):
+ super().__init__(self, Registers(master, address))
+
View
75 src/quick2wire/parts/mcp23x17.py
@@ -5,6 +5,9 @@
# The MCP23x17 has two register addressing modes, depending on the value of bit7 of IOCON
# we assume bank=0 addressing (which is the POR default value)
+from abc import ABCMeta, abstractmethod
+import contextlib
+
IODIR=0
IPOL=1
GPINTEN=2
@@ -25,8 +28,8 @@ def _banked_register(bank, reg):
IODIRA = _banked_register(_BankA, IODIR)
IODIRB = _banked_register(_BankB, IODIR)
-IPOLA = _banked_register(_BankA, IOPOL)
-IPOLB = _banked_register(_BankB, IOPOL)
+IPOLA = _banked_register(_BankA, IPOL)
+IPOLB = _banked_register(_BankB, IPOL)
GPINTENA=_banked_register(_BankA, GPINTEN)
GPINTENB = _banked_register(_BankB, GPINTEN)
DEFVALA = _banked_register(_BankA, DEFVAL)
@@ -48,11 +51,14 @@ def _banked_register(bank, reg):
_initial_register_values = (
- ((IOCON), 0x00),
- ((IODIR), 0xFF),
+ ((IOCON,), 0x00),
+ ((IODIR,), 0xFF),
((IPOL, GPINTEN, DEFVAL, INTCON, GPPU, INTF, INTCAP, GPIO, OLAT), 0x00))
-class MCP23x17:
+
+class Registers(metaclass=ABCMeta):
+ """Abstract interface to MCP23x17 registers"""
+
def reset(self):
"""Reset to power-on state"""
for regs, value in _initial_register_values:
@@ -62,5 +68,62 @@ def reset(self):
# Avoid unnecessary communication
self.write_banked_register(_BankB, reg, value)
- def write_banked_register(bank, reg, value):
+ def write_banked_register(self, bank, reg, value):
self.write_register(_banked_register(bank, reg), value)
+
+ @abstractmethod
+ def write_register(self, reg, value):
+ pass
+
+ @abstractmethod
+ def read_register(self, reg):
+ pass
+
+
+
+class PinBanks:
+ def __init__(self, registers):
+ self.registers = registers
+ self.bank_a = PinBank(self, 0)
+ self.bank_b = PinBank(self, 1)
+ self.banks = (self.bank_a, self.bank_b)
+
+ def reset(self):
+ self.registers.reset()
+ for bank in self.banks:
+ bank.reset()
+
+
+class PinBank:
+ def __init__(self, chip, bank_id):
+ self.chip = chip
+ self._bank_id = bank_id
+ self._pins = tuple([Pin(self, i) for i in range(8)])
+
+ def __len__(self):
+ return len(self._pins)
+
+ @contextlib.contextmanager
+ def __getitem__(self, n):
+ pin = self._pins[n]
+ try:
+ pin._open()
+ yield pin
+ finally:
+ pin.close()
+
+
+class Pin:
+ def __init__(self, bank, index):
+ self.bank = bank
+ self.index = index
+ self._is_claimed = False
+
+ def _open(self):
+ if self._is_claimed:
+ raise ValueError("pin already in use")
+ self._is_claimed = True
+
+ def close(self):
+ self._is_claimed = False
+
View
0 test/quick2wire/parts/__init__.py
No changes.
View
48 test/quick2wire/parts/test_mcp23x17.py
@@ -0,0 +1,48 @@
+
+
+from quick2wire.parts.mcp23x17 import Registers, PinBanks
+
+class FakeRegisters(Registers):
+ def __init__(self):
+ self.registers = {}
+ self.reset()
+
+ def write_register(self, reg, value):
+ self.registers[reg] = value
+
+ def read_register(self, reg):
+ return self.registers[reg]
+
+
+class TestPinBanks:
+ def setup_method(self, method):
+ self.chip = PinBanks(FakeRegisters())
+
+ def test_has_two_banks(self):
+ assert len(self.chip.banks) == 2
+ assert self.chip.banks[0] is not None
+ assert self.chip.banks[1] is not None
+
+ def test_both_banks_have_eight_pins(self):
+ assert len(self.chip.banks[0]) == 8
+ assert len(self.chip.banks[1]) == 8
+
+ def test_can_use_a_context_manager_to_claim_ownership_of_a_pin_in_a_bank_and_release_it(self):
+ with self.chip.banks[0][1] as pin:
+ assert pin.bank == self.chip.banks[0]
+ assert pin.index == 1
+
+ def test_a_pin_can_only_be_claimed_once_at_any_time(self):
+ with self.chip.banks[0][1] as pin:
+ try :
+ with self.chip.banks[0][1] as pin2:
+ raise AssertionError("claim_pin() should have failed")
+ except ValueError as e:
+ pass
+
+ def test_a_pin_can_be_claimed_after_being_released(self):
+ with self.chip.banks[0][1] as pin:
+ pass
+
+ with self.chip.banks[0][1] as pin_again:
+ pass
View
4 test/quick2wire/test_gpio.py
@@ -3,6 +3,8 @@
import pytest
+@pytest.mark.gpio
+@pytest.mark.hardware
class TestPin:
def setup_method(self, method):
self.pin = Pin(25)
@@ -52,6 +54,8 @@ def test_can_export_pin_and_set_direction_on_construction(self):
+@pytest.mark.gpio
+@pytest.mark.hardware
class TestExportedContextManager:
def test_can_automatically_unexport_pin_with_context_manager(self):
with exported(Pin(25)) as p:
View
2 test/quick2wire/test_gpio_loopback.py
@@ -14,7 +14,9 @@ def teardown_module():
if pin.is_exported:
pin.unexport()
+@pytest.mark.hardware
@pytest.mark.loopback
+@pytest.mark.gpio
def test_gpio_loopback():
assert_outputs_seen_at_corresponding_inputs(Pins[:4], Pins[4:])
assert_outputs_seen_at_corresponding_inputs(Pins[4:], Pins[:4])
View
6 test/quick2wire/test_i2c_loopback.py
@@ -63,7 +63,9 @@ def check_high_bits(master, bitpattern):
assert bitpattern << 4 == (read_register(master, GPIO) & 0xF0)
+@pytest.mark.hardware
@pytest.mark.loopback
+@pytest.mark.i2c
def test_mcp23008_loopback_via_i2c_master_api():
bitpatterns = [(1 << i) for i in range(0,4)]
@@ -80,7 +82,9 @@ def test_mcp23008_loopback_via_i2c_master_api():
check_low_bits(master, bitpattern)
+@pytest.mark.hardware
@pytest.mark.loopback
+@pytest.mark.i2c
def test_mcp23008_multibyte_reads():
with i2c.I2CMaster() as master:
reset(master)
@@ -100,7 +104,9 @@ def test_mcp23008_multibyte_reads():
assert iopol_state == 0xAA
+@pytest.mark.hardware
@pytest.mark.loopback
+@pytest.mark.i2c
def test_mcp23008_multibyte_writes():
with i2c.I2CMaster() as master:
reset(master)
View
2 test/quick2wire/test_spi_loopback.py
@@ -32,7 +32,9 @@
address = MCP23S17_BASE_ADDRESS
+@pytest.mark.hardware
@pytest.mark.loopback
+@pytest.mark.spi
def test_loopback_bits():
with SPIDevice(0, 0) as mcp23s17:
prepare_to_send_from_a_to_b(mcp23s17)

0 comments on commit e3bf871

Please sign in to comment.
Something went wrong with that request. Please try again.