SPI reads all 1's (0xFF's) off of FT2232H
Hi there, we are interfacing the FT2232H with a slave device using pyftdi, using SPI for communication. We can write to the device, and it responds correctly on the logic analyzer. However, the return value from pyftdi is always 0xFFFF, as though it is not reading the MISO. We confirmed that MISO is wired to the correct pin on ADBUS.
The read sequence is
Attached is a screen capture of a typical read sequence of register 0:
- It's SPI mode 0 (write on falling edge, sample on rising edge).
- The signals shown are /CS, MISO, SCLK, and MOSI from top to bottom.
- The MOSI commands are all 0's: <0x00> (read command) <0x00> (register 0). <reading MSB> <reading LSB>
- MISO shows the correct return value of 0x0049: <writing cmd> <writing register> <0x00> (MSB) <0x49> (LSB) But we still see 0xFFFF when pyftdi returns.
Any thoughts on what could be going wrong, or other ways to debug why this is happening? Thanks!
Code example:
from pyftdi.ftdi import Ftdi
Ftdi.show_devices()
from pyftdi.spi import SpiController
import time
spi = SpiController(cs_count=1)
spi.configure(f"ftdi://ftdi:2232h/1")
slave = spi.get_port(cs=0,
freq=1e4,
mode=0)
gpio = spi.get_gpio()
# Enable all GPIO pins not used for SPI for digital output.
gpio.set_direction(0xfff0, 0xfff0)
# Reset chip
reset_gpio_bitfield = 0x0000
gpio.write(reset_gpio_bitfield)
end_reset_gpio_bitfield = 0x1000
gpio.write(end_reset_gpio_bitfield)
# Read addr 0.
cmd_buf = b'\x00\x00'
print(f'sending: {cmd_buf}')
read_buf = slave.exchange(cmd_buf,
readlen=2,
duplex=False)
print(f'duplex False: {read_buf}')
cmd_buf = b'\x00\x00\x00\x00'
print(f'sending: {cmd_buf}')
read_buf = slave.exchange(cmd_buf,
readlen=4,
duplex=True)
print(f'duplex True: {read_buf}')
We confirmed that MISO is wired to the correct pin on ADBUS. Do you mean
AD2?
Things you can do:
- check the signals as analog values, to be sure they match the expected levels. What your digital analyzer discretizes may not be what the FTDI chip actually "sees", both in voltage and time domains. The frequency seems quite low, but it is always better to close this door first
- simplify your code: you do not need to configure the GPIO pins if you do not plan to use GPIOs for now. See the
read_jedec_idexample. For debugging this issue, it should be as simple asport = SpiController().get_port(0, freq=1e4, mode=0) out = port.exchange([0, 0], 2) print(out) - try to use alternative SPI modes
- Try to use GPIO mode (not SPI mode) to ensure that AD2 reads
0when this pin is tied to the ground.
We are having a similar problem, using similar code, although we must admit we have not yet probed the device. We are using an FT2232 Mini Module to try and communicate with two transceiver devices.
from pyftdi.ftdi import Ftdi
from pyftdi.spi import SpiController, SpiPort
# devices used for configuration
device_a = "ftdi://ftdi:2232/1"
device_b = "ftdi://ftdi:2232/2"
# Documentation for device states 20 MHz is max
# 20 MHz will result in 25% error of 15 MHz for max, so just using 15 MHz
max_freq = 15e6
freq = 10e6
spi_a_cntrl = SpiController()
spi_b_cntrl = SpiController()
spi_a_cntrl.configure(device_a, frequency=max_freq, debug=True)
spi_b_cntrl.configure(device_b, frequency=max_freq, debug=True)
receiver_a = spi_a_cntrl.get_port(cs=0)
receiver_a.set_frequency(freq)
receiver_a.set_mode(0)
receiver_b = spi_b_cntrl.get_port(cs=0)
receiver_b.set_frequency(freq)
receiver_b.set_mode(0)
cmd = b'\x00\x00\x00\x00\x00\x00' # 6 bytes
spi_1_response = receiver_a.exchange(cmd, duplex=True)
spi_2_response = receiver_b.exchange(cmd, duplex=True)
print(spi_1_response)
print(spi_2_response)
- We are getting a six byte response on each device of all F's.
- Documentation for these devices state SPI mode 0, and we are not using any GPIO pins.
- Using a debugger, pyftdi is reporting that the device is configured correctly.
I know this comment does not provide new or helpful information to the original post, but thought we should share as the issue is very similar to our own. Could this potentially be a bug with pyftdi?
Thanks for the help in brainstorming ideas. It turns out that we needed to initialize the second MPSSE interface in order for the receive buffer to start working. Adding this code (and then doing nothing with the second interface afterward) solved our problem.
# Open up the 2nd MPSSE interface on the FTDI chip.
# We've found experimentally that the 1st MPSSE interface will not
# work until the 2nd is initialized.
# We won't do anything with this interface after this.
b = Ftdi.create_from_url("ftdi://ftdi:2232h/2")
b.open_mpsse_from_url("ftdi://ftdi:2232h/2", 0xFFFB, 0xFFFB, 1e4, 127)
Hello, as we are still having issues, I am curious as to why this solved your problem. Why did you go the route of using .create_from_url() and .open_mpsse_from_url() methods? Shouldn't configuring another SpiController work just as well, as we have done above, as it calls the .open_mpsse_from_url() command?
We are always configuring both SpiControllers on the device, but still not getting anything but F's back from the receive buffer.
Would appreciate any help on this.
Unfortunately, we just stumbled across the "solution". After we initially couldn't get pyftdi to work, we reimplemented the protocol in C using the FT2X libraries from FTDI directly. When we tested that, we were getting the same issues (everything else works, but we read all FFs). So we were pretty sure this wasn't an issue with pyftdi (we didn't look under the hood, but assumed they use the same or similar drivers from FTDI), but instead an FTDI initialization issue.
After scouring all the FTDI literature and examples, we were just methodically measuring and initializing as much of the part as we could when we stumbled upon this in the C version. Then we back-ported it to pyftdi, and it worked there as well. There is no significance to the methods that we used to initialize it. I believe that it will work with the initialization method you use, but we haven't tested that version yet to say it conclusively.
We ended up making no modifications to the original pyftdi code above except adding those lines in my later post. We needed the GPIO for some discrete signals for reset, standby, SPI control selection, etc, so they had to stay. Luckily we found this, since we were 1-2 days away from throwing away the FTDI part and instead driving the chip directly from an FGPA, which would have been a bit more of a hassle in writing our own drivers - not terrible on the SPI protocol side, but a bit of a chore to manage all the initialization, buffering, comms with a PC, etc.
As to your problem - I don't have too much more insight, except noting that it could be a problem on the transceiver side. Did you hook up a logic analyzer to see if your system under test is driving the MISO line? If you have a pull-up and the chip is not driving the line, then you would see all 1's, which would be the correct behavior from the FTDI device, and you'd have to start debugging why the SUT is not responding.
Also, thanks again @eblot for your thoughtful debug steps! Great library!
Also, thanks again @eblot for your thoughtful debug steps! Great library!
Thanks @nickhdc33
Re-opening this issue for now, as I do not understand why this workaround is needed...
Following the same steps as @nickhdc33, I was unable to get a response (other than all F's), so I am not sure it is a universal workaround/fix.
BTW, what are the reference(s) of the SPI slave devices?
With the FTDI2232 Mini Module, we are using a 10 MHz reference clock for both the slave devices. Is this what you are asking?
Not really, the type/kind of SPI device you're trying to communicate with: FTDI being the master/host, what are the (slave) devices?
A couple of transceiver modules, found here.
It turns out that the device we linked does not have wiring as specified in the pyftdi documentation. We only realized this once the manufacturer finally gave us schematics for the device. To get around this, we had to use GPIO pins to control which slave device to communicate with, and we were finally able to get status back. Sorry for any confusion on our part. We thought this issue was very similar to our own.
Thanks for the help in brainstorming ideas. It turns out that we needed to initialize the second MPSSE interface in order for the receive buffer to start working. Adding this code (and then doing nothing with the second interface afterward) solved our problem.
# Open up the 2nd MPSSE interface on the FTDI chip. # We've found experimentally that the 1st MPSSE interface will not # work until the 2nd is initialized. # We won't do anything with this interface after this. b = Ftdi.create_from_url("ftdi://ftdi:2232h/2") b.open_mpsse_from_url("ftdi://ftdi:2232h/2", 0xFFFB, 0xFFFB, 1e4, 127)
Thanks, I can confirm this also fixed the issue of receiving all 1's for me. I was also using a FT2232H and probing the SPI lines, the MISO line was always actively pulled high by the FTDI device. Using this code to initialize the second interface, I can now also read data from the MISO line.