I started porting my PIC USB host code to Arduino platform. There is now a repository on GitHub. Right now it contains a single class of functions talking to MAX3421E. This is not enough to support full USB host functionality, but enough to get started. To follow examples given in this article, create a sub-directory in your Arduino hardware libraries directory and copy MAX3421E*.* files from the repository into it.
MAX3421E talks to Arduino via SPI, so you will need Arduino SPI library. SPI uses pin13 – a pin also used to blink LEDs. Some Arduinos even have this LED hard-wired to pin13. My Arduino has it, and it co-exists peacefully with MAX3421E, however, there is no guarantee it will work on others. It would depend on current drawn by the LED. If you have problems communicating with MAX3421E and/or you can’t see SPI clock on this pin with your oscilloscope or protocol analyzer, try to disconnect the LED and see if it changes anything.
Picture on the right shows the final arrangement. The breakout board sitting on top of protoshield is a 3.3V part. Since I’m using 3.3V Arduino Pro from Sparkfun, no level converters are necessary. Look at the previous article for the closeout picture and schematic. The breakout receives power from Arduino.
Let’s start using our MAX3421E functions to get better understanding how this controller operates. First of all, we need to make sure that Arduino talks to MAX3421E. Below is a little test which writes numbers from 0 to 255 to MAX3421E GPINPOL register and reads them back. It can be run from terminal or directly from IDE. If there is no errors, it will print a dot after each 65535 transmissions.
/* MAX3421E USB Host controller SPI test */ /* This sketch tests SPI communication between Arduino and MAX3421E USB host controller */ #include <Spi.h> #include <Max3421e.h> void setup(); void loop(); byte i; byte j = 0; byte gpinpol_copy; MAX3421E Max; void setup() { Serial.begin( 9600 ); Max.powerOn(); delay(200); } void loop() { gpinpol_copy = Max.regRd( rGPINPOL ); Serial.println("SPI test. Each '.' indicates 64K transferred. Press any key to stop."); while( Serial.available() == 0 ) { for( i = 0; i < 255; i++ ) { Max.regWr( rGPINPOL, i ); if( Max.regRd( rGPINPOL ) != i ) { Serial.println("SPI transmit/receive mismatch"); } }//for( i = 0; i < 255; i++ j++; if( j == 0 ) { Serial.print("."); } }//while( Serial.available() == 0 Max.regWr( rGPINPOL, gpinpol_copy ); Serial.println("\r\nStopped."); while( 1 ); //stop here }
The next sketch outputs MAX3421E registers. It is helpful to see the state of USB bus or transfer result. Here I just dump them all to the screen. There is no input, so this sketch can be run from serial monitor. Regretfully, default Arduino print doesn’t have a format for printing hex numbers with leading zeroes; to generate proper output, I borrowed a function from Peter H Anderson’s website.
/* This sketch dumps MAX3421E registers */ #include <Spi.h> #include <Max3421e.h> MAX3421E Max; //MAX3421E instance /* Regiser names/numbers for MAX3421E register dump */ typedef struct { const char* name; char number; } REGISTER_OUTPUT; REGISTER_OUTPUT max_register[] = { { "\r\nRCVFIFO:\t", rRCVFIFO }, { "\r\nSNDFIFO:\t", rSNDFIFO }, { "\r\nSUDFIFO:\t", rSUDFIFO }, { "\r\nRCVBC:\t", rRCVBC }, { "\r\nSNDBC:\t", rSNDBC }, { "\r\nUSBIRQ:\t", rUSBIRQ }, { "\r\nUSBIEN:\t", rUSBIEN }, { "\r\nUSBCTL:\t", rUSBCTL }, { "\r\nCPUCTL:\t", rCPUCTL }, { "\r\nPINCTL:\t", rPINCTL }, { "\r\nREVISION:\t", rREVISION }, { "\r\nIOPINS1:\t", rIOPINS1 }, { "\r\nIOPINS2:\t", rIOPINS2 }, { "\r\nGPINIRQ:\t", rGPINIRQ }, { "\r\nGPINIEN:\t", rGPINIEN }, { "\r\nGPINPOL:\t", rGPINPOL }, { "\r\nHIRQ:\t", rHIRQ }, { "\r\nHIEN:\t", rHIEN }, { "\r\nMODE:\t", rMODE }, { "\r\nPERADDR:\t", rPERADDR }, { "\r\nHCTL:\t", rHCTL }, { "\r\nHXFR:\t", rHXFR }, { "\r\nHRSL:\t", rHRSL } }; void setup() { Serial.begin( 9600 ); Max.powerOn(); } void loop() { unsigned char i; unsigned char numregs = sizeof( max_register )/sizeof( REGISTER_OUTPUT); for( i = 0; i < numregs; i++ ) { Serial.print( max_register[ i ].name); print_hex( Max.regRd( max_register[ i ].number ), 8 ); } while(1); } /* prints hex numbers with leading zeroes */ // copyright, Peter H Anderson, Baltimore, MD, Nov, '07 // source: http://www.phanderson.com/arduino/arduino_display.html void print_hex(int v, int num_places) { int mask=0, n, num_nibbles, digit; for (n=1; n<=num_places; n++) { mask = (mask << 1) | 0x0001; } v = v & mask; // truncate v to specified number of places num_nibbles = num_places / 4; if ((num_places % 4) != 0) { ++num_nibbles; } do { digit = ((v >> (num_nibbles-1) * 4)) & 0x0f; Serial.print(digit, HEX); } while(--num_nibbles); }
The last sketch demonstrates connection detection. We need to add 5 volts to the Vbus for this sketch to work.
This sketch prints Vbus state. Connection detection interrupts are serviced by MAX3421E::Task() function. print_vbus_state() function prints Vbus state when it changes.
/* MAX3421E interrupt loop */ #include <Spi.h> #include <Max3421e.h> MAX3421E Max; byte rcode; byte vbus_state; void setup() { Serial.begin( 9600 ); Serial.println("Start"); Max.powerOn(); } void loop() { Max.Task(); print_vbus_state(); } void print_vbus_state( void ) { char* vbus_states[] = { "Disconnected", "Illegal", "Full speed", "Low speed" }; byte tmpbyte; static byte last_state = 4; tmpbyte = Max.getVbusState(); if( tmpbyte != last_state ) { last_state = tmpbyte; Serial.println( vbus_states[ tmpbyte ] ); } return; }
Run the sketch and plug a device into USB connector. The sketch will print device speed. Unplug it and plug another one. USB flash drives are usually full-speed devices, mice and keyboards are often low-speed.
This is all about low-level functions. Next step is to generating USB transfers. I’m hoping to finish porting this part from my PIC firmware in a week or two. I will post results here as soon as I’m done.
Oleg.
Related posts:
- PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 3
- PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 2
- PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 1
- How to drive USB keyboard from Arduino
- Arduino USB Host – Peripherals.
- Arduino USB Host Shield build log. Part 4.
- Arduino USB Host Shield build log. Part 2.
- USB Host Shield for Arduino – first prototype.
- Arduino USB Host – USB Descriptors.
- Arduino USB host – Pre-prototyping.

