FTDI FTD2xx driver problem with DLP-2232PB device

Hello all!

I use the DLP-2332PB for development of the data acquisition device. Using the internal A/D converters of the PIC I would like to sample the incoming analog signal with the rate of 20MHz(per single channel)-1kHz. The PIC-side code was written using CCS complier supplied with the device. The host-side code written using C# 2.0 compiler and PInvoke facilities. Device driver is FTD2XX. I use the following simple application for the communication test:

2332PB-side:

#include #device ADC=10 #fuses HS,NOWDT,NOPUT,NOBROWNOUT,LVP,NOWRT,NOPROTECT #use delay(clock=20000000) #use FAST_IO(D) #use FAST_IO(C) #use FAST_IO(B) #use FAST_IO(A) #use FAST_IO(E)

#define WR PIN_B2 #define RD PIN_B1 #define SND PIN_E1 #define TXE PIN_E2 #define RXF PIN_B4

#byte PORTD = 0x08

#inline void write_byte(int data);

void main() { long count=0; SET_TRIS_B(0x11); SET_TRIS_E(0x04); output_high(RD);

while(TRUE) { SET_TRIS_D(0xff); while(input(RXF)) ; SET_TRIS_D(0x00); while(!input(TXE) && count < 255) { write_byte(1); count++; write_byte(2); count++; write_byte(3); count++; write_byte(4); count++; } } }

#inline void write_byte(int data) { PORTD = data; delay_us(1); output_low(WR); delay_us(1); output_high(WR); }

The code is supposed to send 256 bytes of data with "1,2,3,4,1,2,3..." pattern to PC after receiving anything from the PC side. __________________________________ host side:

using System; using System.Collections.Generic; using System.Text; using FTDIWrapper; namespace tryPIC { class Program { static void Main(string[] args) { while(true) { while (!Wrapper.OpenDevice()) ; Wrapper.PurgeAll(); Wrapper.ReadData(Wrapper.CheckQueue()); Wrapper.WriteData(new byte[] { 0 }); System.Threading.Thread.Sleep(100); byte[] data = Wrapper.ReadData(Wrapper.CheckQueue()); while(!Wrapper.CloseDevice()) ; Console.WriteLine("Data:"); for (int i = 0; i < data.Length; i++) { Console.WriteLine("byte #{0} is {1}", i, data[i]); } Console.WriteLine("Continue? (y/n)"); if(Console.ReadLine()!="y") break; } } } }

And the "Wrapper" functions are calls to unmanaged code using DllImport attributes. Functions' signatures are:

public static void WriteData(byte[] data); public static byte[] ReadData(uint bytesToReceiveNum); public static uint CheckQueue(); public static void PurgeAll(); public static bool OpenDevice(); public static bool CloseDevice();

This program sends "request" (any garbage) to the microcontroller, waits approximately 100 milliseconds , then reads the contents of the driver's buffer and prints it on the screen. This continues untill any character other than "y" entered.

The following odd thing happens each and every time: the first loop returns valid data, i.e., 256 values with "1,2,3,4..." pattern; the second returns "null" for the data; during the third loop the program does not return from the call to the FT_Write function at all.

Setting the timeout with FT_SetTimeouts to any value returns "IOError" from the function at third call. And when I set the incoming data size with FT_SetUSBParameters, the FT_Read returns "null" at first call.

Has anybody ever seen something similar? And how can I solve this? Any advice will be appreciated.

Regards, Kris.

Reply to
Kris
Loading thread data ...

Some things to try:

  1. Open the device at the beginning of your host program and close it at the end. Don't open and close every time through the loop. The open and close operations may be interfering with the data flow at the PIC end, or may be doing a bunch of USB control transfers that are messing up the PIC or PC end.

  1. Lose the threads stuff until you get it working in a non-threaded program.

Mark Borgerson

Reply to
Mark Borgerson

First of all, thank you, Mark. Second, simply to make it clear, the maximal sampling rate should be

20kHz, and not 20MHz, as I wrote before.

