Luminardo: USB Host Test

One of the most attractive features of Luminardo is its USB Host functionality built around MAX3421E chip and amazing USB Host Library for Arduino written and maintained by TKJ Electronics and by Circuits@Home. Today we are going to compile their library for our Luminardo and try to run a ‘quality test’ to make sure that SPI interface is functional, MAX3421E is accessible and it detects and can communicate to a random USB dongle being plugged in to Luminardo’s USB connector.

First thing to do is to get the latest Luminardo library (if you haven’t done it yet) from Luminardo Github repository. This post describes in detail how to install the library.

Second thing is to get the latest USB Host 2.0 for Arduino from USB Host 2.0 Library for Arduino Github Repository. Follow the instructions provided with the library to make it available for your Arduino IDE environment. Now we need to make a small change to make the library aware of our hardware platform. Find UsbCore.h and open it for editing. Find the piece of code at the beginnig of the file and add two highlighted lines as shown below:

/* shield pins. First parameter - SS pin, second parameter - INT pin */
#ifdef BOARD_BLACK_WIDDOW
typedef MAX3421e<P6, P3> MAX3421E; // Black Widow
#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
#if EXT_RAM
typedef MAX3421e<P20, P7> MAX3421E; // Teensy++ 2.0 with XMEM2
#else
typedef MAX3421e<P9, P8> MAX3421E; // Teensy++ 1.0 and 2.0
#endif
#elif defined(BOARD_MEGA_ADK)
typedef MAX3421e<P53, P54> MAX3421E; // Arduino Mega ADK
#elif defined(ARDUINO_AVR_BALANDUINO)
typedef MAX3421e<P20, P19> MAX3421E; // Balanduino
#elif defined(LUMINARDO)
typedef MAX3421e<P4, P18> MAX3421E; //Luminardo
#else
typedef MAX3421e<P10, P9> MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.) or Teensy 2.0 and 3.0
#endif

Now open your Arduino IDE, create an empty sketch then copy-and-paste the content of the sketch below:

/* USB Host Shield 2.0 board quality control routine */
/* To see the output set your terminal speed to 115200 */
/* for GPIO test to pass you need to connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, etc. */
/* otherwise press any key after getting GPIO error to complete the test */
/**/

#include <usbhub.h>
// Satisfy IDE, which only needs to see the include statment in the ino.

/* variables */
uint8_t rcode;
uint8_t usbstate;
uint8_t laststate;
USB_DEVICE_DESCRIPTOR buf;

/* objects */
USB Usb;

