123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /* Copyright (C) 2015-2016 Andrew J. Kroll
- and
- 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 : https://www.circuitsathome.com
- e-mail : support@circuitsathome.com
- */
-
- #ifndef USB_HOST_SHIELD_H
- #define USB_HOST_SHIELD_H
-
- // uncomment to get 'printf' console debugging. NOT FOR UNO!
- //#define DEBUG_PRINTF_EXTRA_HUGE_USB_HOST_SHIELD
-
- #ifdef LOAD_USB_HOST_SHIELD
- #include "UHS_max3421e.h"
- #include <SPI.h>
-
-
- #ifndef SPI_HAS_TRANSACTION
- #error "Your SPI library installation is too old."
- #else
- #ifndef SPI_ATOMIC_VERSION
- #warning "Your SPI library installation lacks 'SPI_ATOMIC_VERSION'. Please complain to the maintainer."
- #elif SPI_ATOMIC_VERSION < 1
- #error "Your SPI library installation is too old."
- #endif
-
- #endif
- #if DEBUG_PRINTF_EXTRA_HUGE
- #ifdef DEBUG_PRINTF_EXTRA_HUGE_USB_HOST_SHIELD
- #define MAX_HOST_DEBUG(...) printf_P(__VA_ARGS__)
- #else
- #define MAX_HOST_DEBUG(...) VOID0
- #endif
- #else
- #define MAX_HOST_DEBUG(...) VOID0
- #endif
-
- #ifndef USB_HOST_SHIELD_USE_ISR
- #ifdef USE_MULTIPLE_APP_API
- #define USB_HOST_SHIELD_USE_ISR 0
- #else
- #define USB_HOST_SHIELD_USE_ISR 1
- #endif
- #else
- #define USB_HOST_SHIELD_USE_ISR 1
- #endif
-
-
-
- #if !USB_HOST_SHIELD_USE_ISR
- #error NOISR Polled mode _NOT SUPPORTED YET_
-
- //
- // Polled defaults
- //
- #ifdef BOARD_BLACK_WIDDOW
- #define UHS_MAX3421E_SS_ 6
- #define UHS_MAX3421E_INT_ 3
- #elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
- #if EXT_RAM
- // Teensy++ 2.0 with XMEM2
- #define UHS_MAX3421E_SS_ 20
- #define UHS_MAX3421E_INT_ 7
- #else
- #define UHS_MAX3421E_SS_ 9
- #define UHS_MAX3421E_INT_ 8
- #endif
- #define UHS_MAX3421E_SPD
- #elif defined(ARDUINO_AVR_ADK)
- #define UHS_MAX3421E_SS_ 53
- #define UHS_MAX3421E_INT_ 54
- #elif defined(ARDUINO_AVR_BALANDUINO)
- #define UHS_MAX3421E_SS_ 20
- #define UHS_MAX3421E_INT_ 19
- #else
- #define UHS_MAX3421E_SS_ 10
- #define UHS_MAX3421E_INT_ 9
- #endif
-
- #else
- #ifdef ARDUINO_ARCH_PIC32
- // PIC32 only allows edge interrupts, isn't that lovely? We'll emulate it...
- #if CHANGE < 2
- #error core too old.
- #endif
-
- #define IRQ_IS_EDGE 0
- #ifndef digitalPinToInterrupt
- // great, this isn't implemented.
- #warning digitalPinToInterrupt is not defined, complain here https://github.com/chipKIT32/chipKIT-core/issues/114
- #if defined(_BOARD_UNO_) || defined(_BOARD_UC32_)
- #define digitalPinToInterrupt(p) ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 35 ? 4 : ((p) == 38 ? 0 : NOT_AN_INTERRUPT)))))
- #warning digitalPinToInterrupt is now defined until this is taken care of.
- #else
- #error digitalPinToInterrupt not defined for your board, complain here https://github.com/chipKIT32/chipKIT-core/issues/114
- #endif
- #endif
- #else
- #define IRQ_IS_EDGE 0
- #endif
-
- // More stupidity from our friends @ Sony...
- #ifdef ARDUINO_spresense_ast
- #ifndef NOT_AN_INTERRUPT
- #define NOT_AN_INTERRUPT -1
- #endif
- #endif
-
- // SAMD uses an enum for this instead of a define. Isn't that just dandy?
- #if !defined(NOT_AN_INTERRUPT) && !defined(ARDUINO_ARCH_SAMD)
- #warning NOT_AN_INTERRUPT not defined, possible problems ahead.
- #warning If NOT_AN_INTERRUPT is an enum or something else, complain to UHS30 developers on github.
- #warning Otherwise complain to your board core developer/maintainer.
- #define NOT_AN_INTERRUPT -1
- #endif
-
- //
- // Interrupt defaults. Int0 or Int1
- //
- #ifdef BOARD_BLACK_WIDDOW
- #error "HELP! Please send us an email, I don't know the values for Int0 and Int1 on the Black Widow board!"
- #elif defined(ARDUINO_AVR_ADK)
- #define UHS_MAX3421E_SS_ 53
- #define UHS_MAX3421E_INT_ 54
- #elif defined(ARDUINO_spresense_ast)
- #define UHS_MAX3421E_SS_ 21
- #define UHS_MAX3421E_INT_ 20
- #define SPIclass SPI5
- //#define UHS_MAX3421E_SPD 100000
- #elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))
-
- // TO-DO!
-
- #if EXT_RAM
- // Teensy++ 2.0 with XMEM2
- #define UHS_MAX3421E_SS_ 20
- #define UHS_MAX3421E_INT_ 7
- #else
- #define UHS_MAX3421E_SS_ 9
- #define UHS_MAX3421E_INT_ 8
- #endif
-
- #elif defined(ARDUINO_AVR_BALANDUINO)
- #error "ISR mode is currently not supported on the Balanduino. Please set USB_HOST_SHIELD_USE_ISR to 0."
- #else
- #define UHS_MAX3421E_SS_ 10
- #ifdef __AVR__
- #ifdef __AVR_ATmega32U4__
- #define INT_FOR_PIN2 1
- #define INT_FOR_PIN3 0
- #else
- // Everybody else???
- #define INT_FOR_PIN2 0
- #define INT_FOR_PIN3 1
- #endif
- #define UHS_MAX3421E_INT_ 3
- #else
- // Non-avr
- #ifdef ARDUINO_ARCH_PIC32
- // UNO32 External Interrupts:
- // Pin 38 (INT0), Pin 2 (INT1), Pin 7 (INT2), Pin 8 (INT3), Pin 35 (INT4)
- #define UHS_MAX3421E_INT_ 7
- #else
- #define UHS_MAX3421E_INT_ 9
- #endif
- #endif
- #endif
- #endif
-
-
-
- #ifdef NO_AUTO_SPEED
- // Ugly details section...
- // MAX3421E characteristics
- // SPI Serial - Clock Input. An external SPI master supplies SCLK with frequencies up to 26MHz. The
- // logic level is referenced to the voltage on VL. Data is clocked into the SPI slave inter face on the
- // rising edge of SCLK. Data is clocked out of the SPI slave interface on the falling edge of SCLK.
- // Serial Clock (SCLK) Period 38.4ns minimum. 17ns minimum pulse width. VL >2.5V
- // SCLK Fall to MISO Propagation Delay 14.2ns
- // SCLK Fall to MOSI Propagation Delay 14.2ns
- // SCLK Fall to MOSI Drive 3.5ns
- // Theoretical deadline for reply 17.7ns
- // 26MHz 38.4615ns period <-- MAX3421E theoretical maximum
-
- #ifndef UHS_MAX3421E_SPD
- #ifdef ARDUINO_SAMD_ZERO
- // Zero violates spec early, needs a long setup time, or doesn't like high latency.
- #define UHS_MAX3421E_SPD 10000000
- #elif defined(ARDUINO_ARCH_PIC32)
- // PIC MX 5/6/7 characteristics
- // 25MHZ 40ns period <-- PIC MX 5/6/7 theoretical maximum
- // pulse width minimum Tsclk/2ns
- // Trise/fall 10ns maximum. 5ns is typical but not guaranteed.
- // Tsetup minimum for MISO 10ns.
- // We are in violation by 7.7ns @ 25MHz due to latency alone.
- // Even reading at end of data cycle, we only have a 2.3ns window.
- // This is too narrow to to compensate for capacitance, trace lengths, and noise.
-
- // 17.7ns + 10ns = 27.7ns
- // 18MHz fits and has enough slack time to compensate for capacitance, trace lengths, and noise.
- // For high speeds the SMP bit is recommended too, which samples at the end instead of the middle.
- // 20Mhz seems to work.
-
- #define UHS_MAX3421E_SPD 20000000
- #else
- #define UHS_MAX3421E_SPD 25000000
- #endif
- #endif
- #else
- // We start at 25MHz, and back down until hardware can take it.
- // Of course, SPI library can adjust this for us too.
- // Why not 26MHz? Because I have not found any MCU board that
- // can actually go that fast without problems.
- // Could be a shield limitation too.
- #ifndef UHS_MAX3421E_SPD
- #define UHS_MAX3421E_SPD 25000000
- #endif
- #endif
-
- #ifndef UHS_MAX3421E_INT
- #define UHS_MAX3421E_INT UHS_MAX3421E_INT_
- #endif
-
- #ifndef UHS_MAX3421E_SS
- #define UHS_MAX3421E_SS UHS_MAX3421E_SS_
- #endif
-
- // NOTE: On the max3421e the irq enable and irq bits are in the same position.
-
- // IRQs used if CPU polls
- #define ENIBITSPOLLED (bmCONDETIE | bmBUSEVENTIE | bmFRAMEIE)
- // IRQs used if CPU is interrupted
- #define ENIBITSISR (bmCONDETIE | bmBUSEVENTIE | bmFRAMEIE /* | bmRCVDAVIRQ | bmSNDBAVIRQ | bmHXFRDNIRQ */ )
-
- #if !USB_HOST_SHIELD_USE_ISR
- #define IRQ_CHECK_MASK (ENIBITSPOLLED & ICLRALLBITS)
- #define IRQ_IS_EDGE 0
- #else
- #define IRQ_CHECK_MASK (ENIBITSISR & ICLRALLBITS)
- #endif
-
- #if IRQ_IS_EDGE
- // Note: UNO32 Interrupts can only be RISING, or FALLING.
- // This poses an interesting problem, since we want to use a LOW level.
- // The MAX3421E provides for pulse width control for an IRQ.
- // We do need to watch the timing on this, as a second IRQ could cause
- // a missed IRQ, since we read the level of the line to check if the IRQ
- // is actually for this chip. The only other alternative is to add a capacitor
- // and an NPN transistor, and use two lines. We can try this first, though.
- // Worse case, we can ignore reading the pin for verification on UNO32.
- // Too bad there is no minimum low width setting.
- //
- // Single Clear First Second Clear first Clear last
- // IRQ Single IRQ IRQ Second active pending IRQ
- // | | | | | |
- // V V V V V V
- // _____ _________ _ _ _______
- // |______| |______| |______| |______________|
- //
- #define IRQ_SENSE FALLING
- #ifdef ARDUINO_ARCH_PIC32
- //#define bmPULSEWIDTH PULSEWIDTH10_6
- #define bmPULSEWIDTH 0
- #define bmIRQ_SENSE 0
- #else
- #define bmPULSEWIDTH PULSEWIDTH1_3
- #define bmIRQ_SENSE 0
- #endif
- #else
- #ifndef IRQ_SENSE
- #define IRQ_SENSE LOW
- #endif
- #ifndef bmPULSEWIDTH
- #define bmPULSEWIDTH 0
- #endif
- #ifndef bmIRQ_SENSE
- #define bmIRQ_SENSE bmINTLEVEL
- #endif
- #endif
-
- class MAX3421E_HOST :
- public UHS_USB_HOST_BASE
- #ifdef SWI_IRQ_NUM
- , public dyn_SWI
- #endif
- {
- // TO-DO: move these into the parent class.
- volatile uint8_t vbusState;
- volatile uint16_t sof_countdown;
-
- // TO-DO: pack into a struct/union and use one byte
- volatile bool busevent;
- volatile bool sofevent;
- volatile bool counted;
- volatile bool condet;
- volatile bool doingreset;
-
- #ifdef USB_HOST_MANUAL_POLL
- volatile bool frame_irq_enabled = false;
-
- bool enable_frame_irq(bool enable) {
- const bool prev_state = frame_irq_enabled;
- if(prev_state != enable) {
- if(enable)
- regWr(rHIEN, regRd(rHIEN) | bmFRAMEIE);
- else
- regWr(rHIEN, regRd(rHIEN) & ~bmFRAMEIE);
- frame_irq_enabled = enable;
- }
- return prev_state;
- }
- #endif
-
- public:
- SPISettings MAX3421E_SPI_Settings;
- uint8_t ss_pin;
- uint8_t irq_pin;
- // Will use the defaults UHS_MAX3421E_SS, UHS_MAX3421E_INT and speed
-
- UHS_NI MAX3421E_HOST() {
- sof_countdown = 0;
- busevent = false;
- doingreset = false;
- sofevent = false;
- condet = false;
- ss_pin = UHS_MAX3421E_SS;
- irq_pin = UHS_MAX3421E_INT;
- MAX3421E_SPI_Settings = SPISettings(UHS_MAX3421E_SPD, MSBFIRST, SPI_MODE0);
- hub_present = 0;
- };
-
- // Will use user supplied pins, and UHS_MAX3421E_SPD
-
- UHS_NI MAX3421E_HOST(uint8_t pss, uint8_t pirq) {
- sof_countdown = 0;
- busevent = false;
- doingreset = false;
- sofevent = false;
- condet = false;
- ss_pin = pss;
- irq_pin = pirq;
- MAX3421E_SPI_Settings = SPISettings(UHS_MAX3421E_SPD, MSBFIRST, SPI_MODE0);
- hub_present = 0;
- };
-
- // Will use user supplied pins, and speed
-
- UHS_NI MAX3421E_HOST(uint8_t pss, uint8_t pirq, uint32_t pspd) {
- sof_countdown = 0;
- doingreset = false;
- busevent = false;
- sofevent = false;
- condet = false;
- ss_pin = pss;
- irq_pin = pirq;
- MAX3421E_SPI_Settings = SPISettings(pspd, MSBFIRST, SPI_MODE0);
- hub_present = 0;
- };
-
- virtual bool UHS_NI sof_delay(uint16_t x) {
- #ifdef USB_HOST_MANUAL_POLL
- const bool saved_irq_state = enable_frame_irq(true);
- #endif
- sof_countdown = x;
- while((sof_countdown != 0) && !condet) {
- SYSTEM_OR_SPECIAL_YIELD();
- #if !USB_HOST_SHIELD_USE_ISR
- Task();
- #endif
- }
- #ifdef USB_HOST_MANUAL_POLL
- enable_frame_irq(saved_irq_state);
- #endif
- // Serial.println("...Wake");
- return (!condet);
- };
-
- virtual UHS_EpInfo *ctrlReqOpen(uint8_t addr, uint64_t Request, uint8_t *dataptr);
-
- virtual void UHS_NI vbusPower(VBUS_t state) {
- regWr(rPINCTL, (bmFDUPSPI | bmIRQ_SENSE) | (uint8_t)(state));
- };
-
- void UHS_NI Task();
-
- virtual uint8_t SetAddress(uint8_t addr, uint8_t ep, UHS_EpInfo **ppep, uint16_t &nak_limit);
- virtual uint8_t OutTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
- virtual uint8_t InTransfer(UHS_EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data);
- virtual uint8_t ctrlReqClose(UHS_EpInfo *pep, uint8_t bmReqType, uint16_t left, uint16_t nbytes, uint8_t *dataptr);
- virtual uint8_t ctrlReqRead(UHS_EpInfo *pep, uint16_t *left, uint16_t *read, uint16_t nbytes, uint8_t *dataptr);
- virtual uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);
-
- void UHS_NI ReleaseChildren() {
- for(uint8_t i = 0; i < UHS_HOST_MAX_INTERFACE_DRIVERS; i++)
- if(devConfig[i])
- devConfig[i]->Release();
- hub_present = 0;
- };
-
- virtual bool IsHub(uint8_t klass) {
- if(klass == UHS_USB_CLASS_HUB) {
- hub_present = bmHUBPRE;
- return true;
- }
- return false;
- };
-
- virtual void VBUS_changed();
-
- virtual void UHS_NI doHostReset() {
- #if USB_HOST_SHIELD_USE_ISR
- // Enable interrupts
- noInterrupts();
- #endif
- doingreset = true;
- busevent = true;
- regWr(rHIRQ, bmBUSEVENTIRQ); // see data sheet.
- regWr(rHCTL, bmBUSRST); //issue bus reset
- #if USB_HOST_SHIELD_USE_ISR
- DDSB();
- // Enable interrupts
- interrupts();
- #endif
- while(busevent) {
- DDSB();
- SYSTEM_OR_SPECIAL_YIELD();
- }
- #endif
- #if USB_HOST_SHIELD_USE_ISR
- // Enable interrupts
- noInterrupts();
- #endif
- #ifdef USB_HOST_MANUAL_POLL
- enable_frame_irq(true);
- #endif
- sofevent = true;
- #if USB_HOST_SHIELD_USE_ISR
- DDSB();
- // Enable interrupts
- interrupts();
- #endif
- // Wait for SOF
- while(sofevent) {
- }
- #if USB_HOST_SHIELD_USE_ISR
- // Enable interrupts
- noInterrupts();
- #endif
- doingreset = false;
- #if USB_HOST_SHIELD_USE_ISR
- DDSB();
- // Enable interrupts
- interrupts();
- };
-
-
- int16_t UHS_NI Init(int16_t mseconds);
-
- int16_t UHS_NI Init() {
- return Init(INT16_MIN);
- };
-
- void ISRTask();
- void ISRbottom();
- void busprobe();
- uint16_t reset();
-
- // MAX3421e specific
- void regWr(uint8_t reg, uint8_t data);
- void gpioWr(uint8_t data);
- uint8_t regRd(uint8_t reg);
- uint8_t gpioRd();
- uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
- uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
-
- // ARM/NVIC specific, used to emulate reentrant ISR.
- #ifdef SWI_IRQ_NUM
-
- void dyn_SWISR() {
- ISRbottom();
- };
- #endif
-
- virtual void UHS_NI suspend_host() {
- // Used on MCU that lack control of IRQ priority (AVR).
- // Suspends ISRs, for critical code. IRQ will be serviced after it is resumed.
- // NOTE: you must track the state yourself!
- #ifdef __AVR__
- noInterrupts();
- detachInterrupt(UHS_GET_DPI(irq_pin));
- interrupts();
- #endif
- };
-
- virtual void UHS_NI resume_host();
- };
- #ifndef SPIclass
- #define SPIclass SPI
- #endif
- #ifndef USB_HOST_SHIELD_LOADED
- #include "USB_HOST_SHIELD_INLINE.h"
- #endif
- #else
- #error "define LOAD_USB_HOST_SHIELD in your sketch, never include USB_HOST_SHIELD.h in a driver."
- #endif
- #endif /* USB_HOST_SHIELD_H */
|