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.
Related posts:
- PS3 and Wiimote Game Controllers on the Arduino Host Shield: Part 3
- Arduino USB Host – Peripherals.
- Arduino USB Host – USB Descriptors.
- Arduino USB host – First programs.
- Lightweight USB host. Part 6 – introduction to HID.
- Lightweight USB Host. Part 5 – control transfers.
- Arduino USB host – Pre-prototyping.
- Lightweight USB Host. Part 4 – The code.
- Lightweight USB Host. Part 2 – Hardware.
- Lightweight USB Host. Part 1 – Motivation.


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.
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.
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.