Posts

Lightweight USB Host. Part 3 – accessing MAX3421E.

MAX3421E protoboard setup

MAX3421E protoboard setup


In previous article I wrote about ways to build the project. Picture on the right shows one built on a piece of protoboard. The PIC is clocked at 64MHz using internal oscillator and SPI clock is 16MHz. It works well.
I’d like to start describing the code with an overview of a project layout. All project-related definitions are contained in project_config.h file. Types like BYTE, WORD, BOOL are defined in Generic_Types.h, the rest goes to a header file with the same name as .c module.

At present, the code consists of SPI functions, MAX3421E register read/write functions, MAX3421E event handler, and a small CLI used primarily to aid in debugging. SPI functions are explained in Interfacing LCD via SPI article, take a look if you haven’t already. In this article I will be talking about MAX3421E low-level routines.

MAX3421E-related functions reside in MAX3421E.c module and accompanying header file. All MAX3421E commands, registers and register bits are defined in the header. Access to MAX3421E always begins with sending a byte containing register number and a bit defining direction of subsequent transfer. Here is the ‘write’ function:

1
2
3
4
5
6
7
8
/* Single host register write   */
void MAXreg_wr(BYTE reg, BYTE val)
{
    Select_MAX3421E;
    SPI_wr ( reg + 2 ); //set WR bit and send register number
    SPI_wr ( val );
    Deselect_MAX3421E;
}

The ‘read’ function is similar:

1
2
3
4
5
6
7
8
9
10
/* Single host register read        */
BYTE MAXreg_rd(BYTE reg)
{
 BYTE tmp;
    Select_MAX3421E;
    SPI_wr ( reg );         //send register number
    tmp = SPI_wr ( 0x00 );  //send empty byte, read register contents
    Deselect_MAX3421E;
    return (tmp);
}

By default, MAX3421E operates in USB peripheral mode. To switch it to host mode, we need to set HOST bit and pull-down resistors on USB bus. Here is the power-on initialization function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* MAX3421E initialization after power-on   */
void MAX3421E_init( void )
{
 BYTE tmp;
    /* Configure full-duplex SPI, interrupt pulse   */
    MAXreg_wr( rPINCTL,(bmFDUPSPI+bmINTLEVEL+bmGPXB ));     //Full-duplex SPI, level interrupt, GPX
    MAX3421E_reset();                                       //stop/start the oscillator
    /* configure power switch   */
    Vbus_power( OFF );                                      //turn Vbus power off
    MAXreg_wr( rGPINIEN, bmGPINIEN7 );                      //enable interrupt on GPIN7 (power switch overload flag)
    Vbus_power( ON  );
    /* configure host operation */
    MAXreg_wr( rMODE, bmDPPULLDN|bmDMPULLDN|bmSOFKAENAB|bmHOST|bmSEPIRQ );      // set pull-downs, SOF, Host, Separate GPIN IRQ on GPX
    MAXreg_wr( rHIEN, bmFRAMEIE|bmCONDETIE|bmBUSEVENTIE );                      // enable SOF, connection detection, bus event IRQs
    MAXreg_wr(rHCTL,bmSAMPLEBUS);                                               // update the JSTATUS and KSTATUS bits
    MAX_busprobe();                                                             //check if anything is connected
    MAXreg_wr( rCPUCTL, 0x01 );                                                 //enable interrupt pin
}

On line 6, PINCTL register is loaded. SPI set to work in full-duplex, interrupt is set to level. On line 7, the chip (except SPI configuration) is reset. Here is a reset function – it simply sets ‘Chip reset” bit and waits for OSCIRQ signal to appear indicating that on-chip PLL is stable.

1
2
3
4
5
6
7
8
9
10
11
/* reset MAX3421E using chip reset bit. SPI configuration is not affected   */
void MAX3421E_reset( void )
{
 BYTE tmp = 0;
    MAXreg_wr( rUSBCTL,bmCHIPRES );                     //Chip reset. This stops the oscillator
    MAXreg_wr( rUSBCTL,0x00 );                          //Remove the reset
    while(!(MAXreg_rd( rUSBIRQ ) & bmOSCOKIRQ )) {      //wait until the PLL stabilizes
        tmp++;                                          //timeout after 256 attempts
        if( tmp == 0 ) break;
    }
}

The host initialization itself takes place at line 13. Here I setup pull-down resistors, set host mode, start generating SOF (start of frame) once every millisecond, and separate USB and GPIO IRQs.

The rest of initialization deals with setting up interrupt sources, sampling the USB bus to see if anything is connected to it, and finally allowing interrupts. Once initialized, MAX3421 will generate interrupts on USB bus events; all we need to do is to read those interrupts and act accordingly.

