USB_Host_Shield_2.0/usbhost.h

356 lines
12 KiB
C
Raw Normal View History

2011-06-22 19:41:22 +02:00
/* 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
*/
2011-01-19 07:27:20 +01:00
/* MAX3421E-based USB Host Library header file */
2013-09-24 00:40:10 +02:00
#if !defined(_usb_h_) || defined(_USBHOST_H_)
#error "Never include usbhost.h directly; include Usb.h instead"
#else
2011-01-19 07:27:20 +01:00
#define _USBHOST_H_
/* 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;
}
};
2011-01-19 07:27:20 +01:00
/* SPI pin definitions. see avrpins.h */
#if defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
2011-01-19 07:27:20 +01:00
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__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi;
#endif
2011-01-19 07:27:20 +01:00
typedef enum {
vbus_on = 0,
vbus_off = GPX_VBDET
} VBUS_t;
2013-08-09 08:17:01 +02:00
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();
2013-08-09 08:17:01 +02:00
int8_t Init(int mseconds);
void vbusPower(VBUS_t state) {
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state));
}
2011-01-19 07:27:20 +01:00
uint8_t getVbusState(void) {
return vbusState;
};
void busprobe();
uint8_t GpxHandler();
uint8_t IntHandler();
uint8_t Task();
2011-01-19 07:27:20 +01:00
};
template< typename SS, typename INTR >
uint8_t MAX3421e< SS, INTR >::vbusState = 0;
2011-01-19 07:27:20 +01:00
/* 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 */
2013-08-09 02:21:05 +02:00
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));
2011-01-19 07:27:20 +01:00
};
2011-01-19 07:27:20 +01:00
/* 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;
2011-01-19 07:27:20 +01:00
};
/* multiple-byte write */
2011-01-19 07:27:20 +01:00
/* 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);
2011-01-19 07:27:20 +01:00
}
/* GPIO write */
/*GPIO byte is split between 2 registers, so two writes are needed to write one byte */
2011-01-19 07:27:20 +01:00
/* 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;
2011-01-19 07:27:20 +01:00
}
2011-01-19 07:27:20 +01:00
/* 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);
2011-01-19 07:27:20 +01:00
}
/* multiple-byte register read */
2011-01-19 07:27:20 +01:00
/* 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);
2011-01-19 07:27:20 +01:00
}
/* GPIO read. See gpioWr for explanation */
2011-01-19 07:27:20 +01:00
/* 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);
2011-01-19 07:27:20 +01:00
}
2011-01-19 07:27:20 +01:00
/* 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);
2011-01-19 07:27:20 +01:00
}
2011-01-19 07:27:20 +01:00
/* 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);
}
2013-08-09 02:21:05 +02:00
2013-08-09 08:17:01 +02:00
// GPX pin on.
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
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 SS, typename INTR >
int8_t MAX3421e< SS, INTR >::Init(int mseconds) {
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);
2013-08-09 02:21:05 +02:00
regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
2011-03-01 19:26:31 +01:00
regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection
2011-01-19 07:27:20 +01:00
/* check if device is connected */
regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish
2011-01-19 07:27:20 +01:00
busprobe(); //check if anything is connected
regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
regWr(rCPUCTL, 0x01); //enable interrupt pin
2013-08-09 02:21:05 +02:00
// GPX pin on. This is done here so that busprobe will fail if we have a switch connected.
regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
return( 0);
2011-01-19 07:27:20 +01:00
}
/* probe bus to determine device presence and speed and switch host to this speed */
2011-01-19 07:27:20 +01:00
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;
2011-01-19 07:27:20 +01:00
}//end switch( bus_sample )
}
2011-01-19 07:27:20 +01:00
/* 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;
2013-06-12 05:11:43 +02:00
//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);
}
2011-01-19 07:27:20 +01:00
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);
2011-01-19 07:27:20 +01:00
}
2011-03-05 08:33:02 +01:00
//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 );
//// }
2011-03-05 08:33:02 +01:00
// return( GPINIRQ );
//}
2011-01-19 07:27:20 +01:00
#endif //_USBHOST_H_