Thursday, October 9, 2008

multithreaded approach

I rewrote the first test program with a multithreaded approach. Basically there are three thread involved:

  1. PollThread
  2. LogicThread
  3. MainThread
The Pollthread serve to check the FTDI ADBUS line connected to the interrupt. Whenever an interrupt arrives an Event is set to signal the MainThread that work has to be done. The access to the FDTI is guarded by a mutex. Let me show you the code:

DWORD WINAPI PollThread ( LPVOID lp )
{
UCHAR ad [1];
UCHAR in [5];
UCHAR out [5];

status = FTDI_I2C_STARTUP( _400KHZ );
status = FTDI_I2C_PCA_READN_ALL( PCA9698_INPUT_READ , in );
RefSet.FromByteBuf(in);
RefSet.flip();
RefOut.ToByteBuf(CmdOut);
status = FTDI_I2C_PCA_WRITE_ALL( PCA9698_OUTPUT_WRITE , CmdOut );
while (1)
{
WaitForSingleObject(InterruptAckEvent,INFINITE);
WaitForSingleObject(FtdiMutex,INFINITE);
status = FTDI_2XX_READN_ADBUS(ad);
ReleaseMutex(FtdiMutex);
while ( (ad[0] & INT_MASK_FLAG) != 0 )
{
WaitForSingleObject(FtdiMutex,INFINITE);
status = FTDI_2XX_READN_ADBUS(ad);
ReleaseMutex(FtdiMutex);
}
SetEvent(InterruptEvent);
}
return 0;
}

The second thread is the LogicThread that will be created by the MainThread on event basis. This thread reads the inputs resets the interrupt and sends an acknowledge to the the PollThread. This is needed because otherwise the interrupt event will be send more than ones if it is not reset. The read and writes will be guarded by the same mutex as the PollThread. Here is the code:

DWORD WINAPI LogicThread ( LPVOID lp )
{
UCHAR in [5];
UCHAR out [5];


WaitForSingleObject(FtdiMutex,INFINITE);
status = FTDI_I2C_PCA_READN_ALL( PCA9698_INPUT_READ , in );
ReleaseMutex(FtdiMutex);

SetEvent(InterruptAckEvent);

BitSetExtended<40> InNew(in);
InNew.flip();

for ( int input = 0 ; input <>
{
if ( (RefSet[input] != InNew[input]) && (InNew.test(input)) )
{
RefOut.flip(input);
}
}

RefSet = InNew;
RefOut.ToByteBuf(CmdOut);

WaitForSingleObject(FtdiMutex,INFINITE);
status = FTDI_I2C_PCA_WRITE_ALL( PCA9698_OUTPUT_WRITE , CmdOut );
status = FTDI_I2C_DAC_SET(0,0);
ReleaseMutex(FtdiMutex);

return 0;
}

The last thread is the main and is very small. Basically this thread waits until the interrupt event is set and creates the LogicThread with a priority just a bit higher to force the context switch immediately:

InterruptEvent = CreateEvent(NULL,false,false,L"Local\\INT");
InterruptAckEvent = CreateEvent(NULL,false,true,L"Local\\INTACK");
FtdiMutex = CreateMutex(NULL,false,L"Local\\FTDI");

// start to poll
CreateThread(NULL,0,PollThread,NULL,0,&PollThreadId);

while (1)
{
WaitForSingleObject(InterruptEvent,INFINITE);
hThreadLogic = CreateThread(NULL,0,LogicThread,NULL,CREATE_SUSPENDED,&LogicThreadId);
SetThreadPriority(hThreadLogic,THREAD_PRIORITY_ABOVE_NORMAL);
ResumeThread(hThreadLogic);
CloseHandle(hThreadLogic);
}

The program works very stable. The only thing I noticed was that the process better runs with ABOVE_NORMAL priority as normal on the eee Asus PC. This is due to having only a single core and running quit small with 1GHz. The only improvement I can add is a mutex object on the input BitSet to exclude all possible race conditions possible. But it's only a test of course.




Friday, September 26, 2008

final results


On the picture you can see my test equipment for the FTDI. Three parts are involved:

1. Asus EEE PC as platform
2. FTDI interface board ( small one)
3. I2C I/O board ( big one )

The final test I did was based on the fact that I simulated the real domotic logic as close as possible to the final program logical blocks. I also included the fact of worse case timings. The best way to explain this is by showing you the piece of test code I wrote:

void TestWorstCaseTiming(void)

{
 UCHAR in [5]; //I2C InBuf
 UCHAR out [5]; //I2C OutBuf
 UCHAR ad [1]; //Int

 //*********INIT BLOCK
 status = FTDI_I2C_STARTUP( _400KHZ );
 //*********PREPARE IN AND INVERT
 status = FTDI_I2C_PCA_READN_ALL( PCA9698_INPUT_READ , in );
 BitSetExtended<40> InSet(in);// start with real values
 InSet.flip();// invert push = 1
 //*********PREPARE OUT ALL TO ZERO
 BitSetExtended<40> OutSet(CmdOut);
 //OutSet.none();
 OutSet.ToByteBuf(CmdOut);
 status = FTDI_I2C_PCA_WRITE_ALL( PCA9698_OUTPUT_WRITE , CmdOut );//16ms
 //*********MAIN LOOP
 while (1)
 {
  //*********POLL LOOP
  status = FTDI_2XX_READN_ADBUS(ad);
  while ( (ad[0] & INT_MASK_FLAG) != 0 )
  {
  //overwrite time to have total chain time at the end
  GetSystemTime(&begin);
  status = FTDI_2XX_READN_ADBUS(ad);//2ms
  }
  //*********DOMOTICS LOGIC READ
  status = FTDI_I2C_PCA_READN_ALL( PCA9698_INPUT_READ , in );//22ms
  //*********DOMOTICS LOGIC COMPARE AND MODIFY
  BitSetExtended<40> InNew(in);
  InNew.flip();
  for ( int input = 0 ; input <>
  {
  if ( (InSet[input] != InNew[input]) && (InNew.test(input)) )
  {
  OutSet.flip(input);
  }
  }
  //*********DOMOTICS LOGIC UPDATE LAST IN
  InSet = InNew;
  //*********DOMOTICS LOGIC PREPARE AND OUT
  OutSet.ToByteBuf(CmdOut);
  status = FTDI_I2C_PCA_WRITE_ALL( PCA9698_OUTPUT_WRITE , CmdOut );//16ms
  status = FTDI_I2C_DAC_SET(0,0);//10ms
  //*********CHECK TIME
  //Sleep(30); simulate cpu charge
  GetSystemTime(&end);
  printf ("cpu time : %d ms\n",end.wMilliseconds-begin.wMilliseconds);
  // should be ca 50ms + Xms logic
 }
}


The program flow is very simple. I poll on interrupt flag change in the inner while loop. As soon this becomes signals I leave the loop to read in the values of the PCA-IN. I check upon which bits are changed and also if this bit equal to 1. Due to the fact I use the inputs as button device I'm not interested if this bit is zero because this indicates the released state. I'm only act upon state pushed here. I invert the PCA-OUT accordingly to have a toggle behaviour on the leds. I didn't use a complex mapping for the in and out simply in-bit[0] relates to out-bit[0] etc for all 40 of them. Finally I write the new output state to the I/O board. I also added the DAC write just for timing simulating purposes but has no further meaning in this test. As you can see in the code I use the BitSetExtended<40> InNew(in) class. This one I wrote to ease the work with atomic bits. It's an extended (derived) STD class of BitSet:

template class BitSetExtended : public bitset<_n>
{
 typedef __int64 _Ty;
 public:
  BitSetExtended ( );
  BitSetExtended ( __int64 ll );
  BitSetExtended ( PUCHAR pC );
  void ToByteBuf ( PUCHAR pC);
 private:
  enum { _Nb = CHAR_BIT * sizeof (_Ty),_Nw = _N == 0 ? 0 : (_N - 1) / _Nb };
  void _Treat64( __int64 _X);
  void _Tidy(_Ty _X = 0);
  void _Trim( void );
  _Ty _A[_Nw + 1];

};

My estimated 50ms total time was quite correctly. I tried pretty much with a real button switch connected on several inputs to check if the program was fast enough to trigger all interrupt coming from the PCA-IN. And it worked very well I must say ! I can in some unreal conditions trigger an error but this really testing as a fool and can not be compared to the usual real life application. I inserted a sleep line in the code to simulate higher domotic logic execution time. I went even to 30ms and my system continued working stable. I need this test to be sure that with the final code ,which will be much more than what I have now, the program will not suffer any timing site effects.

Conclusion:

I find this a very good result even with quit slow communication of the FTDI USB device. In this test I wrote the code chained one block after the other. I didn't use any optimizations on the code flow and avoided working with multiple threads. I can think of a couple changes in terms of logic and threading which can optimize the program a little bit eg seperate the poll code into one thread using events and syncronizations on the FTDI object. Perhaps writing the read-in bits to a queue system and trait the changes in an other thread. I will look into this possibilties soon and report in my blog as usually do. But I must say I'm one step closer to my decision for taking this approach as my future domotic server.

Sunday, September 21, 2008

timings

What are the results using the FDTI chip as a I2C peripheral ? The same as you pehaps I was very curious about the performance using it from the USB driver and DLL. To get a good idea I wrote some higher level functions that use the API the make things easier accessing the PCA device. i divided it into:

- Init
- Read ADBUS interrupt line
- Read byte(s) from PCA
- Write bytes(s) to PCA

Basically the init function put the PCA in the correct mode and initialize the FTDI chip properly regarding the speed. I used 1Mhz speed as the PCA can handle this fast I2C speed but this will not be a bottleneck as we'll see. To initialize it took 350ms. This sounds big and actually it's but this is only done ones at the start of the program and is not that important.

Than I started to poll on the INT line :

read int time : 1 ms
status : ef
read int time : 3 ms
status : ef
read int time : 1 ms
status : ef
read int time : 3 ms
status : ef

As you can see an average of 2ms. This was after I changed the LatencyTime parameter of the FTDI from 16ms to 2ms which is the minimum. If you don't do that the fastest result you get is 16ms ! I continued with the read of 5 consequent bytes from the PCA and I got ca 22ms. This is a bit disappointed. I did the same writing 5 bytes and this was a bit better ca 16ms. To deduce the overhead per byte I wrote a command to read/write just one byte:

read pca time : 9 ms
status : ff
read pca time : 9 ms
status : ff
write pca time : 8 ms
write pca time : 8 ms

So the fasted time in read or write to an external I2C device using the USB FTDI chip is ca 10ms. So this had a 2ms overhead for any extra byte to read or send. I see a few reasons for these poor performance results:

1. The USB overhead for small data packets
2. Minimum FTDI latency is 2ms
3. High level MPSSE command structure (atomic)

I checked the DLL code and most of the functions were well written and can not be optimized. However in the read cycle this can be optimized a little bit but not worth to try and see much differences I think.

Although the performance is not that fantastic, this design let you interface a PC platform (linux,windows,CE,mac) with any customized external board that uses a I2C bus as communication. And it's a very cheap and powerful solution. You can make your programs on your favourite platform without using embedded micro controllers. Use the power of that platform to interact with other high level software components without having the limitations of embedded boards. So anyone that have to work with some extended I2C based device can make use of this chip. Be ware this only counts for the latest series of FTDI eg 2232C or D. One of the drawbacks I see is that to serve systems running 24/7 using this solution, you have to let a PC running too and that's may be a kind of overkill. In that case it's better to have a small embedded system with very low power consumption or at least a PC platform without sensitive mechanical parts like fans or hard drives.

So the question still remains for me if I'm going to use this as my domotic server system or not. I keep you posted on in this blog. In my next I'll present the overall setup with a nice picture of the components I used.




Friday, September 19, 2008

FTCI2C DLL functionalities

Nothing received on my last mail. So the FDTI support is not that friendly if you ask me. Nevertheless I modified the DLL code so I now have access to the ADBUS and ACBUS bus GPIO. Although the ADBUS access is limited. But as I promised, this post will be dedicated to the API calls you can use when the FT2232 device is in MPSSE mode. The next post I will show some results on timings from several tests I did to get a better idea on the capabilities of this chip using it for other purposes than usb to serial conversions.

Mainly two possible ways to work with this device. You can pick the VCP (virtual com port) or the D2XX (low level design and special modes). In my case I downloaded the D2XX package. I took the windows based package but also linux , windows CE and even MAC are possible. Resources are available on the following site:

http://www.ftdichip.com/Drivers/D2XX.htm

The FTDI software comes with a zip file archive with following components:

- ftdi driver
- ftd2xx.dll
- ftd2xx.lib
- ftd2xx.h

These are the minimum components you need to start making your programs using visual studio. If you choise to interface an I2C bus peripheral than you must download an ectra development package to be able to work easier with the internal command structure of the FT2232. This can be found :

http://www.ftdichip.com/Projects/MPSSE/FTCI2C.htm

Components included are :

- ftci2c.h
- ftci2c.dll
- ftci2c.lib

Basically you can do pretty much with the default API functions the DLL exports and are quit simple to use. Here is the list of APIs:

FTCI2C_API
FTC_STATUS WINAPI I2C_GetNumDevices(LPDWORD lpdwNumDevices);

FTCI2C_API
FTC_STATUS WINAPI I2C_GetDeviceNameLocID(DWORD dwDeviceNameIndex, LPSTR lpDeviceNameBuffer, DWORD dwBufferSize, LPDWORD lpdwLocationID);

FTCI2C_API
FTC_STATUS WINAPI I2C_OpenEx(LPSTR lpDeviceName, DWORD dwLocationID, FTC_HANDLE *pftHandle);

FTCI2C_API
FTC_STATUS WINAPI I2C_Open(FTC_HANDLE *pftHandle);

FTCI2C_API
FTC_STATUS WINAPI I2C_Close(FTC_HANDLE ftHandle);

FTCI2C_API
FTC_STATUS WINAPI I2C_InitDevice(FTC_HANDLE ftHandle, DWORD dwClockDivisor);

FTCI2C_API
FTC_STATUS WINAPI I2C_GetClock(DWORD dwClockDivisor, LPDWORD lpdwClockFrequencyHz);

FTCI2C_API
FTC_STATUS WINAPI I2C_SetClock(FTC_HANDLE ftHandle, DWORD dwClockDivisor, LPDWORD lpdwClockFrequencyHz);

FTCI2C_API
FTC_STATUS WINAPI I2C_SetLoopback(FTC_HANDLE ftHandle, BOOL bLoopbackState);

FTCI2C_API
FTC_STATUS WINAPI I2C_SetMode(FTC_HANDLE ftHandle, DWORD dwCommsMode);

FTCI2C_API
FTC_STATUS WINAPI I2C_Write(FTC_HANDLE ftHandle, PWriteControlByteBuffer pWriteControlBuffer,
DWORD dwNumControlBytesToWrite, BOOL bControlAcknowledge, DWORD dwControlAckTimeoutmSecs,
BOOL bStopCondition, DWORD dwDataWriteTypes, PWriteDataByteBuffer pWriteDataBuffer, DWORD NumDataBytesToWrite,
BOOL bDataAcknowledge, DWORD dwDataAckTimeoutmSecs, PFTC_PAGE_WRITE_DATA pPageWriteData);

FTCI2C_API
FTC_STATUS WINAPI I2C_Read(FTC_HANDLE ftHandle, PWriteControlByteBuffer pWriteControlBuffer,
DWORD dwNumControlBytesToWrite, BOOL bControlAcknowledge, DWORD dwControlAckTimeoutmSecs,
DWORD dwDataReadTypes, PReadDataByteBuffer pReadDataBuffer, DWORD dwNumDataBytesToRead);

FTCI2C_API
FTC_STATUS WINAPI I2C_GetDllVersion(LPSTR lpDllVersionBuffer, DWORD dwBufferSize);

FTCI2C_API
FTC_STATUS WINAPI I2C_GetErrorCodeString(LPSTR lpLanguage, FTC_STATUS StatusCode,
LPSTR lpErrorMessageBuffer, DWORD dwBufferSize);


As you can see quit a complete list of functions that you can call almost directly. The principe is based on dividing the i2c commands into a ControlBuffer and a DataBuffer. This is done to simplify the internal calls to the FTDI2232 command set available. Here is an example out of my library I write to prepare a command for the PCA device:

FTDI_API FTC_STATUS FTDI_I2C_PCA_WRITE_ALL ( UCHAR adr , PCHAR buf )
{
FTC_STATUS stat;

PageWriteData.dwNumBytesPerPage = PCA9698_MAX_DATA;
PageWriteData.dwNumPages = PCA9698_MAX_PAGE;

WriteControlBuffer[0] = adr;
WriteControlBuffer[1] = PCA9698_WRITE_ALL;

memcpy ( WriteDataBuffer , buf , PCA9698_MAX_DATA );

stat = I2C_Write( hI2C,
&WriteControlBuffer,
PCA9698_CONTROL_BYTES,
FTDI_WANT_ACK,
FTDI_TIME_OUT,
FTDI_WANT_ACK,
PAGE_WRITE_TYPE,
&WriteDataBuffer,
PCA9698_MAX_DATA,
FTDI_WANT_ACK,
FTDI_TIME_OUT,
&PageWriteData
);I2C_RETURN_ERROR;

I2C_RETURN_SUCCES;
}

So in fact you can organize your calls in seperate functions library for readability or you can of course also call it directly within your main logic. Due to the fact I modified the FTDI DLL code I better add my functions directly in the code of the DLL. This has an extra advantage in using only one single device handle. If you need the GPIO calls together with I2C you need to call functions out of the basic ftd2xx.dll and you need to open up an other handle. This means that if you work like that you simply can't let open two handles in your program. I show you the lines that I changed to be able to access the ACBUS:

In function FT2232cMpsseI2c::InitDataInOutClockFrequency I commented out these lines:

// outputs on GPIO21-24
// FTC_AddByteToOutputBuffer(SET_HIGH_BYTE_DATA_BITS_CMD, false);
// FTC_AddByteToOutputBuffer('\x0F', false);
// FTC_AddByteToOutputBuffer('\x0F', false);

If you don't do that every time the device gets initialized the ACBUS is set to output and the 4 lines become high. Another thing I did was changing the WriteProtectEnable flag. This flag is related to pin 4 of the ADBUS and not documented in the specifications. It's not quit clear to me why they use it but I guess to make the internal command calls save for multihreading.

In function FT2232cMpsseI2c::ReadDataFromExternalDevice I modified the flag to false :

Status = WriteAddressExternalDevice(ftHandle, (*pWriteControlBuffer)[0], ControlAckType, dwControlAckTimeoutmSecs, false);

I needed to that because my PCA OE was connected to pin 4. After these changes the OE was working properly and the ACBUS was available for input as well as for output whitout any changes when calling the InitDevice function.

I went not into great details regarding the code and of course I assume that you know how to build a DLL and use third party DLLs from within visual studio. The purpose of this post is just showing you the fact that FDTI device can be put into other modes than the widly used VCP mode. Next I will tell you bit more on the results of my tests. For the ones interested in reading some hardware specifications

http://www.ftdichip.com/Documents/DataSheets.htm

I can't attach the DLL source code here in this google blog which is a pitty but if you're interested just give me a reaction on this post.

Saturday, September 13, 2008

pcb ready

As you can see on the left this is the final design. It's a try out to investigate the possibilities of the FTDI 2232D chip. The objective was using the FTDI provided DLLs and interface my IO board via I2C.

The first tests were perfect ! The chip in MPSSE mode let you communicate via I2C just by calling a few DLL API calls. But soon I noticed that this mode had some drawbacks. In de specifications of the chip nothing is telling you that if you use the MPSSE mode the gpios of the ADBUS and ACBUS are not available ! As my OE and INT of the IO board were connected using these gpio pins, the design became useless. Of course I tried to write to FTDI support but they are not that fast in response. Here are some FTDI answers:

- Is the use of ADBUS4-7 totally free as GPIO when using the DLL FTCI2C with the FTDI 2232D device ?

Ans: Unfortunately when you set the mode for MPSSE those GPIO are not available.

- In case of no please write some additional design remarks in dedicated specifications to avoid confusion and bad design.

Ans: We will elaborate this point in our future updates.

- In case of yes is it also free to use ACBUS as gpio together with I2C DLL ?

Ans: NO

- Is it possible to have the source code of the I2C DLL ?

Ans: Sure attached.

At last they attached the source code of the DLL which was a good thing. Nevertheless I wrote some final mail to point out my situation and what I really need for my design:

First of all thanks for your quick answer and attaching the source code. Bad news of course to hear that the GPIOs can not be combined together with the MPSSE mode. It's not quit clear to me if the behaviour comes from using the DLL or it's inherent on the MPSSE mode itself. (not checked the code in detail yet) But one thing that FTDI definitly must do is add some remark in the specification. The pdf as is now only shows following notes in the column MPSSE:

**Note 4 : MPSSE is Channel A only.
**Note 8 : SI/WU is not available in these modes.

This should be extended with a dedicated note like "GPIOs not available.." in case it's inherent on MPSSE mode and if the cause is due to the DLL in software this can be noted in the API pdf as such.

However my design issue remains and I want to find a solution. The purpose is to have 8 GPIOs available not at the same time when I need the I2C functionality. I want to poll on some eg xxBUS1 signal and act upon it when this is low with a I2C command. So it's important to have a statefull GPIO when using the I2C commands and returning to GPIO mode. Therefore maybe I can use channels A/B in some combination.

Can I use the device channel B in GPIO mode to poll and switch to MPSSE mode on channel A without losing the state on channel B ? What mode for B to use ?
I think the use of EEPROM is mandatory in such a A/B configuration ?

I hope you can point me in the right direction to solve my problems with the right design approach.

Still no answer. So maybe there's still a small change to combine the GPIOs with MPSSE I2C mode but I doubt it really. In the next blog I present some DLL calls I made to work with FTDI.




Wednesday, August 20, 2008

initial host platform

I took the asus eee notebook as the target platform because this it doesn't have a hard disk build in. This pc works completely on SSD. The only mechanical weak part is still the small fan. XP is installed on the 4G C drive and the 8G D drive serves for data and programs. Not having a hard disk has a great advantage espacially if it must work 24/7. Also the small layout is ideal for placing it in a switch board closet. It has default WIFI on board so my access point can be dropped. It runs on a celeron M 1Ghz processor more than enough power to drive my needed applications. I also benefit of the audio availability which is built in by default. I already tested it for a few days with my web server running and the no issues were dedected. The only bad thing I can say for now is that it only has 4Gb to store the operating system on. As windows XP SP3 is installed now I only have 500Mb left. But up till now I see no blocking issues to start working with this configuration. Maybe this can be replaced in time by a server system running on RAID. I'll see what I can get out of this small 3E pc.

first analyse

On the left my first ideas to cope with an FTDI based architecture. I isolated a few topics:

- Development platform
- Target deployment
- Physical composition
- Low level software interpretation

Mean less to say that this can be changed when I'll have some feedback on the overall performance and reliability of the system in this initial layout.

kick off




Just started with the FT2232D chip of the new FTDI USB generation. I tried to order a development kit but the order never came trough so I cancelled it. Meanwhile I wrote some hardware specification to build my own board that easily interfaces my I2C IO board. I already did some first tests with a VC program and the FTDI dedicated DLL to have a first impression on performance. I did these tests with a ft232 chip of the previous generation. One USB call via the DLL api takes a minimum time of 2ms. This is acceptable however. So I continued my strategy in using this chip targeted for I2C communication. So it can be used together with my IO board. This means of course that the final application will be running on a PC platform. I'm not sure yet if the OS will be windows or linux because FTDI drivers are provided for several possible operating systems. But I start my development on windows for a start.

The PCB layout will soon be in production.
Attached to this blog the drawing of the customized pcb. As soon it's soldered properly I post a picture. Further news will follow as we go ahead !