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 */
|
|
|
|
#ifndef _USBHOST_H_
|
|
|
|
#define _USBHOST_H_
|
|
|
|
|
|
|
|
#include "avrpins.h"
|
|
|
|
#include "max3421e.h"
|
|
|
|
#include "usb_ch9.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;
|
|
|
|
/**/
|
|
|
|
tmp = SPSR;
|
|
|
|
tmp = SPDR;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* SPI pin definitions. see avrpins.h */
|
2011-06-27 05:27:44 +02:00
|
|
|
#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
|
2011-06-27 05:27:44 +02:00
|
|
|
#if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__)
|
|
|
|
typedef SPi< Pb7, Pb5, Pb6, Pb4 > spi;
|
|
|
|
#endif
|
2011-01-19 07:27:20 +01: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();
|
|
|
|
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();
|
2012-01-23 09:19:39 +01:00
|
|
|
|
|
|
|
#ifdef BOARD_MEGA_ADK
|
2012-01-23 02:32:51 +01:00
|
|
|
/* For Mega ADK, which has Max3421e on-board, set MAX_RESET to Output mode, and pull Reset to HIGH */
|
|
|
|
DDRJ |= 0x04;
|
|
|
|
PORTJ |= 0x04;
|
2012-01-23 09:19:39 +01:00
|
|
|
#endif
|
2011-01-19 07:27:20 +01:00
|
|
|
|
|
|
|
/* 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 )));
|
|
|
|
*data_p = SPDR;
|
|
|
|
data_p++;
|
|
|
|
}
|
|
|
|
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 );
|
|
|
|
}
|
2011-04-18 18:11:59 +02: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 );
|
|
|
|
// }
|
|
|
|
// regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST ); // set pull-downs, Host
|
|
|
|
//
|
|
|
|
// return( 0 );
|
|
|
|
//}
|
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 );
|
|
|
|
}
|
|
|
|
regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST ); // set pull-downs, Host
|
|
|
|
|
|
|
|
regWr( rHIEN, bmCONDETIE|bmFRAMEIE ); //connection detection
|
2011-03-01 19:26:31 +01:00
|
|
|
|
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-03-01 19:26:31 +01:00
|
|
|
busprobe(); //check if anything is connected
|
2011-01-19 07:27:20 +01:00
|
|
|
|
|
|
|
regWr( rHIRQ, bmCONDETIRQ ); //clear connection detect interrupt
|
|
|
|
regWr( rCPUCTL, 0x01 ); //enable interrupt pin
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* probe bus to determine device presense and speed and switch host to this speed */
|
|
|
|
template< typename SS, typename INTR >
|
|
|
|
void MAX3421e< SS, INTR >::busprobe()
|
|
|
|
{
|
2011-03-05 08:33:02 +01:00
|
|
|
uint8_t bus_sample;
|
2011-03-01 19:26:31 +01:00
|
|
|
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
|
2011-01-19 07:27:20 +01:00
|
|
|
case( bmJSTATUS ):
|
|
|
|
if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) {
|
2011-03-01 19:26:31 +01:00
|
|
|
regWr( rMODE, MODE_FS_HOST ); //start full-speed host
|
2011-01-19 07:27:20 +01:00
|
|
|
vbusState = FSHOST;
|
|
|
|
}
|
|
|
|
else {
|
2011-03-01 19:26:31 +01:00
|
|
|
regWr( rMODE, MODE_LS_HOST); //start low-speed host
|
2011-01-19 07:27:20 +01:00
|
|
|
vbusState = LSHOST;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case( bmKSTATUS ):
|
|
|
|
if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) {
|
2011-03-01 19:26:31 +01:00
|
|
|
regWr( rMODE, MODE_LS_HOST ); //start low-speed host
|
2011-01-19 07:27:20 +01:00
|
|
|
vbusState = LSHOST;
|
|
|
|
}
|
|
|
|
else {
|
2011-03-01 19:26:31 +01:00
|
|
|
regWr( rMODE, MODE_FS_HOST ); //start full-speed host
|
2011-01-19 07:27:20 +01:00
|
|
|
vbusState = FSHOST;
|
|
|
|
}
|
|
|
|
break;
|
2011-03-01 19:26:31 +01:00
|
|
|
case( bmSE1 ): //illegal state
|
2011-01-19 07:27:20 +01:00
|
|
|
vbusState = SE1;
|
|
|
|
break;
|
2011-03-01 19:26:31 +01:00
|
|
|
case( bmSE0 ): //disconnected state
|
2011-06-22 19:54:33 +02:00
|
|
|
regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ);
|
|
|
|
vbusState = SE0;
|
|
|
|
break;
|
2011-01-19 07:27:20 +01:00
|
|
|
}//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 );
|
2011-04-16 07:24:10 +02:00
|
|
|
pinvalue = INTR::IsSet(); //Read();
|
2011-03-05 08:33:02 +01:00
|
|
|
//pinvalue = digitalRead( MAX_INT );
|
2011-04-18 22:32:14 +02:00
|
|
|
if( pinvalue == 0 ) {
|
2011-01-19 07:27:20 +01:00
|
|
|
rcode = IntHandler();
|
|
|
|
}
|
2011-03-05 08:33:02 +01:00
|
|
|
// pinvalue = digitalRead( MAX_GPX );
|
|
|
|
// if( pinvalue == LOW ) {
|
|
|
|
// GpxHandler();
|
|
|
|
// }
|
2011-01-19 07:27:20 +01:00
|
|
|
// 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 );
|
|
|
|
}
|
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 );
|
|
|
|
//// }
|
|
|
|
// return( GPINIRQ );
|
|
|
|
//}
|
2011-01-19 07:27:20 +01:00
|
|
|
|
|
|
|
#endif //_USBHOST_H_
|