In next couple of days I will be moving code for this project to GitHub. As soon as I finish, I’ll post a link here and continue with articles.

Oleg.

35 comments to Lightweight USB Host. Part 3 – accessing MAX3421E.

  • Danny

    Hi,

    Can I physicallay interface the MAX3421E USB Host Controller IC to a 4-Port-USB-HUB so that MCU can access to 4 USB Devices (4 thumbdrive) ?

    OR the MAX3421e can only connect to only one downstream USB Devices like in the case of PIC32’s internal embedded USB host OTG.

    Thanks.

    Rgds,
    Danny

    • Any USB host is only capable of connecting to a single device, that’s in the standard. USB Hub is just a device which makes communication between host controller and devices connected to the hub transparent. However, you still need a piece of code to communicate to the hub to enumerate it and learn about physical bus events like device connect/disconnect to the downstream ports, and send bus resets out the hub ports. Such piece of code is not that hard to develop; hub communications were designed to be very simple.

  • Max

    quote from oleg”However, you still need a piece of code to communicate to the hub to enumerate it and learn about physical bus events like device connect/disconnect to the downstream ports, and send bus resets out the hub ports. Such piece of code is not that hard to develop; hub communications were designed to be very simple.”

    where can I get sample codes for USB hub? I also need this piece of code to be able to use multiple devices.

  • Richard

    The hub is a fairly simple device and described well in the USB specification. Basically you enumerate the hub first and allocate it an address. You can then communicate with the hub to enable ports and enumerate in turn if a device is present. Once all enumerated and addressed you can talk to each device direct simply by address.

    I do not have any MAX3421E code for this, but have implemented on SL811HS. In fact I recall the SL811HS development kit software does have an code example if you can find it.

    The MAX3421E does have advantage over some more “intelligent” processors when using a hub because the processor has more direct control over multiple devices and endpoints.

    Note that multiple devices means lots more endpoints and so greater demand on program and data memory. If as you suggest you wish to use mutiple mass storage devices, you will need at least a large AtMega or better processor with more RAM to support the multiple mass storage protocol layers and likely FAT too.

  • Lexxus

    Hi guys, im noob in USB programming and being playing for a while with the MAX3421e, interfacing with a silabs c8051f586.

    Im trying to make a USB host but after setting the USB host mode bit, if i read the register it says its in peripherial. Also when trying to detect a connection i cant see anything… I guess its becouse of the host bit… Can u help me guys??? i leave the piece of code where i set the host mode and then read it…

    Thx

    wreg(rPINCTL,(bmFDUPSPI)); ///fullduplex
    wreg(rUSBCTL,bmCHIPRES); //Chip reset
    wreg(rUSBCTL,0x00);
    while(!(command2 & bmOSCOKIRQ)) //command2 is where i get the read from the register
    {
    rreg(rUSBIRQ);
    }

    wreg(rMODE,(bmDPPULLDN|bmDMPULLDN|bmHOST)); // Note–initially set up as a FS host (LOWSPEED=0)
    rreg(rMODE); //Here is where I always get the read of 0xC0 shouldnt it be 0xC1 for host????
    wreg(rHIRQ,bmCONDETIRQ); // clear the connection detect IRQ
    // Activate HOST mode & turn on the 15K pulldown resistors on D+ and D-
    LED=0;
    LED2=0;
    do // See if anything is plugged in. If not, hang until something plugs in
    {
    rreg(rHIRQ);
    wreg(rHCTL,bmSAMPLEBUS); // update the JSTATUS and KSTATUS bits
    rreg(rHCTL);
    busstate = command2; // read them
    busstate &= (bmJSTATUS|bmKSTATUS); // check for either of them high
    }
    while (busstate==0);
    if (busstate==bmJSTATUS) // since we’re set to FS, J-state means D+ high
    {
    wreg(rMODE,(bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)); // make the MAX3421E a full speed host
    LED=~LED; //Full-Speed Device Detected
    }
    if (busstate==bmKSTATUS) // K-state means D- high
    {
    wreg(rMODE,(bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)); // make the MAX3421E a low speed host
    LED2=~LED2;
    } //Low-Speed Device Detected\n”);

  • Lexxus

    For the revision reg im getting a 0x13, i went a little backward to the reset part, I cant get the bmOSCOKIRQ to asserts, it was passing through the while because of a previous value loaded in the command2 var, i have clean up the code a little bit so this is the new one, but stills with the same issue…

    mult=rreg(rRevision); //Here the lecture is 0x13
    wreg(rPINCTL,(bmFDUPSPI|bmPOSINT)); ///Set up in fullduplex
    wreg(rUSBCTL,bmCHIPRES);
    wreg(rUSBCTL,0x00);
    while(!(rreg(rUSBIRQ) & bmOSCOKIRQ))
    {
    LED=1;while testing the bit, a led is off
    }
    LED=0; //if it passes the led turns on
    wreg(rMODE,0x01);
    mult=rreg(rMODE); //Here the lecture is 0x00
    wreg(rMODE,0xC1);
    mult=rreg(rMODE);//Here its 0xC0
    wreg(rIOPINS1,0x05);
    mult=rreg(rIOPINS1);//Here its 0xf5 since the IN pins are not connected

  • Lexxus

    Do you think it could be the Clock??? as right now is connected, MAX CLK (Xi)Pin to SILABS SYSCLK output pin P1.0 set up by crossbar, and Xo its not connected.

    Im sure silabs Output Sysclk is ok since i saw it on the Osciloscope

  • Lexxus

    first it was 24Mhz then i decreased it to 12Mhz

  • Lexxus

    it isnt connected

  • Lexxus

    Done…. no luck 🙁

  • Lexxus

    If i ddisconect sysclk from the silabs, this should make the MAX run with its internal osc right???

    • MAX3421E doesn’t have internal oscillator. It needs 12MHz crystal or external clock on Xi. Try it with crystal, your clock could be incorrect duty or something. Also, is your Vcc = 3.3V?

  • Lexxus

    Yeah i was thinking that maybe clock was doing something funny, Vcc is 3.3V so lets wait until monday so i can get the crystal and lets keep working… thx for the help man

  • Lexxus

    Yeah, it makes sense, since i could easily pass all the gpio test… since all are no clock dependancy registers… ill try to debug my sysclk the weekend, until monday that i get a crystal…

    Ill post the results, and thx for the help 🙂

  • Lexxus

    Ok so now the briefing:

    Sysclk from silabs was on 24Mhz not 12Mhz, once i managged to see this on the osciloscope i set it to 12Mhz and now its working!!! I can set to host and manage registers as i want.

    Thanks a lot for the help!!! and quick answering 🙂

  • Lexxus

    So i took a few steps and fall down again… Now im getting the 0x0D error (J-state response), i saw on another topic u said it was caused due Vbus power incorrect… My board doesnt have Vbus power, the final target is a BNR(Bank note recycler) this will be used for ATM’s so they are self powered, my testing device is a Custom Printer also self powered

    What im doing is after detecting something plug, then I see the device speed, configure MAX for high speed (in this case) then issue a reset and start interrogating the endpoint 0 asking for device descriptor… here is where i get the 0x0d any ideas on what could i be doing wrong???

  • Lexxus

    ok, now i have Vbus cnnected to 5v, but still getting the same error

    • Are you sure you’ve waited long enough after USB bus reset? J-state simply means device is not talking. Also, make sure you’re setting the speed correctly and also have SOF generation turned on.

  • Lexxus

    🙁 no luck!!!, i checked the SOF, is ok, speed is also ok, , im making 1 and 2 usb reset… im waiting 200 frames, ive wait more… like 800 frames, im loading rSUDFIFO with 0,0,0,0,1,0,8,0 from byte 0 to 7, i read this reg 8 times and i get this values, then load the setup token 0x10 or’ed with endpoint 0 so its 0x10 wait for the irq for the transmision to finish and then checking the HRSL and still have the 0x0D, if i disconnect the usb this change to 0x0E but cant get any further… pls help!!!

  • Lexxus

    i tried it, no luck… im rewritting all the program to se if something is wrong with the spi routines… but i doubt it… 🙁

  • Lexxus

    I rewrite everything and no luck… here is the code hope u can help me man

    SPI2SEND[0]=(bmFDUPSPI|bmPOSINT);
    writebytes(rPINCTL,1); ///Lo pone en fullduplex
    SPI2SEND[0]=bmCHIPRES;
    writebytes(rUSBCTL,1); // chip reset This stops the oscillator
    SPI2SEND[0]=0x00;
    writebytes(rUSBCTL,1); // remove the reset
    while(!(rreg(rUSBIRQ) & bmOSCOKIRQ)) ; // hang until the PLL stabilizes
    SPI2SEND[0]=(bmDPPULLDN|bmDMPULLDN|bmHOST);
    writebytes(rMODE,1); //Set as Host
    ///
    SPI2SEND[0]=bmCONDETIRQ;
    writebytes(rHIRQ,1); // clear the connection detect IRQ
    do // See if anything is plugged in. If not, hang until something plugs in
    {
    SPI2SEND[0]=bmSAMPLEBUS;
    writebytes(rHCTL,1); // clear the connection detect IRQ
    busstate = rreg(rHRSL); // read them
    busstate &= (bmJSTATUS|bmKSTATUS); // check for either of them high
    }
    while (busstate==0);
    if (busstate==bmJSTATUS) // since we’re set to FS, J-state means D+ high
    {
    SPI2SEND[0]=(bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB);
    writebytes(rMODE,1); // make the MAX3421E a full speed host
    LED=~LED; //Full-Speed Device Detected
    }
    if (busstate==bmKSTATUS) // K-state means D- high
    {
    SPI2SEND[0]=(bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB);
    writebytes(rMODE,1); // make the MAX3421E a full speed host
    LED2=~LED2;
    }
    SPI2SEND[0]=bmBUSRST;
    writebytes(rHCTL,1); // initiate the 50 msec bus reset
    while(rreg(rHCTL) & bmBUSRST); // Wait for the bus reset to complete
    k=0;
    frames=200;
    SPI2SEND[0]=bmFRAMEIRQ;
    writebytes(rHIRQ,1); // clear any pending
    while(k!=frames) // do this at least once
    {
    while(!(rreg(rHIRQ)& bmFRAMEIRQ));
    SPI2SEND[0]=bmFRAMEIRQ;
    writebytes(rHIRQ,1); // clear the IRQ
    k++;
    }
    SPI2SEND[0]=0x00; //80 06 00 01 00 00 08 00
    writebytes(rPERADDR,1);
    SPI2SEND[0]=0x00;
    SPI2SEND[1]=0x08;
    SPI2SEND[2]=0x00;
    SPI2SEND[3]=0x00;
    SPI2SEND[4]=0x01;
    SPI2SEND[5]=0x00;
    SPI2SEND[6]=0x06;
    SPI2SEND[7]=0x80;
    writebytes(rSUDFIFO,8);
    SPI2SEND[0]=bmHXFRDNIRQ;
    writebytes(rHIRQ,1); // clear the IRQ
    SPI2SEND[0]=(tokSETUP|0x00);
    writebytes(rHXFR,1); // launch the transfer
    while(!(rreg(rHIRQ) & bmHXFRDNIRQ)); // wait for the completion IRQ
    SPI2SEND[0]=bmHXFRDNIRQ;
    writebytes(rHIRQ,1); // clear the IRQ
    resultcode = (rreg(rHRSL) & 0x0F); // get the result
    if (resultcode==0x0D)
    {
    LED2=~LED2;
    }

    And the writebytes function…

    void writebytes (char reg,int length)
    {
    U8 SFRPAGE_save = SFRPAGE;
    U16 j;
    SFRPAGE = ACTIVE_PAGE;
    while (!NSSMD0); // Wait until the SPI is free, in case it’s busy
    reg=reg|0x02;
    ESPI0 = 0; // Disable SPI interrupts
    NSSMD0 = 0;
    SPI0DAT = reg;
    while (TXBMT != 1); // Wait until the command is moved into the XMIT buffer
    for(j=0; j<length; j++)
    {
    SPIF=0;
    SPI0DAT = SPI2SEND[j]; // Load the data into the buffer
    while (!SPIF); // Wait until the data is moved into
    }
    SPIF = 0;
    while (SPIF != 1); // Wait until the last byte of the data reaches the Slave
    SPIF = 0;
    NSSMD0 = 1; // Diable the Slave
    ESPI0 = 1; // Enable SPI interrupts
    SFRPAGE = SFRPAGE_save;
    }

    PLS 😀

  • Lexxus

    Hi Man, its being a long, i was moved to another project and hadnt time to post the final issue…

    After trying ur program and keep working on mine, i discovered that the electronic designes had put something like a filter for the USB datalines missplaced… so when i removed it everything worked flawlesly… so the issue is solved, also now the bill validator’s company is saying that his device cannot be driven with a micro since the responses to commands are 1-4 Mb size… what u think about it, would this be a limitation?

    • Take a look at my other projects, like Arduino library for MAX3421E and PTP digital camera control library on top of this where large chunks of data, some even more than 4mb, are processed by a small micro with not much difficulty.

  • Pedro

    Is it possible to connect a standard USB webcam with a microprocessor through a max3421 ?? Is it hard to implement ?

    please reply, thanks

  • Wes

    Any chance you have a linux driver ready for this chip? You seem to be the most experienced person with it so I thought I would ask.