If this is not the case, we can just do this next time, and perhaps Paul will include this new version.
Yes, I'd love to include an updated UHS lib with the Teensyduino 1.21 installer. I've been averaging a new version every 3-4 months, so the time frame would probably be in January.
Long term, I'd really also like to help with integrating Teensy 3.1's USB host mode with the library, especially if you're moving to a new design that allows a nice hardware abstraction. Realistically, to develop code by USB, some sort of special USB mux will likely be needed. Maybe it's time to think about what a first gen USB mux board might look like?
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2014 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "core_pins.h"
#include "HardwareSerial.h"
#include "usb_serial.h"
#include "usb_seremu.h"
void yield(void) __attribute__ ((weak));
void yield(void)
{
static uint8_t running=0;
if (running) return; // TODO: does this need to be atomic?
running = 1;
#ifdef USB_SERIAL
if (Serial.available()) serialEvent();
#endif
if (Serial1.available()) serialEvent1();
if (Serial2.available()) serialEvent2();
if (Serial3.available()) serialEvent3();
running = 0;
};
inline static void beginTransaction(SPISettings settings) {
if (interruptMode > 0) {
#ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
interruptSave = SPI_AVR_EIMSK;
SPI_AVR_EIMSK &= ~interruptMask;
} else
#endif
{
interruptSave = SREG;
cli();
}
}
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 1;
#endif
SPCR = settings.spcr;
SPSR = settings.spsr;
}
inline static void beginTransaction(SPISettings settings) {
if (interruptMode > 0) {
#ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
interruptSave = SPI_AVR_EIMSK;
SPI_AVR_EIMSK &= ~interruptMask;
} else
#endif
{
uint8_t x = SREG;
cli();
interruptSave = x;
}
}
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 1;
#endif
SPCR = settings.spcr;
SPSR = settings.spsr;
}
if (interruptMode == 1) {
uint8_t tmp = SREG;
cli();
interruptSave = SPI_AVR_EIMSK;
SPI_AVR_EIMSK &= ~interruptMask;
SREG = tmp;
} else
You are quite welcomeYes, I believe you're right.
If the interrupt occurs exactly between the write to interruptSave and cli(), then the interrupt will overwrite interruptSave and when control returns to the main program, it will execute the cli(), do a complete transaction, and then erroneously restore SREG with the value the ISR's transaction saved into interruptSave.
I'm pretty impressed you found this! Thanks.
Looking at this a bit more, it seems the same might be able to happen in the EIMSK code. Maybe? If the interrupt occurs after interruptSave is written, but before EIMSK is written, when the non-interrupt transaction completes last, it could end up restoring EIMSK with the ISR's saved copy.
Maybe something like this is needed? Any thoughts?
Code:if (interruptMode == 1) { uint8_t tmp = SREG; cli(); interruptSave = SPI_AVR_EIMSK; SPI_AVR_EIMSK &= ~interruptMask; SREG = tmp; } else
...(SNIP!)...
SPI.begin();
#endif
SPI.usingInterrupt(255);
pinMode(irq, INPUT);
UHS_PIN_WRITE(irq, HIGH);
pinMode(ss, OUTPUT);
UHS_PIN_WRITE(ss, HIGH);
...(SNIP!)...
#if USB_HOST_SHIELD_USE_ISR
// Attach ISR to service IRQ from MAX3421e
// supports only pins 2 and 3 int0 and int1 for avr
cli();
#ifdef __AVR__
if(irq == 2) {
ISReven = this;
attachInterrupt(INT_FOR_PIN2, call_ISReven, LOW); // avr
} else if(irq == 2) {
ISRodd = this;
attachInterrupt(INT_FOR_PIN3, call_ISRodd, LOW); // avr
} else {
sei();
return (-2); // -2 means not able to assign ISR
}
#else
// Everybody else should be able to use any pin
....(SNIP!)....
#endif
sei();
#endif
I realize this is a bit of a rant, but I am wondering if we now need to push the arduino team into using a better interrupt scheme.
#if USB_HOST_SHIELD_USE_ISR
// Attach ISR to service IRQ from MAX3421e
cli();
#ifdef digitalPinToInterrupt
int n = digitalPinToInterrupt(irq);
if (n == NOT_AN_INTERRUPT) return (-2);
ISReven = this;
attachInterrupt(n, call_ISReven, LOW);
#else
// ugly fallback code for Arduino 1.0.5 & earlier...
// supports only pins 2 and 3 int0 and int1 for avr
#endif
sei();
#endif