Posts

Arduino USB host – First programs.

MAX3421E on a protoshield

MAX3421E on a protoshield

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.

37 comments to Arduino USB host – First programs.

  • yashar

    hi,
    i am uploading the codes in my usb host shield and im getting errors like “Error: OSCOKIRQ failed to assert” and “Error: Vbus overload”. these errors come from the max.poweron(). and there is a problem with reading and writing max3421e regiters using spi protocol. also, i dont know if i have to connect gpin0-7 to anything or not!!
    please help

    thanks
    yashar

    • You can’t get vbus.overload error unless you get a short somewhere. Did you make this shield yourself or bought it here?

      • yashar

        i have bought it from sparkfun. but here are the step i took!!!
        i have all the source code necessary and the board and i swapped the pin numbers for GPX and RESET but it seems like nothing is getting written in the max3421e registers cause when i read them i dont get the same values . i even run your above codes that prints registers and all the registers are 00 and im getting “Error: OSCOKIRQ failed to assert” and “Error: Vbus overload” as well. however GPIN0-7 and GPout0-7 are not attached to anything in my board. i was wondering if they are significant or i should attach them some circuitry that im not aware of!!!

        thanks

  • Richard

    Did you make the changes required for the differences in the Sparkfun shield ?
    the pin numbers are not simply swapped.
    For the Sparkfun shield the reset must be on #define MAX_RESET 8
    MAX_GPX is not used, but is best as #define MAX_GPX 7

  • Richard

    Also remember the Sparkfun USB Host Shield is powered from Vin.
    Do you have an external power supply to Vin ?

  • Tom

    Hi

    last time i got all Errors from above this thread. the prob`s solved when i connect to a power-supply, then OSCOKIRQ will now work without undervoltage(VIN)–SPI also run`s like hell :-)Every Device will connect as defined(fast/slow) and no errors at all.

    think PC 5v from Arduino-Host is a little bit too small to support the usb-shield when testing githuk-files;-)
    All of the testfiles from githuk now working perfectly…i`am very happy!!

    dont forget to spend Volts on the Board! 🙂

    Good work @oleg!

    • Thanks for posting this! My guess is you are talking about Sparkfun shield since mine is powered from 5V and not from Vin. As a result, it will work off of USB power with no problems.

  • Jason Mitnick

    Oleg-

    I am also getting the above error messages. However, I purchased the shield directly from you. Any idea what the problem is? :/

    Thank you,

    Jason

    • Check that all connectors are in place, especially 2×3 ICSP one. Take a good picture of your setup and e-mail it to me – I’ll try to troubleshoot.

  • Vik

    I have tried the first code given in this post and I have tried it on Arduino 1.0 and on Arduino 0023 but I keep getting the below error

    In file included from sketch_apr23a.cpp:4:
    C:\Users\EWAQAMU\Desktop\arduino-0023\hardware\arduino\cores\arduino/max3421e.h:11: error: expected class-name before ‘{‘ token

    Can you please help in giving me an idea where I can be going wrong ?

  • Vik

    And I get the same error for all the three sketches given in this post.

  • Vik

    I have used the header files for the USB host and the now the code is working for Arduino 0023 platform

    • Fabian

      Hey Vik,
      could you please specify, which header yo used.I made it to get rid of all the other errors but i still get the one you mentioned!
      Thanks!

  • João Blasius

    Hello.

    I am connecting Max3421e with ATXmega192A3U to have USB host.

    I am using your code but I had to change it to adjust it for the ATXmega192A3U.

    The functions to read an write to registers are working but now I am stuck in the reset process.

    I am getting “OSCOKIRQ failed to assert”.

    The reset is ok because the CHIPRES bit is clearing the registers that it is suppose to clear.

    The problem is that the internal oscillator does not restart.

    Is there some configurations that I am missing?

    • How long do you wait for OSCOKIRQ to assert? It is ~700 cycles on 16MHz Atmega328. Make sure you don’t overflow the counter.

  • João Blasius

    Hello,

    The chip is dead.

    The internal ppl didn’t start and it should start as soon as we power it. That is when I realized that it was an hardware problem.

    I’ve replaced the chip and now it is working.

    Thanks for the quick response.

  • João Blasius

    Hello,

    I am going on with the project. I am implementing a Host and I want to read a pendrive.

    I am able to read and write to the control endPint wish allowed me to get the Configuration Descriptor and configure the rest of the endpoints.

    Now I am writing to the out endpint with no errors but I am unable to read.

    I am getting no data available (MAX_REG_HIRQ & bmHXFRDNIRQ != bmHXFRDNIRQ)

    I think that there is a problem with the SCSI Command that I am sending.

    Do you know some “Hello World” command to verify that I am communicating with the pendrive?

  • João Blasius

    Hello,

    Thanks for the quick response.

    I am getting the hrWRONGPID error in the dispatchPacket function.

    The packet ID is (tokIN | nDevice->bulk_in.address) so what can be wrong here…

    I don’t have any problems in reading or writing during the configuration process.

    The dispatchPacket function also doesn’t give any errors when I’m writing to the bulk_in.

    I only have the hrWRONGPID error when I read from the bulk_out.

    The tokIN is 0b00000000 (HS=0, ISO=0, OUTNIN=0, SETUP=0) so the only thing that can be wrong in the PID is the endpoint adress.

    These are the descriptors that I got during configuration:
    Configuration Descriptor: 09 02 20 00 01 01 00 80 64
    Interface Descriptor: 09 04 00 00 02 08 06 50 00
    InEndpoint Descriptor: 07 05 81 02 40 00 00
    OutEndpoint Descriptor: 07 05 02 02 40 00 00

    So the bulk_in.address is 0x81 and the bulk_out.address is 02.

    And I’ve initialized the bulk_out using the usb_initEndPoint function:
    bulk_out.address = address;//address = 0x02
    bulk_out.sendToggle = bmSNDTOG0;
    bulk_out.receiveToggle = bmRCVTOG0;
    bulk_out.attributes = USB_TRANSFER_TYPE_BULK;
    bulk_out.maxPacketSize = Endpoint.wMaxPacketSize;//wMaxPacketSize = 0x0040

    Do you have any idea on this?

    I am lost here…

  • João Blasius

    Hello,

    It was a stupid error 😛

    In the dispatch packet when we are reading we launch the transfer with:
    max3421e_write(MAX_REG_HXFR, (token | endpoint->address));

    where bulk_in.address is 0x01 and not 0x81.

    The Endpoint Address is:
    Bits 0..3b: Endpoint Number
    Bits 4..6b: Reserved – Set to Zero
    Bits 7: Direction 0 = Out, 1 = In (Ignored for Control Endpoints)

    The “Ignored for Control Endpoints” in bit 7 is very important 😛

    tokIN = 0b00000000
    bulk_in.address = 0b10000001
    So tokIN | bulk_in.address = 0b10000001

    The dispatch packet function returns hrWRONGPID error because the token sent was the bit 7 from the address.

  • Serge Yvan

    Oleg,

    I’m having problems with USB::getConfDescr.

    When I read config descriptor need to get length in first byte but I get the type descriptor.

    Read is missing the first by but rest is ok.

    Do you get same problem?

    • The length of the whole descriptor, i.e, interface, endpoint, etc. is contained in the second byte. I don’t have problems when I read it like that.

  • Serge Yvan

    In example from Blasius the descriptors is
    09 02 20 00 01 01 00 80 64 09 04 00 00 02 08 06 50 00 07 05 81 02 40 00 00 07 05 02 02 40 00 00

    mine is missing first 0x09 a became
    02 20 00 01 01 00 80 64 09 04 00 00 02 08 06 50 00 07 05 81 02 40 00 00 07 05 02 02 40 00 00 eb

    and 0xeb is garbage.

    Maxim application note 4000 has bug from NAK and bug is for OUT Transfers.

    Can this be same bug for IN Transfers?

  • Serge Yvan

    The project for school is interface to mouse and I use USB Host Shield
    library.

    The byte not important for enumeration but I tell teacher and now is ask this for project.

    Now I need to know to answer …

    This can be NAK bug like for OUT Transfers but is strange to be first byte.

  • Hello,

    I am developing USB HOST for V850 renesas controller using MAX3421E interface.

    I have some issue in communication of packets the process between USB Host(V850 controller) and peripheral which I follow is as below

    1. Enumeration step is working as expected.

    2. when I send an output packet to (End point 2)a positive acknowledgement is received (i.e.. 0x00). 0x00 means successful transfer.

    3. After sending output packet I am trying to receive input data by sending input packet to ( End point 1) a positive acknowledgement is received (i.e.. 0x00). 0x00 means successful transfer .but I am getting all 64 bytes of data is 0x00.

    Can you please suggest me whether I am following the steps properly and why their is a all data’s are 0x00 for the input packet. and please share any example code for OUT put packet sending and INPUT packet receiving. I am waiting for your replay.

    • What is the value in RCVBC after the transfer?

      • Alpay

        i copied the code and pasted it to my sketch and i downloaded the usb host shield 20 library and usb gpio library but i there are always errors when i compile the code before downloading the program to the avr chip . i don’t know the sources of error .

        they are the errors :
        sketch_mar13a:12: error: ‘MAX3421E’ does not name a type
        sketch_mar13a.cpp: In function ‘void setup()’:
        sketch_mar13a:17: error: ‘Max’ was not declared in this scope
        sketch_mar13a.cpp: In function ‘void loop()’:
        sketch_mar13a:23: error: ‘Max’ was not declared in this scope
        sketch_mar13a:23: error: ‘rGPINPOL’ was not declared in this scope

  • Mario

    Hello,
    I’m reading your examples because I’m ready to start on usb application for my arduino. One question regarding the slave select of the device. Is where, in your code, ss declared? Becuse I have to customize it on my application due to the fact that I have two spi devices, one on standard pin 10 and other (usb) planned on 8.
    Thank you in advance

    mm

  • Mario

    Hello oleg
    Thank you for your suggest, works fine.
    If is possible, can you answer again to my new questions, easy, I hope.
    On my project I have to send through usb a hex string 20 00 08 01 00 00 00 00, from Arduino host shield to the device.
    Can you suggest the command or something that could help me?
    I’m apologize for that but my sw knowledge is basic :0)

    Thank you

    mm

    • This would depend on the device, most have method called SndData(), which can be used for sending data to the device.

  • Mario

    It’s a oregon scientific weather station that has a hid usb interface, to start the data download I have to sent the init string.
    I’ll try with your comand

    here what I have seen with hid descriptor

    HU Init
    Addr:01
    NC:01

    Cnf:01
    HU configured

    Usage Page Undef(00)
    Usage
    Collection Application
    Usage
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(08)
    Input(00000000)
    Usage
    Logical Min(00)
    Logical Max(FF00)
    Report Size(08)
    Report Count(08)
    Output(00000010)
    End Collection