|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- /* 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_
|