Skip to content

Commit 306906d

Browse files
committed
higher level API for analogue output
1 parent d7d55d7 commit 306906d

2 files changed

Lines changed: 90 additions & 76 deletions

File tree

quick2wire/parts/pcf8591.py

Lines changed: 19 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def open(self):
2828
self.bank._enable_output()
2929

3030
def close(self):
31-
self.bank._close_output()
31+
self.bank._disable_output()
3232

3333
def __enter__(self):
3434
self.open()
@@ -81,7 +81,9 @@ def __exit__(self, *exc):
8181
return False
8282

8383

84-
class PinBank(object):
84+
class PCF8591(object):
85+
"""Access to the PCF8591 A/D and D/A converter"""
86+
8587
def __init__(self, master, mode, address=0x48, samples=3):
8688
self.master = master
8789
self.address = address
@@ -104,13 +106,27 @@ def input_pin(self, n):
104106

105107
def _enable_output(self):
106108
self._control_flags |= ANALOGUE_OUTPUT_ENABLE_FLAG
109+
self._write_control_flags()
107110

108111
def _disable_output(self):
109112
self._control_flags &= ~ANALOGUE_OUTPUT_ENABLE_FLAG
113+
self._write_control_flags()
110114

115+
def _write_control_flags(self):
116+
if self._last_channel_read is None:
117+
self._last_channel_read = 0
118+
119+
self.master.transaction(
120+
writing_bytes(self.address, self._control_flags|self._last_channel_read))
121+
111122
def write(self, value):
123+
int_value = min(max(0, int(value*255)), 0xFF)
124+
125+
if self._last_channel_read is None:
126+
self._last_channel_read = 0
127+
112128
self.master.transaction(
113-
writing_bytes(control_flags, (value*255) & 0xFF))
129+
writing_bytes(self.address, self._control_flags|self._last_channel_read, int_value))
114130

115131
def read(self, channel):
116132
if channel != self._last_channel_read:
@@ -122,74 +138,3 @@ def read(self, channel):
122138

123139
return results[0][0] / 255.0
124140

125-
126-
class PCF8591:
127-
"""Access to the PCF8591 A/D and D/A converter"""
128-
129-
def __init__ (self, master, address = 0x48):
130-
self.master = master
131-
self.address = address
132-
self._control = 0
133-
134-
@property
135-
def input_channel(self):
136-
"""
137-
Channel that will be converted on next read command
138-
"""
139-
return self._control & 7
140-
141-
@property
142-
def autoincrement(self):
143-
"""
144-
Enables the auto increment flag for reads
145-
"""
146-
return self._control & 8 == 8
147-
148-
@property
149-
def inputs(self):
150-
"""
151-
Selects the input configuration. Supply one of FOUR_SINGLE_ENDED,
152-
THREE_DIFFERENTIAL, SINGLE_ENDED_AND_DIFFERENTIAL, or TWO_DIFFERENTIAL
153-
"""
154-
return self._control & 0x30 >> 4
155-
156-
def begin_analogue_read(self, channel, set=FOUR_SINGLE_ENDED):
157-
"""
158-
Selects an analogue input configuration. The specified channel
159-
will be enabled in the configuration set (which defaults to FOUR_SINGLE_ENDED)
160-
"""
161-
162-
self._control = ANALOGUE_OUTPUT_ENABLE_FLAG | set << 4 | channel
163-
self.master.transaction(writing_bytes(self.address, self._control))
164-
return SingleInputPin(self, self._control & 0x3f)
165-
166-
def read(self, count=1):
167-
"""
168-
Read the next conversion with the current input channel and
169-
programming
170-
"""
171-
return self.master.transaction(reading(self.address, count))[0]
172-
173-
def analogue_out(self, value):
174-
self._control = ANALOGUE_OUTPUT_ENABLE_FLAG | self._control
175-
self.master.transaction(writing_bytes(self.address, self._control, value))
176-
177-
def _validate_input(self, config):
178-
"""Resets the input configuration to the supplied configuration"""
179-
if (self._control & 0x3f) != config:
180-
self._control = self._control & 0x40 | config
181-
self.master.transaction(writing_bytes(self.address, self._control))
182-
183-
class SingleInputPin:
184-
def __init__(self, chip, input_config):
185-
self._chip = chip
186-
self._input_config = input_config
187-
188-
@property
189-
def chip(self):
190-
return self._chip
191-
192-
def read(self):
193-
"""Read a single value from the PCF8591"""
194-
self.chip._validate_input(self._input_config)
195-
return self.chip.read(1)[0]