void setup() {
        laststate = 0;
        Serial.begin(57600);
        E_Notify(PSTR("\r\nCircuits At Home 2011"), 0x80);
        E_Notify(PSTR("\r\nUSB Host Shield Quality Control Routine"), 0x80);
        E_Notify(PSTR("\r\nModified in Magictale Electronics for testing Luminardo boards"), 0x80);
        /* SPI quick test - check revision register */
        E_Notify(PSTR("\r\nReading REVISION register... Die revision "), 0x80);
        Usb.Init(); // Initializes SPI, we don't care about the return value here
        {
                uint8_t tmpbyte = Usb.regRd(rREVISION);
                switch (tmpbyte) {
                        case( 0x01): //rev.01
                                E_Notify(PSTR("01"), 0x80);
                                break;
                        case( 0x12): //rev.02
                                E_Notify(PSTR("02"), 0x80);
                                break;
                        case( 0x13): //rev.03
                                E_Notify(PSTR("03"), 0x80);
                                break;
                        default:
                                E_Notify(PSTR("invalid. Value returned: "), 0x80);
                                print_hex(tmpbyte, 8);
                                halt55();
                                break;
                }//switch( tmpbyte...
        }//check revision register
        /* SPI long test */
        {
                E_Notify(PSTR("\r\nSPI long test. Transfers 1MB of data. Each dot is 64K"), 0x80);
                uint8_t sample_wr = 0;
                uint8_t sample_rd = 0;
                uint8_t gpinpol_copy = Usb.regRd(rGPINPOL);
                for (uint8_t i = 0; i < 16; i++) {
                        for (uint16_t j = 0; j < 65535; j++) {
                                Usb.regWr(rGPINPOL, sample_wr);
                                sample_rd = Usb.regRd(rGPINPOL);
                                if (sample_rd != sample_wr) {
                                        E_Notify(PSTR("\r\nTest failed.  "), 0x80);
                                        E_Notify(PSTR("Value written: "), 0x80);
                                        print_hex(sample_wr, 8);
                                        E_Notify(PSTR(" read: "), 0x80);
                                        print_hex(sample_rd, 8);
                                        halt55();
                                }//if( sample_rd != sample_wr..
                                sample_wr++;
                        }//for( uint16_t j...
                        E_Notify(PSTR("."), 0x80);
                }//for( uint8_t i...
                Usb.regWr(rGPINPOL, gpinpol_copy);
                E_Notify(PSTR(" SPI long test passed"), 0x80);
        }//SPI long test
        /* PLL test. Stops/starts MAX3421E oscillator several times */
        {
                E_Notify(PSTR("\r\nPLL test. 100 chip resets will be performed"), 0x80);
                /* check current state of the oscillator */
                if (!(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ)) { //wrong state - should be on
                        E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80);
                        press_any_key();
                }
                /* Restart oscillator */
                E_Notify(PSTR("\r\nResetting oscillator\r\n"), 0x80);
                for (uint16_t i = 0; i < 100; i++) {
                        E_Notify(PSTR("\rReset number "), 0x80);
                        Serial.print(i, DEC);
                        Usb.regWr(rUSBCTL, bmCHIPRES); //reset
                        if (Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) { //wrong state - should be off
                                E_Notify(PSTR("\r\nCurrent oscillator state unexpected."), 0x80);
                                halt55();
                        }
                        Usb.regWr(rUSBCTL, 0x00); //release from reset
                        uint16_t j = 0;
                        for (j = 0; j < 65535; j++) { //tracking off to on time
                                if (Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) {
                                        E_Notify(PSTR(" Time to stabilize - "), 0x80);
                                        Serial.print(j, DEC);
                                        E_Notify(PSTR(" cycles\r\n"), 0x80);
                                        break;
                                }
                        }//for( uint16_t j = 0; j < 65535; j++
                        if (j == 0) {
                                E_Notify(PSTR("PLL failed to stabilize"), 0x80);
                                press_any_key();
                        }
                }//for( uint8_t i = 0; i < 255; i++

        }//PLL test
        /* initializing USB stack */
        if (Usb.Init() == -1) {
                E_Notify(PSTR("\r\nOSCOKIRQ failed to assert"), 0x80);
                halt55();
        }
        E_Notify(PSTR("\r\nChecking USB device communication.\r\n"), 0x80);
}

