Skip to content
This repository was archived by the owner on Sep 30, 2019. It is now read-only.

Commit be1d242

Browse files
committed
Fix bug with FT232H read polling, improve reliability, and refactor FT232H I2C to batch calls into a single transaction/command.
1 parent 5482c70 commit be1d242

File tree

2 files changed

+142
-76
lines changed

2 files changed

+142
-76
lines changed

Adafruit_GPIO/FT232H.py

+141-75
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
# THE SOFTWARE.
2121

2222
import atexit
23-
import binascii
2423
import logging
2524
import math
2625
import os
@@ -41,6 +40,8 @@
4140
MSBFIRST = 0
4241
LSBFIRST = 1
4342

43+
_REPEAT_DELAY = 4
44+
4445

4546
def _check_running_as_root():
4647
# NOTE: Checking for root with user ID 0 isn't very portable, perhaps
@@ -109,9 +110,13 @@ def __init__(self, vid=FT232H_VID, pid=FT232H_PID):
109110
self._check(ftdi.usb_open, vid, pid)
110111
# Reset device.
111112
self._check(ftdi.usb_reset)
113+
# Disable flow control. Commented out because it is unclear if this is necessary.
114+
#self._check(ftdi.setflowctrl, ftdi.SIO_DISABLE_FLOW_CTRL)
112115
# Change read & write buffers to maximum size, 65535 bytes.
113116
self._check(ftdi.read_data_set_chunksize, 65535)
114117
self._check(ftdi.write_data_set_chunksize, 65535)
118+
# Clear pending read data & write buffers.
119+
self._check(ftdi.usb_purge_buffers)
115120
# Enable MPSSE and syncronize communication with device.
116121
self._mpsse_enable()
117122
self._mpsse_sync()
@@ -130,6 +135,12 @@ def _write(self, string):
130135
"""Helper function to call write_data on the provided FTDI device and
131136
verify it succeeds.
132137
"""
138+
# Get modem status. Useful to enable for debugging.
139+
#ret, status = ftdi.poll_modem_status(self._ctx)
140+
#if ret == 0:
141+
# logger.debug('Modem status {0:02X}'.format(status))
142+
#else:
143+
# logger.debug('Modem status error {0}'.format(ret))
133144
length = len(string)
134145
ret = ftdi.write_data(self._ctx, string, length)
135146
# Log the string that was written in a python hex string format using a very
@@ -156,13 +167,21 @@ def _poll_read(self, expected, timeout_s=5.0):
156167
the read data as a string if successful, otherwise raises an execption.
157168
"""
158169
start = time.time()
170+
# Start with an empty response buffer.
171+
response = bytearray(expected)
172+
index = 0
173+
# Loop calling read until the response buffer is full or a timeout occurs.
159174
while time.time() - start <= timeout_s:
160-
ret, data = ftdi.read_data(self._ctx, expected)
175+
ret, data = ftdi.read_data(self._ctx, expected - index)
176+
# Fail if there was an error reading data.
161177
if ret < 0:
162178
raise RuntimeError('ftdi_read_data failed with error code {0}.'.format(ret))
163-
if ret == expected:
164-
#logger.debug('Read {0}'.format(''.join(['\\x{0:02X}'.format(ord(x)) for x in data])))
165-
return data
179+
# Add returned data to the buffer.
180+
response[index:index+ret] = data[:ret]
181+
index += ret
182+
# Buffer is full, return the result data.
183+
if index >= expected:
184+
return str(response)
166185
time.sleep(0.01)
167186
raise RuntimeError('Timeout while polling ftdi_read_data for {0} bytes!'.format(expected))
168187

@@ -449,84 +468,90 @@ def __init__(self, ft232h, address, clock_hz=100000):
449468
# This matches the protocol for I2C communication so multiple devices can
450469
# share the I2C bus.
451470
self._ft232h._write('\x9E\x07\x00')
452-
self._i2c_idle()
471+
self._idle()
453472

454-
def _i2c_idle(self):
473+
def _idle(self):
455474
"""Put I2C lines into idle state."""
456-
# Set SCL and data out to output high, data in to input.
475+
# Put the I2C lines into an idle state with SCL and SDA high.
457476
self._ft232h.setup_pins({0: GPIO.OUT, 1: GPIO.OUT, 2: GPIO.IN},
458477
{0: GPIO.HIGH, 1: GPIO.HIGH})
459478

479+
def _transaction_start(self):
480+
"""Start I2C transaction."""
481+
# Clear command buffer and expected response bytes.
482+
self._command = []
483+
self._expected = 0
484+
485+
def _transaction_end(self):
486+
"""End I2C transaction and get response bytes, including ACKs."""
487+
# Ask to return response bytes immediately.
488+
self._command.append('\x87')
489+
# Send the entire command to the MPSSE.
490+
self._ft232h._write(''.join(self._command))
491+
# Read response bytes and return them.
492+
return bytearray(self._ft232h._poll_read(self._expected))
493+
460494
def _i2c_start(self):
461-
"""Send I2C start condition."""
462-
# Set SCL high and SDA low. Repeat command 4 times to allow some time
463-
# for the signal to be read.
495+
"""Send I2C start signal. Must be called within a transaction start/end.
496+
"""
497+
# Set SCL high and SDA low, repeat 4 times to stay in this state for a
498+
# short period of time.
464499
self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False)
465-
self._ft232h._write(self._ft232h.mpsse_gpio() * 4)
466-
# Set SCL low and SDA low for a short period.
500+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
501+
# Now drop SCL to low (again repeat 4 times for short delay).
467502
self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False)
468-
self._ft232h._write(self._ft232h.mpsse_gpio() * 4)
503+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
504+
505+
def _i2c_idle(self):
506+
"""Set I2C signals to idle state with SCL and SDA at a high value. Must
507+
be called within a transaction start/end.
508+
"""
509+
self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False)
510+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
469511

470512
def _i2c_stop(self):
471-
"""Send I2C stop condition."""
513+
"""Send I2C stop signal. Must be called within a transaction start/end.
514+
"""
472515
# Set SCL low and SDA low for a short period.
473516
self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.LOW}, write=False)
474-
self._ft232h._write(self._ft232h.mpsse_gpio() * 4)
517+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
475518
# Set SCL high and SDA low for a short period.
476519
self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.LOW}, write=False)
477-
self._ft232h._write(self._ft232h.mpsse_gpio() * 4)
520+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
478521
# Finally set SCL high and SDA high for a short period.
479522
self._ft232h.output_pins({0: GPIO.HIGH, 1: GPIO.HIGH}, write=False)
480-
self._ft232h._write(self._ft232h.mpsse_gpio() * 4)
523+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
481524

482525
def _i2c_read_bytes(self, length=1):
483526
"""Read the specified number of bytes from the I2C bus. Length is the
484-
number of bytes to read (must be 1 or more), and nak is a boolean to
485-
indicate if an acknowledgement NAK should be sent back after the read.
527+
number of bytes to read (must be 1 or more).
486528
"""
487-
# Build string of commands to read bytes and respond with ACK bits.
488-
command = []
489529
for i in range(length-1):
490530
# Read a byte and send ACK.
491-
command.append('\x20\x00\x00\x13\x00\x00')
531+
self._command.append('\x20\x00\x00\x13\x00\x00')
532+
# Make sure pins are back in idle state with clock low and data high.
533+
self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
534+
self._command.append(self._ft232h.mpsse_gpio())
492535
# Read last byte and send NAK.
493-
command.append('\x20\x00\x13\x13\x00\xFF')
536+
self._command.append('\x20\x00\x00\x13\x00\xFF')
494537
# Make sure pins are back in idle state with clock low and data high.
495538
self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
496-
command.append(self._ft232h.mpsse_gpio())
497-
# Ask that read bytes are immediately returned.
498-
command.append('\x87')
499-
# Send command to the chip.
500-
self._ft232h._write(''.join(command))
501-
# Read response bytes.
502-
return bytearray(self._ft232h._poll_read(length))
539+
self._command.append(self._ft232h.mpsse_gpio())
540+
# Increase expected number of bytes.
541+
self._expected += length
503542

504-
def _i2c_write_bytes(self, data, expect_nak=False):
505-
"""Write the specified number of bytes to the chip, expecting an ACK after
506-
every byte.
507-
"""
508-
command = []
543+
def _i2c_write_bytes(self, data):
544+
"""Write the specified number of bytes to the chip."""
509545
for byte in data:
510546
# Write byte.
511-
command.append(str(bytearray((0x11, 0x00, 0x00, byte))))
547+
self._command.append(str(bytearray((0x11, 0x00, 0x00, byte))))
548+
# Read bit for ACK/NAK.
549+
self._command.append('\x22\x00')
512550
# Make sure pins are back in idle state with clock low and data high.
513551
self._ft232h.output_pins({0: GPIO.LOW, 1: GPIO.HIGH}, write=False)
514-
command.append(self._ft232h.mpsse_gpio())
515-
# Read bit for ACK/NAK.
516-
command.append('\x22\x00')
517-
# Return results immediately.
518-
command.append('\x87')
519-
# Send command to the chip.
520-
self._ft232h._write(''.join(command))
521-
# Read response bytes.
522-
response = bytearray(self._ft232h._poll_read(len(data)))
523-
# Check all responses are ACK and end is NAK.
524-
for byte in response[:-1]:
525-
if (byte & 0x01) != 0x00:
526-
raise RuntimeError('Expected I2C ACK but none was received.')
527-
if expect_nak:
528-
if (response[-1] & 0x01) != 0x01:
529-
raise RuntimeError('Expected I2C NAK but none was received.')
552+
self._command.append(self._ft232h.mpsse_gpio() * _REPEAT_DELAY)
553+
# Increase expected response bytes.
554+
self._expected += len(data)
530555

531556
def _address_byte(self, read=True):
532557
"""Return the address byte with the specified R/W bit set. If read is
@@ -537,21 +562,35 @@ def _address_byte(self, read=True):
537562
else:
538563
return self._address << 1
539564