quick2wire/parts/test_pcf8591.py

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
from quick2wire.i2c_ctypes import I2C_M_RD
3-
from quick2wire.parts.pcf8591 import PinBank as PCF8591
4-
from quick2wire.parts.pcf8591 import FOUR_SINGLE_ENDED, THREE_DIFFERENTIAL, SINGLE_ENDED_AND_DIFFERENTIAL, TWO_DIFFERENTIAL
3+
from quick2wire.parts.pcf8591 import PCF8591, FOUR_SINGLE_ENDED, THREE_DIFFERENTIAL, SINGLE_ENDED_AND_DIFFERENTIAL, TWO_DIFFERENTIAL
54
from factcheck import forall, from_range
65

76
class FakeI2CMaster:
@@ -172,5 +171,75 @@ def test_does_not_write_control_byte_to_switch_channel_if_multiple_reads_from_sa
172171
assert ctl2.buf[0][0] == 0b00010001
173172

174173

174+
def test_opening_and_closing_the_output_pin_turns_the_digital_to_analogue_converter_on_and_off():
175+
adc = PCF8591(i2c, FOUR_SINGLE_ENDED)
176+
177+
adc.output_pin.open()
178+
assert i2c.request_count == 1
179+
m1, = i2c.request(0)
180+
assert is_write(m1)
181+
assert m1.addr == adc.address
182+
assert m1.len == 1
183+
assert m1.buf[0][0] == 0b01000000
184+
185+
adc.output_pin.close()
186+
assert i2c.request_count == 2
187+
m2, = i2c.request(1)
188+
assert is_write(m2)
189+
assert m2.addr == adc.address
190+
assert m2.len == 1
191+
assert m2.buf[0][0] == 0b00000000
192+
193+
194+
def test_output_pin_opens_and_closes_itself_when_used_as_a_context_manager():
195+
adc = PCF8591(i2c, FOUR_SINGLE_ENDED)
196+
197+
with adc.output_pin:
198+
assert i2c.request_count == 1
175199

200+
assert i2c.request_count == 2
201+
202+
203+
def test_setting_value_of_output_pin_sends_value_as_second_written_byte():
204+
adc = PCF8591(i2c, FOUR_SINGLE_ENDED, samples=1)
205+
206+
with adc.output_pin as pin:
207+
pin.value = 0.5
208+
209+
assert i2c.request_count == 2
210+
m1, = i2c.request(1)
211+
assert m1.addr == adc.address
212+
assert m1.len == 2
213+
assert m1.buf[0][0] == 0b01000000
214+
assert m1.buf[1][0] == 127
215+
216+
pin.value = 0.25
217+
218+
assert i2c.request_count == 3
219+
m2, = i2c.request(2)
220+
assert m2.addr == adc.address
221+
assert m2.len == 2
222+
assert m2.buf[0][0] == 0b01000000
223+
assert m2.buf[1][0] == 63
224+
176225

226+
227+
def test_setting_value_of_output_pin_does_not_affect_currently_selected_input_pin():
228+
adc = PCF8591(i2c, FOUR_SINGLE_ENDED, samples=1)
229+
230+
with adc.output_pin as opin:
231+
assert i2c.request_count == 1
232+
233+
adc.input_pin(1).get()
234+
assert i2c.request_count == 3
235+
236+
opin.value = 0.5
237+
assert i2c.request_count == 4
238+
assert i2c.request(3)[0].buf[0][0] == 0b01000001
239+
240+
adc.input_pin(2).get()
241+
assert i2c.request_count == 6
242+
243+
opin.value = 0.5
244+
assert i2c.request_count == 7
245+
assert i2c.request(6)[0].buf[0][0] == 0b01000010

0 commit comments

Comments
 (0)