FTDI Community

Please login or register.

Login with username, password and session length.
Advanced Search  

News:

Welcome to the FTDI Community!

Please read our Welcome Note

Technical Support enquires
please contact the team
@ FTDI Support


New Bridgetek Community is now open

Please note that we have created the Bridgetek Community to discuss all Bridgetek products e.g. EVE, MCU.

Please follow this link and create a new user account to get started.

Bridgetek Community

Show Posts

You can view here all posts made by this member. Note that you can only see posts made in areas to which you currently have access.

Topics - allenhuffman

Pages: [1] 2
1
We have systems communication over I2C with a Windows machine acting as master. The system will chug along doing millions of messages over weeks or months just fine, or it may run for a few hours then get an FTDI error. When this happens, the program is usually locked up (spinny blue donut) and we have to force quit or restart Windows.

When that happens, I do not expect there is anything we can do about it.

But other times, we'll sometimes get back the error 1011 (failed to read device) error and not lock up, but all further I2C writes will be failing with errors.

Do we have any hope of finding a way to prevent this from happening?

2
It is my understanding that FT_SetTimeouts() will cause a Read/Write to timeout. How can you tell if this happened? I did not see an obvious return code.

Code: [Select]
ftStatus = FT_SetTimeouts (ftHandle, 5000, 5000);

...

ft4222Status = FT4222_I2CMaster_Read (ftHandle, address, buffer, bufferSize, &sizeTransferred);

In this I2C example, suppose the slave device invoked clock stretching and held it longer than 5000ms. I would expect the Read call to return due to the SetTimeouts.

We sometimes see unexpected FT4222_FAILED_TO_READ_DEVICE errors, so I added code to print any time "bufferSize" and "sizeTransferred" do not match (and did the same in the Master_Write. I am not seeing those print, but I do see the program hang for 5 seconds from time to time, which makes me think it's being stuck in I/O then timing out.

Thanks, much. I am working on a project that uses FTDI I2C to communicate between a host PC and almost 30 boards using a write/read protocol. While it's been proven very solid (I've personally tested it with over 41 million messages with 0 errors), if/when a line glitch does happen, I want to make the master code robust.

3
We have been using these chips for years along with a Windows host program. We start getting FTDI errors when we are doing PWM pulsing of an RF signal in our product. In my system logs, I start seeing read errors from one of our boards, and then all the rest (as if the FTDI driver itself is messed up; inspecting the I2C line with a Saleae logic analyzer shows it is not locked):

Code: [Select]
20230724 092734.302 FTDI Read 100 (Exc): Status 1011 (FT4222_FAILED_TO_READ_DEVICE).
Restarting our Windows host resumes, so I expect we should be able to recover in software. I've tried doing Bus resets, Master resets and other things, and can make it continue, but there is usually a hang long enough that it shuts down our boards (for safety, they shut down if they don't hear from Master Control Program ;-).

Does anyone have any tips on what can be done with an I2C read or write returns one of these errors? One it happens, our system is basically a brick until I restart the Windows host program.

Thanks, much.

4
I just learned there is a LibFT4222-v1.4.5.zip out, so I was trying to use it. There are two header files included:

  • LibFT4222-v1.4.5\imports\ftd2xx\ftd2xx.h
  • LibFT4222-v1.4.5.zip\LibFT4222-v1.4.5\imports\LibFT4222\inc\LibFT4222.h

My IDE could open the second file opens just fine, but the first one is rejected as being a binary file ("Cannot open binary files as text files"). Using a hex editor, I can see that the ftd2xx.h file does appear to be non-ASCII. It has a zero byte every other character, so I am guessing it was saved out as something like unicode (whatever format uses two bytes per character, I guess).