And now back to the main topic - I've tried the above in the following way: while (!Wrapper.OpenDevice()) ; while (true) { Wrapper.SetTimeouts(100, 100); //Wrapper.SetUSBLatencyTimer(50); //Wrapper.SetUSBParams(256); Wrapper.PurgeAll(); Wrapper.ReadData(Wrapper.CheckQueue()); Wrapper.WriteData(new byte[] { 0 }); for(short j=short.MaxValue; j>0; j--) ; byte[] data = Wrapper.ReadData(Wrapper.CheckQueue());

Console.WriteLine("Data:"); for (int i = 0; i < data.Length; i++) { Console.WriteLine("byte #{0} is {1}", i, data[i]); } Console.Write("Continue? (y/n) "); if (Console.ReadKey().KeyChar != 'y') break; } while (!Wrapper.CloseDevice()) ;

But the same thing happens. Moreover, closing the program after second loop and then running it again, immediately returns "IOError" from the first call to the FT_Write function. Additional tests with loopback programs were successfull. I've just thought about another possibility - could it be something with the managed code? May be the old good native C/C++ is the way to take care of this driver?

Regards, Kris

Reply to
Kris

What is that, C#? Where did you get FTDIWrapper?

All I can tell you is that I've been using FTDI chips (FT245BM, mainly) for a couple of years now in embedded projects. But on the host side, we just use the D2XX driver from FTDI via Visual C++, and it works pretty much the way the documentation says it does.

I would also echo the thought that opening/closing the device on every iteration is probably a bad idea.

-- Dave Tweed

Reply to
David Tweed

Hello, Dave. Yes, it's C#. FTDIWrapper is the namespace of my Wrapper class, that uses PInvoke for communication between managed code and unmanaged functions of the D2XX driver. These functions were tested before through a couple of loopback tests, and it was ok. Now, the "opening/closing" issue is addressed in the code snippet above. But, as I said before, this does not help - to leave the device open. Your experience with the FT245BM chips is very relevant in this case, I think, because the DLP-2232PB module is based on FT2232C - "double" FT245BM with some additions. May be you can tell me, transfer of what amounts of data and at what rate is involved in your applications. It could be the key to solution. Regards, Kris.

Reply to
Kris

That's been my experience also. I haven't changed my host side software in several years.

The source code for my windows host program uses the following to read a file transferred from the embedded system. The program is in Borland C++ builder. FTH is the handle for the FTDI FT245 device:

void __fastcall TFormLink::BTXferClick(TObject *Sender) { char *fullstring, fnstring[15], cmdstring[16];

int i,spos; FILE *fp;

static char spbuff[4098]; long bytecount; DWORD numwritten, num2read,numread; AnsiString str; char *fnp;

if(FTH == 0) return; // exit if not open

/// Snipped file opening code

FT_Write(FTH,cmdstring, strlen(cmdstring), &numwritten); // //send the file name

timeout = 50; // sets 5-second timeout to start file // at this point, the logger starts sending the file data while(timeout > 0){ fts = FT_GetQueueStatus(FTH, &num2read); if((fts == FT_OK) && (num2read > 0)){ FT_Read(FTH,&spbuff[0],num2read,&numread); if(numread > 0){ if(fp != NULL) fwrite(&spbuff[0], numread,1,fp); bytecount+= numread; EDCount->Text = bytecount; // show bytes sent timeout = 4; // reset timeout counter to 0.4 } } Application->ProcessMessages(); // check windows msgs

} if(fp!= NULL) fclose(fp);

}

If you want any more help, you're going to have to show us what is going on inside your wrapper functions.

You can find the logger-side source code at

formatting link
Look for the USB-1 page.

Mark Borgerson

Reply to
Mark Borgerson

Reply to
Andrew Jackson
[Sorry for previous post, over hasty key press]
[deletia]

I've used the FTDI device with both C++ and C# without problems. It would be useful to see what some of your wrapper functions look like. The code that I wrote for C#, after finding and opening the device, wrote a command and read a result back (similar to yours but without the flush).

Andrew