void loop() {
        delay(200);
        Usb.Task();
        usbstate = Usb.getUsbTaskState();
        if (usbstate != laststate) {
                laststate = usbstate;
                /**/
                switch (usbstate) {
                        case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE):
                                E_Notify(PSTR("\r\nWaiting for device..."), 0x80);
                                E_Notify(PSTR("Enabling VBus..."), 0x80);  
                                Usb.gpioWr(1);
                                E_Notify(PSTR(" enabled"), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_RESET_DEVICE):
                                E_Notify(PSTR("\r\nDevice connected. Resetting..."), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_WAIT_SOF):
                                E_Notify(PSTR("\r\nReset complete. Waiting for the first SOF..."), 0x80);
                                break;
                        case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE):
                                E_Notify(PSTR("\r\nSOF generation started. Enumerating device..."), 0x80);
                                break;
                        case( USB_STATE_ADDRESSING):
                                E_Notify(PSTR("\r\nSetting device address..."), 0x80);
                                break;
                        case( USB_STATE_RUNNING):
                                E_Notify(PSTR("\r\nGetting device descriptor"), 0x80);
                                rcode = Usb.getDevDescr(1, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*) & buf);

                                if (rcode) {
                                        E_Notify(PSTR("\r\nError reading device descriptor. Error code "), 0x80);
                                        print_hex(rcode, 8);
                                } else {
                                        /**/
                                        E_Notify(PSTR("\r\nDescriptor Length:\t"), 0x80);
                                        print_hex(buf.bLength, 8);
                                        E_Notify(PSTR("\r\nDescriptor type:\t"), 0x80);
                                        print_hex(buf.bDescriptorType, 8);
                                        E_Notify(PSTR("\r\nUSB version:\t\t"), 0x80);
                                        print_hex(buf.bcdUSB, 16);
                                        E_Notify(PSTR("\r\nDevice class:\t\t"), 0x80);
                                        print_hex(buf.bDeviceClass, 8);
                                        E_Notify(PSTR("\r\nDevice Subclass:\t"), 0x80);
                                        print_hex(buf.bDeviceSubClass, 8);
                                        E_Notify(PSTR("\r\nDevice Protocol:\t"), 0x80);
                                        print_hex(buf.bDeviceProtocol, 8);
                                        E_Notify(PSTR("\r\nMax.packet size:\t"), 0x80);
                                        print_hex(buf.bMaxPacketSize0, 8);
                                        E_Notify(PSTR("\r\nVendor  ID:\t\t"), 0x80);
                                        print_hex(buf.idVendor, 16);
                                        E_Notify(PSTR("\r\nProduct ID:\t\t"), 0x80);
                                        print_hex(buf.idProduct, 16);
                                        E_Notify(PSTR("\r\nRevision ID:\t\t"), 0x80);
                                        print_hex(buf.bcdDevice, 16);
                                        E_Notify(PSTR("\r\nMfg.string index:\t"), 0x80);
                                        print_hex(buf.iManufacturer, 8);
                                        E_Notify(PSTR("\r\nProd.string index:\t"), 0x80);
                                        print_hex(buf.iProduct, 8);
                                        E_Notify(PSTR("\r\nSerial number index:\t"), 0x80);
                                        print_hex(buf.iSerialNumber, 8);
                                        E_Notify(PSTR("\r\nNumber of conf.:\t"), 0x80);
                                        print_hex(buf.bNumConfigurations, 8);
                                        /**/
                                        E_Notify(PSTR("\r\n\nAll tests passed. Press RESET to restart test"), 0x80);
                                        while (1);
                                }
                                break;
                        case( USB_STATE_ERROR):
                                E_Notify(PSTR("\r\nUSB state machine reached error state"), 0x80);
                                break;

                        default:
                                break;
                }//switch( usbstate...
        }
}//loop()...

/* constantly transmits 0x55 via SPI to aid probing */
void halt55() {

        E_Notify(PSTR("\r\nUnrecoverable error - test halted!!"), 0x80);
        E_Notify(PSTR("\r\n0x55 pattern is transmitted via SPI"), 0x80);
        E_Notify(PSTR("\r\nPress RESET to restart test"), 0x80);

        while (1) {
                Usb.regWr(0x55, 0x55);
        }
}

/* prints hex numbers with leading zeroes */
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);
}

/* prints "Press any key" and returns when key is pressed */
void press_any_key() {
        E_Notify(PSTR("\r\nPress any key to continue..."), 0x80);
        while (Serial.available() <= 0); //wait for input
        Serial.read(); //empty input buffer
        return;
}

Pick a random USB dongle (we used a Bluetooth dongle ‘Targus’) and plug it into Luminardo board as shown on the picture below:

Luminardo with Bluetooth Dongle 'Targus'

Luminardo with Bluetooth Dongle ‘Targus’

Compile the sketch, upload it to Luminardo, open terminal application, configure select appropriate serial port and baudrate (57600bps in our case) and watch what is being reported. If everything is done correctly you should be able so see something like this:

Circuits At Home 2011
USB Host Shield Quality Control Routine
Modified in Magictale Electronics for testing Luminardo boards
Reading REVISION register... Die revision 03
SPI long test. Transfers 1MB of data. Each dot is 64K................ SPI long test passed
PLL test. 100 chip resets will be performed
Resetting oscillator
Reset number 0 Time to stabilize - 349 cycles
Reset number 1 Time to stabilize - 350 cycles
...
Reset number 99 Time to stabilize - 349 cycles

Checking USB device communication.

Waiting for device...Enabling VBus... enabled
Reset complete. Waiting for the first SOF...
Getting device descriptor
Descriptor Length:      12
Descriptor type:        01
USB version:            0200
Device class:           E0
Device Subclass:        01
Device Protocol:        01
Max.packet size:        40
Vendor  ID:             0A12
Product ID:             0001
Revision ID:            3164
Mfg.string index:       00
Prod.string index:      02
Serial number index:    00
Number of conf.:        01

All tests passed. Press RESET to restart test

If there is ‘All tests passed’ at the end it means that the hardware is functional and USB Host library is configured correctly. The platform is ready for real experiments!