/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. This software may be distributed and modified under the terms of the GNU General Public License version 2 (GPL2) as published by the Free Software Foundation and appearing in the file GPL2.TXT included in the packaging of this file. Please note that GPL2 Section 2[b] requires that all works based on this software must also be made publicly available under the terms of the GPL2 ("Copyleft"). Contact information ------------------- Circuits At Home, LTD Web : http://www.circuitsathome.com e-mail : support@circuitsathome.com */ /* MAX3421E-based USB Host Library header file */ #ifndef _USBHOST_H_ #define _USBHOST_H_ #include "avrpins.h" #include "max3421e.h" #include "usb_ch9.h" #include /* SPI initialization */ template< typename CLK, typename MOSI, typename MISO, typename SPI_SS > class SPi { public: static void init() { //uint8_t tmp; CLK::SetDirWrite(); MOSI::SetDirWrite(); MISO::SetDirRead(); SPI_SS::SetDirWrite(); /* mode 00 (CPOL=0, CPHA=0) master, fclk/2. Mode 11 (CPOL=11, CPHA=11) is also supported by MAX3421E */ SPCR = 0x50; SPSR = 0x01; // 0x01 /**/ //tmp = SPSR; //tmp = SPDR; } }; /* SPI pin definitions. see avrpins.h */ #if defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) typedef SPi< Pb1, Pb2, Pb3, Pb0 > spi; #endif #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) typedef SPi< Pb5, Pb3, Pb4, Pb2 > spi; #endif #if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi; #endif template< typename SS, typename INTR > class MAX3421e /* : public spi */ { static uint8_t vbusState; public: MAX3421e(); void regWr(uint8_t reg, uint8_t data); uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p); void gpioWr(uint8_t data); uint8_t regRd(uint8_t reg); uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p); uint8_t gpioRd(); uint16_t reset(); int8_t Init(); uint8_t getVbusState(void) { return vbusState; }; void busprobe(); uint8_t GpxHandler(); uint8_t IntHandler(); uint8_t Task(); }; template< typename SS, typename INTR > uint8_t MAX3421e< SS, INTR >::vbusState = 0; /* constructor */ template< typename SS, typename INTR > MAX3421e< SS, INTR >::MAX3421e() { /* pin and peripheral setup */ SS::SetDirWrite(); SS::Set(); spi::init(); INTR::SetDirRead(); #ifdef BOARD_MEGA_ADK /* For Mega ADK, which has Max3421e on-board, set MAX_RESET to Output mode, and pull Reset to HIGH */ DDRJ |= _BV(PJ2); PORTJ &= ~_BV(PJ2); PORTJ |= _BV(PJ2); #endif /* MAX3421E - full-duplex SPI, level interrupt */ regWr(rPINCTL, (bmFDUPSPI + bmINTLEVEL)); }; /* write single byte into MAX3421 register */ template< typename SS, typename INTR > void MAX3421e< SS, INTR >::regWr(uint8_t reg, uint8_t data) { SS::Clear(); SPDR = (reg | 0x02); while(!(SPSR & (1 << SPIF))); SPDR = data; while(!(SPSR & (1 << SPIF))); SS::Set(); return; }; /* multiple-byte write */ /* returns a pointer to memory position after last written */ template< typename SS, typename INTR > uint8_t* MAX3421e< SS, INTR >::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { SS::Clear(); SPDR = (reg | 0x02); //set WR bit and send register number while(nbytes--) { while(!(SPSR & (1 << SPIF))); //check if previous byte was sent SPDR = (*data_p); // send next data byte data_p++; // advance data pointer } while(!(SPSR & (1 << SPIF))); SS::Set(); return( data_p); } /* GPIO write */ /*GPIO byte is split between 2 registers, so two writes are needed to write one byte */ /* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */ template< typename SS, typename INTR > void MAX3421e< SS, INTR >::gpioWr(uint8_t data) { regWr(rIOPINS1, data); data >>= 4; regWr(rIOPINS2, data); return; } /* single host register read */ template< typename SS, typename INTR > uint8_t MAX3421e< SS, INTR >::regRd(uint8_t reg) { SS::Clear(); SPDR = reg; while(!(SPSR & (1 << SPIF))); SPDR = 0; //send empty byte while(!(SPSR & (1 << SPIF))); SS::Set(); return( SPDR); } /* multiple-byte register read */ /* returns a pointer to a memory position after last read */ template< typename SS, typename INTR > uint8_t* MAX3421e< SS, INTR >::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { SS::Clear(); SPDR = reg; while(!(SPSR & (1 << SPIF))); //wait while(nbytes) { SPDR = 0; //send empty byte nbytes--; while(!(SPSR & (1 << SPIF))); #if 0 { *data_p = SPDR; printf("%2.2x ", *data_p); } data_p++; } printf("\r\n"); #else *data_p++ = SPDR; } #endif SS::Set(); return( data_p); } /* GPIO read. See gpioWr for explanation */ /* GPIN pins are in high nibbles of IOPINS1, IOPINS2 */ template< typename SS, typename INTR > uint8_t MAX3421e< SS, INTR >::gpioRd() { uint8_t gpin = 0; gpin = regRd(rIOPINS2); //pins 4-7 gpin &= 0xf0; //clean lower nibble gpin |= (regRd(rIOPINS1) >> 4); //shift low bits and OR with upper from previous operation. return( gpin); } /* reset MAX3421E. Returns number of cycles it took for PLL to stabilize after reset or zero if PLL haven't stabilized in 65535 cycles */ template< typename SS, typename INTR > uint16_t MAX3421e< SS, INTR >::reset() { uint16_t i = 0; regWr(rUSBCTL, bmCHIPRES); regWr(rUSBCTL, 0x00); while(++i) { if((regRd(rUSBIRQ) & bmOSCOKIRQ)) { break; } } return( i); } ///* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */ //template< typename SS, typename INTR > //int8_t MAX3421e< SS, INTR >::Init() //{ // if( reset() == 0 ) { //OSCOKIRQ hasn't asserted in time // return ( -1 ); // } // regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST ); // set pull-downs, Host // // return( 0 ); //} /* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */ template< typename SS, typename INTR > int8_t MAX3421e< SS, INTR >::Init() { if(reset() == 0) { //OSCOKIRQ hasn't asserted in time return( -1); } regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection /* check if device is connected */ regWr(rHCTL, bmSAMPLEBUS); // sample USB bus while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish busprobe(); //check if anything is connected regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt regWr(rCPUCTL, 0x01); //enable interrupt pin return( 0); } /* probe bus to determine device presence and speed and switch host to this speed */ template< typename SS, typename INTR > void MAX3421e< SS, INTR >::busprobe() { uint8_t bus_sample; bus_sample = regRd(rHRSL); //Get J,K status bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the byte switch(bus_sample) { //start full-speed or low-speed host case( bmJSTATUS): if((regRd(rMODE) & bmLOWSPEED) == 0) { regWr(rMODE, MODE_FS_HOST); //start full-speed host vbusState = FSHOST; } else { regWr(rMODE, MODE_LS_HOST); //start low-speed host vbusState = LSHOST; } break; case( bmKSTATUS): if((regRd(rMODE) & bmLOWSPEED) == 0) { regWr(rMODE, MODE_LS_HOST); //start low-speed host vbusState = LSHOST; } else { regWr(rMODE, MODE_FS_HOST); //start full-speed host vbusState = FSHOST; } break; case( bmSE1): //illegal state vbusState = SE1; break; case( bmSE0): //disconnected state regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ); vbusState = SE0; break; }//end switch( bus_sample ) } /* MAX3421 state change task and interrupt handler */ template< typename SS, typename INTR > uint8_t MAX3421e< SS, INTR >::Task(void) { uint8_t rcode = 0; uint8_t pinvalue; //Serial.print("Vbus state: "); //Serial.println( vbusState, HEX ); pinvalue = INTR::IsSet(); //Read(); //pinvalue = digitalRead( MAX_INT ); if(pinvalue == 0) { rcode = IntHandler(); } // pinvalue = digitalRead( MAX_GPX ); // if( pinvalue == LOW ) { // GpxHandler(); // } // usbSM(); //USB state machine return( rcode); } template< typename SS, typename INTR > uint8_t MAX3421e< SS, INTR >::IntHandler() { uint8_t HIRQ; uint8_t HIRQ_sendback = 0x00; HIRQ = regRd(rHIRQ); //determine interrupt source //if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler // HIRQ_sendback |= bmFRAMEIRQ; //}//end FRAMEIRQ handling if(HIRQ & bmCONDETIRQ) { busprobe(); HIRQ_sendback |= bmCONDETIRQ; } /* End HIRQ interrupts handling, clear serviced IRQs */ regWr(rHIRQ, HIRQ_sendback); return( HIRQ_sendback); } //template< typename SS, typename INTR > //uint8_t MAX3421e< SS, INTR >::GpxHandler() //{ // uint8_t GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register //// if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload //// vbusPwr( OFF ); //attempt powercycle //// delay( 1000 ); //// vbusPwr( ON ); //// regWr( rGPINIRQ, bmGPINIRQ7 ); //// } // return( GPINIRQ ); //} #endif //_USBHOST_H_