Towards an FT232 Driver for the USB Host Shield- Part 0

FTDI232RL IC on a breakout board

FTDI232RL IC on a breakout board

Based on some discussion on the Arduino Forum, Richard has added this blog entry as work in progress on developing a library to support FTDI Serial port devices on the USB Host Shield. While cautious to publish at this early testing stage, the content here should help parallel developers.

My thoughts last year were that an FT232 driver for the Host Shield was not really useful. It seemed rather reverse to add a USB host plus a USB device to achieve something that can be done with a piece of wire. However USB is coming to be the baseline interface and is now the only interface offered on many serial devices.

USB Shield Hardware
Since my last blog update, Sparkfun have released their own version of the USB Host Shield. Great to have another source of the shield, with good stock at many International Sparkfun distributors. However they did add a couple of problems too:
Sparkfun swapped the RESET and the GPX pins from the original from Oleg. This means the shield will not work with the libraries from Oleg without a modification.
The current Max3421e_constants.h from Oleg has:

/* Arduino pin definitions for USB Host Shield signals. They can be changed here or while calling constructor */
#define MAX_SS 10
#define MAX_INT 9
//#define MAX_GPX 8
#define MAX_RESET 7

These work for the shield from Oleg, and existing published code and libraries without change.

However for the Sparkfun Shield they must be changed in Max3421e_constants.h, or optionally changed when the constructor is called to:

#define MAX_SS 10
#define MAX_INT 9
//#define MAX_GPX 7
#define MAX_RESET 8

To add further complication, In the FTDI code I will describe below MAX_GPX is used, so must be uncommented in Max3421e_constants.h or added as a #define in the sketch. Again this should match the shield type as above.

The other common problem with the Sparkfun shield is that it is powered differently from the shield from Oleg. The Sparkfun Shield and the connected USB device is powered from Vin via voltage regulators. While the Arduino 5V and 3.3V supplies can be provided by either Vin or Vusb, Vin cannot be provided by Vusb. This means the Sparkfun shield must have an external Vin supply to work, while the shield from Oleg does not need this.

Library Updates
There have been some library updated from Oleg over the last months to include a number of changes to support latest applications. The latest version from Oleg should be backward compatible to existing sketches and examples.
Here again, I will propose another addtional change to the usb.h library. The reason for this is that the existing devices which have been used on the USB Host Shield, all returned either a known number of bytes during an in transfer, or the data bytes contained the length within the data itself. The FTDI device, does not do this and sends a variable length packet, without length indication within the data. Thus we need to pass the data length back to the sketch in some other way.
To maintain backward compatibility I added an new version of the in transfer:
In usb.h
byte inTransfern( byte addr, byte ep, unsigned int* nbytes, char* data, unsigned int nak_limit = USB_NAK_LIMIT );

and in usb.cpp

