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 0x00 (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:

I²C transaction

I²C transaction

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 0x00, 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.

This entry was posted in Hardware and tagged , , , by twam. Bookmark the permalink.

About the author

My name is Tobias Müller. I'm interested in com­puters, physics, elec­tronics and photo­graphy. more …

36 thoughts on “I²C via USB on OS X using FT232H

  1. 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

  2. @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.

  3. Hi,

    I got a question. How do you get this work for a windows user? I understand that you use Apple
    for it. But i dont get it how you get this to work for a windows pc. Also, which program do you use
    to compile the program and to run it. I work with codeblocks, but i dont know if it works.

    Gr,
    Sergio

  4. Hi,

    Thanks for the quick reply. I saw the examples indeed, but they didn't work for me.
    If i can ask. How did you upload your program? The applications notes on the site all use
    FT2232. Im not sure if this is so different then the FT232. I also tried to use the D2xx drivers.
    But I understand that it is only for the connection with your pc. It does it all automatic. My biggest
    problem is, how do I get my program in the UM232H and how can I get it to communicate trough a I2C
    connection.

    Gr,
    Sergio

  5. Then with program did you do that? You had to write the c code in some program, right?
    I mean you didn't write it on a notepad of word document.

  6. Hi,

    I still got some trouble with the program. He tells me that FT_ListDevices is a undefined reference. So he cant find that function. You got a solution for that?

  7. Hi,

    I got it working a little bit, but I don's get the temperature.
    I got the line:
    "fail to get ACK when send control byte 1 [Program Section]"
    I don't know how to get it done.

    Also I still got the temperature of 0.0C
    Maybe the IC is broken, I will test a new one and let you know how it went.

  8. I tried switching them etc. But still same result.
    The file you made is a c file right, not c++ or c#?
    If I compile the c file I got an error in the ftd2xx.h header.
    How can I get an error in a header that is from FTDI.
    I used the exact file you did.

  9. Hi,

    I got it working, but still with some problems.
    First I got an temperature of 255.5 degrees.
    And when I want to send the second control byte 2 I didn't get an ACK.
    It's on this part:

    ftStatus = FT_Read(ftHandle, InputBuffer, 1, &dwNumBytesRead);
    if ((ftStatus != FT_OK) || (dwNumBytesRead == 0))
    {
    printf("\n\nfail to get ACK when send control byte 1 [Program Section] \n");
    return -1;
    }
    else
    {
    if ( ((InputBuffer[0] & BYTE('\x1')) != BYTE('\x0')) )
    {
    printf("\n\nfail to get ACK when send control byte 2 [Program Section] \n");
    return -1;
    }
    }
    OutputBuffer[dwNumBytesToSend++] = '\x80';
    OutputBuffer[dwNumBytesToSend++] = '\x02';
    OutputBuffer[dwNumBytesToSend++] = '\x03';
    return 0;

    When he is trying to send the second ACK, he don't get one back.
    I am not sure if that is the reason that the temperature is that high, or that the printf of the temperature is just wrong.

    Thanks for your time.

    PS
    My program is in C++, but there are no big differents.

  10. when i compile your file lm75.c with gcc compiler it gives fatal error (ftd2xx.h No such file or directory). So pls give full compile method

  11. Hay,

    I got it working, but I want to use this example for something else.
    I want to read out 4 bytes and after the 4th byte I want to send an NACK. The rest is just ACK.
    How do I do that? In your example your reading out 2 bytes right? How do you that with 4 bytes?

  12. Well I want to read 4 bytes so I adjust i < 2 to i < 4.
    And I adjust the ftRead(....., ......, 2, ......) to ftRead(......., ........, 4, .........)
    But it doesn't work. I want to read from the slave the bytes:
    byte 1 = 0
    byte 2 = 0
    byte 3 = 0
    byte 4 = 4
    But i get in the InputBuffer after the read function this:
    InputBuffer[0] = 0
    InputBuffer[1] = 1 (NACK???)
    InputBuffer[2] = 250
    InputBuffer[3] = 0
    InputBuffer[4] = 255
    InputBuffer[5] = 255
    InputBuffer[6] = 0

    I don't get it. Here is my code:
    HighSpeedSetI2CStart();
    SendByteAndCheckACK(0x28 | 0x01);

    OutputBuffer[dwNumBytesToSend++] = 0x80;
    OutputBuffer[dwNumBytesToSend++] = 0x00;
    OutputBuffer[dwNumBytesToSend++] = 0x11;

    for(int p =0; p<4; ++p)
    {
    OutputBuffer[dwNumBytesToSend++] = MSB_FALLING_EDGE_CLOCK_BYTE_IN;
    OutputBuffer[dwNumBytesToSend++] = 0x00;
    OutputBuffer[dwNumBytesToSend++] = 0x00;
    OutputBuffer[dwNumBytesToSend++] = MSB_RISING_EDGE_CLOCK_BIT_IN;
    OutputBuffer[dwNumBytesToSend++] = 0x00;

    if(p < 4-1)
    {
    OutputBuffer[dwNumBytesToSend++] = 0x00;
    }
    else
    {
    OutputBuffer[dwNumBytesToSend++] = 0x01;
    }

    }

    OutputBuffer[dwNumBytesToSend++] = 0x87;

    ftStatus = FT_Write(ftHandle, OutputBuffer, dwNumBytesToSend, &dwNumBytesSent);
    dwNumBytesToSend = 0;

    ftStatus = FT_Read(ftHandle, InputBuffer, 6, &dwNumBytesRead);
    int stepping_mode = InputBuffer[0]; //<< 24 | InputBuffer[1] << 16 | InputBuffer[2] << 8 | InputBuffer[3];

    printf("\nStepping mode = %d\n", InputBuffer[0]);
    printf("\nStepping mode = %d\n", InputBuffer[1]);
    printf("\nStepping mode = %d\n", InputBuffer[2]);
    printf("\nStepping mode = %d\n", InputBuffer[3]);
    printf("\nStepping mode = %d\n", InputBuffer[4]);
    printf("\nStepping mode = %d\n", InputBuffer[5]);
    printf("\nStepping mode = %d\n", InputBuffer[6]);
    printf("\nStepping mode = %d\n", InputBuffer[7]);

    OutputBuffer[dwNumBytesToSend++] = 0x80;
    OutputBuffer[dwNumBytesToSend++] = 0x02;
    OutputBuffer[dwNumBytesToSend++] = 0x13;

    HighSpeedSetI2CStop();

  13. @Sergio
    I don't know the I²C Slave Chip you're using, but usually you don't have to send more stuff to the chip, just to receive more data. So you wouldn't need to adjust the for loop. But this depends on your chip.

  14. The chip I use is an Microcontroller: STMF103
    In the datasheet for i2c the Microcontroller puts the byte in an DR register. If the Master (UM232H) reads this register, the register is cleared and an other byte of data is put here from the Microcontroller.
    I tested the settings of the Microcontroller for reading with an Arduino, and there it works. The Arduino reads the byte from the Microcontroller and stores it in an buffer. After the 4th byte the Arduino sends an NACK bit to the microcontroller, so it knows its done.
    So the max amount of databytes to read is always 4. I tested your program with the Microcontroller as Slavedevice. When I put the 4th Byte directly in the DR register, I can read this byte no problem. In the register there is an 4 then, and in mine InputBuffer[0] I see the 4. In my Inputbuffer[1] I see an 1, what means an NACK bit is send from the UM232H.
    But what I really want is this:
    The UM232H has to read 4 times to the Microcontroller. So here is what it should do:
    1 time:
    UM232H reads the DR register (DR register = 0) -> InputBuffer[0] = 0
    InputBuffer[1] = 0 (ACK bit)
    2:
    UM232H reads the DR register (DR register = 0) -> Inputbuffer[2] = 0
    InputBuffer[3] = 0 (ACK bit)
    3:
    UM232H reads the DR register (DR register = 0) -> InputBuffer[4] = 0
    InputBuffer[5] = 0 (ACK bit)
    4:
    UM232H reads the DR register (DR register = 4) -> InputBuffer[6] = 4
    InputBuffer[7] = 1 (NACK bit)
    Then the cycle is done.
    I can't get it right. I don't know what I'm doing wrong. Writing to the Microcontroller is no problem, but reading sucks. Can you help?

    Ty

    • Hello Sergio. I know what your running into cause I've hit the same walls. I use VB.NET and the FTD2XX_NET wrapper. It works great. The first thing I noticed is that you MUST have a delay between your I2C_Stop condition and your I2C_Start. I use system.threading.thread.sleep(50) and it allows for enough time for devices to settle. The biggest issue I see here is that you are not flipping your MPSSE IOs to be an input so that you CAN receive 1 BIT. Your pin state is incorrect at that time cause they are set as CLK=Out, DO=Out.

      I found the best way is to fill your buffer with the start condition first(I use a module and call it from my forms...):

      'top of module
      Public FT_STATUS As FTDI.FT_STATUS = FTDI.FT_STATUS.FT_IO_ERROR
      Dim FT2232H As FTDI = New FTDI
      Dim FTDIDeviceList() As FTDI.FT_DEVICE_INFO_NODE = {New FTDI.FT_DEVICE_INFO_NODE}
      Dim fthandle As Integer = 0
      Dim Current_BitMode As Byte = 0
      Dim numDevices As UInt32 = 0
      Dim OutputBuffer(1024) As Byte
      Dim InputBuffer(1024) As Byte
      Dim NumBytesToSend As UInteger = 0
      Dim NumBytesSent As UInteger = 0
      Dim NumBytesRead As UInteger = 0
      Dim NumInputBuffer As UInteger = 0
      Dim ClockDivisor As Integer = &H95 '0x0095 = 149. Freq is 60/((1+0x0095)*2 = 200Khz

      Public Sub I2C_SetI2CStart()
      'Send this command set 30 times to ensure pins stay in state long enough to acheive 10,000ns == 0.01ms
      For i As Integer = 0 To 30 Step 1
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H3
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1
      Next

      'Send this command set 30 times to ensure pins stay in state long enough to acheive 10,000ns == 0.01ms
      For i As Integer = 0 To 30 Step 1
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H1
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1
      Next

      'Set default pins to Output mode
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1

      End Sub

      Then use a function like:
      Public Function I2C_SendByteGetACK(ByVal DataByte As Byte) As Boolean

      'Preset buffer to ouput mode
      ' [MPSSE Set data bits low Byte(==SK,DO,DI,CS,GPIOL1...)
      ' + 0x00 = All other pins Low-GND(0)
      ' + 0x13 = SK+DO+GPIOL1(Output),all other pins(Input)]
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1

      'Add commands to buffer.
      '[MPSSE Send byte command + 2 bytes for count(0x0000=1byte to send) + DataByte to send]
      OutputBuffer(NumBytesToSend) = MSB_FALLING_EDGE_CLOCK_BYTE_OUT
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = DataByte
      NumBytesToSend += 1

      'Add another command to buffer. Need to check for ACK
      'Set Pin states for input mode, send get bit command.
      ' [MPSSE Set data bits low Byte(==SK,DO,DI,CS,GPIOL1...)
      ' + 0x00 = All pins Low-GND(0)
      ' + 0x11 = SK+GPIOL1(Output),all other pins(Input)]
      'Check for ACK
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H11
      NumBytesToSend += 1

      'MPSSE Get 1 Bit command. 0x00 = 1Bit to get.
      OutputBuffer(NumBytesToSend) = MSB_RISING_EDGE_CLOCK_BIT_IN
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1

      'MPSSE command. Process and return data immediately.
      OutputBuffer(NumBytesToSend) = &H87
      NumBytesToSend += 1

      'Buffer is complete with all commands. Ready to fire down the wire.
      FT_STATUS = WriteFTDIAsBytes()
      If FT_STATUS FTDI.FT_STATUS.FT_OK Then
      Return False
      End If

      Do While NumInputBuffer = 0
      FT_STATUS = GetRXBytesInputBuffer()
      Loop

      'Read wire. Looking for ACK.
      If ReadFTDIAsBytes(1) = False Then
      'Should have got 1 bit for ACK...
      Return False
      End If

      If (InputBuffer(0) And &H1) &H0 Then
      'ACK bit is 0! should be 1...
      Return False
      End If

      ClearBuffers()
      Return True
      End Function

      Here are 2 more functions I made. I start a loop and call I2C_ReadByteSendACK() until the loop reaches the last iteration, then I call I2C_ReadByteSendNACK(). Otherwise it makes it difficult for the I2C_Stop condition to be recognized. The EEPROM is trying to pull the lines to ACK/NACK while your trying to say I2C_STOP......you will loose and the EEPROM will clock stretch until the master stops trying to toggle IOs.

      Public Function I2C_ReadByteSendACK() As Byte
      Dim returnByte As Byte = 0

      'Set Pins for Input mode. Set command to read Byte.
      ' [MPSSE Set data bits low Byte(==SK,DO,DI,CS,GPIOL1...)
      ' + 0x00 = All pins Low-GND(0)
      ' + 0x11 = SK+GPIOL1(Output),all other pins(Input)]
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H11
      NumBytesToSend += 1

      'MPSSE Command get byte.
      ' [0x0000 == Get 1 Byte] Send Low byte, then High byte for command.
      OutputBuffer(NumBytesToSend) = MSB_FALLING_EDGE_CLOCK_BYTE_IN
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1

      'We received our byte, lets ACK to the EEPROM.
      'Setup Pins for Output mode.
      'SCL=OUTPUT[________]low
      'SDA=OUTPUT[________]low
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1

      'MPSSE Send 1 Bit command. 0x00 = 1Bit to send, 0x00 = The bit value to send.
      'ACK = 0, NACK = 1. If we wanted to NACK we would send [Command(Send 1 BIT), 0x00(Num of Bits(0x00=1)), 0x80(0b10000000)] Command sends MSB FIRST!!! Hence 0x80....
      '(Command clocks MSB FIRST of bit value. so 0x00 = 0b00000000. I'm sending 1 bit. SDO will = 0/0v/LOW == I2C ACK)
      OutputBuffer(NumBytesToSend) = MSB_FALLING_EDGE_CLOCK_BIT_OUT
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1

      'MPSSE command. Process and return data immediately.
      OutputBuffer(NumBytesToSend) = &H87
      NumBytesToSend += 1

      'Send buffer and see if we get a byte.
      FT_STATUS = WriteFTDIAsBytes()
      If FT_STATUS FTDI.FT_STATUS.FT_OK Then
      Return False
      End If

      ClearBuffers()
      Do While NumInputBuffer = 0
      FT_STATUS = GetRXBytesInputBuffer()
      Loop

      If ReadFTDIAsBytes(1) = False Then
      'Should have got 1 data byte, no ack...
      Return False
      End If

      returnByte = InputBuffer(0)
      ClearBuffers()
      Return returnByte

      End Function

      and.......................
      Public Function I2C_ReadByteSendNACK() As Byte
      Dim returnByte As Byte = 0

      'Set Pins for Input mode. Set command to read Byte.
      ' [MPSSE Set data bits low Byte(==SK,DO,DI,CS,GPIOL1...)
      ' + 0x00 = All pins Low-GND(0)[________]
      ' + 0x11 = SK+GPIOL1(Output),all other pins(Input)]
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H11
      NumBytesToSend += 1

      'MPSSE Command get byte.
      ' [0x0000 == Get 1 Byte] Send Low byte, then High byte for command.
      OutputBuffer(NumBytesToSend) = MSB_FALLING_EDGE_CLOCK_BYTE_IN
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1

      'We received our byte, lets NACK to the EEPROM. Dont send any more data...
      'Setup Pins for Output mode.
      'SCL=OUTPUT[________]low
      'SDA=OUTPUT[________]low
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H13
      NumBytesToSend += 1

      'MPSSE Send 1 Bit command. 0x00 = 1Bit to send, 0x80 = The bit value to send.
      'ACK = 0, NACK = 1. If we wanted to NACK we would send [Command(Send 1 BIT), 0x00(Num of Bits(0x00=1)), 0x80(0b10000000)] Command sends MSB FIRST!!! Hence 0x80....
      '(Command clocks MSB FIRST of bit value. so 0x80 = 0b10000000. I'm sending 1 bit. SDO will = 1/3.3v/HIGH == I2C NACK)
      OutputBuffer(NumBytesToSend) = MSB_FALLING_EDGE_CLOCK_BIT_OUT
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H0
      NumBytesToSend += 1
      OutputBuffer(NumBytesToSend) = &H80
      NumBytesToSend += 1

      'MPSSE command. Process and return data immediately.
      OutputBuffer(NumBytesToSend) = &H87
      NumBytesToSend += 1

      'Send buffer and check if we get a byte.
      FT_STATUS = WriteFTDIAsBytes()
      If FT_STATUS FTDI.FT_STATUS.FT_OK Then
      Return False
      End If

      ClearBuffers()
      Do While NumInputBuffer = 0
      FT_STATUS = GetRXBytesInputBuffer()
      Loop

      If ReadFTDIAsBytes(1) = False Then
      'Should have got 1 data byte, no ack...
      Return False
      End If
      returnByte = InputBuffer(0)
      ClearBuffers()
      Return returnByte
      End Function

      Hope this helps. I will be publishing my tool when I have completed my NAND reader section....Open source!!!

  15. @Sergio
    As far as I know the FT_Read command does not write 0/1 for ACK/NACK in the input buffers.

    Besides that I could be that if you want to read the same register over and over again that you might have to send the address of the register before each read command.

  16. I tried to put in an loop to run 4 times. But even that doesn't work.
    When I use your loop, that reads 2 bytes, and I put the 4 directly in the DR register.
    The UM232H reads the 4 from the DR register. So the Inputbuffer[0] = 4. But my Inputbuffer[1] = 9 ?
    I dont get it.
    _[]_[]_[]_[]_[]_[]_[]_[]_[]_
    _____________[]______[]_
    It's like this. If you think the 9th puls away the data = 0x04 what is right. But if you count the 9th puls with the data, you get 0x09.

  17. Hello Twam,
    First of all great post.
    and secondly can you guide me how can I send multiple byte to I2c slave device. I am using CD1316l tunner as my slave i2c device which requires 1 address byte and 4 data bytes in order to fully program it. I am also using UM232H-B(FT232H) like you.

    • If you want to read 4 bytes instead of 2 as in the example you just have to extend the for loop to 4 cycles. If you want to sent more data you should be able to call i2c_send_byte_and_check_ack() more times.

      • ya exactly thats what I was thinking.is there no way that I can send more than one byte at a time? because
        in
        http://www.ftdichip.com/Support/Documents/AppNotes/AN_108_Command_Processor_for_MPSSE_and_MCU_Host_Bus_Emulation_Modes.pdf

        they say
        3.4.1
        Clock Data Bytes Out on +ve clock edge LSB first (no read
        )
        Use if CLK starts at '1'
        0x18,
        LengthL,
        LengthH,
        Byte1
        ..
        Byte65536 (max)

        This will clock out bytes on TDI/DO from 1 to 65536 depending on
        the Length bytes. A length of 0x0000 will do 1 byte and a length of 0xffff will do 65536 bytes. The data is sent LSB first. Bit 0 of the first byte is placed on TDI/DO then the CLK pin is clocked. The data will change to the next bit on the rising edge of the CLK pin.No data is clocked into the device on TDO/DI

        And I am not using the default raspberry pi I2c bus just to make my slave device more portable that can also be used with my laptop. directly.

        • Then you could adjust the function i2c_send_byte_and_check_ack itself to send more data like you explained from the datasheet.

  18. I am trying to use this application to get the data from HMC5883L using FT2232H. I have modified the commands as per the HMC5883L. Following are the write set of commands:
    i2c_start();
    i2c_send_byte_and_check_ack(0x3C);
    i2c_send_byte_and_check_ack(0x02);
    i2c_send_byte_and_check_ack(0x01);
    i2c_start();
    i2c_send_byte_and_check_ack(0x3D);

    The read part as the following:

    for (i = 0; i < 1; ++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;
    }
    }

    ftStatus = FT_Read(ftHandle, inputBuffer, 1, &inputRead);

    I am first trying to read just one register, that is, 0x02. But when I run this code the output I get is -16, which is not the same as I have written.

    I am relatively new to the I2c protocol and FTDI chips, so any comments will be helpful. Please let me know your inputs.

    • Your read part is incomplete. For every read command you first have to write the register to read, then do a i2c_restart and then start reading your data. Also the code as posted configures the outputBuffer and then you only call the FT_Read function with the input buffer. IIRC you still need send the output buffer to the FTDI lib as this tells the chip what to do next. Try to adapt to line 258 - 301

      • Thanks a lot for your comment, it really helped. Now I am able to read the correct value from one register, that is, 0x02 (also 0x00). But one thing I noticed while debugging this was when I put a breakpoint and single step at each stage, I get a different end result. This may be due to change in timing, is that correct?

        Now, I want to extend this to get data continuously from HMC5883L. The datasheet of HMC5883L specifies the following steps:
        1. Send 0x3c 0x00 0x70 command
        2. Send 0x3c 0x01 0xA0 command
        3. Send 0x3c 0x02 0x00 to run in continuous mode
        4. In a loop keep sending 0x3D 0x06, to read 6 bytes
        5. Write 0x3C 0x03

        I have modified your code as per this. But instead of running in a loop, I thought of reading the data at least once first. But with this I am getting some junk data
        Following is the code (I could not find an attachment option here, so pasted):

        #include
        #include
        #include
        #include
        #include

        const char MSB_FALLING_EDGE_CLOCK_BYTE_IN = 0x20;
        const char MSB_FALLING_EDGE_CLOCK_BYTE_OUT = 0x11;
        const char MSB_FALLING_EDGE_CLOCK_BIT_OUT = 0x13;
        const char MSB_RISING_EDGE_CLOCK_BIT_IN = 0x22;

        #define MAX_DEVICES 5

        const char LM75 = 0x9E;

        FT_STATUS ftStatus;
        FT_HANDLE ftHandle;

        char inputBuffer[1024] = {0};
        char outputBuffer[1024];
        unsigned int outputSize;
        unsigned int outputSent;
        unsigned int inputSize;
        unsigned int inputRead;

        int scan_devices() {
        char* pcBufLD[MAX_DEVICES + 1];
        char cBufLD[MAX_DEVICES][64];
        int iNumDevs = 0;
        int i;

        for(i = 0; i < MAX_DEVICES; i++) {
        pcBufLD[i] = cBufLD[i];
        }
        pcBufLD[MAX_DEVICES] = NULL;

        FT_STATUS ftStatus = FT_ListDevices(pcBufLD, &iNumDevs, FT_LIST_ALL | FT_OPEN_BY_SERIAL_NUMBER);

        if (ftStatus != FT_OK) {
        fprintf(stderr, "Error: FT_ListDevices(%d)\n", ftStatus);
        return 1;
        }

        for (i = 0; ( (i <MAX_DEVICES) && (i < iNumDevs) ); i++) {
        printf("Device %d Serial Number - %s\n", i, cBufLD[i]);
        }

        return 0;
        }

        void i2c_start() {
        int i;

        for (i = 0; i < 4; ++i) {
        // SDA high, SCL high
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x03;
        outputBuffer[outputSize++] = 0x03;
        }

        for (i = 0; i < 4; ++i) {
        // SDA low, SCL high
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x01;
        outputBuffer[outputSize++] = 0x03;
        }

        // SDA low, SCL low
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x03;
        }

        void i2c_stop() {
        int i;

        for (i = 0; i < 4; ++i) {
        // SDA low, SCL high
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x01;
        outputBuffer[outputSize++] = 0x03;
        }

        for (i = 0; i < 4; ++i) {
        // SDA high, SCL high
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x03;
        outputBuffer[outputSize++] = 0x03;
        }

        // SDA tristate, SCL tristate
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x00;
        }

        int i2c_send_byte_and_check_ack(char data) {
        // clock data byte on clock edge MSB first
        outputBuffer[outputSize++] = MSB_FALLING_EDGE_CLOCK_BYTE_OUT;
        // data length of 0x0000 means 1 byte
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = data;

        // SDA tristate, SCL low
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x01;

        outputBuffer[outputSize++] = MSB_RISING_EDGE_CLOCK_BIT_IN;
        // length of 0x00 means scan 1 bit
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x87;

        ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
        outputSize = 0;

        ftStatus = FT_Read(ftHandle, inputBuffer, 1, &inputRead);

        if ((ftStatus != FT_OK) || (inputRead == 0)) {
        return -1;
        } else if (((inputBuffer[0] & 0x01) != 0x00)) {
        return -1;
        }

        // SDA high, SCL low
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x02;
        outputBuffer[outputSize++] = 0x03;

        return 0;
        }

        int main(int argc, char** argv) {
        int i;

        // print scan if no parameter is given
        if (argc 0))
        FT_Read(ftHandle, &inputBuffer, inputSize, &inputRead);

        //Set USB request transfer size
        ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);
        //Disable event and error characters
        ftStatus |= FT_SetChars(ftHandle, 0, 0, 0, 0);
        //Sets the read and write timeouts in milliseconds for the FT2232H
        ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000);
        //Set the latency timer
        ftStatus |= FT_SetLatencyTimer(ftHandle, 16);
        //Reset controller
        ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00);

        // enable MPSEE mode
        ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02);

        if (ftStatus != FT_OK) {
        fprintf(stderr, "Error occured: %u\n", ftStatus);
        }

        // Disables the clk divide by 5 to allow for a 60MHz master clock.
        outputBuffer[0] = '\x8A';
        // Disable adaptive clocking
        outputBuffer[1] = '\x97';
        // Enables 3 phase data clocking. Used by I2C interfaces to allow data on both clock edges.
        outputBuffer[2] = '\x8C';
        // sent of commands
        ftStatus = FT_Write(ftHandle, outputBuffer, 3, &outputSent);

        /*
        ADBUS0 TCK/SK ---> SCL
        ADBUS1 TDI/DO -+-> SDA
        ADBUS2 TDO/DI -+
        ADBUS3 TMS/CS
        ADBUSS GPIOL0
        ADBUS5 GPIOL1
        ADBUS6 GPIOl2
        ADBUS7 GPIOL3
        */

        // Set values and directions of lower 8 pins (ADBUS7-0)
        outputBuffer[0] = 0x80;
        // Set SK,DO high
        outputBuffer[1] = 0x03;
        // Set SK,DO as output, other as input
        outputBuffer[2] = 0x03;

        // Set clock divisor
        outputBuffer[3] = 0x86;
        // low byte
        outputBuffer[4] = dwClockDivisor & 0xFF;
        // high byte
        outputBuffer[5] = (dwClockDivisor >> 8) & 0xFF;

        // sent of commands
        ftStatus = FT_Write(ftHandle, outputBuffer, 6, &outputSent);

        // Turn of Loopback
        outputBuffer[0] = 0x85;
        ftStatus = FT_Write(ftHandle, outputBuffer, 1, &outputSent);

        outputSize = 0;

        int status = -1;

        i2c_start();
        status = i2c_send_byte_and_check_ack(0x3C);
        status = i2c_send_byte_and_check_ack(0x00);
        status = i2c_send_byte_and_check_ack(0x70);

        i2c_start();
        status = i2c_send_byte_and_check_ack(0x3C);
        status = i2c_send_byte_and_check_ack(0x00);
        status = i2c_send_byte_and_check_ack(0xA0);

        i2c_start();
        status = i2c_send_byte_and_check_ack(0x3C);
        status = i2c_send_byte_and_check_ack(0x02);
        status = i2c_send_byte_and_check_ack(0x00);

        i2c_start();
        i2c_send_byte_and_check_ack(0x3C);
        i2c_send_byte_and_check_ack(0x02);

        i2c_start();
        status = i2c_send_byte_and_check_ack(0x3C);
        status = i2c_send_byte_and_check_ack(0x03);

        i2c_start();
        i2c_send_byte_and_check_ack(0x3D);

        // SCL low, SDA tristate
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x00;
        outputBuffer[outputSize++] = 0x01;

        //for (i = 0; i < 2; ++i) {
        for (i = 0; i < 6; ++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 < 6-1) {
        outputBuffer[outputSize++] = 0x00;
        } else {
        outputBuffer[outputSize++] = 0x01;
        }
        }

        // send answer back immediate
        outputBuffer[outputSize++] = 0x87;

        i2c_start();
        ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
        outputSize = 0;

        i2c_stop();
        i2c_start();
        ftStatus = FT_Read(ftHandle, inputBuffer, 6, &inputRead);

        // SDA high, SCL low
        outputBuffer[outputSize++] = 0x80;
        outputBuffer[outputSize++] = 0x02;
        outputBuffer[outputSize++] = 0x03;

        i2c_stop();
        ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
        outputSize = 0;

        FT_Close(ftHandle);

        signed int mode_register = inputBuffer[0];
        printf("Mode Register: %u %u %u %u %u %u\n", inputBuffer[0], inputBuffer[1], inputBuffer[2], inputBuffer[3], inputBuffer[4], inputBuffer[5]);

        return 0;
        }

        Briefly, I have changed the commands I am sending, the for loop and the number of bytes which are to be read.
        Am I doing something wrong in the above code? Please let me know your comments.

        • Glad to hear it worked out.

          You're right with assumption of timing issues while debugging. Good thing to debug I²C is always to have a look at the discrete signals with the scope.

          I did not go thoroughly through your code, but at least spotted two things: The second command sequence you're sending is different to that you have in your descriptive text at the beginning and also the i2c_start/i2c_stops after the for-loop with the outputBuffer look wrong to me.

          • Yes, you are correct. That was a typo and there was i2c_stop placed which I removed. Regarding junk values it was not actually junk but negative values. The strange part is they do not change at all, that's and seem incorrect.
            I noticed in your code the following lines:
            i2c_start();
            i2c_send_byte_and_check_ack(LM75 | 0x01);
            As I understand, this is to issue a read command to LM75. Does the LM75 sensor require the number of bytes to be sent as well along with the read command? I ask this question as HMC5883L states to issue 3D 06.

            Also, I am not clear what this segment of the code is doing:
            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;
            }
            }

            // send answer back immediate
            outputBuffer[outputSize++] = 0x87;

            ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
            outputSize = 0;

            Following is the updated code:
            #include
            #include
            #include
            #include
            #include

            const char MSB_FALLING_EDGE_CLOCK_BYTE_IN = 0x20;
            const char MSB_FALLING_EDGE_CLOCK_BYTE_OUT = 0x11;
            const char MSB_FALLING_EDGE_CLOCK_BIT_OUT = 0x13;
            const char MSB_RISING_EDGE_CLOCK_BIT_IN = 0x22;

            #define MAX_DEVICES 5

            const char LM75 = 0x9E;

            FT_STATUS ftStatus;
            FT_HANDLE ftHandle;

            char inputBuffer[1024] = {0};
            char outputBuffer[1024];
            unsigned int outputSize;
            unsigned int outputSent;
            unsigned int inputSize;
            unsigned int inputRead;

            int scan_devices() {
            char* pcBufLD[MAX_DEVICES + 1];
            char cBufLD[MAX_DEVICES][64];
            int iNumDevs = 0;
            int i;

            for(i = 0; i < MAX_DEVICES; i++) {
            pcBufLD[i] = cBufLD[i];
            }
            pcBufLD[MAX_DEVICES] = NULL;

            FT_STATUS ftStatus = FT_ListDevices(pcBufLD, &iNumDevs, FT_LIST_ALL | FT_OPEN_BY_SERIAL_NUMBER);

            if (ftStatus != FT_OK) {
            fprintf(stderr, "Error: FT_ListDevices(%d)\n", ftStatus);
            return 1;
            }

            for (i = 0; ( (i <MAX_DEVICES) && (i < iNumDevs) ); i++) {
            printf("Device %d Serial Number - %s\n", i, cBufLD[i]);
            }

            return 0;
            }

            void i2c_start() {
            int i;

            for (i = 0; i < 4; ++i) {
            // SDA high, SCL high
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x03;
            outputBuffer[outputSize++] = 0x03;
            }

            for (i = 0; i < 4; ++i) {
            // SDA low, SCL high
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x01;
            outputBuffer[outputSize++] = 0x03;
            }

            // SDA low, SCL low
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x03;
            }

            void i2c_stop() {
            int i;

            for (i = 0; i < 4; ++i) {
            // SDA low, SCL high
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x01;
            outputBuffer[outputSize++] = 0x03;
            }

            for (i = 0; i < 4; ++i) {
            // SDA high, SCL high
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x03;
            outputBuffer[outputSize++] = 0x03;
            }

            // SDA tristate, SCL tristate
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x00;
            }

            int i2c_send_byte_and_check_ack(char data) {
            // clock data byte on clock edge MSB first
            outputBuffer[outputSize++] = MSB_FALLING_EDGE_CLOCK_BYTE_OUT;
            // data length of 0x0000 means 1 byte
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = data;

            // SDA tristate, SCL low
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x01;

            outputBuffer[outputSize++] = MSB_RISING_EDGE_CLOCK_BIT_IN;
            // length of 0x00 means scan 1 bit
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x87;

            ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
            outputSize = 0;

            ftStatus = FT_Read(ftHandle, inputBuffer, 1, &inputRead);

            if ((ftStatus != FT_OK) || (inputRead == 0)) {
            return -1;
            } else if (((inputBuffer[0] & 0x01) != 0x00)) {
            return -1;
            }

            // SDA high, SCL low
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x02;
            outputBuffer[outputSize++] = 0x03;

            return 0;
            }

            int main(int argc, char** argv) {
            int i;

            // print scan if no parameter is given
            if (argc 0))
            FT_Read(ftHandle, &inputBuffer, inputSize, &inputRead);

            //Set USB request transfer size
            ftStatus |= FT_SetUSBParameters(ftHandle, 65536, 65535);
            //Disable event and error characters
            ftStatus |= FT_SetChars(ftHandle, 0, 0, 0, 0);
            //Sets the read and write timeouts in milliseconds for the FT2232H
            ftStatus |= FT_SetTimeouts(ftHandle, 0, 5000);
            //Set the latency timer
            ftStatus |= FT_SetLatencyTimer(ftHandle, 16);
            //Reset controller
            ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x00);

            // enable MPSEE mode
            ftStatus |= FT_SetBitMode(ftHandle, 0x0, 0x02);

            if (ftStatus != FT_OK) {
            fprintf(stderr, "Error occured: %u\n", ftStatus);
            }

            // Disables the clk divide by 5 to allow for a 60MHz master clock.
            outputBuffer[0] = '\x8A';
            // Disable adaptive clocking
            outputBuffer[1] = '\x97';
            // Enables 3 phase data clocking. Used by I2C interfaces to allow data on both clock edges.
            outputBuffer[2] = '\x8C';
            // sent of commands
            ftStatus = FT_Write(ftHandle, outputBuffer, 3, &outputSent);

            /*
            ADBUS0 TCK/SK ---> SCL
            ADBUS1 TDI/DO -+-> SDA
            ADBUS2 TDO/DI -+
            ADBUS3 TMS/CS
            ADBUSS GPIOL0
            ADBUS5 GPIOL1
            ADBUS6 GPIOl2
            ADBUS7 GPIOL3
            */

            // Set values and directions of lower 8 pins (ADBUS7-0)
            outputBuffer[0] = 0x80;
            // Set SK,DO high
            outputBuffer[1] = 0x03;
            // Set SK,DO as output, other as input
            outputBuffer[2] = 0x03;

            // Set clock divisor
            outputBuffer[3] = 0x86;
            // low byte
            outputBuffer[4] = dwClockDivisor & 0xFF;
            // high byte
            outputBuffer[5] = (dwClockDivisor >> 8) & 0xFF;

            // sent of commands
            ftStatus = FT_Write(ftHandle, outputBuffer, 6, &outputSent);

            // Turn of Loopback
            outputBuffer[0] = 0x85;
            ftStatus = FT_Write(ftHandle, outputBuffer, 1, &outputSent);

            outputSize = 0;

            int status = -1;

            i2c_start();
            status = i2c_send_byte_and_check_ack(0x3C);
            status = i2c_send_byte_and_check_ack(0x00);
            status = i2c_send_byte_and_check_ack(0x70);

            i2c_start();
            status = i2c_send_byte_and_check_ack(0x3C);
            status = i2c_send_byte_and_check_ack(0x01);
            status = i2c_send_byte_and_check_ack(0xA0);

            i2c_start();
            status = i2c_send_byte_and_check_ack(0x3C);
            status = i2c_send_byte_and_check_ack(0x02);
            status = i2c_send_byte_and_check_ack(0x00);

            //i2c_start();
            //i2c_send_byte_and_check_ack(0x3C);
            //i2c_send_byte_and_check_ack(0x02);

            i2c_start();
            status = i2c_send_byte_and_check_ack(0x3C);
            status = i2c_send_byte_and_check_ack(0x03);

            i2c_start();
            i2c_send_byte_and_check_ack(0x3D);
            i2c_send_byte_and_check_ack(0x06);

            // SCL low, SDA tristate
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x00;
            outputBuffer[outputSize++] = 0x01;

            //for (i = 0; i < 2; ++i) {
            for (i = 0; i < 6; ++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 < 6-1) {
            outputBuffer[outputSize++] = 0x00;
            } else {
            outputBuffer[outputSize++] = 0x01;
            }
            }

            // send answer back immediate
            outputBuffer[outputSize++] = 0x87;

            i2c_start();
            ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
            outputSize = 0;

            //i2c_stop();
            i2c_start();
            ftStatus = FT_Read(ftHandle, inputBuffer, 6, &inputRead);

            // SDA high, SCL low
            outputBuffer[outputSize++] = 0x80;
            outputBuffer[outputSize++] = 0x02;
            outputBuffer[outputSize++] = 0x03;

            i2c_stop();
            ftStatus = FT_Write(ftHandle, outputBuffer, outputSize, &outputSent);
            outputSize = 0;

            FT_Close(ftHandle);

            //signed int mode_register = inputBuffer[0];
            signed int mode_register[10];

            for(i = 0; i < 6; i++)
            {
            mode_register[i] = (signed int)inputBuffer[i];
            printf("Mode Register for %u: %d\n", i, mode_register[i]);
            }
            return 0;
            }

            Please let me know your comments. Really appreciate your prompt response.

          • You're right. The first line is to trigger a read comment.

            To get an understanding off all the settings written to the output buffer I recommend to have a deep look into this application note from FTDI. All supported commands of the chip are described there. As a starter, e.g. try to understand the i2c_start/i2c_stop functions.

  19. Thanks, this document it is really helpful. I will go through it.

    Do you feel I am missing anything in the code I shared ? I am kind of stuck with it and I tried various options but I am not sure if I am doing something wrong.

    Another question I had was that I saw a different read result while single stepping the code. My final goal is to add this code to a multi-threaded application. Will the thread switching have an impact on the results read from the i2c device?

    If you can review the code I shared earlier and let me know if I am doing anything incorrect it will be great.

    • I haven't worked with that library for years, so I cannot answer everything for sure by heart. I guess that you also need to increase the loop count on the for loop as you want to read more than one byte. As already pointed out earlier it is always a very good idea to have a look at the actuals signals with an oscilloscope. You can easily see if the FTDI chip is really reading 6 bytes and also the data you then expect in the buffer.

      I'm not sure that the library is thread-safe, but I think if you use it only in one thread I wouldn't expect to much hassle.

Leave a Reply to twam Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.