From 96ee4f1efce502d479f5d67a83ec943430c02bf2 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Mon, 18 Feb 2013 22:30:34 +0000 Subject: [PATCH 01/17] Adding write tests for first 8 segment outputs. Need to think about the PinBank model for this. [SE] --- quick2wire/parts/saa1064.py | 69 +++++++++++++++++++++ quick2wire/parts/test_saa1064.py | 103 +++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 quick2wire/parts/saa1064.py create mode 100644 quick2wire/parts/test_saa1064.py diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py new file mode 100644 index 0000000..0241d4c --- /dev/null +++ b/quick2wire/parts/saa1064.py @@ -0,0 +1,69 @@ +from functools import reduce +from operator import or_ + +__author__ = 'stuartervine' + +from quick2wire.i2c import writing_bytes + +displayController = 0x38 +STATIC_MODE = 0b00000000 +DYNAMIC_MODE = 0b01000000 + +class SAA1064(object): + def __init__(self, master): + self.master = master + self._mode = STATIC_MODE + self._brightness = 7 + self._segment_output = tuple(_OutputPin(i) for i in range(8)) + + def display(self, digits): + pass + + def configure(self): + self.master.transaction( + writing_bytes(displayController, 0b00000000, self.mode | self.brightness) + ) + + def write(self): + bits_to_write = [output.asBinary for output in self._segment_output] + byte_to_write = reduce(or_, bits_to_write) + self.master.transaction( + writing_bytes(displayController, 0b00000001, byte_to_write) + ) + + @property + def mode(self): + return self._mode + + @mode.setter + def mode(self, value): + self._mode = value + + @property + def brightness(self): + return self._brightness + + @brightness.setter + def brightness(self, value): + self._brightness = value + + def segment_output(self, index): + return self._segment_output[index] + + +class _OutputPin(object): + def __init__(self, index): + self._value = 0 + self._binary = 2 ** index + + @property + def asBinary(self): + return self._value * self._binary + + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + self._value = value \ No newline at end of file diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py new file mode 100644 index 0000000..e4f27d1 --- /dev/null +++ b/quick2wire/parts/test_saa1064.py @@ -0,0 +1,103 @@ +from quick2wire.i2c_ctypes import I2C_M_RD +from quick2wire.parts.saa1064 import SAA1064, STATIC_MODE, DYNAMIC_MODE + +__author__ = 'stuartervine' + + +class FakeI2CMaster: + def __init__(self): + self._requests = [] + self._responses = [] + self._next_response = 0 + self.message_precondition = lambda m: True + + def all_messages_must(self, p): + self.message_precondition + + def clear(self): + self.__init__() + + def transaction(self, *messages): + for m in messages: + self.message_precondition(m) + + self._requests.append(messages) + return [] + + def add_response(self, *messages): + self._responses.append(messages) + + + @property + def request_count(self): + return len(self._requests) + + + def request(self, n): + return self._requests[n] + + +i2c = FakeI2CMaster() + +def setup_function(f): + i2c.clear() + +def test_static_display_brightest_by_default(): + saa1064 = SAA1064(i2c) + saa1064.configure() + + assert i2c.request_count == 1 + + controlMessage = i2c.request(0)[0] + assert controlMessage.len == 2 + assert controlMessage.buf[0][0] == 0b00000000 + assert controlMessage.buf[1][0] == 0b00000111 + + +def test_dynamic_display_brightest_by_default(): + saa1064 = SAA1064(i2c) + saa1064.mode=DYNAMIC_MODE + saa1064.configure() + + assert i2c.request_count == 1 + + controlMessage = i2c.request(0)[0] + assert controlMessage.len == 2 + assert controlMessage.buf[0][0] == 0b00000000 + assert controlMessage.buf[1][0] == 0b01000111 + +def test_display_brightness(): + saa1064 = SAA1064(i2c) + saa1064.mode=DYNAMIC_MODE + saa1064.brightness=5 + saa1064.configure() + + assert i2c.request_count == 1 + + controlMessage = i2c.request(0)[0] + assert controlMessage.len == 2 + assert controlMessage.buf[0][0] == 0b00000000 + assert controlMessage.buf[1][0] == 0b01000101 + +def test_setting_pins_and_writing_outputs_to_i2c(): + saa1064 = SAA1064(i2c) + saa1064.mode=STATIC_MODE + saa1064.brightness=5 + saa1064.configure() + + saa1064.segment_output(0).value=1 + saa1064.segment_output(1).value=0 + saa1064.segment_output(2).value=0 + saa1064.segment_output(3).value=1 + saa1064.segment_output(4).value=1 + saa1064.segment_output(5).value=1 + saa1064.segment_output(6).value=0 + saa1064.segment_output(7).value=1 + + saa1064.write() + + assert i2c.request_count == 2 + dataMessage = i2c.request(1)[0] + assert dataMessage.len == 2 + assert dataMessage.buf[0][0] == 0b00000001 + assert dataMessage.buf[1][0] == 0b10111001 From 8851d1ece879301336ff32f04615e36c46581f95 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Mon, 18 Feb 2013 23:08:08 +0000 Subject: [PATCH 02/17] Got the control messages the wrong way round. [SE] --- quick2wire/parts/saa1064.py | 7 ++++--- quick2wire/parts/test_saa1064.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 0241d4c..a469303 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -7,13 +7,14 @@ displayController = 0x38 STATIC_MODE = 0b00000000 -DYNAMIC_MODE = 0b01000000 +DYNAMIC_MODE = 0b00000001 +CONTINUOUS_DISPLAY = 0b00000110 class SAA1064(object): def __init__(self, master): self.master = master self._mode = STATIC_MODE - self._brightness = 7 + self._brightness = 0b11100000 self._segment_output = tuple(_OutputPin(i) for i in range(8)) def display(self, digits): @@ -21,7 +22,7 @@ def display(self, digits): def configure(self): self.master.transaction( - writing_bytes(displayController, 0b00000000, self.mode | self.brightness) + writing_bytes(displayController, 0b00000000, self.mode | self.brightness | CONTINUOUS_DISPLAY) ) def write(self): diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index e4f27d1..78c59f0 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -51,7 +51,7 @@ def test_static_display_brightest_by_default(): controlMessage = i2c.request(0)[0] assert controlMessage.len == 2 assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b00000111 + assert controlMessage.buf[1][0] == 0b11100110 def test_dynamic_display_brightest_by_default(): @@ -64,12 +64,12 @@ def test_dynamic_display_brightest_by_default(): controlMessage = i2c.request(0)[0] assert controlMessage.len == 2 assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b01000111 + assert controlMessage.buf[1][0] == 0b11100111 def test_display_brightness(): saa1064 = SAA1064(i2c) saa1064.mode=DYNAMIC_MODE - saa1064.brightness=5 + saa1064.brightness=0b01100000 saa1064.configure() assert i2c.request_count == 1 @@ -77,7 +77,7 @@ def test_display_brightness(): controlMessage = i2c.request(0)[0] assert controlMessage.len == 2 assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b01000101 + assert controlMessage.buf[1][0] == 0b01100111 def test_setting_pins_and_writing_outputs_to_i2c(): saa1064 = SAA1064(i2c) From e40f7461e5eb0467e0faaeeeffa0c6e46de45b39 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Wed, 20 Feb 2013 08:43:30 +0000 Subject: [PATCH 03/17] Moving more towards a pinbank model for the chip. [SE] --- quick2wire/parts/saa1064.py | 51 +++++++++++++++++++++++++------- quick2wire/parts/test_saa1064.py | 38 +++++++++++------------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index a469303..0b461b3 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -1,8 +1,7 @@ -from functools import reduce -from operator import or_ - __author__ = 'stuartervine' +from functools import reduce +from operator import or_ from quick2wire.i2c import writing_bytes displayController = 0x38 @@ -11,11 +10,14 @@ CONTINUOUS_DISPLAY = 0b00000110 class SAA1064(object): - def __init__(self, master): + def createPinBank(self, i): + return _PinBank(i) + + def __init__(self, master, digits=1): self.master = master self._mode = STATIC_MODE self._brightness = 0b11100000 - self._segment_output = tuple(_OutputPin(i) for i in range(8)) + self._pin_bank = tuple(self.createPinBank(i) for i in range(digits)) def display(self, digits): pass @@ -26,11 +28,8 @@ def configure(self): ) def write(self): - bits_to_write = [output.asBinary for output in self._segment_output] - byte_to_write = reduce(or_, bits_to_write) - self.master.transaction( - writing_bytes(displayController, 0b00000001, byte_to_write) - ) + i2c_messages = [pin_bank.i2c_message for pin_bank in self._pin_bank] + self.master.transaction(i2c_messages) @property def mode(self): @@ -48,9 +47,39 @@ def brightness(self): def brightness(self, value): self._brightness = value + def pin_bank(self, index): + return self._pin_bank[index] + + def __getitem__(self, n): + if 0 < n < len(self): + raise ValueError("no pin bank index {n} out of range", n=n) + return self._pin_bank[n] + + def __len__(self): + return len(self._pin_bank) + + +class _PinBank(object): + def __init__(self, index): + self._segment_output = tuple(_OutputPin(i) for i in range(8)) + self.segment_address = index+1 + def segment_output(self, index): return self._segment_output[index] + @property + def i2c_message(self): + bits_to_write = [output.bit_value for output in self._segment_output] + byte_to_write = reduce(or_, bits_to_write) + return writing_bytes(displayController, self.segment_address, byte_to_write) + + def __getitem__(self, n): + if 0 < n < len(self): + raise ValueError("no segment output index {n} out of range", n=n) + return self._segment_output[n] + + def __len__(self): + return len(self._segment_output) class _OutputPin(object): def __init__(self, index): @@ -58,7 +87,7 @@ def __init__(self, index): self._binary = 2 ** index @property - def asBinary(self): + def bit_value(self): return self._value * self._binary @property diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index 78c59f0..f75cf4b 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -42,7 +42,7 @@ def request(self, n): def setup_function(f): i2c.clear() -def test_static_display_brightest_by_default(): +def test_static_non_blanked_brightest_display_by_default(): saa1064 = SAA1064(i2c) saa1064.configure() @@ -54,7 +54,7 @@ def test_static_display_brightest_by_default(): assert controlMessage.buf[1][0] == 0b11100110 -def test_dynamic_display_brightest_by_default(): +def test_configuring_dynamic_display(): saa1064 = SAA1064(i2c) saa1064.mode=DYNAMIC_MODE saa1064.configure() @@ -66,7 +66,7 @@ def test_dynamic_display_brightest_by_default(): assert controlMessage.buf[0][0] == 0b00000000 assert controlMessage.buf[1][0] == 0b11100111 -def test_display_brightness(): +def test_configuring_display_brightness(): saa1064 = SAA1064(i2c) saa1064.mode=DYNAMIC_MODE saa1064.brightness=0b01100000 @@ -79,25 +79,21 @@ def test_display_brightness(): assert controlMessage.buf[0][0] == 0b00000000 assert controlMessage.buf[1][0] == 0b01100111 -def test_setting_pins_and_writing_outputs_to_i2c(): +def test_writing_first_bank_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c) - saa1064.mode=STATIC_MODE - saa1064.brightness=5 - saa1064.configure() - - saa1064.segment_output(0).value=1 - saa1064.segment_output(1).value=0 - saa1064.segment_output(2).value=0 - saa1064.segment_output(3).value=1 - saa1064.segment_output(4).value=1 - saa1064.segment_output(5).value=1 - saa1064.segment_output(6).value=0 - saa1064.segment_output(7).value=1 + saa1064.pin_bank(0).segment_output(0).value=1 + saa1064.pin_bank(0).segment_output(1).value=0 + saa1064.pin_bank(0).segment_output(2).value=0 + saa1064.pin_bank(0).segment_output(3).value=1 + saa1064.pin_bank(0).segment_output(4).value=1 + saa1064.pin_bank(0).segment_output(5).value=1 + saa1064.pin_bank(0).segment_output(6).value=0 + saa1064.pin_bank(0).segment_output(7).value=1 saa1064.write() - assert i2c.request_count == 2 - dataMessage = i2c.request(1)[0] - assert dataMessage.len == 2 - assert dataMessage.buf[0][0] == 0b00000001 - assert dataMessage.buf[1][0] == 0b10111001 + assert i2c.request_count == 1 + dataMessage = i2c.request(0)[0] + # assert dataMessage.len == 2 + assert dataMessage[0].buf[0][0] == 0b00000001 + assert dataMessage[0].buf[1][0] == 0b10111001 From e3e96eec5e829f0fe7e000587a9fbf65de5b5448 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Wed, 20 Feb 2013 22:13:27 +0000 Subject: [PATCH 04/17] Added support for multiple pin banks (correspondng to digits) on SAA1064. [SE] --- quick2wire/parts/test_saa1064.py | 57 ++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index f75cf4b..8de04da 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -36,6 +36,17 @@ def request_count(self): def request(self, n): return self._requests[n] + def message(self, request_index, message_index): + request = self.request(0) + return I2CMessageWrapper(request[request_index][message_index]) + +class I2CMessageWrapper: + def __init__(self, i2_message): + self._i2c_message = i2_message + self.len = i2_message.len + + def byte(self, index): + return self._i2c_message.buf[index][0] i2c = FakeI2CMaster() @@ -79,8 +90,8 @@ def test_configuring_display_brightness(): assert controlMessage.buf[0][0] == 0b00000000 assert controlMessage.buf[1][0] == 0b01100111 -def test_writing_first_bank_segment_outputs_to_i2c(): - saa1064 = SAA1064(i2c) +def test_writing_single_digit_segment_outputs_to_i2c(): + saa1064 = SAA1064(i2c, digits=1) saa1064.pin_bank(0).segment_output(0).value=1 saa1064.pin_bank(0).segment_output(1).value=0 @@ -93,7 +104,41 @@ def test_writing_first_bank_segment_outputs_to_i2c(): saa1064.write() assert i2c.request_count == 1 - dataMessage = i2c.request(0)[0] - # assert dataMessage.len == 2 - assert dataMessage[0].buf[0][0] == 0b00000001 - assert dataMessage[0].buf[1][0] == 0b10111001 + dataMessage = i2c.message(0, 0) + assert dataMessage.len == 2 + assert dataMessage.byte(0) == 0b00000001 + assert dataMessage.byte(1) == 0b10111001 + +def test_writing_two_digit_segment_outputs_to_i2c(): + saa1064 = SAA1064(i2c, digits=2) + + saa1064.pin_bank(0).segment_output(0).value=1 + saa1064.pin_bank(0).segment_output(1).value=0 + saa1064.pin_bank(0).segment_output(2).value=1 + saa1064.pin_bank(0).segment_output(3).value=0 + saa1064.pin_bank(0).segment_output(4).value=0 + saa1064.pin_bank(0).segment_output(5).value=0 + saa1064.pin_bank(0).segment_output(6).value=0 + saa1064.pin_bank(0).segment_output(7).value=0 + + saa1064.pin_bank(1).segment_output(0).value=0 + saa1064.pin_bank(1).segment_output(1).value=0 + saa1064.pin_bank(1).segment_output(2).value=0 + saa1064.pin_bank(1).segment_output(3).value=0 + saa1064.pin_bank(1).segment_output(4).value=1 + saa1064.pin_bank(1).segment_output(5).value=0 + saa1064.pin_bank(1).segment_output(6).value=1 + saa1064.pin_bank(1).segment_output(7).value=0 + saa1064.write() + + assert i2c.request_count == 1 + + message1 = i2c.message(0, 0) + assert message1.len == 2 + assert message1.byte(0) == 0b00000001 + assert message1.byte(1) == 0b00000101 + + message2 = i2c.message(0, 1) + assert message2.len == 2 + assert message2.byte(0) == 0b00000010 + assert message2.byte(1) == 0b01010000 From b05b9b1d288f5cdb09c4ec7784f25ca733ce445a Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Wed, 20 Feb 2013 22:26:12 +0000 Subject: [PATCH 05/17] Optimizing memory by holding a single byte on pin banks and twiddling that using Output wrapper objects. [SE] --- quick2wire/parts/saa1064.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 0b461b3..674fce6 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -61,17 +61,24 @@ def __len__(self): class _PinBank(object): def __init__(self, index): - self._segment_output = tuple(_OutputPin(i) for i in range(8)) + self._value = 0 + self._segment_output = tuple(_OutputPin(self, i) for i in range(8)) self.segment_address = index+1 def segment_output(self, index): return self._segment_output[index] + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + self._value = value + @property def i2c_message(self): - bits_to_write = [output.bit_value for output in self._segment_output] - byte_to_write = reduce(or_, bits_to_write) - return writing_bytes(displayController, self.segment_address, byte_to_write) + return writing_bytes(displayController, self.segment_address, self._value) def __getitem__(self, n): if 0 < n < len(self): @@ -82,18 +89,14 @@ def __len__(self): return len(self._segment_output) class _OutputPin(object): - def __init__(self, index): - self._value = 0 + def __init__(self, pin_bank, index): + self._pin_bank = pin_bank self._binary = 2 ** index - @property - def bit_value(self): - return self._value * self._binary - @property def value(self): - return self._value + return self._pin_bank.value & self._binary @value.setter def value(self, value): - self._value = value \ No newline at end of file + self._pin_bank.value = self._pin_bank.value | (self._binary * value) \ No newline at end of file From df7bcf480c632dafaba1a899d9306adc52bb74fd Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Wed, 20 Feb 2013 22:30:22 +0000 Subject: [PATCH 06/17] Support 4 pin banks (or 4 digits), and improve api to allow values to be set directly on the pin bank. [SE] --- quick2wire/parts/test_saa1064.py | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index 8de04da..8106f10 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -142,3 +142,36 @@ def test_writing_two_digit_segment_outputs_to_i2c(): assert message2.len == 2 assert message2.byte(0) == 0b00000010 assert message2.byte(1) == 0b01010000 + +def test_writing_four_digit_segment_outputs_to_i2c(): + saa1064 = SAA1064(i2c, digits=4) + + saa1064.pin_bank(0).value=255 + saa1064.pin_bank(1).value=127 + saa1064.pin_bank(2).value=63 + saa1064.pin_bank(3).value=31 + saa1064.write() + + assert i2c.request_count == 1 + + message1 = i2c.message(0, 0) + assert message1.len == 2 + assert message1.byte(0) == 1 + assert message1.byte(1) == 255 + + message2 = i2c.message(0, 1) + assert message2.len == 2 + assert message2.byte(0) == 2 + assert message2.byte(1) == 127 + + message3 = i2c.message(0, 2) + assert message3.len == 2 + assert message3.byte(0) == 3 + assert message3.byte(1) == 63 + + message4 = i2c.message(0, 3) + assert message4.len == 2 + assert message4.byte(0) == 4 + assert message4.byte(1) == 31 + + From 1c6227ec1f1df7af3fc0f0442bbe45b63293ef03 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Thu, 21 Feb 2013 22:48:11 +0000 Subject: [PATCH 07/17] Sorted out lists being passed through on transactions. [SE] --- quick2wire/parts/fake_i2c.py | 46 +++++++++++++++++++ quick2wire/parts/saa1064.py | 18 +++++--- quick2wire/parts/test_pcf8591.py | 46 +------------------ quick2wire/parts/test_saa1064.py | 76 +++++++++----------------------- 4 files changed, 79 insertions(+), 107 deletions(-) create mode 100644 quick2wire/parts/fake_i2c.py diff --git a/quick2wire/parts/fake_i2c.py b/quick2wire/parts/fake_i2c.py new file mode 100644 index 0000000..d0e34b6 --- /dev/null +++ b/quick2wire/parts/fake_i2c.py @@ -0,0 +1,46 @@ +__author__ = 'stuart' + +class FakeI2CMaster: + def __init__(self): + self._requests = [] + self._responses = [] + self._next_response = 0 + self.message_precondition = lambda m: True + + def all_messages_must(self, p): + self.message_precondition + + def clear(self): + self.__init__() + + def transaction(self, *messages): + for m in messages: + self.message_precondition(m) + + self._requests.append(messages) + return [] + + def add_response(self, *messages): + self._responses.append(messages) + + + @property + def request_count(self): + return len(self._requests) + + + def request(self, n): + return self._requests[n] + + def message(self, message_index): + request = self.request(0) + return I2CMessageWrapper(request[message_index]) + +class I2CMessageWrapper: + def __init__(self, i2_message): + self._i2c_message = i2_message + self.len = i2_message.len + + def byte(self, index): + return self._i2c_message.buf[index][0] + diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 674fce6..12c883c 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -17,6 +17,8 @@ def __init__(self, master, digits=1): self.master = master self._mode = STATIC_MODE self._brightness = 0b11100000 + if(digits > 4): + raise ValueError('SAA1064 only supports driving up to 4 digits') self._pin_bank = tuple(self.createPinBank(i) for i in range(digits)) def display(self, digits): @@ -29,30 +31,32 @@ def configure(self): def write(self): i2c_messages = [pin_bank.i2c_message for pin_bank in self._pin_bank] - self.master.transaction(i2c_messages) + self.master.transaction(*i2c_messages) @property def mode(self): return self._mode @mode.setter - def mode(self, value): - self._mode = value + def mode(self, mode): + if(mode > 1): + raise ValueError('invalid mode ' + str(mode) + ' only STATIC_MODE and DYNAMIC_MODE are supported') + self._mode = mode @property def brightness(self): return self._brightness @brightness.setter - def brightness(self, value): - self._brightness = value + def brightness(self, brightness): + self._brightness = brightness def pin_bank(self, index): return self._pin_bank[index] def __getitem__(self, n): if 0 < n < len(self): - raise ValueError("no pin bank index {n} out of range", n=n) + raise ValueError('no pin bank index {n} out of range', n=n) return self._pin_bank[n] def __len__(self): @@ -82,7 +86,7 @@ def i2c_message(self): def __getitem__(self, n): if 0 < n < len(self): - raise ValueError("no segment output index {n} out of range", n=n) + raise ValueError('no segment output index {n} out of range', n=n) return self._segment_output[n] def __len__(self): diff --git a/quick2wire/parts/test_pcf8591.py b/quick2wire/parts/test_pcf8591.py index cd2bee8..3cee12f 100644 --- a/quick2wire/parts/test_pcf8591.py +++ b/quick2wire/parts/test_pcf8591.py @@ -1,50 +1,10 @@ from quick2wire.i2c_ctypes import I2C_M_RD from quick2wire.gpio import In +from quick2wire.parts.fake_i2c import FakeI2CMaster from quick2wire.parts.pcf8591 import PCF8591, FOUR_SINGLE_ENDED, THREE_DIFFERENTIAL, SINGLE_ENDED_AND_DIFFERENTIAL, TWO_DIFFERENTIAL import pytest - -class FakeI2CMaster: - def __init__(self): - self._requests = [] - self._responses = [] - self._next_response = 0 - self.message_precondition = lambda m: True - - def all_messages_must(self, p): - self.message_precondition - - def clear(self): - self.__init__() - - def transaction(self, *messages): - for m in messages: - self.message_precondition(m) - - self._requests.append(messages) - - read_count = sum(bool(m.flags & I2C_M_RD) for m in messages) - if read_count == 0: - return [] - elif self._next_response < len(self._responses): - response = self._responses[self._next_response] - self._next_response += 1 - return response - else: - return [(0x00,)]*read_count - - def add_response(self, *messages): - self._responses.append(messages) - - @property - def request_count(self): - return len(self._requests) - - def request(self, n): - return self._requests[n] - - i2c = FakeI2CMaster() def is_read(m): @@ -56,7 +16,6 @@ def is_write(m): def assert_is_approx(expected, value, delta=0.005): assert abs(value - expected) <= delta - def correct_message_for(adc): def check(m): assert m.addr == adc.address @@ -70,8 +29,6 @@ def check(m): return check - - def setup_function(f): i2c.clear() @@ -84,7 +41,6 @@ def assert_all_input_pins_report_direction(adc): assert all(adc.single_ended_input(p).direction == In for p in range(adc.single_ended_input_count)) assert all(adc.differential_input(p).direction == In for p in range(adc.differential_input_count)) - def test_can_be_created_with_four_single_ended_inputs(): adc = PCF8591(i2c, FOUR_SINGLE_ENDED) assert adc.single_ended_input_count == 4 diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index 8106f10..f0e68fb 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -1,62 +1,24 @@ +import pytest from quick2wire.i2c_ctypes import I2C_M_RD +from quick2wire.parts.fake_i2c import FakeI2CMaster from quick2wire.parts.saa1064 import SAA1064, STATIC_MODE, DYNAMIC_MODE __author__ = 'stuartervine' - -class FakeI2CMaster: - def __init__(self): - self._requests = [] - self._responses = [] - self._next_response = 0 - self.message_precondition = lambda m: True - - def all_messages_must(self, p): - self.message_precondition - - def clear(self): - self.__init__() - - def transaction(self, *messages): - for m in messages: - self.message_precondition(m) - - self._requests.append(messages) - return [] - - def add_response(self, *messages): - self._responses.append(messages) - - - @property - def request_count(self): - return len(self._requests) - - - def request(self, n): - return self._requests[n] - - def message(self, request_index, message_index): - request = self.request(0) - return I2CMessageWrapper(request[request_index][message_index]) - -class I2CMessageWrapper: - def __init__(self, i2_message): - self._i2c_message = i2_message - self.len = i2_message.len - - def byte(self, index): - return self._i2c_message.buf[index][0] - i2c = FakeI2CMaster() def setup_function(f): i2c.clear() -def test_static_non_blanked_brightest_display_by_default(): +def test_cannot_be_created_with_invalid_number_of_digits(): + with pytest.raises(ValueError): + SAA1064(i2c, digits=5) + +def test_static_non_blanked_brightest_display_single_digit_by_default(): saa1064 = SAA1064(i2c) - saa1064.configure() + assert len(saa1064._pin_bank) == 1 + saa1064.configure() assert i2c.request_count == 1 controlMessage = i2c.request(0)[0] @@ -64,7 +26,6 @@ def test_static_non_blanked_brightest_display_by_default(): assert controlMessage.buf[0][0] == 0b00000000 assert controlMessage.buf[1][0] == 0b11100110 - def test_configuring_dynamic_display(): saa1064 = SAA1064(i2c) saa1064.mode=DYNAMIC_MODE @@ -90,6 +51,11 @@ def test_configuring_display_brightness(): assert controlMessage.buf[0][0] == 0b00000000 assert controlMessage.buf[1][0] == 0b01100111 +def test_cannot_be_configured_with_invalid_mode(): + saa1064 = SAA1064(i2c) + with pytest.raises(ValueError): + saa1064.mode=99 + def test_writing_single_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=1) @@ -104,7 +70,7 @@ def test_writing_single_digit_segment_outputs_to_i2c(): saa1064.write() assert i2c.request_count == 1 - dataMessage = i2c.message(0, 0) + dataMessage = i2c.message(0) assert dataMessage.len == 2 assert dataMessage.byte(0) == 0b00000001 assert dataMessage.byte(1) == 0b10111001 @@ -133,12 +99,12 @@ def test_writing_two_digit_segment_outputs_to_i2c(): assert i2c.request_count == 1 - message1 = i2c.message(0, 0) + message1 = i2c.message(0) assert message1.len == 2 assert message1.byte(0) == 0b00000001 assert message1.byte(1) == 0b00000101 - message2 = i2c.message(0, 1) + message2 = i2c.message(1) assert message2.len == 2 assert message2.byte(0) == 0b00000010 assert message2.byte(1) == 0b01010000 @@ -154,22 +120,22 @@ def test_writing_four_digit_segment_outputs_to_i2c(): assert i2c.request_count == 1 - message1 = i2c.message(0, 0) + message1 = i2c.message(0) assert message1.len == 2 assert message1.byte(0) == 1 assert message1.byte(1) == 255 - message2 = i2c.message(0, 1) + message2 = i2c.message(1) assert message2.len == 2 assert message2.byte(0) == 2 assert message2.byte(1) == 127 - message3 = i2c.message(0, 2) + message3 = i2c.message(2) assert message3.len == 2 assert message3.byte(0) == 3 assert message3.byte(1) == 63 - message4 = i2c.message(0, 3) + message4 = i2c.message(3) assert message4.len == 2 assert message4.byte(0) == 4 assert message4.byte(1) == 31 From 2607994dc628c51f8265351dadb71f0615a4c20f Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Fri, 22 Feb 2013 09:15:38 +0000 Subject: [PATCH 08/17] Added concept of digits and mapped to underlying byte value inside SAA1064 (these are tied to the chip). [SE] --- examples/saa1064-two-digits.py | 17 ++++++++++++++ quick2wire/parts/saa1064.py | 38 ++++++++++++++++++++++++++++++++ quick2wire/parts/test_saa1064.py | 34 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 examples/saa1064-two-digits.py diff --git a/examples/saa1064-two-digits.py b/examples/saa1064-two-digits.py new file mode 100644 index 0000000..5a5fd65 --- /dev/null +++ b/examples/saa1064-two-digits.py @@ -0,0 +1,17 @@ +import quick2wire.i2c as i2c +from quick2wire.parts.saa1064 import SAA1064, STATIC_MODE +from time import sleep + +numbers=[126, 48, 109, 121, 51, 91, 95, 112, 127, 123] + +saa1064 = SAA1064(i2c.I2CMaster(), digits=2) +saa1064.mode=STATIC_MODE +saa1064.brightness=0b01100000 +saa1064.configure() + +for y in range(10): + saa1064.pin_bank(0).value=numbers[y] + for x in range(10): + saa1064.pin_bank(1).value=numbers[x] + saa1064.write() + sleep(0.1) \ No newline at end of file diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 12c883c..3376111 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -9,6 +9,32 @@ DYNAMIC_MODE = 0b00000001 CONTINUOUS_DISPLAY = 0b00000110 +""" + Value conversions for the display segments + + --64-- + | | + 2 32 + | | + -- 1-- + | | + 4 16 + | | + -- 8-- DP-128 + """ +digit_map = { + '0':126, + '1':48, + '2':109, + '3':121, + '4':51, + '5':91, + '6':95, + '7':112, + '8':127, + '9':123 +} + class SAA1064(object): def createPinBank(self, i): return _PinBank(i) @@ -51,6 +77,9 @@ def brightness(self): def brightness(self, brightness): self._brightness = brightness + def digit(self, index): + return Digit(self._pin_bank[index]) + def pin_bank(self, index): return self._pin_bank[index] @@ -62,6 +91,15 @@ def __getitem__(self, n): def __len__(self): return len(self._pin_bank) +class Digit(object): + def __init__(self, pin_bank): + self._pin_bank = pin_bank + + def value(self, value): + try: + self._pin_bank.value=digit_map[str(value)] + except: + raise ValueError('cannot display digit ' + value) class _PinBank(object): def __init__(self, index): diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index f0e68fb..c6c20d4 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -140,4 +140,38 @@ def test_writing_four_digit_segment_outputs_to_i2c(): assert message4.byte(0) == 4 assert message4.byte(1) == 31 +def test_digits_are_mapped_into_correct_i2c_message(): + saa1064 = SAA1064(i2c, digits=2) + + saa1064.digit(0).value('9') + saa1064.digit(1).value('5') + saa1064.write() + + assert i2c.request_count == 1 + message1 = i2c.message(0) + assert message1.len == 2 + assert message1.byte(0) == 1 + assert message1.byte(1) == 123 + + message2 = i2c.message(1) + assert message2.len == 2 + assert message2.byte(0) == 2 + assert message2.byte(1) == 91 +def test_digits_can_be_set_using_integer_value(): + saa1064 = SAA1064(i2c, digits=2) + + saa1064.digit(0).value(9) + saa1064.write() + + assert i2c.request_count == 1 + message1 = i2c.message(0) + assert message1.len == 2 + assert message1.byte(0) == 1 + assert message1.byte(1) == 123 + +def test_does_not_accept_invalid_digit_values(): + saa1064 = SAA1064(i2c, digits=2) + + with pytest.raises(ValueError): + saa1064.digit(0).value('@') From 90ca30d4d4f9c05cae44464e5e0bfa2de8cbd1d6 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Fri, 22 Feb 2013 21:59:53 +0000 Subject: [PATCH 09/17] Adding 2 ways to configure the chip, properties for each configurable, and a writable byte for all configuration. [SE] --- examples/saa1064-two-digits.py | 6 ++---- quick2wire/parts/saa1064.py | 20 ++++++++++++++++---- quick2wire/parts/test_saa1064.py | 10 +++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/examples/saa1064-two-digits.py b/examples/saa1064-two-digits.py index 5a5fd65..48f27cd 100644 --- a/examples/saa1064-two-digits.py +++ b/examples/saa1064-two-digits.py @@ -2,16 +2,14 @@ from quick2wire.parts.saa1064 import SAA1064, STATIC_MODE from time import sleep -numbers=[126, 48, 109, 121, 51, 91, 95, 112, 127, 123] - saa1064 = SAA1064(i2c.I2CMaster(), digits=2) saa1064.mode=STATIC_MODE saa1064.brightness=0b01100000 saa1064.configure() for y in range(10): - saa1064.pin_bank(0).value=numbers[y] + saa1064.digit(0).value(y) for x in range(10): - saa1064.pin_bank(1).value=numbers[x] + saa1064.digit(1).value(x) saa1064.write() sleep(0.1) \ No newline at end of file diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 3376111..894e76c 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -32,7 +32,14 @@ '6':95, '7':112, '8':127, - '9':123 + '9':123, + 'A':119, + 'B':31, + 'C':78, + 'D':61, + 'E':79, + 'F':71, + 'R':5 } class SAA1064(object): @@ -51,8 +58,11 @@ def display(self, digits): pass def configure(self): + self.write_control(self.mode|self._brightness|CONTINUOUS_DISPLAY) + + def write_control(self, control_byte): self.master.transaction( - writing_bytes(displayController, 0b00000000, self.mode | self.brightness | CONTINUOUS_DISPLAY) + writing_bytes(displayController, 0b00000000, control_byte) ) def write(self): @@ -71,11 +81,13 @@ def mode(self, mode): @property def brightness(self): - return self._brightness + return self._brightness >> 5 @brightness.setter def brightness(self, brightness): - self._brightness = brightness + if(brightness > 7): + raise ValueError('invalid brightness, valid between 0-7.') + self._brightness = brightness << 5 def digit(self, index): return Digit(self._pin_bank[index]) diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index c6c20d4..1e4b808 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -41,7 +41,7 @@ def test_configuring_dynamic_display(): def test_configuring_display_brightness(): saa1064 = SAA1064(i2c) saa1064.mode=DYNAMIC_MODE - saa1064.brightness=0b01100000 + saa1064.brightness=3 saa1064.configure() assert i2c.request_count == 1 @@ -56,6 +56,14 @@ def test_cannot_be_configured_with_invalid_mode(): with pytest.raises(ValueError): saa1064.mode=99 +def test_cannot_be_configured_with_invalid_brightness(): + saa1064 = SAA1064(i2c) + for x in range(8): + saa1064.brightness = x + + with pytest.raises(ValueError): + saa1064.brightness=8 + def test_writing_single_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=1) From 8a864d91a217925e99039fc9bf10efe7a973cc9b Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sat, 23 Feb 2013 14:08:44 +0000 Subject: [PATCH 10/17] Added higher level seven segment display component, as a light wrapper around the SAA1064. Still need to add some protection to it. [SE] --- quick2wire/parts/saa1064.py | 22 +++++++--- quick2wire/parts/seven_segment_display.py | 15 +++++++ quick2wire/parts/test_saa1064.py | 12 ++++++ .../parts/test_seven_segment_display.py | 41 +++++++++++++++++++ 4 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 quick2wire/parts/seven_segment_display.py create mode 100644 quick2wire/parts/test_seven_segment_display.py diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 894e76c..b850141 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -9,6 +9,8 @@ DYNAMIC_MODE = 0b00000001 CONTINUOUS_DISPLAY = 0b00000110 +DECIMAL_POINT = 0b10000000 + """ Value conversions for the display segments @@ -48,10 +50,14 @@ def createPinBank(self, i): def __init__(self, master, digits=1): self.master = master - self._mode = STATIC_MODE self._brightness = 0b11100000 - if(digits > 4): - raise ValueError('SAA1064 only supports driving up to 4 digits') + if digits <= 0 or digits > 4: + raise ValueError('SAA1064 only supports driving 1 to 4 digits') + elif digits <= 2: + self._mode = STATIC_MODE + else: + self._mode = DYNAMIC_MODE + self._pin_bank = tuple(self.createPinBank(i) for i in range(digits)) def display(self, digits): @@ -75,7 +81,7 @@ def mode(self): @mode.setter def mode(self, mode): - if(mode > 1): + if mode > 1 or mode < 0: raise ValueError('invalid mode ' + str(mode) + ' only STATIC_MODE and DYNAMIC_MODE are supported') self._mode = mode @@ -85,7 +91,7 @@ def brightness(self): @brightness.setter def brightness(self, brightness): - if(brightness > 7): + if brightness > 7: raise ValueError('invalid brightness, valid between 0-7.') self._brightness = brightness << 5 @@ -108,8 +114,12 @@ def __init__(self, pin_bank): self._pin_bank = pin_bank def value(self, value): + digit = str(value) try: - self._pin_bank.value=digit_map[str(value)] + byte_value = digit_map[digit[:1]] + if digit.find(".") > -1: + byte_value |= DECIMAL_POINT + self._pin_bank.value=byte_value except: raise ValueError('cannot display digit ' + value) diff --git a/quick2wire/parts/seven_segment_display.py b/quick2wire/parts/seven_segment_display.py new file mode 100644 index 0000000..97e4a25 --- /dev/null +++ b/quick2wire/parts/seven_segment_display.py @@ -0,0 +1,15 @@ +import re + +__author__ = 'stuartervine' + +class SevenSegmentDisplay(object): + def __init__(self, driver_chip): + self._driver_chip = driver_chip + + def display(self, value): + digit_values = re.findall(".\.?", value) + digit_index=0 + for digit_value in digit_values: + self._driver_chip.digit(digit_index).value = digit_value + digit_index += 1 + diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index 1e4b808..c12ac67 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -178,6 +178,18 @@ def test_digits_can_be_set_using_integer_value(): assert message1.byte(0) == 1 assert message1.byte(1) == 123 +def test_digit_with_decimal_point_sets_bit_for_decimal_point(): + saa1064 = SAA1064(i2c, digits=2) + + saa1064.digit(0).value('9.') + saa1064.write() + + assert i2c.request_count == 1 + message1 = i2c.message(0) + assert message1.len == 2 + assert message1.byte(0) == 1 + assert message1.byte(1) == 251 + def test_does_not_accept_invalid_digit_values(): saa1064 = SAA1064(i2c, digits=2) diff --git a/quick2wire/parts/test_seven_segment_display.py b/quick2wire/parts/test_seven_segment_display.py new file mode 100644 index 0000000..dc44ad9 --- /dev/null +++ b/quick2wire/parts/test_seven_segment_display.py @@ -0,0 +1,41 @@ +from quick2wire.parts.saa1064 import DECIMAL_POINT +from quick2wire.parts.seven_segment_display import SevenSegmentDisplay + +__author__ = 'stuartervine' + +class FakeDigit(object): + def __init__(self): + self.value = 0 + + def value(self, value): + self.value = value + +class FakeSAA1064(object): + def __init__(self): + self._fake_digits = [FakeDigit() for x in range(4)] + + def digit(self, index): + return self._fake_digits[index] + +saa1064 = FakeSAA1064() + +def test_cannot_be_created_with_invalid_number_of_digits(): + display = SevenSegmentDisplay(saa1064) + display.display('1234') + assert saa1064.digit(0).value == '1' + assert saa1064.digit(1).value == '2' + assert saa1064.digit(2).value == '3' + assert saa1064.digit(3).value == '4' + +def test_shows_decimal_point(): + display = SevenSegmentDisplay(saa1064) + display.display('0.123') + assert saa1064.digit(0).value == '0.' + assert saa1064.digit(1).value == '1' + assert saa1064.digit(2).value == '2' + assert saa1064.digit(3).value == '3' + +def test_hands_through_potentially_undisplayable_values(): + display = SevenSegmentDisplay(saa1064) + display.display('@') + assert saa1064.digit(0).value == '@' From a2f69cfe0f369875f08c1f52f956c2be910fe49f Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sat, 23 Feb 2013 14:20:23 +0000 Subject: [PATCH 11/17] Forgot value is not a property on digit. It probably should be. [Se] --- examples/seven_segment_display_with_two_digits.py | 10 ++++++++++ quick2wire/parts/seven_segment_display.py | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 examples/seven_segment_display_with_two_digits.py diff --git a/examples/seven_segment_display_with_two_digits.py b/examples/seven_segment_display_with_two_digits.py new file mode 100644 index 0000000..d8ca25d --- /dev/null +++ b/examples/seven_segment_display_with_two_digits.py @@ -0,0 +1,10 @@ +from quick2wire import i2c +from quick2wire.parts.saa1064 import SAA1064 +from quick2wire.parts.seven_segment_display import SevenSegmentDisplay + +__author__ = 'stuartervine' + +saa1064 = SAA1064(i2c.I2CMaster(), digits=2) +saa1064.configure() + +SevenSegmentDisplay(saa1064).display('1.5') diff --git a/quick2wire/parts/seven_segment_display.py b/quick2wire/parts/seven_segment_display.py index 97e4a25..394bc81 100644 --- a/quick2wire/parts/seven_segment_display.py +++ b/quick2wire/parts/seven_segment_display.py @@ -10,6 +10,7 @@ def display(self, value): digit_values = re.findall(".\.?", value) digit_index=0 for digit_value in digit_values: - self._driver_chip.digit(digit_index).value = digit_value + self._driver_chip.digit(digit_index).value(digit_value) digit_index += 1 + self._driver_chip.write() From a20f3805b935eb037f36900f35920ea02d7a75e4 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sat, 23 Feb 2013 14:25:47 +0000 Subject: [PATCH 12/17] Updating example for seven segment display. [Se] --- examples/seven_segment_display_with_two_digits.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/seven_segment_display_with_two_digits.py b/examples/seven_segment_display_with_two_digits.py index d8ca25d..7c1ae6b 100644 --- a/examples/seven_segment_display_with_two_digits.py +++ b/examples/seven_segment_display_with_two_digits.py @@ -1,10 +1,14 @@ +__author__ = 'stuartervine' + from quick2wire import i2c from quick2wire.parts.saa1064 import SAA1064 from quick2wire.parts.seven_segment_display import SevenSegmentDisplay - -__author__ = 'stuartervine' +from time import sleep saa1064 = SAA1064(i2c.I2CMaster(), digits=2) saa1064.configure() -SevenSegmentDisplay(saa1064).display('1.5') +sevenSegmentDisplay=SevenSegmentDisplay(saa1064) +sevenSegmentDisplay.display('1.5') +sleep(1) +sevenSegmentDisplay.display('9R') \ No newline at end of file From 2538c95b593f04ad233d206f3157eae4151a8e05 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sat, 23 Feb 2013 14:48:53 +0000 Subject: [PATCH 13/17] Added some documentation to the SAA1064 class. [SE] --- quick2wire/parts/saa1064.py | 89 ++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index b850141..db8d031 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -1,18 +1,49 @@ -__author__ = 'stuartervine' +""" +API for the SAA1064 seven segment display driver chip. -from functools import reduce -from operator import or_ -from quick2wire.i2c import writing_bytes +The SAA1064 can drive up to 4 seven segment displays simultaneously. +There are 2 modes the chip can run in: -displayController = 0x38 -STATIC_MODE = 0b00000000 -DYNAMIC_MODE = 0b00000001 -CONTINUOUS_DISPLAY = 0b00000110 +STATIC mode - drives 2 seven segment displays. +Where no multiplexing is required, and the outputs of the pins 3-11 +and 14-22 can be connected directly to the LED displays. +[URL] -DECIMAL_POINT = 0b10000000 +DYNAMIC mode - drives 4 seven segment displays. +The chip multiplexes the output of displays 1+3 and 2+4. The pairs +of displays are connected to the same output pins as above. The circuit +requires 2 transistors to be connected to the multiplex outputs to control +which display to trigger. -""" - Value conversions for the display segments +Applications talk to the chip via objects of the SAA1064 class. A +SAA1064 object is created with an I2CMaster and a number of digits between 1-4. + +The display brightness can be supplied using the brightness property. +This supports values between 0-7. + +The displays can then be controlled individually by setting valid values +on digits and then calling write(). + +For example: + + with I2CMaster() as i2c: + saa1064 = SAA1064(i2c, digits=2) + + saa1064.digit(0).value('1') + saa1064.digit(1).value('2') + saa1064.write() + +The current values supported for a digit are: 0-9, A-F, r + +Specifying a decimal point after the value will light the point in the display. + +For example: + with I2CMaster() as i2c: + saa1064 = SAA1064(i2c, digits=2) + + saa1064.digit(0).value('1.') + +The displays are configured using the following value conversions for the display segments --64-- | | @@ -23,7 +54,24 @@ 4 16 | | -- 8-- DP-128 - """ + +""" + + +__author__ = 'stuartervine' + +from functools import reduce +from operator import or_ +from quick2wire.i2c import writing_bytes + +displayController = 0x38 +STATIC_MODE = 0b00000000 +DYNAMIC_MODE = 0b00000001 +CONTINUOUS_DISPLAY = 0b00000110 + +DECIMAL_POINT = 0b10000000 + + digit_map = { '0':126, '1':48, @@ -48,9 +96,9 @@ class SAA1064(object): def createPinBank(self, i): return _PinBank(i) - def __init__(self, master, digits=1): + def __init__(self, master, digits=1, brightness=7): self.master = master - self._brightness = 0b11100000 + self.brightness = brightness if digits <= 0 or digits > 4: raise ValueError('SAA1064 only supports driving 1 to 4 digits') elif digits <= 2: @@ -60,48 +108,55 @@ def __init__(self, master, digits=1): self._pin_bank = tuple(self.createPinBank(i) for i in range(digits)) - def display(self, digits): - pass - def configure(self): + """Writes the configured control byte to the chip.""" self.write_control(self.mode|self._brightness|CONTINUOUS_DISPLAY) def write_control(self, control_byte): + """Writes a raw control byte to the chip.""" self.master.transaction( writing_bytes(displayController, 0b00000000, control_byte) ) def write(self): + """Writes the segment outputs to the chip.""" i2c_messages = [pin_bank.i2c_message for pin_bank in self._pin_bank] self.master.transaction(*i2c_messages) @property def mode(self): + """Returns the display mode the chip is currently configured in, 0: static, 1: dynamic""" return self._mode @mode.setter def mode(self, mode): + """Changes the display mode the chip is configured in.""" if mode > 1 or mode < 0: raise ValueError('invalid mode ' + str(mode) + ' only STATIC_MODE and DYNAMIC_MODE are supported') self._mode = mode @property def brightness(self): + """Returns the current brightness level configured for the LED displays.""" return self._brightness >> 5 @brightness.setter def brightness(self, brightness): + """Sets the brightness of the LED displays, between 0-7.""" if brightness > 7: raise ValueError('invalid brightness, valid between 0-7.') self._brightness = brightness << 5 def digit(self, index): + """Returns a Digit object for the display at given 'index'.""" return Digit(self._pin_bank[index]) def pin_bank(self, index): + """Returns a PinBank object for the display at given 'index'.""" return self._pin_bank[index] def __getitem__(self, n): + """Allows the SAA1064 to iterate through it's pin banks.""" if 0 < n < len(self): raise ValueError('no pin bank index {n} out of range', n=n) return self._pin_bank[n] From 6c450504674b37bf62bfcba6d515515b1e40b704 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sat, 2 Mar 2013 21:02:14 +0000 Subject: [PATCH 14/17] Fixing up the seven segment display tests. [SE] --- .../seven_segment_display_with_two_digits.py | 2 + quick2wire/parts/saa1064.py | 13 +++-- quick2wire/parts/seven_segment_display.py | 4 +- quick2wire/parts/test_saa1064.py | 58 +++++++++---------- .../parts/test_seven_segment_display.py | 28 +++++---- 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/examples/seven_segment_display_with_two_digits.py b/examples/seven_segment_display_with_two_digits.py index 7c1ae6b..dd87021 100644 --- a/examples/seven_segment_display_with_two_digits.py +++ b/examples/seven_segment_display_with_two_digits.py @@ -8,6 +8,8 @@ saa1064 = SAA1064(i2c.I2CMaster(), digits=2) saa1064.configure() +#TODO: Write a reset method. + sevenSegmentDisplay=SevenSegmentDisplay(saa1064) sevenSegmentDisplay.display('1.5') sleep(1) diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index db8d031..46b60bf 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -71,7 +71,6 @@ DECIMAL_POINT = 0b10000000 - digit_map = { '0':126, '1':48, @@ -93,7 +92,7 @@ } class SAA1064(object): - def createPinBank(self, i): + def create_pin_bank(self, i): return _PinBank(i) def __init__(self, master, digits=1, brightness=7): @@ -106,7 +105,7 @@ def __init__(self, master, digits=1, brightness=7): else: self._mode = DYNAMIC_MODE - self._pin_bank = tuple(self.createPinBank(i) for i in range(digits)) + self._pin_bank = tuple(self.create_pin_bank(i) for i in range(digits)) def configure(self): """Writes the configured control byte to the chip.""" @@ -147,11 +146,12 @@ def brightness(self, brightness): raise ValueError('invalid brightness, valid between 0-7.') self._brightness = brightness << 5 + #TODO: ditch this for write_digit def digit(self, index): """Returns a Digit object for the display at given 'index'.""" return Digit(self._pin_bank[index]) - def pin_bank(self, index): + def bank(self, index): """Returns a PinBank object for the display at given 'index'.""" return self._pin_bank[index] @@ -168,6 +168,7 @@ class Digit(object): def __init__(self, pin_bank): self._pin_bank = pin_bank + #convert to property def value(self, value): digit = str(value) try: @@ -179,6 +180,7 @@ def value(self, value): raise ValueError('cannot display digit ' + value) class _PinBank(object): + #extends pinbankapi def __init__(self, index): self._value = 0 self._segment_output = tuple(_OutputPin(self, i) for i in range(8)) @@ -187,6 +189,8 @@ def __init__(self, index): def segment_output(self, index): return self._segment_output[index] + pin = segment_output + @property def value(self): return self._value @@ -208,6 +212,7 @@ def __len__(self): return len(self._segment_output) class _OutputPin(object): + #extend pinapi def __init__(self, pin_bank, index): self._pin_bank = pin_bank self._binary = 2 ** index diff --git a/quick2wire/parts/seven_segment_display.py b/quick2wire/parts/seven_segment_display.py index 394bc81..b188ab1 100644 --- a/quick2wire/parts/seven_segment_display.py +++ b/quick2wire/parts/seven_segment_display.py @@ -7,8 +7,10 @@ def __init__(self, driver_chip): self._driver_chip = driver_chip def display(self, value): - digit_values = re.findall(".\.?", value) + digit_values = re.findall(".\.?", str(value)) digit_index=0 + + #TODO: Replace with zip with index. for digit_value in digit_values: self._driver_chip.digit(digit_index).value(digit_value) digit_index += 1 diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index c12ac67..f543654 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -67,14 +67,14 @@ def test_cannot_be_configured_with_invalid_brightness(): def test_writing_single_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=1) - saa1064.pin_bank(0).segment_output(0).value=1 - saa1064.pin_bank(0).segment_output(1).value=0 - saa1064.pin_bank(0).segment_output(2).value=0 - saa1064.pin_bank(0).segment_output(3).value=1 - saa1064.pin_bank(0).segment_output(4).value=1 - saa1064.pin_bank(0).segment_output(5).value=1 - saa1064.pin_bank(0).segment_output(6).value=0 - saa1064.pin_bank(0).segment_output(7).value=1 + saa1064.bank(0).segment_output(0).value=1 + saa1064.bank(0).segment_output(1).value=0 + saa1064.bank(0).segment_output(2).value=0 + saa1064.bank(0).segment_output(3).value=1 + saa1064.bank(0).segment_output(4).value=1 + saa1064.bank(0).segment_output(5).value=1 + saa1064.bank(0).segment_output(6).value=0 + saa1064.bank(0).segment_output(7).value=1 saa1064.write() assert i2c.request_count == 1 @@ -86,23 +86,23 @@ def test_writing_single_digit_segment_outputs_to_i2c(): def test_writing_two_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=2) - saa1064.pin_bank(0).segment_output(0).value=1 - saa1064.pin_bank(0).segment_output(1).value=0 - saa1064.pin_bank(0).segment_output(2).value=1 - saa1064.pin_bank(0).segment_output(3).value=0 - saa1064.pin_bank(0).segment_output(4).value=0 - saa1064.pin_bank(0).segment_output(5).value=0 - saa1064.pin_bank(0).segment_output(6).value=0 - saa1064.pin_bank(0).segment_output(7).value=0 - - saa1064.pin_bank(1).segment_output(0).value=0 - saa1064.pin_bank(1).segment_output(1).value=0 - saa1064.pin_bank(1).segment_output(2).value=0 - saa1064.pin_bank(1).segment_output(3).value=0 - saa1064.pin_bank(1).segment_output(4).value=1 - saa1064.pin_bank(1).segment_output(5).value=0 - saa1064.pin_bank(1).segment_output(6).value=1 - saa1064.pin_bank(1).segment_output(7).value=0 + saa1064.bank(0).segment_output(0).value=1 + saa1064.bank(0).segment_output(1).value=0 + saa1064.bank(0).segment_output(2).value=1 + saa1064.bank(0).segment_output(3).value=0 + saa1064.bank(0).segment_output(4).value=0 + saa1064.bank(0).segment_output(5).value=0 + saa1064.bank(0).segment_output(6).value=0 + saa1064.bank(0).segment_output(7).value=0 + + saa1064.bank(1).segment_output(0).value=0 + saa1064.bank(1).segment_output(1).value=0 + saa1064.bank(1).segment_output(2).value=0 + saa1064.bank(1).segment_output(3).value=0 + saa1064.bank(1).segment_output(4).value=1 + saa1064.bank(1).segment_output(5).value=0 + saa1064.bank(1).segment_output(6).value=1 + saa1064.bank(1).segment_output(7).value=0 saa1064.write() assert i2c.request_count == 1 @@ -120,10 +120,10 @@ def test_writing_two_digit_segment_outputs_to_i2c(): def test_writing_four_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=4) - saa1064.pin_bank(0).value=255 - saa1064.pin_bank(1).value=127 - saa1064.pin_bank(2).value=63 - saa1064.pin_bank(3).value=31 + saa1064.bank(0).value=255 + saa1064.bank(1).value=127 + saa1064.bank(2).value=63 + saa1064.bank(3).value=31 saa1064.write() assert i2c.request_count == 1 diff --git a/quick2wire/parts/test_seven_segment_display.py b/quick2wire/parts/test_seven_segment_display.py index dc44ad9..927caea 100644 --- a/quick2wire/parts/test_seven_segment_display.py +++ b/quick2wire/parts/test_seven_segment_display.py @@ -5,10 +5,10 @@ class FakeDigit(object): def __init__(self): - self.value = 0 + self._value = 0 def value(self, value): - self.value = value + self._value = value class FakeSAA1064(object): def __init__(self): @@ -17,25 +17,31 @@ def __init__(self): def digit(self, index): return self._fake_digits[index] + def write(self): + pass + + def __len__(self): + return len(self._fake_digits) + saa1064 = FakeSAA1064() def test_cannot_be_created_with_invalid_number_of_digits(): display = SevenSegmentDisplay(saa1064) display.display('1234') - assert saa1064.digit(0).value == '1' - assert saa1064.digit(1).value == '2' - assert saa1064.digit(2).value == '3' - assert saa1064.digit(3).value == '4' + assert saa1064.digit(0)._value == '1' + assert saa1064.digit(1)._value == '2' + assert saa1064.digit(2)._value == '3' + assert saa1064.digit(3)._value == '4' def test_shows_decimal_point(): display = SevenSegmentDisplay(saa1064) display.display('0.123') - assert saa1064.digit(0).value == '0.' - assert saa1064.digit(1).value == '1' - assert saa1064.digit(2).value == '2' - assert saa1064.digit(3).value == '3' + assert saa1064.digit(0)._value == '0.' + assert saa1064.digit(1)._value == '1' + assert saa1064.digit(2)._value == '2' + assert saa1064.digit(3)._value == '3' def test_hands_through_potentially_undisplayable_values(): display = SevenSegmentDisplay(saa1064) display.display('@') - assert saa1064.digit(0).value == '@' + assert saa1064.digit(0)._value == '@' From 8f39a5219655a33f04aab9eaa2f1f8c14c8a387d Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sun, 3 Mar 2013 00:32:29 +0000 Subject: [PATCH 15/17] Moved SAA1064 over to common PinAPIs from GPIO to remove a little duplication. [SE] --- quick2wire/gpio.py | 44 +----------------- quick2wire/parts/saa1064.py | 45 ++++++++++++------- quick2wire/parts/seven_segment_display.py | 2 +- quick2wire/parts/test_saa1064.py | 23 +++++++--- .../parts/test_seven_segment_display.py | 23 +++++----- quick2wire/pin.py | 36 +++++++++++++++ 6 files changed, 95 insertions(+), 78 deletions(-) create mode 100644 quick2wire/pin.py diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index fe8e3a8..98d021f 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -6,6 +6,7 @@ import subprocess from contextlib import contextmanager from quick2wire.board_revision import revision +from quick2wire.pin import PinAPI, PinBankAPI from quick2wire.selector import EDGE @@ -27,46 +28,6 @@ def gpio_admin(subcommand, pin, pull=None): PullUp = "pullup" - -class PinAPI(object): - def __init__(self, bank, index): - self._bank = bank - self._index = index - - @property - def index(self): - return self._index - - @property - def bank(self): - return self._bank - - def __enter__(self): - self.open() - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - value = property(lambda p: p.get(), - lambda p,v: p.set(v), - doc="""The value of the pin: 1 if the pin is high, 0 if the pin is low.""") - - -class PinBankAPI(object): - def __getitem__(self, n): - if 0 < n < len(self): - raise ValueError("no pin index {n} out of range", n=n) - return self.pin(n) - - def write(self): - pass - - def read(self): - pass - - - class Pin(PinAPI): """Controls a GPIO pin.""" @@ -204,9 +165,6 @@ def __str__(self): index=self.index) - - - class PinBank(PinBankAPI): def __init__(self, index_to_soc_fn, count=None): super(PinBank,self).__init__() diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 46b60bf..9ed3b41 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -56,6 +56,7 @@ -- 8-- DP-128 """ +from quick2wire.pin import PinAPI, PinBankAPI __author__ = 'stuartervine' @@ -168,20 +169,28 @@ class Digit(object): def __init__(self, pin_bank): self._pin_bank = pin_bank - #convert to property - def value(self, value): - digit = str(value) + value = property(lambda p: p.get(), + lambda p,v: p.set(v), + doc="""The value represented by the digit""") + + def get(self): + return self._pin_bank.value + + def set(self, new_value): + digit = str(new_value) try: byte_value = digit_map[digit[:1]] if digit.find(".") > -1: byte_value |= DECIMAL_POINT self._pin_bank.value=byte_value except: - raise ValueError('cannot display digit ' + value) + raise ValueError('cannot display digit ' + new_value) + + +class _PinBank(PinBankAPI): -class _PinBank(object): - #extends pinbankapi def __init__(self, index): + super(_PinBank,self).__init__() self._value = 0 self._segment_output = tuple(_OutputPin(self, i) for i in range(8)) self.segment_address = index+1 @@ -211,16 +220,20 @@ def __getitem__(self, n): def __len__(self): return len(self._segment_output) -class _OutputPin(object): - #extend pinapi +class _OutputPin(PinAPI): def __init__(self, pin_bank, index): - self._pin_bank = pin_bank - self._binary = 2 ** index + super(_OutputPin,self).__init__(pin_bank, index) + self._binary = 1 << index - @property - def value(self): - return self._pin_bank.value & self._binary + def open(self): + pass - @value.setter - def value(self, value): - self._pin_bank.value = self._pin_bank.value | (self._binary * value) \ No newline at end of file + def close(self): + pass + + def get(self): + """The current value of the pin: 1 if the pin is high or 0 if the pin is low.""" + return (self._bank.value & (1 << self._index)) >> self._index + + def set(self, new_value): + self._bank.value = self._bank.value | (self._binary * new_value) diff --git a/quick2wire/parts/seven_segment_display.py b/quick2wire/parts/seven_segment_display.py index b188ab1..1f570e7 100644 --- a/quick2wire/parts/seven_segment_display.py +++ b/quick2wire/parts/seven_segment_display.py @@ -12,7 +12,7 @@ def display(self, value): #TODO: Replace with zip with index. for digit_value in digit_values: - self._driver_chip.digit(digit_index).value(digit_value) + self._driver_chip.digit(digit_index).value=digit_value digit_index += 1 self._driver_chip.write() diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index f543654..c92e9fe 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -83,6 +83,19 @@ def test_writing_single_digit_segment_outputs_to_i2c(): assert dataMessage.byte(0) == 0b00000001 assert dataMessage.byte(1) == 0b10111001 +def test_individual_pin_values_can_be_read(): + saa1064 = SAA1064(i2c, digits=1) + + saa1064.bank(0).segment_output(0).value=1 + saa1064.bank(0).segment_output(1).value=0 + saa1064.bank(0).segment_output(2).value=0 + saa1064.bank(0).segment_output(3).value=1 + + assert saa1064.bank(0).segment_output(0).value == 1 + assert saa1064.bank(0).segment_output(1).value == 0 + assert saa1064.bank(0).segment_output(2).value == 0 + assert saa1064.bank(0).segment_output(3).value == 1 + def test_writing_two_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=2) @@ -151,8 +164,8 @@ def test_writing_four_digit_segment_outputs_to_i2c(): def test_digits_are_mapped_into_correct_i2c_message(): saa1064 = SAA1064(i2c, digits=2) - saa1064.digit(0).value('9') - saa1064.digit(1).value('5') + saa1064.digit(0).value='9' + saa1064.digit(1).value='5' saa1064.write() assert i2c.request_count == 1 @@ -169,7 +182,7 @@ def test_digits_are_mapped_into_correct_i2c_message(): def test_digits_can_be_set_using_integer_value(): saa1064 = SAA1064(i2c, digits=2) - saa1064.digit(0).value(9) + saa1064.digit(0).value=9 saa1064.write() assert i2c.request_count == 1 @@ -181,7 +194,7 @@ def test_digits_can_be_set_using_integer_value(): def test_digit_with_decimal_point_sets_bit_for_decimal_point(): saa1064 = SAA1064(i2c, digits=2) - saa1064.digit(0).value('9.') + saa1064.digit(0).value='9.' saa1064.write() assert i2c.request_count == 1 @@ -194,4 +207,4 @@ def test_does_not_accept_invalid_digit_values(): saa1064 = SAA1064(i2c, digits=2) with pytest.raises(ValueError): - saa1064.digit(0).value('@') + saa1064.digit(0).value='@' diff --git a/quick2wire/parts/test_seven_segment_display.py b/quick2wire/parts/test_seven_segment_display.py index 927caea..1e3bce0 100644 --- a/quick2wire/parts/test_seven_segment_display.py +++ b/quick2wire/parts/test_seven_segment_display.py @@ -5,10 +5,7 @@ class FakeDigit(object): def __init__(self): - self._value = 0 - - def value(self, value): - self._value = value + self.value = 0 class FakeSAA1064(object): def __init__(self): @@ -28,20 +25,20 @@ def __len__(self): def test_cannot_be_created_with_invalid_number_of_digits(): display = SevenSegmentDisplay(saa1064) display.display('1234') - assert saa1064.digit(0)._value == '1' - assert saa1064.digit(1)._value == '2' - assert saa1064.digit(2)._value == '3' - assert saa1064.digit(3)._value == '4' + assert saa1064.digit(0).value == '1' + assert saa1064.digit(1).value == '2' + assert saa1064.digit(2).value == '3' + assert saa1064.digit(3).value == '4' def test_shows_decimal_point(): display = SevenSegmentDisplay(saa1064) display.display('0.123') - assert saa1064.digit(0)._value == '0.' - assert saa1064.digit(1)._value == '1' - assert saa1064.digit(2)._value == '2' - assert saa1064.digit(3)._value == '3' + assert saa1064.digit(0).value == '0.' + assert saa1064.digit(1).value == '1' + assert saa1064.digit(2).value == '2' + assert saa1064.digit(3).value == '3' def test_hands_through_potentially_undisplayable_values(): display = SevenSegmentDisplay(saa1064) display.display('@') - assert saa1064.digit(0)._value == '@' + assert saa1064.digit(0).value == '@' diff --git a/quick2wire/pin.py b/quick2wire/pin.py new file mode 100644 index 0000000..0c426e7 --- /dev/null +++ b/quick2wire/pin.py @@ -0,0 +1,36 @@ +class PinAPI(object): + def __init__(self, bank, index): + self._bank = bank + self._index = index + + @property + def index(self): + return self._index + + @property + def bank(self): + return self._bank + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + value = property(lambda p: p.get(), + lambda p,v: p.set(v), + doc="""The value of the pin: 1 if the pin is high, 0 if the pin is low.""") + + +class PinBankAPI(object): + def __getitem__(self, n): + if 0 < n < len(self): + raise ValueError("no pin index {n} out of range", n=n) + return self.pin(n) + + def write(self): + pass + + def read(self): + pass \ No newline at end of file From bfa65df94f8b7848c441d97a5e9ec3d4971a4670 Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Sun, 3 Mar 2013 01:21:26 +0000 Subject: [PATCH 16/17] Reset the pin cache on new digits being displayed, padded leading unused digits, and configured on first write. [SE] --- quick2wire/parts/fake_i2c.py | 12 ++++- quick2wire/parts/saa1064.py | 11 +++- quick2wire/parts/seven_segment_display.py | 9 ++-- quick2wire/parts/test_saa1064.py | 53 ++++++++++++++----- .../parts/test_seven_segment_display.py | 15 +++++- 5 files changed, 79 insertions(+), 21 deletions(-) diff --git a/quick2wire/parts/fake_i2c.py b/quick2wire/parts/fake_i2c.py index d0e34b6..be014e9 100644 --- a/quick2wire/parts/fake_i2c.py +++ b/quick2wire/parts/fake_i2c.py @@ -28,14 +28,24 @@ def add_response(self, *messages): def request_count(self): return len(self._requests) - def request(self, n): return self._requests[n] + def request_at(self, n): + return I2CRequestWrapper(self._requests[n]) + def message(self, message_index): request = self.request(0) return I2CMessageWrapper(request[message_index]) +class I2CRequestWrapper: + def __init__(self, i2c_request): + self._i2c_request = i2c_request + + def message(self, index): + return I2CMessageWrapper(self._i2c_request[index]) + + class I2CMessageWrapper: def __init__(self, i2_message): self._i2c_message = i2_message diff --git a/quick2wire/parts/saa1064.py b/quick2wire/parts/saa1064.py index 9ed3b41..944b59d 100644 --- a/quick2wire/parts/saa1064.py +++ b/quick2wire/parts/saa1064.py @@ -99,6 +99,8 @@ def create_pin_bank(self, i): def __init__(self, master, digits=1, brightness=7): self.master = master self.brightness = brightness + self.configured = False + if digits <= 0 or digits > 4: raise ValueError('SAA1064 only supports driving 1 to 4 digits') elif digits <= 2: @@ -112,14 +114,22 @@ def configure(self): """Writes the configured control byte to the chip.""" self.write_control(self.mode|self._brightness|CONTINUOUS_DISPLAY) + def reset(self): + for pin_bank in self: + pin_bank.value = 0 + def write_control(self, control_byte): """Writes a raw control byte to the chip.""" self.master.transaction( writing_bytes(displayController, 0b00000000, control_byte) ) + self.configured = True def write(self): """Writes the segment outputs to the chip.""" + if(not self.configured): + self.configure() + i2c_messages = [pin_bank.i2c_message for pin_bank in self._pin_bank] self.master.transaction(*i2c_messages) @@ -147,7 +157,6 @@ def brightness(self, brightness): raise ValueError('invalid brightness, valid between 0-7.') self._brightness = brightness << 5 - #TODO: ditch this for write_digit def digit(self, index): """Returns a Digit object for the display at given 'index'.""" return Digit(self._pin_bank[index]) diff --git a/quick2wire/parts/seven_segment_display.py b/quick2wire/parts/seven_segment_display.py index 1f570e7..2fb8ebf 100644 --- a/quick2wire/parts/seven_segment_display.py +++ b/quick2wire/parts/seven_segment_display.py @@ -7,12 +7,11 @@ def __init__(self, driver_chip): self._driver_chip = driver_chip def display(self, value): + self._driver_chip.reset() + digit_values = re.findall(".\.?", str(value)) - digit_index=0 + for i, digit_value in zip(reversed(range(len(self._driver_chip))), reversed(digit_values)): + self._driver_chip.digit(i).value=digit_value - #TODO: Replace with zip with index. - for digit_value in digit_values: - self._driver_chip.digit(digit_index).value=digit_value - digit_index += 1 self._driver_chip.write() diff --git a/quick2wire/parts/test_saa1064.py b/quick2wire/parts/test_saa1064.py index c92e9fe..2aadb52 100644 --- a/quick2wire/parts/test_saa1064.py +++ b/quick2wire/parts/test_saa1064.py @@ -21,10 +21,10 @@ def test_static_non_blanked_brightest_display_single_digit_by_default(): saa1064.configure() assert i2c.request_count == 1 - controlMessage = i2c.request(0)[0] + controlMessage = i2c.request_at(0).message(0) assert controlMessage.len == 2 - assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b11100110 + assert controlMessage.byte(0) == 0b00000000 + assert controlMessage.byte(1) == 0b11100110 def test_configuring_dynamic_display(): saa1064 = SAA1064(i2c) @@ -33,10 +33,10 @@ def test_configuring_dynamic_display(): assert i2c.request_count == 1 - controlMessage = i2c.request(0)[0] + controlMessage = i2c.request_at(0).message(0) assert controlMessage.len == 2 - assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b11100111 + assert controlMessage.byte(0) == 0b00000000 + assert controlMessage.byte(1) == 0b11100111 def test_configuring_display_brightness(): saa1064 = SAA1064(i2c) @@ -46,10 +46,10 @@ def test_configuring_display_brightness(): assert i2c.request_count == 1 - controlMessage = i2c.request(0)[0] + controlMessage = i2c.request_at(0).message(0) assert controlMessage.len == 2 - assert controlMessage.buf[0][0] == 0b00000000 - assert controlMessage.buf[1][0] == 0b01100111 + assert controlMessage.byte(0) == 0b00000000 + assert controlMessage.byte(1) == 0b01100111 def test_cannot_be_configured_with_invalid_mode(): saa1064 = SAA1064(i2c) @@ -64,8 +64,30 @@ def test_cannot_be_configured_with_invalid_brightness(): with pytest.raises(ValueError): saa1064.brightness=8 +def test_first_write_sends_configuration_to_chip(): + saa1064 = SAA1064(i2c, digits=1) + saa1064.write() + + assert i2c.request_count == 2 + dataMessage = i2c.message(0) + assert dataMessage.len == 2 + assert dataMessage.byte(0) == 0b00000000 + assert dataMessage.byte(1) == 0b11100110 + +def test_second_write_only_writes_data_to_chip(): + saa1064 = SAA1064(i2c, digits=1) + saa1064.write() + saa1064.write() + + assert i2c.request_count == 3 + dataMessage = i2c.request_at(2).message(0) + assert dataMessage.len == 2 + assert dataMessage.byte(0) == 0b00000001 + assert dataMessage.byte(1) == 0b00000000 + def test_writing_single_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=1) + saa1064.configured = True saa1064.bank(0).segment_output(0).value=1 saa1064.bank(0).segment_output(1).value=0 @@ -78,7 +100,7 @@ def test_writing_single_digit_segment_outputs_to_i2c(): saa1064.write() assert i2c.request_count == 1 - dataMessage = i2c.message(0) + dataMessage = i2c.request_at(0).message(0) assert dataMessage.len == 2 assert dataMessage.byte(0) == 0b00000001 assert dataMessage.byte(1) == 0b10111001 @@ -98,6 +120,7 @@ def test_individual_pin_values_can_be_read(): def test_writing_two_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=2) + saa1064.configured = True saa1064.bank(0).segment_output(0).value=1 saa1064.bank(0).segment_output(1).value=0 @@ -120,18 +143,19 @@ def test_writing_two_digit_segment_outputs_to_i2c(): assert i2c.request_count == 1 - message1 = i2c.message(0) + message1 = i2c.request_at(0).message(0) assert message1.len == 2 assert message1.byte(0) == 0b00000001 assert message1.byte(1) == 0b00000101 - message2 = i2c.message(1) + message2 = i2c.request_at(0).message(1) assert message2.len == 2 assert message2.byte(0) == 0b00000010 assert message2.byte(1) == 0b01010000 def test_writing_four_digit_segment_outputs_to_i2c(): saa1064 = SAA1064(i2c, digits=4) + saa1064.configured = True saa1064.bank(0).value=255 saa1064.bank(1).value=127 @@ -141,7 +165,7 @@ def test_writing_four_digit_segment_outputs_to_i2c(): assert i2c.request_count == 1 - message1 = i2c.message(0) + message1 = i2c.request_at(0).message(0) assert message1.len == 2 assert message1.byte(0) == 1 assert message1.byte(1) == 255 @@ -163,6 +187,7 @@ def test_writing_four_digit_segment_outputs_to_i2c(): def test_digits_are_mapped_into_correct_i2c_message(): saa1064 = SAA1064(i2c, digits=2) + saa1064.configured = True saa1064.digit(0).value='9' saa1064.digit(1).value='5' @@ -181,6 +206,7 @@ def test_digits_are_mapped_into_correct_i2c_message(): def test_digits_can_be_set_using_integer_value(): saa1064 = SAA1064(i2c, digits=2) + saa1064.configured = True saa1064.digit(0).value=9 saa1064.write() @@ -193,6 +219,7 @@ def test_digits_can_be_set_using_integer_value(): def test_digit_with_decimal_point_sets_bit_for_decimal_point(): saa1064 = SAA1064(i2c, digits=2) + saa1064.configured = True saa1064.digit(0).value='9.' saa1064.write() diff --git a/quick2wire/parts/test_seven_segment_display.py b/quick2wire/parts/test_seven_segment_display.py index 1e3bce0..3d95f63 100644 --- a/quick2wire/parts/test_seven_segment_display.py +++ b/quick2wire/parts/test_seven_segment_display.py @@ -11,6 +11,10 @@ class FakeSAA1064(object): def __init__(self): self._fake_digits = [FakeDigit() for x in range(4)] + def reset(self): + for digit in self._fake_digits: + digit.value = 0 + def digit(self, index): return self._fake_digits[index] @@ -41,4 +45,13 @@ def test_shows_decimal_point(): def test_hands_through_potentially_undisplayable_values(): display = SevenSegmentDisplay(saa1064) display.display('@') - assert saa1064.digit(0).value == '@' + assert saa1064.digit(3).value == '@' + +def test_pads_unused_digits_when_not_all_are_used(): + display = SevenSegmentDisplay(saa1064) + display.display('12') + assert saa1064.digit(0).value == 0 + assert saa1064.digit(1).value == 0 + assert saa1064.digit(2).value == '1' + assert saa1064.digit(3).value == '2' + From 0f354269ba51c217cca83116dd68331b4808c45d Mon Sep 17 00:00:00 2001 From: Stuart Ervine Date: Mon, 4 Mar 2013 08:35:07 +0000 Subject: [PATCH 17/17] Updating examples for SAA1064 and SevenSegmentDisplay, removing unnecessary calls. [SE] --- examples/saa1064-two-digits.py | 2 -- examples/seven_segment_display_with_two_digits.py | 13 +++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/saa1064-two-digits.py b/examples/saa1064-two-digits.py index 48f27cd..e42de4f 100644 --- a/examples/saa1064-two-digits.py +++ b/examples/saa1064-two-digits.py @@ -3,9 +3,7 @@ from time import sleep saa1064 = SAA1064(i2c.I2CMaster(), digits=2) -saa1064.mode=STATIC_MODE saa1064.brightness=0b01100000 -saa1064.configure() for y in range(10): saa1064.digit(0).value(y) diff --git a/examples/seven_segment_display_with_two_digits.py b/examples/seven_segment_display_with_two_digits.py index dd87021..4002c59 100644 --- a/examples/seven_segment_display_with_two_digits.py +++ b/examples/seven_segment_display_with_two_digits.py @@ -5,12 +5,13 @@ from quick2wire.parts.seven_segment_display import SevenSegmentDisplay from time import sleep -saa1064 = SAA1064(i2c.I2CMaster(), digits=2) -saa1064.configure() - -#TODO: Write a reset method. - +saa1064 = SAA1064(i2c.I2CMaster(), digits=4) sevenSegmentDisplay=SevenSegmentDisplay(saa1064) + sevenSegmentDisplay.display('1.5') sleep(1) -sevenSegmentDisplay.display('9R') \ No newline at end of file +sevenSegmentDisplay.display('100R') +sleep(1) + +for i in range(9999): + sevenSegmentDisplay.display(i)