/* Version with pointer to nbytes, to return actual number */
/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
/* Keep sending INs and writes data to memory area pointed by 'data' */
/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error,
fe USB xfer timeout */
byte USB::inTransfern( byte addr, byte ep, unsigned int* nbytes, char* data, unsigned int nak_limit )
 byte rcode;
 byte pktsize;
 byte maxpktsize = devtable[ addr ].epinfo[ ep ].MaxPktSize;
 unsigned int xfrlen = 0;
  regWr( rHCTL, devtable[ addr ].epinfo[ ep ].rcvToggle ); //set toggle value
  while( 1 ) { // use a 'return' to exit this loop
    rcode = dispatchPkt( tokIN, ep, nak_limit ); //IN packet to EP-'endpoint'. Function takes care of NAKS.
    if( rcode ) {
      return( rcode ); //should be 0, indicating ACK. Else return error code.
    /* check for RCVDAVIRQ and generate error if not present */
    /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */
    if(( regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) {
      return ( 0xf0 ); //receive error
    pktsize = regRd( rRCVBC ); //number of received bytes
    data = bytesRd( rRCVFIFO, pktsize, data );
    regWr( rHIRQ, bmRCVDAVIRQ ); // Clear the IRQ & free the buffer
    xfrlen += pktsize; // add this packet's byte count to total transfer length
    /* The transfer is complete under two conditions: */
    /* 1. The device sent a short packet (L.T. maxPacketSize) */
    /* 2. 'nbytes' have been transferred. */
    if (( pktsize = *nbytes )) { // have we transferred 'nbytes' bytes?
      if( regRd( rHRSL ) & bmRCVTOGRD ) { //save toggle value
        devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1;
      else {
        devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG0;
      *nbytes = xfrlen;
      return( 0 );
    }//if (( pktsize = *nbytes ))...
  }//while( 1 )...

FTDI FT232 devices
The FT232 range of devices from FTDI is now fairly large and quite a lot of data is available on the FTDI Website.
Further information was taken from linux driver sources and other USB Host sources.
I did not get a chance to test devices other than the FT232B and FT232R at this time.

Blocking vs. Unblocking code
The eventual objective is to create a version of the serial library which supports the FTDI Serial port over the USB Host Shield. The existing serial libraries allow for blocking code, especially when waiting for input. It was therefore decided to use interrupts to perform the regular tasks required for the USB protocol layers, so the FTDI libraries could appear identical to the existing serial drivers.

The MAX3421e MAX_GPX pin is set up to send a pulse output at the SOF rate of the USB (1 per ms) and this generates a pin change interrupt on the Arduino. This interrupt routine then performs the required USB tasks without need for the sketch to repeatedly call them. Some protection is also added to ensure that USB transactions generated outside the interrupt routine and any associated temporary data, are protected from the interrupt routines.

Buffer Management
Seperate data buffers are created for the transmit and receive directions.
The transmit buffer is filled by the calls to send a serial byte via the FTDI. If the buffer is filled by the byte the data is sent over the USB immediately, other wise it is sent at the next SOF interrupt.

Calls to read the receive buffer check for any characters locally, and return them if found. If there are no characters in the local buffer, a read is made to the remote FTDI device to check if any data is ready there. This process is to reduce the local buffer requirement. The first two bytes returned over USB are status bytes, not data, and these are also sent at regular intervals without data. I am not sure whether this is the best way to handle the receive buffers.

Code is here on github

6 comments to Towards an FT232 Driver for the USB Host Shield- Part 0

  • Max

    Hey, I have never programmed in C/C+ but I think there is a slight mistake in the code you posted. Wherever there is a “amp;” it should be changed to “&”. The code as it currently is does not compile.
    Also, i like this FT232 project and i hope you finish it soon because I could really use it.

  • rgm

    I’m very excited for the FT232 driver for the USB host shield! I’ve accidentally purchased a Sparkfun Buttonpad Controller USB (instead of SPI) and am looking forward to being able to drive it easily from a microcontroller / Arduino. It uses an FT245RL, which I hope is similar enough to the FT232 to make porting straightforward.

    You’re right, it’s totally perverse to have this serial-USB-USB-serial situation, but sometimes it’s hard to bypass the conversion chip on the target board. While I wait for USB Host Shield code I’ll keep looking for a way to patch directly into serial for my Buttonpad Controller USB…

  • I too am very excited to get my shield (I just ordered one) to see if I can capture the serial data coming out of my Ohaus scale. Funny enough it uses a FTDI chip to convert serial to USB which I want to convert back to serial and read with my Arduino. I hope this is easy enough with the FT232 library….

  • Ian Carson

    I hope to be able to use this excellent work to send control signal to a 4axis stepper motor board from JAFMotion produced by a sketch on an Arduino Mega. The sketch is intended to drive a robotic panorama head and will also control the taking of HDR exposures at each camera position is moves the head to. Looking forward to seeing Part 1…

    • FTDI chip is supported by rev.2.0 library. I don’t have example sketch specific to FTDI but the interface is very similar to ACM or PL2303.