565+
def _verify_acks(self, response):
566+
"""Check all the specified bytes have the ACK bit set. Throws a
567+
RuntimeError exception if not all the ACKs are set.
568+
"""
569+
for byte in response:
570+
if byte & 0x01 != 0x00:
571+
raise RuntimeError('Failed to find expected I2C ACK!')
572+
540573
def writeRaw8(self, value):
541574
"""Write an 8-bit value on the bus (without register)."""
542575
value = value & 0xFF
543-
self._i2c_idle()
576+
self._idle()
577+
self._transaction_start()
544578
self._i2c_start()
545579
self._i2c_write_bytes([self._address_byte(False), value])
546580
self._i2c_stop()
581+
response = self._transaction_end()
582+
self._verify_acks(response)
547583

548584
def write8(self, register, value):
549585
"""Write an 8-bit value to the specified register."""
550586
value = value & 0xFF
551-
self._i2c_idle()
587+
self._idle()
588+
self._transaction_start()
552589
self._i2c_start()
553590
self._i2c_write_bytes([self._address_byte(False), register, value])
554591
self._i2c_stop()
592+
response = self._transaction_end()
593+
self._verify_acks(response)
555594

556595
def write16(self, register, value, little_endian=True):
557596
"""Write a 16-bit value to the specified register."""
@@ -560,52 +599,74 @@ def write16(self, register, value, little_endian=True):
560599
value_high = (value >> 8) & 0xFF
561600
if not little_endian:
562601
value_low, value_high = value_high, value_low
563-
self._i2c_idle()
602+
self._idle()
603+
self._transaction_start()
564604
self._i2c_start()
565605
self._i2c_write_bytes([self._address_byte(False), register, value_low,
566606
value_high])
567607
self._i2c_stop()
608+
response = self._transaction_end()
609+
self._verify_acks(response)
568610