Reply to
Andrew Jackson

We have implemented a message protocol on top of the raw "byte stream" presented by the USB interface. Our primary traffic is a 38-byte message

200 times a second (about 8000 bytes/second) plus a few thousand bytes of other miscellaneous messages each second. It ends up being about 10K bytes/second on the average. Windows XP generally does a pretty good job of keeping up in real time, although we see occasional "hiccups" when it gets busy with something else.

-- Dave Tweed

Reply to
David Tweed

Here are the functions from the Wrapper class:

public class Wrapper { private static IntPtr m_USBhandler;

[DllImport("FTD2XX.dll", EntryPoint="FT_Open")] public static extern uint FT_Open(int iDevice, ref IntPtr ftHandle); [DllImport("FTD2XX.dll", EntryPoint="FT_Close")] public static extern uint FT_Close(IntPtr ftHandle); [DllImport("FTD2XX.dll", EntryPoint="FT_Read")] public static extern uint FT_Read(IntPtr ftHandle, byte[] lpBuffer, uint dwBytesToRead, ref uint lpdwBytesReturned); [DllImport("FTD2XX.dll", EntryPoint="FT_Write")] public static extern uint FT_Write(IntPtr ftHandle, byte[] lpBuffer, uint dwBytesToWrite, ref uint lpdwBytesWritten); [DllImport("FTD2XX.dll", EntryPoint="FT_Purge")] public static extern uint FT_Purge(IntPtr ftHandle, uint dwMask);

public static bool OpenDevice() { if (((FT_Status)Wrapper.FT_Open(1, ref m_USBhandler)) == FT_Status.Ok) { return true; } else { return false; } }

public static bool CloseDevice() { if (((FT_Status)Wrapper.FT_Close(m_USBhandler)) == FT_Status.Ok) { return true; } else { return false; } }

public static void PurgeAll() { FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Purge(m_USBhandler, (uint)FT_PurgeRXTX.TX & FT_PurgeRXTX.RX))) != FT_Status.Ok) { throw new USBException("Could not purge the buffers.", opResult); } }

public static uint CheckQueue() { uint bytesToReceiveNum = 0; FT_Status opResult; if ((opResult = (FT_Status)Wrapper.FT_GetQueueStatus(m_USBhandler, ref bytesToReceiveNum)) != FT_Status.Ok) { throw new USBException("Could not check the USB device queue status.", opResult); } else { return bytesToReceiveNum; } }

public static byte[] ReadData(uint bytesToReceiveNum) { uint readBytesNumber = 0; byte[] receivedBytes = new byte[bytesToReceiveNum]; FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Read(m_USBhandler, receivedBytes, bytesToReceiveNum, ref readBytesNumber)) != FT_Status.Ok) { throw new USBException("Could not read data from the USB device.", opResult); } else if (bytesToReceiveNum != readBytesNumber) { throw new USBException("Could not retreive the full data from the USB device.", FT_Status.OtherError); } else { return receivedBytes; } }

public static void WriteData(byte[] data) { uint bytesToWriteNum = (uint)data.Length; uint writtenBytesNumber = 0; FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Write(m_USBhandler, data, bytesToWriteNum, ref writtenBytesNumber)) != FT_Status.Ok) { throw new USBException("Could not write the data into the USB device.", opResult); } else if (bytesToWriteNum != writtenBytesNumber) { throw new USBException("Could not write the full data into the USB device.", FT_Status.OtherError); } } }

For me, it seems to be ok, but may be there is some problem.

Kris

Reply to
Kris

Here are the functions from the Wrapper class:

