diff --git a/doc/getting-started-with-i2c.md b/doc/getting-started-with-i2c.md index 2308c7e..eebcdff 100644 --- a/doc/getting-started-with-i2c.md +++ b/doc/getting-started-with-i2c.md @@ -5,13 +5,15 @@ Getting Started With I2C Warning: ------- -[Revision 2.0](http://www.raspberrypi.org/archives/1929) of the Raspberry Pi swaps the connections to I2C buses 0 and 1. +[Revision 2.0](http://www.raspberrypi.org/archives/1929) of the +Raspberry Pi swaps the connections to I2C buses 0 and 1. -With a revision 2.0 board, if you connect an I2C device to the appropriate header, -you will see it when you run `i2cdetect 1` instead of `i2cdetect 0`. +With a revision 2.0 board, if you connect an I2C device to the +appropriate header, you will see it when you run `i2cdetect 1` instead +of `i2cdetect 0`. -The library now auto-detects whether you are running version 1.0 or 2.0 of the board, so the same code will work on -either. +The library now auto-detects whether you are running version 1.0 or +2.0 of the board, so the same code will work on either. The example: ------------ @@ -99,22 +101,23 @@ write to and the value of the register. Then we'll read the value of the chip's GPIO register by performing a transaction containing two operations: a write operation that tells the chip which register we want to read, and a read operation that -reads a single byte from that register. +reads a single byte from that register. To issue a read operation we +must allocate a buffer (of type bytearray) for the data to be read +into and use that buffer to create a i2c.reading_into operation. We +only want to read a single byte, so we'll create a bytearray of size +1. + buf = bytearray(1) + read_results = bus.transaction( i2c.writing_bytes(address, gpio_register), - i2c.reading(address, 1)) - -The I2CMaster' transaction method returns a list of byte sequences, one -for each read operation performed. Each result is an array of bytes -read from the device. So the state of the GPIO pins is the first and -only byte of the first and only byte sequence returned. + i2c.reading_into(address, buf)) - gpio_state = read_results[0][0] +After the transaction has completed, we print out the state of the +gpio_register, which has been read into the first and only byte in our +byte buffer. -We finally print that in hexadecimal: - - print("%02x" % gpio_state) + print("%02x" % buf[0]) Putting it all together: @@ -129,11 +132,11 @@ Putting it all together: with i2c.I2CMaster() as bus: bus.transaction( i2c.writing_bytes(address, iodir_register, 0xFF)) - + + buf = bytearray(1) + read_results = bus.transaction( i2c.writing_bytes(address, gpio_register), - i2c.reading(address, 1)) - - gpio_state = read_results[0][0] + i2c.reading_into(address, buf)) - print("%02x" % gpio_state) + print("%02x" % buf[0]) diff --git a/examples/i2c-counter b/examples/i2c-counter deleted file mode 100755 index 4f6cf6a..0000000 --- a/examples/i2c-counter +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 - -import quick2wire.i2c as i2c -import time - -address = 0x20 -iodir_register = 0x00 -gpio_register = 0x09 - -def write_register(bus, reg, b): - bus.transaction( - i2c.writing_bytes(address, reg, b)) - -def read_register(bus, reg): - return bus.transaction( - i2c.writing_bytes(address, reg), - i2c.reading(address, 1))[0][0] - -with i2c.I2CMaster() as bus: - write_register(bus, iodir_register, 0x80) - write_register(bus, gpio_register, 0x00) - - print("ready") - - button_down = False - count = 0 - - while True: - gpio_state = read_register(bus, gpio_register) - - button_was_down = button_down - button_down = bool(gpio_state & 0x80) - - if button_down and not button_was_down: - count = min(count + 1, 127) - write_register(bus, gpio_register, count) - - time.sleep(0.05) diff --git a/examples/i2c-example b/examples/i2c-example index 5e56ae3..2d18e5e 100755 --- a/examples/i2c-example +++ b/examples/i2c-example @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from quick2wire.i2c import I2CMaster, writing_bytes, reading +from quick2wire.i2c import I2CMaster, writing_bytes, reading_into import time address = 0x20 @@ -10,14 +10,14 @@ gpio_register = 0x09 with I2CMaster() as master: master.transaction( writing_bytes(address, iodir_register, 0xFF)) - - while True: - read_results = master.transaction( + + buf = bytearray(1) + + while True: + master.transaction( writing_bytes(address, gpio_register), - reading(address, 1)) - - gpio_state = read_results[0][0] + reading_into(address, buf)) - print("%02x" % gpio_state) + print("%02x" % buf[0]) time.sleep(1) diff --git a/examples/i2c-multibyte-read b/examples/i2c-multibyte-read index 35f9edb..9f28748 100755 --- a/examples/i2c-multibyte-read +++ b/examples/i2c-multibyte-read @@ -22,16 +22,18 @@ with i2c.I2CMaster() as bus: i2c.writing_bytes(address, iopol_register, 0b10101010)) # Read a single byte (the GPIO register) - gpio_state = bus.transaction( + gpio_buf = bytearray(1) + bus.transaction( i2c.writing_bytes(address, gpio_register), - i2c.reading(address, 1))[0][0] + i2c.reading_into(address, gpio_buf)) # Read two bytes, the IODIR register and, thanks to sequential # addressing mode, the next register, which is IOPOL - iodir_state, iopol_state = bus.transaction( + iodir_iopol_buf = bytearray(2) + bus.transaction( i2c.writing_bytes(address, iodir_register), - i2c.reading(address, 2))[0] + i2c.reading_into(address, iodir_iopol_buf)) - print("GPIO: ", bin(gpio_state)[2:]) - print("IODIR:", bin(iodir_state)[2:]) - print("IOPOL:", bin(iopol_state)[2:]) + print("GPIO: ", bin(gpio_buf[0])[2:]) + print("IODIR:", bin(iodir_iopol_buf[0])[2:]) + print("IOPOL:", bin(iodir_iopol_buf[1])[2:]) diff --git a/quick2wire/i2c.py b/quick2wire/i2c.py index 0b6b9fe..e2a46c0 100644 --- a/quick2wire/i2c.py +++ b/quick2wire/i2c.py @@ -4,14 +4,14 @@ import posix from fcntl import ioctl from quick2wire.i2c_ctypes import * -from ctypes import create_string_buffer, sizeof, c_int, byref, pointer, addressof, string_at +from ctypes import create_string_buffer, sizeof, c_int, byref, string_at from quick2wire.board_revision import revision assert sys.version_info.major >= 3, __name__ + " is only supported on Python 3" - default_bus = 1 if revision() > 1 else 0 + class I2CMaster(object): """Performs I2C I/O transactions on an I2C bus. @@ -76,18 +76,13 @@ def transaction(self, *msgs): ioctl_arg = i2c_rdwr_ioctl_data(msgs=msg_array, nmsgs=msg_count) ioctl(self.fd, I2C_RDWR, ioctl_arg) - - return [i2c_msg_to_bytes(m) for m in msgs if (m.flags & I2C_M_RD)] -def reading(addr, n_bytes): - """An I2C I/O message that reads n_bytes bytes of data""" - return reading_into(addr, create_string_buffer(n_bytes)) - def reading_into(addr, buf): - """An I2C I/O message that reads into an existing ctypes string buffer.""" - return _new_i2c_msg(addr, I2C_M_RD, buf) + """An I2C I/O message that reads into a mutable byte buffer, such as a bytearray or a ctypes struct.""" + ptr = (c_char*len(buf)).from_buffer(buf) + return _new_i2c_msg(addr, I2C_M_RD, ptr) def writing_bytes(addr, *bytes): """An I2C I/O message that writes one or more bytes of data. @@ -101,8 +96,11 @@ def writing(addr, byte_seq): The bytes are passed to this function as a sequence. """ - buf = bytes(byte_seq) - return _new_i2c_msg(addr, 0, create_string_buffer(buf, len(buf))) + buf = bytearray(byte_seq) + ptr = (c_char*len(buf)).from_buffer(buf) + return _new_i2c_msg(addr, 0, ptr) + +writing_from = writing def _new_i2c_msg(addr, flags, buf): diff --git a/quick2wire/parts/mcp23017.py b/quick2wire/parts/mcp23017.py index 90a26c0..cdfdee8 100644 --- a/quick2wire/parts/mcp23017.py +++ b/quick2wire/parts/mcp23017.py @@ -3,7 +3,7 @@ interface for the MCP23017 I2C GPIO expander. """ -from quick2wire.i2c import writing_bytes, reading +from quick2wire.i2c import writing_bytes, reading_into import quick2wire.parts.mcp23x17 as mcp23x17 from quick2wire.parts.mcp23x17 import deferred_read, immediate_read, deferred_write, immediate_write, In, Out @@ -56,8 +56,10 @@ def read_register(self, register_id): Returns: the value of the register. """ - return self.master.transaction( + buf = bytearray(1) + self.master.transaction( writing_bytes(self.address, register_id), - reading(self.address, 1))[0][0] + reading_into(self.address, buf)) + return buf[0] diff --git a/quick2wire/parts/pcf8591.py b/quick2wire/parts/pcf8591.py index 63f67fe..2660961 100644 --- a/quick2wire/parts/pcf8591.py +++ b/quick2wire/parts/pcf8591.py @@ -75,7 +75,7 @@ """ -from quick2wire.i2c import writing_bytes, reading +from quick2wire.i2c import writing_bytes, reading_into from quick2wire.gpio import Out, In BASE_ADDRESS = 0x48 @@ -191,14 +191,15 @@ def read_differential(self, channel): return (unsigned & 127) - (unsigned & 128) def read_raw(self, channel): + buf = bytearray(2) if channel != self._last_channel_read: self.master.transaction(writing_bytes(self.address, self._control_flags|channel), - reading(self.address, 2)) + reading_into(self.address, buf)) self._last_channel_read = channel results = self.master.transaction( - reading(self.address, 2)) - return results[0][-1] + reading_into(self.address, buf)) + return buf[-1] class _OutputChannel(object): diff --git a/quick2wire/parts/test_pcf8591.py b/quick2wire/parts/test_pcf8591.py index 3c48318..c8dbcca 100644 --- a/quick2wire/parts/test_pcf8591.py +++ b/quick2wire/parts/test_pcf8591.py @@ -1,4 +1,5 @@ +from itertools import repeat from quick2wire.i2c_ctypes import I2C_M_RD from quick2wire.gpio import In from quick2wire.parts.pcf8591 import PCF8591, FOUR_SINGLE_ENDED, THREE_DIFFERENTIAL, SINGLE_ENDED_AND_DIFFERENTIAL, TWO_DIFFERENTIAL @@ -24,15 +25,21 @@ def transaction(self, *messages): 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 + read_requests = [m for m in messages if bool(m.flags & I2C_M_RD)] + + if len(read_requests) > 0: + if self._next_response < len(self._responses): + responses = self._responses[self._next_response] + self._next_response += 1 + assert len(responses) == len(read_requests), \ + "expected " + str(len(responses)) + " read requests, got " + str(len(read_requests)) + else: + responses = repeat(repeat(0)) + + for request, response in zip(read_requests, responses): + for i, b in zip(range(request.len), response): + request.buf[i] = b + def add_response(self, *messages): self._responses.append(messages)