Compilers may or may not have an issue with that (I do not know what the current C standard says regarding file formats). The editor I am using will not open it. (And even saving it as a .txt or copy/pasting in to another editor and saving retains the 0 -- I guess it's a standard file format Windows supports).

Just a heads-up on this. I have reported it to FTDI, and I am going to either write a quick script to convert the file, or find some tool to do it for me.

Cheers...

5
I have only been building for 64-bit, but recently was told to create a 32-bit as well. During the process of updating from 1.4.4 to the 1.4.5 zip, I noticed there are duplicate lib files with the same name, but different file sizes:

LibFT4222-v1.4.5.zip\LibFT4222-v1.4.5\imports\LibFT4222\dll\amd64 -- 3 KB

LibFT4222-v1.4.5.zip\LibFT4222-v1.4.5\imports\LibFT4222\lib\amd64 -- 743 KB

In our existing project, the files needed were collected together, but I am not sure which one I should use now.


6
I created a DLL for handling our I2C communications protocol. It requires the LibFT4222-64.dll from FTDI. I am using the LabWindows/CVI 2017 compiler.

Everything has been working fine the past few months, but I was requested to make a 32-bit version of the DLL as well. I only changed a few options in the compiler, and now it won't link.

I even made a new project, brought in my source code and the .lib files, and it does the same. I'm not sure what changed.

Do any of these symbols look familiar? Is this something the FTDI DLL is needing? If I don't have it included, it complains about FT_xxx calls. As soon as I add the .libs, then it goes to these:

Build Status (z.prj - Release64)
 Link z.dll
  error: Undefined symbol '??0exception@std@@QEAA@AEBV01@@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??2@YAPEAX_K@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??3@YAXPEAX@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_7type_info@@6B@' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_E?$clone_impl@U?$error_info_injector@Vlock_error@boost@@@exception_detail@boost@@@exception_detail@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_E?$clone_impl@U?$error_info_injector@Vthread_resource_error@boost@@@exception_detail@boost@@@exception_detail@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_E?$error_info_injector@Vlock_error@boost@@@exception_detail@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_E?$error_info_injector@Vthread_resource_error@boost@@@exception_detail@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_ERingQueue@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_ERxBuffer@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Ebad_alloc@std@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Eclone_base@exception_detail@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Elock_error@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Eruntime_error@std@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Esystem_error@system@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Ethread_exception@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '??_Ethread_resource_error@boost@@UEAAPEAXI@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '?generic_category@system@boost@@YAAEBVerror_category@12@XZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '?system_category@system@boost@@YAAEBVerror_category@12@XZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '?what@exception@std@@UEBAPEBDXZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '_CxxThrowException' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__CxxFrameHandler3' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_??0exception@std@@QEAA@AEBQEBD@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_??0exception@std@@QEAA@AEBV01@@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_??1_Container_base12@std@@QEAA@XZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_??1exception@std@@UEAA@XZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_?_Xlength_error@std@@YAXPEBD@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_?_Xout_of_range@std@@YAXPEBD@Z' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '__imp_?what@exception@std@@UEBAPEBDXZ' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
  error: Undefined symbol '_purecall' referenced in "c:\path\msvc64\LibFT4222-64.lib". 
Build failed.

7
Since the bug in the FTDI I2C chip presents using FT4222_I2CMaster_GetStatus() after a write (unless you can wait long enough to know the write has completed), I came up with a workaround.

We blindly write our our message (without knowing if anything saw it and ACK'd it), then we go to read our special protocol.

We read the first two bytes (a start code, and number of data bytes) like this:

Code: [Select]
// Read two bytes (Start Code, Number of Data Bytes).
ft4222Status = FT4222_I2CMaster_ReadEx (ftHandle, deviceAddress, START, buffer, 2, &sizeTransferred);

Initially, I thought we might be able to use GetStatus() hear and see if the READ was ACK'd, but it will return a BUS_BUSY due to the ReadEx being in the middle (no STOP bit yet). At least that's what it looks like -- always BUS_BUSY until I do the second ReadEx.

I do a check like this:

Code: [Select]
// Fake it. Reads from non-responding devices will return FFs.
if ((buffer[0] == 0xff) && (buffer[1] == 0xff))
{
    // Nothing responded, or other issue.
}

...BUT, this means I never got to the second half of the ReadEx that completes it, with the STOP:

Code: [Select]
ft4222Status = FT4222_I2CMaster_ReadEx (ftHandle, deviceAddress, STOP, &buffer[2], messageSize-2, &sizeTransferred);
/code]

How can I just send the "STOP"? I tried this (ReadEx with 0 bytes) and that doesn't do it:

[code]// Fake it. Reads from non-responding devices will return FFs.
if ((buffer[0] == 0xff) && (buffer[1] == 0xff))
{
    // Nothing responded, or other issue.
    FT4222_I2CMaster_ReadEx (ftHandle, deviceAddress, STOP, buffer, 0, &sizeTransferred);
}

Looking at the I2C line in my Saleae analyzer, I see no STOP bit, though the next write/read is still able to happen.

How can I send a STOP without reading data? Is that possible?

8
As has been reported elsewhere in this forum...

https://www.ftdicommunity.com/index.php?topic=822

...problems can happen if you try to use FT4222_I2CMaster_GetStatus() after a Write operation if the Write has not completed.

See: https://ftdichip.com/wp-content/uploads/2022/03/TN_161_FT4222H-Errata-Technical-Note.pdf
Section 3.4.2

FT4222_I2CMaster_GetStatus() is used to determine if the I2C write was ACK'd by a device. But, you are told to only use it AFTER transmisson is complete:

Quote
Read the status of the I2C master controller. This can be used to poll a slave after i2c transmission is complete.

One of the GetStatus bits is "BUS BUSY" and it seems you could have used it to determine when a write was complete, but due to the problem in the FTDI chip, you cannot. I suspect something like this was the intent:

Code: [Select]
// This WILL NOT WORK. See:
//
// TTN_161 FT422H Errate Technical Note
// Version 1.5
// Document Reference No.: FT_001198
//
// Section 3.4.2
// 'I2C data is corrupt when FT4222_I2CMaster_GetStatus" is being called'
//

// Write message.
ft4222Status = FT4222_I2CMaster_Write(ftHandle, deviceAddress,
&msg[0], sizeof(msg), &sizeTransferred);

if (ft4222Status == FT4222_OK)
{
// Wait for it to be sent so we can check status.
unsigned char controllerStatus;
NAKSeen = false;

// Deadlock prevention. Try up to 10 times...
for (int tries = 0; tries < 10; tries++)
{
ft4222Status = FT4222_I2CMaster_GetStatus (ftHandle,
&controllerStatus);

if (ft4222Status == FT4222_OK) // We have a status.
{
// If CONTROLLER BUSY, no status bits are valid.
if (I2CM_CONTROLLER_BUSY(controllerStatus)==1)
{
// controller busy: all other status bits invalid
continue;
}

// If here, status bits are valid.

// If BUS BUSY, we are still writing.
if (I2CM_BUS_BUSY(controllerStatus))
{
continue;
}

// Check to see if ADDR was ACK'd.
if (I2CM_ADDRESS_NACK(controllerStatus))
{
NAKSeen = true;
}
// If here, we were NACK'd or ACK'd.
break;
}
else // ft4222Status NOT OK
{
// Considered.
}
} // end of for.

if (NAKSeen == true)
{
printf ("NAK\n");

// Handle...
}
else // ACK'd
{
// Write was okay.
printf ("Wrote %u bytes.\n", sizeTransferred);

// Handle...

} // end of if (NAKSeen == true) else

} // end of if (ft4222Status == FT4222_OK)

If FTDI releases a fixed chip in the future, that code could be enhanced to check for other error conditions as well.

Currently, if you want to use it to see if your write was received (ACK), you have to wait until transmission is complete then use GetStatus. For casual hobby projects, it may be fine to just blindly write to non-existent (or locked up) devices, but if the project is important knowing is a device is present (and has received the message) may be required.

Since FT4222_I2CMaster_Write() is non-blocking, the call returns immediately, and the write is handled in the background. This means if you do a Write and follow it with a GetStatus, problems may occur. The above-linked post demonstrates data corruption as one such problem.

I am not sure what happens if you try doing a Write while a previous Write is still in progress, but I *assume* it may also be bad. It is unfortunate this defect exists.

FTDI Workaround

The FTDI response is to wait before calling GetStatus, so I thought I'd provide details in one post (which I will edit with corrections/updates if needed) for others who have run in to this.

The delay can be calculated by knowing the baud rate -- the kbps used in FT4222_I2CMaster_Init() -- and the number of bytes written.

Keep in mind that each byte has an ACK/NAK bit added, so calculate 9 bits per byte rather than 8.

Also, if you look at the output of the I2C write under a logic analyzer (such as a Saleae), you will see there are also gaps between bytes (see attached screenshot). These gaps are not consistent. In my example, I see sending a byte took about 10 uS, with a gap afterwards that ranged from 7 uS to more than 60 uS.

To use the following workaround, you need to use a worst-case "gap time" and add it to the calculation. I am unsure. I am going to *assume* that this gap time can vary -- on a busy Windows machine I expect they could be much larger in time.

Calculating

The FTDI part can operate from 60 kbps to 3400 kbps. Since the Sleep resolution may be limited to milliseconds, a Sleep(1) is the minimal time. This may be long enough to cover writing 128 bytes of I2C data at high baud rates (1000 or faster). But, at slower speeds, you will see the calculated write time needs to be much longer. Just doing a Sleep(1) is not enough if you plan to use a lower baud rate.

The wait time formula is basically something like:"

Code: [Select]
ms = (9 * (1000/(kbps)) * (sizeTransferred+1)) / 1000;
The 9 is for the 9 clock pulses to send a byte (8 pulses for the 8 bits, plus 1 pulse for ack/nak).

The sizeTransferred (bytes sent) has 1 added to it to round up.

Unfortunately, this does not take in to consideration the gaps between bytes.  If I knew that it took 10 uS to send a byte, and then there was a 7 uS gap after, I could tweak this time by adding extra bits. Say, 7 bits of overhead (worth of time):  "(9+7) * ..."

BUT, since I see that sometimes the gap might be closer to 70 uS (7 times as long as it took to send the byte), I might just want to use "9" there, and multiply the "sizeTransferred" by 7. Worst case.

This produces much longer waits than are needing, slowing down the I2C system considerably. But, if you really need to use GetStatus, this may be the only option.

Here is a C program to print out an example table. You can see the formula I was using added 6 extra bits, but I have since found this is not long enough:

Code: [Select]
#include <stdio.h>  // for printf
#include <stdint.h> // for uint16_t
#include <stdlib.h> // for EXIT_SUCCESS

// Table of I2C baud rates.
uint16_t kbps[] = { 60, 100, 300, 500, 1000, 3400 };

int main()
{
    // Print header.
    printf ("bytes  ");

    for (int idx=0; idx<sizeof(kbps)/sizeof(kbps[0]); idx++)
    {
        printf ("%4u  ", kbps[idx]);
    }
    printf ("\n");

    // Print table entries.
    for (uint16_t sizeTransferred=0; sizeTransferred<=128; sizeTransferred++)
    {
        printf ("%5u  ", sizeTransferred); // Bytes written.

        for (int speed=0; speed<sizeof(kbps)/sizeof(kbps[0]); speed++)
        {
            // "Since there is a separate byte for the address, and since
    // there are 9 pulses for each byte (ack/nak bit), a formula
    // probably needs to take that in to consideration."
    //
    // "When looking at the FTDI Master (Windows 11) in a Saleae capture,
    // I also see gaps between each byte that range from 10us to 14us, so
    // I added some worst-case extra bits (6 for my case) to that to cover
    // it:"
    //
    //             (bits ) * (         uS       ) * (      bytes      )
        uint32_t ms = ((8+1+6) * (1000/(kbps[speed])) * (sizeTransferred+1)) / 1000;
        if (ms < 1)
        {
        ms = 1;
        }

            printf ("%4u  ", ms);
        }
        printf ("\n");
    }

    return EXIT_SUCCESS;
}

And here is the output:

Code: [Select]
bytes    60   100   300   500  1000  3400
    0     1     1     1     1     1     1
    1     1     1     1     1     1     1
    2     1     1     1     1     1     1
    3     1     1     1     1     1     1
    4     1     1     1     1     1     1
    5     1     1     1     1     1     1
    6     1     1     1     1     1     1
    7     1     1     1     1     1     1
    8     2     1     1     1     1     1
    9     2     1     1     1     1     1
   10     2     1     1     1     1     1
   11     2     1     1     1     1     1
   12     3     1     1     1     1     1
   13     3     2     1     1     1     1
   14     3     2     1     1     1     1
   15     3     2     1     1     1     1
   16     4     2     1     1     1     1
   17     4     2     1     1     1     1
   18     4     2     1     1     1     1
   19     4     3     1     1     1     1
   20     5     3     1     1     1     1
   21     5     3     1     1     1     1
   22     5     3     1     1     1     1
   23     5     3     1     1     1     1
   24     6     3     1     1     1     1
   25     6     3     1     1     1     1
   26     6     4     1     1     1     1
   27     6     4     1     1     1     1
   28     6     4     1     1     1     1
   29     7     4     1     1     1     1
   30     7     4     1     1     1     1
   31     7     4     1     1     1     1
   32     7     4     1     1     1     1
   33     8     5     1     1     1     1
   34     8     5     1     1     1     1
   35     8     5     1     1     1     1
   36     8     5     1     1     1     1
   37     9     5     1     1     1     1
   38     9     5     1     1     1     1
   39     9     6     1     1     1     1
   40     9     6     1     1     1     1
   41    10     6     1     1     1     1
   42    10     6     1     1     1     1
   43    10     6     1     1     1     1
   44    10     6     2     1     1     1
   45    11     6     2     1     1     1
   46    11     7     2     1     1     1
   47    11     7     2     1     1     1
   48    11     7     2     1     1     1
   49    12     7     2     1     1     1
   50    12     7     2     1     1     1
   51    12     7     2     1     1     1
   52    12     7     2     1     1     1
   53    12     8     2     1     1     1
   54    13     8     2     1     1     1
   55    13     8     2     1     1     1
   56    13     8     2     1     1     1
   57    13     8     2     1     1     1
   58    14     8     2     1     1     1
   59    14     9     2     1     1     1
   60    14     9     2     1     1     1
   61    14     9     2     1     1     1
   62    15     9     2     1     1     1
   63    15     9     2     1     1     1
   64    15     9     2     1     1     1
   65    15     9     2     1     1     1
   66    16    10     3     2     1     1
   67    16    10     3     2     1     1
   68    16    10     3     2     1     1
   69    16    10     3     2     1     1
   70    17    10     3     2     1     1
   71    17    10     3     2     1     1
   72    17    10     3     2     1     1
   73    17    11     3     2     1     1
   74    18    11     3     2     1     1
   75    18    11     3     2     1     1
   76    18    11     3     2     1     1
   77    18    11     3     2     1     1
   78    18    11     3     2     1     1
   79    19    12     3     2     1     1
   80    19    12     3     2     1     1
   81    19    12     3     2     1     1
   82    19    12     3     2     1     1
   83    20    12     3     2     1     1
   84    20    12     3     2     1     1
   85    20    12     3     2     1     1
   86    20    13     3     2     1     1
   87    21    13     3     2     1     1
   88    21    13     4     2     1     1
   89    21    13     4     2     1     1
   90    21    13     4     2     1     1
   91    22    13     4     2     1     1
   92    22    13     4     2     1     1
   93    22    14     4     2     1     1
   94    22    14     4     2     1     1
   95    23    14     4     2     1     1
   96    23    14     4     2     1     1
   97    23    14     4     2     1     1
   98    23    14     4     2     1     1
   99    24    15     4     3     1     1
  100    24    15     4     3     1     1
  101    24    15     4     3     1     1
  102    24    15     4     3     1     1
  103    24    15     4     3     1     1
  104    25    15     4     3     1     1
  105    25    15     4     3     1     1
  106    25    16     4     3     1     1
  107    25    16     4     3     1     1
  108    26    16     4     3     1     1
  109    26    16     4     3     1     1
  110    26    16     4     3     1     1
  111    26    16     5     3     1     1
  112    27    16     5     3     1     1
  113    27    17     5     3     1     1
  114    27    17     5     3     1     1
  115    27    17     5     3     1     1
  116    28    17     5     3     1     1
  117    28    17     5     3     1     1
  118    28    17     5     3     1     1
  119    28    18     5     3     1     1
  120    29    18     5     3     1     1
  121    29    18     5     3     1     1
  122    29    18     5     3     1     1
  123    29    18     5     3     1     1
  124    30    18     5     3     1     1
  125    30    18     5     3     1     1
  126    30    19     5     3     1     1
  127    30    19     5     3     1     1
  128    30    19     5     3     1     1

You will see that at the lowest baud rate, writing out 128 bytes is calculated to take around 30 ms. If you actually test this in a logic analyzer, you will see it's not very accurate. See my second screen shot and you will see that when writing 128 bytes, the gaps between the bytes vary - some are 7 uS, some are 13 uS, some are 51 uS (!), even 63 uS. There is overhead on the Windows system and other things are happening.

My table claims that 128 bytes at 1000 kbps should need a 1ms delay. BUT, when I actually look at the time it took, it was OVER 3ms. I was off by a factor of 3! Using my formula to calculate wait time could have led to reading corrupt data (per the poster in the other topic).

Because of this, the formula I was using will not work. As far as I know, currently, you just have to make sure you always wait a worst-worst case amount of time before trying to use GetStatus. And since I don't know how fast the PC will be that runs my I2C code, whatever timing I come up with may not be long enough.

That's the problem.

Ever since we tried to implement ACK/NAK detection (probing for devices), we've had occasional problems, and it seems this is the culprit.

I hope this information helps others who run in to this. What we really need is a way to determine if a Write is busy, but at least when I asked last year, the only option was "Sleep". But...how long?

Cheers!


9
Discussion - Software / I2C Master Read in multiple read requests.
« on: November 14, 2022, 05:27:55 PM »
UPDATE: Next post has confirmation this works, with a Saleae capture showing the start and stop bits are handled properly.

We use a custom protocol over I2C that embeds message length in our messages. In our PIC24 firmware, we have one board that will read the first two bytes (to get our protocol start code, and the number of data bytes in the message) and then it will read only the expected number of bytes. (Our actual protocol does more - having CRC and other things we validate, but I am simplyfing it here for example and removing most error checking.)

Code: [Select]
i2c_start (I2C_SUBNET_BUS);

i2c_write (I2C_SUBNET_BUS, I2CSlaveAddress + 1);

// Read the message on the I2C bus.
startCode = i2c_read (I2C_SUBNET_BUS, 1); // Start Code

numDataBytes = i2c_read (I2C_SUBNET_BUS, 1); // Num Data Bytes

for (int idx = 0; idx<(numDataBytes-1); idx++)
{
    RXBuffer[idx] = i2c_read (I2C_SUBNET_BUS, 1);
}

// Read the last byte, but don't ACK.
RXBuffer[idx] = i2c_read (I2C_SUBNET_BUS, ZERO);

i2c_stop (I2C_SUBNET_BUS);

Our system is fairly complex with as many as 27 different boards in communication over I2C.

On our FTDI Windows host program side, we hard code the expected response message size. We do something like:

Code: [Select]
FT4222_I2CMaster_Write(ftHandle, slaveAddress, buffer, bytesToWrite, &sizeTransferred);
...
FT4222_I2CMaster_Read(ftHandle, slaveAddress, responseBufferPtr, ExpectedResponseSizeForThatMessage, &sizeTransferred);

This is fine, but if we are writing something that has a large message expected, and there is a problem, the slave board will return a NACK message (7 bytes in our case) and the Master reads that and keeps reading up to the expected size.

No big deal, since our firmware zeros out the response buffer so if the Master reads 10 bytes at any time, it just gets back 10 0s.

BUT, I wanted to make our messages more flexible and maybe speeds things up. It looks like I could recreate what I want using ReadEx(), since I see:

Quote
The I2C condition will be sent with this I2C transaction
 START = 0x02
 Repeated_START = 0x03
 Repeated_START will not send master code in HS mode
 STOP = 0x04
 START_AND_STOP = 0x06

It looks like I would do a ReadEx of 2 bytes, with the flag set to START, then I can error check our start code (byte 0) and number of data bytes (byte 1) and then issue a ReadEx of that many bytes with flag set to Repeated_Start (for one less byte) then read the final byte using STOP.

I thought I'd ask if this was worth trying, or if there was a better way. Perhaps the driver handles doing a read with just STOP and I can save a step.

I plan to experiment on this before the end of the year, but thought I'd ask in case I couldn't even do this.

Thanks, much.



10
Discussion - Software / msvcp100.dll and msvcr100.dll dependencies?
« on: March 29, 2022, 03:04:51 PM »
We have a large LabWindows program and the only external component it uses is the FTDI drivers:

ftd2xx.lib
LibFT4222-64.lib and LibFT4222-64.dll

When we first ran it under Windows 10 LTSC, it complained about missing msvcp100.dll and msvcr100.dll. These come from the Visual C++ 2010 redistributable. These files are included with Windows 10, but are not part of LTSC and have to be installed separately.

Are these FTDI files what is needing them? A simple LabWindows U.I. program that does not use the FTDI code seems to work.

It appears the dependency is either in the FTDI software, or some library item that LabWindows is pulling in for our product (FTDI is the only component we use that is not built-in to LabWindows).

Hopefully someone can confirm whether or not these DLLs are used by FTDI. That would greatly help me know where to focus my research in to this issue.

Thanks, much!

11
We make use of LibFT4222.dll for I2C Master communication on a Windows PC.

I would like to make our error detection much more robust. For example, I want to be able to detect issues such as:
  • USB cable disconnected.
  • I2C bus lock up.
  • ? ? ?
I get back an FT4222_STATUS from all read/write operations, but I am not real sure which errors are "best practice" to try to detect and handle.  I expect we can put in things like:

If we think I2C bus is locked, try an FT4222_I2CMaster_ResetBus().

If we think the USB connection is bad, un-init, close and try to re-open and init.

Library functions such as FT_CyclePort() may also be useful.

Could someone point me to some documention on "best practices" for a robust I2C system?

Thanks, much.

12
We use Windows and the FT4222 driver to talk to a variety of boards over I2C. Over the years, we have seen many issues with I2C bus locks and have tried to mitigate them in software/firmware.

I thought I'd ask here if folks had any elegant solutions for detecting an I2C bus lock and attempting recovery.

The last release of the library (1.4.4) added FT4222_I2CMaster_ResetBus(); and finally allowed sending the 9 clock pulses to try to unstick a stuck I2C slave device. Before this function was added, we tried doing it manually but found you could not access the GPIO pins when in I2C mode.

I believe we may have been able to just uninitialized from I2C and initialize in GPIO mode to do this:

Code: [Select]
FT_HANDLE ftHandle = NULL;

ftStatus = FT_OpenEx ("PrecisePower B", FT_OPEN_BY_DESCRIPTION,
                      &ftHandle);

if (ftStatus == FT_OK)
{
     GPIO_Dir gpioDir[4] = { GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT };
     
     ft4222Status = FT4222_GPIO_Init(ftHandle, gpioDir);

     //disable suspend out , enable gpio 2
     ft4222Status = FT4222_SetSuspendOut(ftdiInfoPtr->ftHandle, false);
     
     //disable interrupt , enable gpio 3
     ft4222Status = FT4222_SetWakeUpInterrupt(ftdiInfoPtr->ftHandle, false);
     
     // set gpio0/gpio1/gpio2/gpio3 output level high
     for (int pulse=0; pulse<9; pulse++)
     {
        ft4222Status = FT4222_GPIO_Write(ftHandle, GPIO_PORT0, 1); // Clock pin
        ft4222Status = FT4222_GPIO_Write(ftHandle, GPIO_PORT0, 0); // Clock pin
     }
     Sleep (1);
     
     FT4222_UnInitialize(ftHandle);

    // Re-init as I2C and use it...           
}
FT_Close (ftHandle);

(Untested; not sure the timing from within Windows toggling that pin on/off would do the trick.)

I wondered if anyone had any clever ways of detecting an I2C stuck bus condition (like when a slave device is using clock stretching, and the master doesn't do a read).

I'll do another post about our experiments with ACK/NACK.

Cheers.

13
FTDI added FT4222_I2CMaster_ResetBus() in the 1.4.4 release (I believe).  It is supposed to send 9 I2C clock pulses to unstick the I2C bus. I recall testing this when I first received this version, and saw it work.

But today, I am noticing it is not sending clock pulses.  Attached is a screen shot of the Saleae capture.

To verify my Saleae was working, I swapped my two input probes and saw the pulses move to the other input.

My code is very simple and is doing this:

Code: [Select]
    ftStatus = FT_OpenEx ("MyDeviceNameHere", FT_OPEN_BY_DESCRIPTION,
                          &ftdiInfoPtr->ftHandle);
   
    if (ftStatus == FT_OK)
    {
        ResetDevice ();
        // Need to wait for the FTDI device to complete the reset.
        Sleep (ONE_THOUSAND);

        // Set the device timeouts.
        ftStatus = FT_SetTimeouts (ftdiInfoPtr->ftHandle,
                                   FT_DEFAULT_RX_TIMEOUT,
                                   FT_DEFAULT_TX_TIMEOUT);

        // Initialize the I2C master.
        if (ftStatus == FT_OK)
        {
            // Initialize the FTDI device chosen.
            ft4222Status = I2CMasterInit (ftdiInfoPtr->ftHandle, I2C_SPECIAL_FAST_CLK);

            // Initialization is done.
            if (ft4222Status == FT4222_OK)
            {
                for (int idx=0; idx < 10; idx++)
                {
                    // Send nine clock pulses to reset a stuck Slave device.
                    ft4222Status = I2CMasterResetBus (ftdiInfoPtr->ftHandle);
                    Sleep (1);
                }

We have wrappers for the different functions, so I2CMasterResetBus is just:

Code: [Select]
FT4222_STATUS I2CMasterResetBus (FT_HANDLE ftHandle)
{
    return FT4222_I2CMaster_ResetBus (ftHandle);
}

And ResetDevice() is just calling FT_ResetDevice ();

Anyone used this? It's new, and wasn't even documented at first (beyond header file entries).

Thanks, much.

14
I have a C# wrapper for the LibFT4222 and D2XX DLLs. The PC is acting as an I2C slave, and we have another project where the PC is the master.

I am trying to add recovery code to detect if the I2C connection is lost due to a USB cable unplug. Currently, we have close/reopen manually.

I have a loop that checks for data using FT4222_I2CSlave_GetRxStatus() and does a read if the expected bytes are available, then I do a FT4222_I2CSlave_Read().

I have noticed that the status returned from FT4222_I2CSlave_GetRxStatus continues to be FT4222_OK even if I unplug the USB cable.

I am trying to find a clue in these two manuals for how to detect a lost connection:

https://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer's_Guide(FT_000071).pdf
https://www.ftdichip.com/Support/Documents/AppNotes/AN_329_User_Guide_for_LibFT4222.pdf

My app also talks to a GPIB interface to a signal generator and have detection code for that working as expected. It returns an error when the device is turned off or unplugged.

Thanks for any pointers to what I need to be looking for in the manual.

15
I am using a C# wrapper for the FTDI I2C DLL. My program is acting as a Slave device. I have noticed that when the screen saver kicks in, I stop getting data.

The other DLL I am using (GPIB interface to a signal generator) keeps chugging along just fine. Only the I2C stops reading. The reads from the I2C is based on a Windows 10 timer. The control on the GPIB device is based on another timer. Since I can tell the GPIB timer is still working, I don't think it's a timer issue.

Has anyone else encountered this?

Pages: [1] 2