569611
def writeList(self, register, data):
570612
"""Write bytes to the specified register."""
571-
self._i2c_idle()
613+
self._idle()
614+
self._transaction_start()
572615
self._i2c_start()
573616
self._i2c_write_bytes([self._address_byte(False), register] + data)
574617
self._i2c_stop()
618+
response = self._transaction_end()
619+
self._verify_acks(response)
575620

576621
def readList(self, register, length):
577622
"""Read a length number of bytes from the specified register. Results
578623
will be returned as a bytearray."""
579624
if length <= 0:
580625
raise ValueError("Length must be at least 1 byte.")
581-
self._i2c_idle()
626+
self._idle()
627+
self._transaction_start()
582628
self._i2c_start()
583629
self._i2c_write_bytes([self._address_byte(True), register])
584-
response = self._i2c_read_bytes(length)
585630
self._i2c_stop()
586-
return response
631+
self._i2c_idle()
632+
self._i2c_start()
633+
self._i2c_read_bytes(length)
634+
self._i2c_stop()
635+
response = self._transaction_end()
636+
self._verify_acks(response[:-length])
637+
return response[-length:]
587638

588639
def readRaw8(self):
589640
"""Read an 8-bit value on the bus (without register)."""
641+
self._idle()
642+
self._transaction_start()
643+
self._i2c_start()
644+
self._i2c_write_bytes([self._address_byte(False)])
645+
self._i2c_stop()
590646
self._i2c_idle()
591647
self._i2c_start()
592648
self._i2c_write_bytes([self._address_byte(True)])
593-
response = self._i2c_read_bytes(1)
649+
self._i2c_read_bytes(1)
594650
self._i2c_stop()
595-
if len(response) != 1:
596-
raise RuntimeError('Expected 1 byte response but received {0} byte(s).'.format(len(response)))
597-
return response[0]
651+
response = self._transaction_end()
652+
self._verify_acks(response[:-1])
653+
return response[-1]
598654