public class Wrapper { private static IntPtr m_USBhandler;

[DllImport("FTD2XX.dll", EntryPoint="FT_Open")] public static extern uint FT_Open(int iDevice, ref IntPtr ftHandle); [DllImport("FTD2XX.dll", EntryPoint="FT_Close")] public static extern uint FT_Close(IntPtr ftHandle); [DllImport("FTD2XX.dll", EntryPoint="FT_Read")] public static extern uint FT_Read(IntPtr ftHandle, byte[] lpBuffer, uint dwBytesToRead, ref uint lpdwBytesReturned); [DllImport("FTD2XX.dll", EntryPoint="FT_Write")] public static extern uint FT_Write(IntPtr ftHandle, byte[] lpBuffer, uint dwBytesToWrite, ref uint lpdwBytesWritten); [DllImport("FTD2XX.dll", EntryPoint="FT_Purge")] public static extern uint FT_Purge(IntPtr ftHandle, uint dwMask);

public static bool OpenDevice() { if (((FT_Status)Wrapper.FT_Open(1, ref m_USBhandler)) == FT_Status.Ok) { return true; } else { return false; }

}

public static bool CloseDevice() { if (((FT_Status)Wrapper.FT_Close(m_USBhandler)) == FT_Status.Ok) { return true; } else { return false; }

}

public static void PurgeAll() { FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Purge(m_USBhandler, (uint)FT_PurgeRXTX.TX & FT_PurgeRXTX.RX))) != FT_Status.Ok) { throw new USBException("Could not purge the buffers.", opResult); }

}

public static uint CheckQueue() { uint bytesToReceiveNum = 0; FT_Status opResult; if ((opResult = (FT_Status)Wrapper.FT_GetQueueStatus(m_USBhandler, ref bytesToReceiveNum)) != FT_Status.Ok) { throw new USBException("Could not check the USB device queue status.", opResult); } else { return bytesToReceiveNum; }

}

public static byte[] ReadData(uint bytesToReceiveNum) { uint readBytesNumber = 0; byte[] receivedBytes = new byte[bytesToReceiveNum]; FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Read(m_USBhandler, receivedBytes, bytesToReceiveNum, ref readBytesNumber)) != FT_Status.Ok) { throw new USBException("Could not read data from the USB device.", opResult); } else if (bytesToReceiveNum != readBytesNumber) { throw new USBException("Could not retreive the full data from the USB device.", FT_Status.OtherError); } else { return receivedBytes; }

}

public static void WriteData(byte[] data) { uint bytesToWriteNum = (uint)data.Length; uint writtenBytesNumber = 0; FT_Status opResult;

if ((opResult = (FT_Status)Wrapper.FT_Write(m_USBhandler, data, bytesToWriteNum, ref writtenBytesNumber)) != FT_Status.Ok) { throw new USBException("Could not write the data into the USB device.", opResult); } else if (bytesToWriteNum != writtenBytesNumber) { throw new USBException("Could not write the full data into the USB device.", FT_Status.OtherError); }

} }

For me, it seems to be ok, but may be there is some problem.

Thank you in advance, Kris

Reply to
Kris

It looks like you're doing the same things I do with the FT245, except that I never use the Purge or GetQueueStatus calls. I purge buffers by calling the Read function until there is no data left. I don't use GetQueueStatus, but just read until there is nothing left.

in WinXP, I have set up and installed the FTDI driver using their Vendor ID and the product ID for my device.

Here is the code to open the device:

