diff options
Diffstat (limited to 'lib/usbhost/USB_Host_Shield_2.0/usbhost.h')
| -rw-r--r-- | lib/usbhost/USB_Host_Shield_2.0/usbhost.h | 529 | 
1 files changed, 529 insertions, 0 deletions
| diff --git a/lib/usbhost/USB_Host_Shield_2.0/usbhost.h b/lib/usbhost/USB_Host_Shield_2.0/usbhost.h new file mode 100644 index 0000000000..eba480e60c --- /dev/null +++ b/lib/usbhost/USB_Host_Shield_2.0/usbhost.h @@ -0,0 +1,529 @@ +/* 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   :  [email protected] + */ +/* MAX3421E-based USB Host Library header file */ + + +#if !defined(_usb_h_) || defined(_USBHOST_H_) +#error "Never include usbhost.h directly; include Usb.h instead" +#else +#define _USBHOST_H_ + +#if USING_SPI4TEENSY3 +#include <spi4teensy3.h> +#include <sys/types.h> +#endif + +/* SPI initialization */ +template< typename SPI_CLK, typename SPI_MOSI, typename SPI_MISO, typename SPI_SS > class SPi { +public: +#if USING_SPI4TEENSY3 +        static void init() { +                // spi4teensy3 inits everything for us, except /SS +                // CLK, MOSI and MISO are hard coded for now. +                // spi4teensy3::init(0,0,0); // full speed, cpol 0, cpha 0 +                spi4teensy3::init(); // full speed, cpol 0, cpha 0 +                SPI_SS::SetDirWrite(); +                SPI_SS::Set(); +        } +#elif SPI_HAS_TRANSACTION +        static void init() { +                SPI.begin(); // The SPI library with transaction will take care of setting up the pins - settings is set in beginTransaction() +        } +#elif !defined(SPDR) +        static void init() { +                SPI_SS::SetDirWrite(); +                SPI_SS::Set(); +                SPI.begin(); +#if defined(__MIPSEL__) +                SPI.setClockDivider(1); +#elif defined(__ARDUINO_X86__) +                #ifdef SPI_CLOCK_1M // Hack used to check if setClockSpeed is available +                    SPI.setClockSpeed(12000000); // The MAX3421E can handle up to 26MHz, but in practice this was the maximum that I could reliably use +                #else +                    SPI.setClockDivider(SPI_CLOCK_DIV2); // This will set the SPI frequency to 8MHz - it could be higher, but it is not supported in the old API +                #endif +#else +                SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz +#endif +        } +#elif defined(RBL_NRF51822) +        static void init() { +                SPI_SS::SetDirWrite(); +                SPI_SS::Set(); +                SPI.begin(); +                // SPI.setFrequency(SPI_FREQUENCY_8M); +        } +#else +        static void init() { +                //uint8_t tmp; +                SPI_CLK::SetDirWrite(); +                SPI_MOSI::SetDirWrite(); +                SPI_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; +        } +#endif +}; + +/* 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; +#elif  defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) +typedef SPi< Pb5, Pb3, Pb4, Pb2 > spi; +#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi; +#elif (defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))) || defined(__ARDUINO_X86__) || defined(__MIPSEL__) +typedef SPi< P13, P11, P12, P10 > spi; +#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__) +typedef SPi< P76, P75, P74, P10 > spi; +#elif defined(RBL_NRF51822) +typedef SPi< P16, P18, P17, P10 > spi; +#else +#error "No SPI entry in usbhost.h" +#endif + +typedef enum { +        vbus_on = 0, +        vbus_off = GPX_VBDET +} VBUS_t; + +template< typename SPI_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(); +        int8_t Init(int mseconds); + +        void vbusPower(VBUS_t state) { +                regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state)); +        } + +        uint8_t getVbusState(void) { +                return vbusState; +        }; +        void busprobe(); +        uint8_t GpxHandler(); +        uint8_t IntHandler(); +        uint8_t Task(); +}; + +template< typename SPI_SS, typename INTR > +        uint8_t MAX3421e< SPI_SS, INTR >::vbusState = 0; + +/* constructor */ +template< typename SPI_SS, typename INTR > +MAX3421e< SPI_SS, INTR >::MAX3421e() { +        // Leaving ADK hardware setup in here, for now. This really belongs with the other parts. +#ifdef BOARD_MEGA_ADK +        // For Mega ADK, which has a Max3421e on-board, set MAX_RESET to output mode, and then set it to HIGH +        P55::SetDirWrite(); +        P55::Set(); +#endif +}; + +/* write single byte into MAX3421 register */ +template< typename SPI_SS, typename INTR > +void MAX3421e< SPI_SS, INTR >::regWr(uint8_t reg, uint8_t data) { +        XMEM_ACQUIRE_SPI(); +#if SPI_HAS_TRANSACTION +        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0 +#endif +        SPI_SS::Clear(); + +#if USING_SPI4TEENSY3 +        uint8_t c[2]; +        c[0] = reg | 0x02; +        c[1] = data; +        spi4teensy3::send(c, 2); +#elif SPI_HAS_TRANSACTION +        uint8_t c[2]; +        c[0] = reg | 0x02; +        c[1] = data; +        SPI.transfer(c, 2); +#elif !defined(SPDR) +        SPI.transfer(reg | 0x02); +        SPI.transfer(data); +#else +        SPDR = (reg | 0x02); +        while(!(SPSR & (1 << SPIF))); +        SPDR = data; +        while(!(SPSR & (1 << SPIF))); +#endif + +        SPI_SS::Set(); +#if SPI_HAS_TRANSACTION +        SPI.endTransaction(); +#endif +        XMEM_RELEASE_SPI(); +        return; +}; +/* multiple-byte write                            */ + +/* returns a pointer to memory position after last written */ +template< typename SPI_SS, typename INTR > +uint8_t* MAX3421e< SPI_SS, INTR >::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { +        XMEM_ACQUIRE_SPI(); +#if SPI_HAS_TRANSACTION +        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0 +#endif +        SPI_SS::Clear(); + +#if USING_SPI4TEENSY3 +        spi4teensy3::send(reg | 0x02); +        spi4teensy3::send(data_p, nbytes); +        data_p += nbytes; +#elif SPI_HAS_TRANSACTION +        SPI.transfer(reg | 0x02); +        SPI.transfer(data_p, nbytes); +        data_p += nbytes; +#elif defined(__ARDUINO_X86__) +        SPI.transfer(reg | 0x02); +        SPI.transferBuffer(data_p, NULL, nbytes); +        data_p += nbytes; +#elif !defined(SPDR) +        SPI.transfer(reg | 0x02); +        while(nbytes) { +                SPI.transfer(*data_p); +                nbytes--; +                data_p++; // advance data pointer +        } +#else +        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 +                nbytes--; +                data_p++; // advance data pointer +        } +        while(!(SPSR & (1 << SPIF))); +#endif + +        SPI_SS::Set(); +#if SPI_HAS_TRANSACTION +        SPI.endTransaction(); +#endif +        XMEM_RELEASE_SPI(); +        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 SPI_SS, typename INTR > +void MAX3421e< SPI_SS, INTR >::gpioWr(uint8_t data) { +        regWr(rIOPINS1, data); +        data >>= 4; +        regWr(rIOPINS2, data); +        return; +} + +/* single host register read    */ +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::regRd(uint8_t reg) { +        XMEM_ACQUIRE_SPI(); +#if SPI_HAS_TRANSACTION +        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0 +#endif +        SPI_SS::Clear(); + +#if USING_SPI4TEENSY3 +        spi4teensy3::send(reg); +        uint8_t rv = spi4teensy3::receive(); +        SPI_SS::Set(); +#elif !defined(SPDR) || SPI_HAS_TRANSACTION +        SPI.transfer(reg); +        uint8_t rv = SPI.transfer(0); // Send empty byte +        SPI_SS::Set(); +#else +        SPDR = reg; +        while(!(SPSR & (1 << SPIF))); +        SPDR = 0; // Send empty byte +        while(!(SPSR & (1 << SPIF))); +        SPI_SS::Set(); +        uint8_t rv = SPDR; +#endif + +#if SPI_HAS_TRANSACTION +        SPI.endTransaction(); +#endif +        XMEM_RELEASE_SPI(); +        return (rv); +} +/* multiple-byte register read  */ + +/* returns a pointer to a memory position after last read   */ +template< typename SPI_SS, typename INTR > +uint8_t* MAX3421e< SPI_SS, INTR >::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) { +        XMEM_ACQUIRE_SPI(); +#if SPI_HAS_TRANSACTION +        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0 +#endif +        SPI_SS::Clear(); + +#if USING_SPI4TEENSY3 +        spi4teensy3::send(reg); +        spi4teensy3::receive(data_p, nbytes); +        data_p += nbytes; +#elif SPI_HAS_TRANSACTION +        SPI.transfer(reg); +        memset(data_p, 0, nbytes); // Make sure we send out empty bytes +        SPI.transfer(data_p, nbytes); +        data_p += nbytes; +#elif defined(__ARDUINO_X86__) +        SPI.transfer(reg); +        SPI.transferBuffer(NULL, data_p, nbytes); +        data_p += nbytes; +#elif !defined(SPDR) +        SPI.transfer(reg); +        while(nbytes) { +            *data_p++ = SPI.transfer(0); +            nbytes--; +        } +#else +        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 +#endif + +        SPI_SS::Set(); +#if SPI_HAS_TRANSACTION +        SPI.endTransaction(); +#endif +        XMEM_RELEASE_SPI(); +        return ( data_p); +} +/* GPIO read. See gpioWr for explanation */ + +/* GPIN pins are in high nibbles of IOPINS1, IOPINS2    */ +template< typename SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_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 SPI_SS, typename INTR > +uint16_t MAX3421e< SPI_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 SPI_SS, typename INTR > +int8_t MAX3421e< SPI_SS, INTR >::Init() { +        XMEM_ACQUIRE_SPI(); +        // Moved here. +        // you really should not init hardware in the constructor when it involves locks. +        // Also avoids the vbus flicker issue confusing some devices. +        /* pin and peripheral setup */ +        SPI_SS::SetDirWrite(); +        SPI_SS::Set(); +        spi::init(); +        INTR::SetDirRead(); +        XMEM_RELEASE_SPI(); +        /* MAX3421E - full-duplex SPI, level interrupt */ +        // GPX pin on. Moved here, otherwise we flicker the vbus. +        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL)); + +        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); +} + +/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */ +template< typename SPI_SS, typename INTR > +int8_t MAX3421e< SPI_SS, INTR >::Init(int mseconds) { +        XMEM_ACQUIRE_SPI(); +        // Moved here. +        // you really should not init hardware in the constructor when it involves locks. +        // Also avoids the vbus flicker issue confusing some devices. +        /* pin and peripheral setup */ +        SPI_SS::SetDirWrite(); +        SPI_SS::Set(); +        spi::init(); +        INTR::SetDirRead(); +        XMEM_RELEASE_SPI(); +        /* MAX3421E - full-duplex SPI, level interrupt, vbus off */ +        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET)); + +        if(reset() == 0) { //OSCOKIRQ hasn't asserted in time +                return ( -1); +        } + +        // Delay a minimum of 1 second to ensure any capacitors are drained. +        // 1 second is required to make sure we do not smoke a Microdrive! +        if(mseconds < 1000) mseconds = 1000; +        delay(mseconds); + +        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 + +        // GPX pin on. This is done here so that busprobe will fail if we have a switch connected. +        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL)); + +        return ( 0); +} + +/* probe bus to determine device presence and speed and switch host to this speed */ +template< typename SPI_SS, typename INTR > +void MAX3421e< SPI_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 SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_SS, INTR >::Task(void) { +        uint8_t rcode = 0; +        uint8_t pinvalue; +        //USB_HOST_SERIAL.print("Vbus state: "); +        //USB_HOST_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 SPI_SS, typename INTR > +uint8_t MAX3421e< SPI_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 SPI_SS, typename INTR > +//uint8_t MAX3421e< SPI_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_ | 