599655
def readU8(self, register):
600656
"""Read an unsigned byte from the specified register."""
657+
self._idle()
658+
self._transaction_start()
659+
self._i2c_start()
660+
self._i2c_write_bytes([self._address_byte(False), register])
661+
self._i2c_stop()
601662
self._i2c_idle()
602663
self._i2c_start()
603-
self._i2c_write_bytes([self._address_byte(True), register])
604-
response = self._i2c_read_bytes(1)
664+
self._i2c_write_bytes([self._address_byte(True)])
665+
self._i2c_read_bytes(1)
605666
self._i2c_stop()
606-
if len(response) != 1:
607-
raise RuntimeError('Expected 1 byte response but received {0} byte(s).'.format(len(response)))
608-
return response[0]
667+
response = self._transaction_end()
668+
self._verify_acks(response[:-1])
669+
return response[-1]
609670

610671
def readS8(self, register):
611672
"""Read a signed byte from the specified register."""
@@ -618,17 +679,22 @@ def readU16(self, register, little_endian=True):
618679
"""Read an unsigned 16-bit value from the specified register, with the
619680
specified endianness (default little endian, or least significant byte
620681
first)."""
682+
self._idle()
683+
self._transaction_start()
684+
self._i2c_start()
685+
self._i2c_write_bytes([self._address_byte(False), register])
686+
self._i2c_stop()
621687
self._i2c_idle()
622688
self._i2c_start()
623-
self._i2c_write_bytes([self._address_byte(True), register])
624-
response = self._i2c_read_bytes(2)
689+
self._i2c_write_bytes([self._address_byte(True)])
690+
self._i2c_read_bytes(2)
625691
self._i2c_stop()
626-
if len(response) != 2:
627-
raise RuntimeError('Expected 2 byte response but received {0} byte(s).'.format(len(response)))
692+
response = self._transaction_end()
693+
self._verify_acks(response[:-2])
628694
if little_endian:
629-
return (response[1] << 8) | response[0]
695+
return (response[-1] << 8) | response[-2]
630696
else:
631-
return (response[0] << 8) | response[1]
697+
return (response[-2] << 8) | response[-1]
632698

633699
def readS16(self, register, little_endian=True):
634700
"""Read a signed 16-bit value from the specified register, with the

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
requires.append('spidev')
1212

1313
setup(name = 'Adafruit_GPIO',
14-
version = '0.6',
14+
version = '0.6.1',
1515
author = 'Tony DiCola',
1616
author_email = 'tdicola@adafruit.com',
1717
description = 'Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the RPi.GPIO and Adafruit_BBIO libraries.',

0 commit comments

Comments
 (0)