I²C via USB on OS X using FT232H
Many sensor chips use the I²C bus, sometimes called TWI or SMBus, for communication. Most microcontrollers support I²C also natively and if not I can be implemented easily in software. Connecting I²C devices to a PC is much more difficult as soldering them onto the mainboard (where a SMBus can be found usually) is not a viable solution. Up to now I usually used a Atmel AVR microcontroller in teamwork with a FT232R (a standard USB↔RS232 converter).
The USB 2.0 successor of the FT232R, the FT232H, has a Multi-Protocol Synchronous Serial Engine (MPSSE) included which is designed to support serial interfaces such as I²C, SPI or JTAG at speeds up to 30 Mbps. This sounded to me as an interesting option to test I²C and SPI devices directly from my PC.
In this post, I want to describe how I connected the FT232H using a UM232H development module to a LM75 temperature sensor and read out the temperature on OS X. I used only the D2XX drivers as the LibMPSSE-I2C is only available for Windows and Linux.
Connecting of the setup is rather easy: To use the UM232H in host-powered mode, we have to connect VIO and 3V3 and USB and 5V0. The LM75 is powered by the 3V3 line and needs also a connection to GND. The I²C clock line is on the LM75 on the SCL pin and on the UM232H on the AD0. The I²C data line is on the LM75 on the SDA pin and on the UM232 on the AD1 and AD2 pin (AD1 handles the output of the data and AD2 the input). Both, the I²C clock line and the I²C data line need a pull-up to 3V3. A value between 1kΩ and 10kΩ is fine for low frequencies (< 100 kHz). The address lines of the LM75 (A0,A1,A2) are all set to 3V3 to this example, resulting in an address of 0x9E for the LM75.
On the software side, we need to install the D2XX drivers. Download them and follow the instructions in the ReadMe file to install them. Download my C program lm75.c and compile it with
gcc -lftd2xx -o lm75 lm75.c
If everything worked, a simple
./lm75
should list all FTDI devices found:
Device 0 Serial Number - FTUBIQVH
Now we can adress the device by its serial number and query it with
./lm75 FTUBIQVH
and get the temperature of the LM75:
Temperature: 26.0 C
If the query fails, the problem is usually that the virtual comport driver (VCP) is already occupying the device. Try to unload the driver with
sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext
The code itself should be rather self-explanatory if your familiar with I²C. If not, here’s a short overview: The first lines setup the FT232H in MPSSE mode. A list of all the MPSSE commands understood by the FT232H can be found here. The we send a I²C start condition followed by the address of the LM75 with the write bit set and the adress 0×00 (The temperature register, see LM75 datasheet for details). Next is a I²C restart condition followed by the address of the LM75 with the read bit set. Now we can read 2 bytes of data representing the temperature. To finalize the transaction we send an I²C stop condition.
This gets maybe a little clearer when we look at the transaction on the oscilloscope:
The yellow curve is the SCL line and the green curve shows the SDA line. In the upper panel we see the whole transaction consisting of 4 parts: Addressing the LM75 the first time with the write bit set, sending the address 0×00, addressing the LM75 the second time with the read bit set and the 2 data bytes sent by the LM75. The lower panal is a zoom on the first block were the address the LM75 for the first time. We can see the I²C start condition where the SDA goes low while SCL is high and afterwards the clock changes nine times. The first eight cycles transmit the address of the LM75 which is 0x9E or 0b10011110. The last bit is the ACK by the slave.
If you like this article, feel free to flattr it:
Oct23


2 Responses to “I²C via USB on OS X using FT232H”
Hi Tobias,
I’m actually working on I2C application with FT232H chip, and I have found your work very very interesting, above all because using the official MPSSE-I2C lib from FTDI, I get poor results, so I need to rewrite the low level fuctions from scrath.
I have a question regarding your reading procedure: why are you using this strange for loop?
for (i = 0; i < 2; ++i) {outputBuffer[outputSize++] = MSB_FALLING_EDGE_CLOCK_BYTE_IN;
// data length of 0x0000 means 1 byte
outputBuffer[outputSize++] = 0x00;
outputBuffer[outputSize++] = 0x00;
outputBuffer[outputSize++] = MSB_FALLING_EDGE_CLOCK_BIT_OUT;
// 0x00 means scan 1 bit
outputBuffer[outputSize++] = 0x00;
if (i < 2-1) {
outputBuffer[outputSize++] = 0x00;
} else {
outputBuffer[outputSize++] = 0x01;
}
}
That make you to go non with SCL for a byte more before the stop.. even if it works (but i cannnot explain the reason..) why don't to simply use the following:
outputBuffer[outputSize++] = MSB_FALLING_EDGE_CLOCK_BYTE_IN;// data length of 0x0000 means 1 byte
outputBuffer[outputSize++] = 0x00;
outputBuffer[outputSize++] = 0x00;
outputBuffer[outputSize++] = MSB_RISING_EDGE_CLOCK_BIT_IN;
// 0x00 means scan 1 bit
outputBuffer[outputSize++] = 0x00;
Thank you in advance,
Graziano
@Graziano
The LM75 returns 2 Bytes on a read request, so the loop is run through twice. The first iteration it reads a byte and sends back an ACK. In the second iteration it reads a byte and sends than a NACK so that the LM75 should not send any additional data.