void __fastcall TFormLink::BTOpenClick(TObject *Sender) { // try to find and open a CF-1 interface

char devstring[100]; DWORD numdevs, devindex = 0; if(FTH != 0) { // if already open, close first FT_Close(FTH); FTH = 0; } fts = FT_ListDevices( &numdevs, NULL, FT_LIST_NUMBER_ONLY);

if(numdevs >0) fts = FT_Open(0, &FTH); strcpy(devstring, "No CF-1 Interface Found" ); EDDevice->Text = (const char *)devstring; // assume no device

if(fts == FT_OK) { // if found change message, open device FT_ResetDevice(FTH); FT_SetTimeouts(FTH, 300,300); EDDevice->Text = "CF-1 USB Interface"; } }

The code which traverses a directory list and copies each file from the data logger is this:

void __fastcall TFormLink::BTXferClick(TObject *Sender) { char *fullstring, fnstring[15], cmdstring[16];

int i,spos; FILE *fp;

static char spbuff[4098]; long bytecount; DWORD numwritten, num2read,numread; AnsiString str; char *fnp;

if(FTH == 0) return; // exit if not open // transfer files that are selected for(i= 0; i < LBoxdir->Items->Count; i++){ if(LBoxdir->Selected[i]){ str = LBoxdir->Items->Strings[i]; fullstring = str.c_str(); strcpy(cmdstring, "s"); strncpy(&fnstring[0],fullstring,13); for(spos = 0; spos < 14; spos++){ if (fnstring[spos] == ' ') fnstring[spos] = 0; } // truncate string at trailing spaces strcat(cmdstring,fnstring); strcat(cmdstring, "\n");

// now collect the data from USB port bytecount = 0; num2read = 256; EDCount->Text = 0; EDFolder->Text = TCDO1->Directory+"\\" + / (const char *)fnstring;

fnp = EDFolder->Text.c_str(); fp = fopen( fnp, "wb"); // now send the command //and file name to the cf-1 Application->ProcessMessages(); // catch up on windows //message handling FT_Write(FTH,cmdstring, strlen(cmdstring), &numwritten); //send the file name

timeout = 50; // sets 5-second timeout to start file

while(timeout > 0){ fts = FT_GetQueueStatus(FTH, &num2read); if((fts == FT_OK) && (num2read > 0)){ FT_Read(FTH,&spbuff[0],num2read,&numread); if(numread > 0){ if(fp != NULL) fwrite(&spbuff[0],numread,1, fp); bytecount+= numread; EDCount->Text = bytecount; timeout = 4; // reset timeout counter to 0.4 } } Application->ProcessMessages(); // catch up on windows //message handling

} if(fp!= NULL) fclose(fp); } // end of if(selected... } // end of for(i= 0 ....

}

As you can see, it's pretty simple: Send a command and read the returned data until there is no more (as indicated by the timeout). No need for purges or GetQueueStatus() (Which may not even be valid for the FT245).

In your code you use the apparent global variable (or object field) "m_USBhandler" in all your functions. I would suggest that you check that this handle is valid before using it. I'm always suspicious of object handles---especially if the handle is global. This could be even more of a problem in a multi-threaded application where another thread might be using the same handle.

if m_USBhandler is an object field, try making it a static global.

Can you simply link in the FTDI.sys driver and get the program working with normal C code, rather than using DLLImport and the wrapper functions? (I don't know C# well enough to know if this is possible. My C# system is still in its unopened box--waiting for a lull in the consulting schedule).

Mark Borgerson

Reply to
Mark Borgerson

Kris

I'm not sure that it makes a difference but in my C# code I used out rather than ref.

Are you certain that there is just the one FTDI device on your system?

Don't you mean or (|) rather than and (&) here? You won't be purging anything otherwise.

Otherwise your functions look pretty reasonable.

Andrew

Reply to
Andrew Jackson

I just finished solving a problem where I needed to call a vendor's dll from VB6 and perl. And I was having all sorts of problems.

It turned out that the vendor's dll parameter passing used the _cdelc ('C' convention) for passing parameters instead of the _stdcall parameter passing convention.

I ended up writing a wrapper dll in VC6 that used the _stdcall parameter passing convention, and this solved all of my problems. Since I'm mainly a hardware/VHDL guy, I've only dabbled in VC6, so it took lots of googling before I was able to make my wrapper dll work. But, I did.

If FTDI supplies a C header file, you might be able to tell what parameter passing convention is used.

I know that all of the Win32 API dlls, such as kernel32.dll, use the _stdcall convention. It is my understanding that any non-C programs linking to a dll require the _stdcall parameter passing convention.

BTW, the difference betweeen the two calling conventions is that when using _cdecl, the calling program cleans up the stack after the call, but with _stdcall, the called (dll) program does the cleanup.

Any of you real software guys out there, please feel free to chime in if my info is inaccurate.

Urb

_______________________________________________________________________________ Posted Via Uncensored-News.Com - Accounts Starting At $6.95 -

formatting link
The Worlds Uncensored News Source

Reply to
Paul Urbanus

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.