2015-02-22 19:18:45 +01:00
|
|
|
/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
|
|
|
|
|
2018-04-10 11:34:43 +02:00
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2015-02-22 19:18:45 +01:00
|
|
|
|
|
|
|
Contact information
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
Circuits At Home, LTD
|
|
|
|
Web : http://www.circuitsathome.com
|
|
|
|
e-mail : support@circuitsathome.com
|
|
|
|
*/
|
|
|
|
/* 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:
|
2015-04-20 21:44:31 +02:00
|
|
|
#if USING_SPI4TEENSY3
|
2015-02-22 19:18:45 +01:00
|
|
|
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();
|
|
|
|
}
|
2016-10-25 06:11:46 +02:00
|
|
|
#elif defined(SPI_HAS_TRANSACTION)
|
2015-04-20 21:44:31 +02:00
|
|
|
static void init() {
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.begin(); // The SPI library with transaction will take care of setting up the pins - settings is set in beginTransaction()
|
2015-07-21 21:10:49 +02:00
|
|
|
SPI_SS::SetDirWrite();
|
|
|
|
SPI_SS::Set();
|
2015-04-20 21:44:31 +02:00
|
|
|
}
|
2015-10-13 13:35:26 +02:00
|
|
|
#elif defined(STM32F4)
|
|
|
|
#warning "You need to initialize the SPI interface manually when using the STM32F4 platform"
|
|
|
|
static void init() {
|
|
|
|
// Should be initialized by the user manually for now
|
|
|
|
}
|
2021-08-10 01:08:46 +02:00
|
|
|
#elif defined(AM_PART_APOLLO3)
|
|
|
|
static void init() {
|
|
|
|
SPI::begin();
|
|
|
|
}
|
2015-02-22 19:18:45 +01:00
|
|
|
#elif !defined(SPDR)
|
|
|
|
static void init() {
|
|
|
|
SPI_SS::SetDirWrite();
|
|
|
|
SPI_SS::Set();
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.begin();
|
2015-02-22 19:18:45 +01:00
|
|
|
#if defined(__MIPSEL__)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.setClockDivider(1);
|
2015-03-19 16:36:51 +01:00
|
|
|
#elif defined(__ARDUINO_X86__)
|
2015-03-19 21:20:06 +01:00
|
|
|
#ifdef SPI_CLOCK_1M // Hack used to check if setClockSpeed is available
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.setClockSpeed(12000000); // The MAX3421E can handle up to 26MHz, but in practice this was the maximum that I could reliably use
|
2015-03-19 21:20:06 +01:00
|
|
|
#else
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_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
|
2015-03-19 21:20:06 +01:00
|
|
|
#endif
|
2021-03-22 14:28:22 +01:00
|
|
|
#elif !defined(RBL_NRF51822) && !defined(NRF52_SERIES)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz
|
2015-02-22 19:18:45 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#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 */
|
2016-10-25 10:23:40 +02:00
|
|
|
#if defined(PIN_SPI_SCK) && defined(PIN_SPI_MOSI) && defined(PIN_SPI_MISO) && defined(PIN_SPI_SS)
|
|
|
|
// Use pin defines: https://github.com/arduino/Arduino/pull/4814
|
|
|
|
// Based on: https://www.mikeash.com/pyblog/friday-qa-2015-03-20-preprocessor-abuse-and-optional-parentheses.html
|
|
|
|
#define NOTHING_EXTRACT
|
|
|
|
#define EXTRACT(...) EXTRACT __VA_ARGS__
|
|
|
|
#define PASTE(x, ...) x ## __VA_ARGS__
|
|
|
|
#define EVALUATING_PASTE(x, ...) PASTE(x, __VA_ARGS__)
|
|
|
|
#define UNPAREN(x) EVALUATING_PASTE(NOTHING_, EXTRACT x)
|
|
|
|
#define APPEND_PIN(pin) P ## pin // Appends the pin to 'P', e.g. 1 becomes P1
|
|
|
|
#define MAKE_PIN(x) EVALUATING_PASTE(APPEND_, PIN(UNPAREN(x)))
|
|
|
|
typedef SPi< MAKE_PIN(PIN_SPI_SCK), MAKE_PIN(PIN_SPI_MOSI), MAKE_PIN(PIN_SPI_MISO), MAKE_PIN(PIN_SPI_SS) > spi;
|
|
|
|
#undef MAKE_PIN
|
|
|
|
#elif defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
|
2015-02-22 19:18:45 +01:00
|
|
|
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;
|
2017-02-04 07:52:01 +01:00
|
|
|
#elif (defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(__MKL26Z64__))) || defined(__ARDUINO_ARC__) || defined(__ARDUINO_X86__) || defined(__MIPSEL__) || defined(STM32F4)
|
2015-02-22 19:18:45 +01:00
|
|
|
typedef SPi< P13, P11, P12, P10 > spi;
|
|
|
|
#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)
|
|
|
|
typedef SPi< P76, P75, P74, P10 > spi;
|
2015-03-19 15:34:07 +01:00
|
|
|
#elif defined(RBL_NRF51822)
|
|
|
|
typedef SPi< P16, P18, P17, P10 > spi;
|
2016-10-25 06:01:19 +02:00
|
|
|
#elif defined(ESP8266)
|
|
|
|
typedef SPi< P14, P13, P12, P15 > spi;
|
2017-10-29 13:08:39 +01:00
|
|
|
#elif defined(ESP32)
|
|
|
|
typedef SPi< P18, P23, P19, P5 > spi;
|
2021-03-22 14:28:22 +01:00
|
|
|
#elif defined(ARDUINO_NRF52840_FEATHER)
|
|
|
|
typedef SPi< P26, P25, P24, P5 > spi;
|
2021-08-09 02:10:09 +02:00
|
|
|
#elif defined(AM_PART_APOLLO3)
|
2021-08-10 01:08:46 +02:00
|
|
|
typedef SPi< P13, P11, P12, P10 > spi;
|
2015-02-22 19:18:45 +01:00
|
|
|
#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();
|
2018-10-14 10:21:01 +02:00
|
|
|
uint8_t gpioRdOutput();
|
2015-02-22 19:18:45 +01:00
|
|
|
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();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Clear();
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-04-20 21:44:31 +02:00
|
|
|
#if USING_SPI4TEENSY3
|
2015-03-19 15:34:07 +01:00
|
|
|
uint8_t c[2];
|
|
|
|
c[0] = reg | 0x02;
|
|
|
|
c[1] = data;
|
2015-04-20 21:44:31 +02:00
|
|
|
spi4teensy3::send(c, 2);
|
2017-10-29 13:08:39 +01:00
|
|
|
#elif defined(SPI_HAS_TRANSACTION) && !defined(ESP8266) && !defined(ESP32)
|
2017-06-13 11:55:05 +02:00
|
|
|
uint8_t c[2];
|
|
|
|
c[0] = reg | 0x02;
|
|
|
|
c[1] = data;
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(c, 2);
|
2015-10-13 13:35:26 +02:00
|
|
|
#elif defined(STM32F4)
|
|
|
|
uint8_t c[2];
|
|
|
|
c[0] = reg | 0x02;
|
|
|
|
c[1] = data;
|
|
|
|
HAL_SPI_Transmit(&SPI_Handle, c, 2, HAL_MAX_DELAY);
|
2017-10-29 13:08:39 +01:00
|
|
|
#elif !defined(SPDR) // ESP8266, ESP32
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg | 0x02);
|
|
|
|
USB_SPI.transfer(data);
|
2015-02-22 19:18:45 +01:00
|
|
|
#else
|
|
|
|
SPDR = (reg | 0x02);
|
|
|
|
while(!(SPSR & (1 << SPIF)));
|
|
|
|
SPDR = data;
|
|
|
|
while(!(SPSR & (1 << SPIF)));
|
|
|
|
#endif
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Set();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.endTransaction();
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
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();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Clear();
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-04-20 21:44:31 +02:00
|
|
|
#if USING_SPI4TEENSY3
|
2015-02-22 19:18:45 +01:00
|
|
|
spi4teensy3::send(reg | 0x02);
|
|
|
|
spi4teensy3::send(data_p, nbytes);
|
|
|
|
data_p += nbytes;
|
2015-10-13 13:35:26 +02:00
|
|
|
#elif defined(STM32F4)
|
|
|
|
uint8_t data = reg | 0x02;
|
|
|
|
HAL_SPI_Transmit(&SPI_Handle, &data, 1, HAL_MAX_DELAY);
|
|
|
|
HAL_SPI_Transmit(&SPI_Handle, data_p, nbytes, HAL_MAX_DELAY);
|
|
|
|
data_p += nbytes;
|
2019-07-02 21:49:41 +02:00
|
|
|
#elif !defined(__AVR__) || !defined(SPDR)
|
|
|
|
#if defined(ESP8266) || defined(ESP32)
|
2018-11-18 10:46:36 +01:00
|
|
|
yield();
|
2019-07-02 21:49:41 +02:00
|
|
|
#endif
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg | 0x02);
|
2015-02-22 19:18:45 +01:00
|
|
|
while(nbytes) {
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(*data_p);
|
2015-02-22 19:18:45 +01:00
|
|
|
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
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Set();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.endTransaction();
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
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();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Clear();
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-04-20 21:44:31 +02:00
|
|
|
#if USING_SPI4TEENSY3
|
2015-02-22 19:18:45 +01:00
|
|
|
spi4teensy3::send(reg);
|
|
|
|
uint8_t rv = spi4teensy3::receive();
|
|
|
|
SPI_SS::Set();
|
2015-10-13 13:35:26 +02:00
|
|
|
#elif defined(STM32F4)
|
|
|
|
HAL_SPI_Transmit(&SPI_Handle, ®, 1, HAL_MAX_DELAY);
|
|
|
|
uint8_t rv = 0;
|
|
|
|
HAL_SPI_Receive(&SPI_Handle, &rv, 1, HAL_MAX_DELAY);
|
|
|
|
SPI_SS::Set();
|
2016-10-25 06:11:46 +02:00
|
|
|
#elif !defined(SPDR) || defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg);
|
|
|
|
uint8_t rv = USB_SPI.transfer(0); // Send empty byte
|
2015-04-20 21:44:31 +02:00
|
|
|
SPI_SS::Set();
|
2015-02-22 19:18:45 +01:00
|
|
|
#else
|
|
|
|
SPDR = reg;
|
|
|
|
while(!(SPSR & (1 << SPIF)));
|
2015-03-19 15:34:07 +01:00
|
|
|
SPDR = 0; // Send empty byte
|
2015-02-22 19:18:45 +01:00
|
|
|
while(!(SPSR & (1 << SPIF)));
|
|
|
|
SPI_SS::Set();
|
|
|
|
uint8_t rv = SPDR;
|
|
|
|
#endif
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.endTransaction();
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
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();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Clear();
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-04-20 21:44:31 +02:00
|
|
|
#if USING_SPI4TEENSY3
|
|
|
|
spi4teensy3::send(reg);
|
|
|
|
spi4teensy3::receive(data_p, nbytes);
|
|
|
|
data_p += nbytes;
|
2017-10-29 13:08:39 +01:00
|
|
|
#elif defined(SPI_HAS_TRANSACTION) && !defined(ESP8266) && !defined(ESP32)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg);
|
2015-03-19 15:34:07 +01:00
|
|
|
memset(data_p, 0, nbytes); // Make sure we send out empty bytes
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(data_p, nbytes);
|
2015-03-19 15:34:07 +01:00
|
|
|
data_p += nbytes;
|
2015-03-19 16:36:51 +01:00
|
|
|
#elif defined(__ARDUINO_X86__)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg);
|
|
|
|
USB_SPI.transferBuffer(NULL, data_p, nbytes);
|
2015-03-19 16:36:51 +01:00
|
|
|
data_p += nbytes;
|
2015-10-13 13:35:26 +02:00
|
|
|
#elif defined(STM32F4)
|
|
|
|
HAL_SPI_Transmit(&SPI_Handle, ®, 1, HAL_MAX_DELAY);
|
|
|
|
memset(data_p, 0, nbytes); // Make sure we send out empty bytes
|
|
|
|
HAL_SPI_Receive(&SPI_Handle, data_p, nbytes, HAL_MAX_DELAY);
|
|
|
|
data_p += nbytes;
|
2017-10-29 13:08:39 +01:00
|
|
|
#elif !defined(SPDR) // ESP8266, ESP32
|
2018-11-18 10:46:36 +01:00
|
|
|
yield();
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.transfer(reg);
|
2015-02-22 19:18:45 +01:00
|
|
|
while(nbytes) {
|
2017-11-09 19:13:42 +01:00
|
|
|
*data_p++ = USB_SPI.transfer(0);
|
2015-02-22 19:18:45 +01:00
|
|
|
nbytes--;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
SPDR = reg;
|
|
|
|
while(!(SPSR & (1 << SPIF))); //wait
|
|
|
|
while(nbytes) {
|
2015-03-19 15:34:07 +01:00
|
|
|
SPDR = 0; // Send empty byte
|
2015-02-22 19:18:45 +01:00
|
|
|
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
|
2015-03-19 15:34:07 +01:00
|
|
|
|
2015-02-22 19:18:45 +01:00
|
|
|
SPI_SS::Set();
|
2016-10-25 06:11:46 +02:00
|
|
|
#if defined(SPI_HAS_TRANSACTION)
|
2017-11-09 19:13:42 +01:00
|
|
|
USB_SPI.endTransaction();
|
2015-03-19 15:34:07 +01:00
|
|
|
#endif
|
2015-02-22 19:18:45 +01:00
|
|
|
XMEM_RELEASE_SPI();
|
|
|
|
return ( data_p);
|
|
|
|
}
|
|
|
|
/* GPIO read. See gpioWr for explanation */
|
|
|
|
|
2018-10-14 10:21:01 +02:00
|
|
|
/** @brief Reads the current GPI input values
|
|
|
|
* @retval uint8_t Bitwise value of all 8 GPI inputs
|
|
|
|
*/
|
2015-02-22 19:18:45 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2018-10-14 10:21:01 +02:00
|
|
|
/** @brief Reads the current GPI output values
|
|
|
|
* @retval uint8_t Bitwise value of all 8 GPI outputs
|
|
|
|
*/
|
|
|
|
/* GPOUT pins are in low nibbles of IOPINS1, IOPINS2 */
|
|
|
|
template< typename SPI_SS, typename INTR >
|
|
|
|
uint8_t MAX3421e< SPI_SS, INTR >::gpioRdOutput() {
|
|
|
|
uint8_t gpout = 0;
|
|
|
|
gpout = regRd(rIOPINS1); //pins 0-3
|
|
|
|
gpout &= 0x0f; //clean upper nibble
|
|
|
|
gpout |= (regRd(rIOPINS2) << 4); //shift high bits and OR with lower from previous operation.
|
|
|
|
return ( gpout);
|
|
|
|
}
|
|
|
|
|
2015-02-22 19:18:45 +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 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();
|
2021-08-11 03:30:42 +02:00
|
|
|
spi::init();
|
2015-02-22 19:18:45 +01:00
|
|
|
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()
|
|
|
|
//{
|
2015-03-19 15:34:07 +01:00
|
|
|
// uint8_t GPINIRQ = regRd( rGPINIRQ ); //read GPIN IRQ register
|
2015-02-22 19:18:45 +01:00
|
|
|
//// if( GPINIRQ & bmGPINIRQ7 ) { //vbus overload
|
|
|
|
//// vbusPwr( OFF ); //attempt powercycle
|
|
|
|
//// delay( 1000 );
|
|
|
|
//// vbusPwr( ON );
|
|
|
|
//// regWr( rGPINIRQ, bmGPINIRQ7 );
|
|
|
|
//// }
|
|
|
|
// return( GPINIRQ );
|
|
|
|
//}
|
|
|
|
|
2015-03-19 18:24:41 +01:00
|
|
|
#endif // _USBHOST_H_
|