Browse Source

Merge pull request #11 from KurtE/SPI1-for-3.4/3.5

Added SPI1 object for Teensy 3.5 test board
main
Paul Stoffregen 8 years ago
parent
commit
aef5445f90
2 changed files with 350 additions and 12 deletions
  1. +149
    -10
      SPI.cpp
  2. +201
    -2
      SPI.h

+ 149
- 10
SPI.cpp View File

@@ -48,7 +48,7 @@ void SPIClass::begin()
// MISO pin automatically overrides to INPUT.
// By doing this AFTER enabling SPI, we avoid accidentally
// clocking in a single bit since the lines go directly
// from "input" to SPI control.
// from "input" to SPI control.
// http://code.google.com/p/arduino/issues/detail?id=888
pinMode(SCK, OUTPUT);
pinMode(MOSI, OUTPUT);
@@ -145,6 +145,7 @@ void SPIClass::usingInterrupt(uint8_t interruptNumber)

#elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISK)


SPIClass SPI;

uint8_t SPIClass::interruptMasksUsed = 0;
@@ -274,20 +275,33 @@ void SPIClass::setClockDivider_noInline(uint32_t clk)
updateCTAR(ctar);
}

bool SPIClass::pinIsChipSelect(uint8_t pin)
uint8_t SPIClass::pinIsChipSelect(uint8_t pin)
{
if (pin == 10 || pin == 9 || pin == 6 || pin == 2 || pin == 15) return true;
if (pin >= 20 && pin <= 23) return true;
return false;
switch (pin) {
case 10: return 0x01; // PTC4
case 2: return 0x01; // PTD0
case 9: return 0x02; // PTC3
case 6: return 0x02; // PTD4
case 20: return 0x04; // PTD5
case 23: return 0x04; // PTC2
case 21: return 0x08; // PTD6
case 22: return 0x08; // PTC1
case 15: return 0x10; // PTC0
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
case 26: return 0x01;
#endif
}

return 0;
}

bool SPIClass::pinIsChipSelect(uint8_t pin1, uint8_t pin2)
{
if (!pinIsChipSelect(pin1) || !pinIsChipSelect(pin2)) return false;
if ((pin1 == 2 && pin2 == 10) || (pin1 == 10 && pin2 == 2)) return false;
if ((pin1 == 6 && pin2 == 9) || (pin1 == 9 && pin2 == 6)) return false;
if ((pin1 == 20 && pin2 == 23) || (pin1 == 23 && pin2 == 20)) return false;
if ((pin1 == 21 && pin2 == 22) || (pin1 == 22 && pin2 == 21)) return false;
uint8_t pin1_mask, pin2_mask;
if ((pin1_mask = (uint8_t)pinIsChipSelect(pin1)) == 0) return false;
if ((pin2_mask = (uint8_t)pinIsChipSelect(pin2)) == 0) return false;
Serial.printf("pinIsChipSelect %d %d %x %x\n\r", pin1, pin2, pin1_mask, pin2_mask);
if ((pin1_mask & pin2_mask) != 0) return false;
return true;
}

@@ -308,6 +322,131 @@ uint8_t SPIClass::setCS(uint8_t pin)
}



/**********************************************************/
/* 32 bit Teensy-3.4/3.5 */
/**********************************************************/
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
SPI1Class SPI1;

uint8_t SPI1Class::interruptMasksUsed = 0;
uint32_t SPI1Class::interruptMask[(NVIC_NUM_INTERRUPTS+31)/32];
uint32_t SPI1Class::interruptSave[(NVIC_NUM_INTERRUPTS+31)/32];
#ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t SPI1Class::inTransactionFlag = 0;
#endif

void SPI1Class::begin()
{
SIM_SCGC6 |= SIM_SCGC6_SPI1;
SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI1_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
SPI1_CTAR1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
SPCR1.enable_pins(); // pins managed by SPCRemulation in avr_emulation.h
}

void SPI1Class::end() {
SPCR1.disable_pins();
SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
}

void SPI1Class::usingInterrupt(IRQ_NUMBER_t interruptName)
{
uint32_t n = (uint32_t)interruptName;

if (n >= NVIC_NUM_INTERRUPTS) return;

//Serial.print("usingInterrupt ");
//Serial.println(n);
interruptMasksUsed |= (1 << (n >> 5));
interruptMask[n >> 5] |= (1 << (n & 0x1F));
//Serial.printf("interruptMasksUsed = %d\n", interruptMasksUsed);
//Serial.printf("interruptMask[0] = %08X\n", interruptMask[0]);
//Serial.printf("interruptMask[1] = %08X\n", interruptMask[1]);
//Serial.printf("interruptMask[2] = %08X\n", interruptMask[2]);
}

void SPI1Class::notUsingInterrupt(IRQ_NUMBER_t interruptName)
{
uint32_t n = (uint32_t)interruptName;
if (n >= NVIC_NUM_INTERRUPTS) return;
interruptMask[n >> 5] &= ~(1 << (n & 0x1F));
if (interruptMask[n >> 5] == 0) {
interruptMasksUsed &= ~(1 << (n >> 5));
}
}


static void updateCTAR1(uint32_t ctar)
{
if (SPI1_CTAR0 != ctar) {
uint32_t mcr = SPI1_MCR;
if (mcr & SPI_MCR_MDIS) {
SPI1_CTAR0 = ctar;
SPI1_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
} else {
SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI1_CTAR0 = ctar;
SPI1_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
SPI1_MCR = mcr;
}
}
}

void SPI1Class::setBitOrder(uint8_t bitOrder)
{
SIM_SCGC6 |= SIM_SCGC6_SPI1;
uint32_t ctar = SPI1_CTAR0;
if (bitOrder == LSBFIRST) {
ctar |= SPI_CTAR_LSBFE;
} else {
ctar &= ~SPI_CTAR_LSBFE;
}
updateCTAR1(ctar);
}

void SPI1Class::setDataMode(uint8_t dataMode)
{
SIM_SCGC6 |= SIM_SCGC6_SPI1;

// TODO: implement with native code
SPCR1 = (SPCR1 & ~SPI_MODE_MASK) | dataMode;
}

void SPI1Class::setClockDivider_noInline(uint32_t clk)
{
SIM_SCGC6 |= SIM_SCGC6_SPI1;
uint32_t ctar = SPI1_CTAR0;
ctar &= (SPI_CTAR_CPOL | SPI_CTAR_CPHA | SPI_CTAR_LSBFE);
if (ctar & SPI_CTAR_CPHA) {
clk = (clk & 0xFFFF0FFF) | ((clk & 0xF000) >> 4);
}
ctar |= clk;
updateCTAR1(ctar);
}

bool SPI1Class::pinIsChipSelect(uint8_t pin)
{
if (pin == 6 || pin == 31) return true;
return false;
}

bool SPI1Class::pinIsChipSelect(uint8_t pin1, uint8_t pin2)
{
return false; // only one CS bith 6 and 31 or logially the same.
}

uint8_t SPI1Class::setCS(uint8_t pin)
{
switch (pin) {
case 6: CORE_PIN6_CONFIG = PORT_PCR_MUX(7); return 0x01; // PTD4
case 31: CORE_PIN31_CONFIG = PORT_PCR_MUX(2); return 0x01; // PTD5
}
return 0;
}
#endif

/**********************************************************/
/* 32 bit Teensy-LC */
/**********************************************************/

+ 201
- 2
SPI.h View File

@@ -395,6 +395,9 @@ private:
static const uint32_t ctar_clock_table[23];
uint32_t ctar;
friend class SPIClass;
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
friend class SPI1Class;
#endif
};


@@ -580,7 +583,7 @@ public:
SPCR.setSCK(pin);
}
// return true if "pin" has special chip select capability
static bool pinIsChipSelect(uint8_t pin);
static uint8_t pinIsChipSelect(uint8_t pin);
// return true if both pin1 and pin2 have independent chip select capability
static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2);
// configure a pin for chip select and return its SPI_MCR_PCSIS bitmask
@@ -596,6 +599,200 @@ private:
};


/**********************************************************/
/* Teensy 3.4 and 3.5 have SPI1 as well */
/**********************************************************/
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
class SPI1Class {
public:
// Initialize the SPI library
static void begin();

// If SPI is to used from within an interrupt, this function registers
// that interrupt with the SPI library, so beginTransaction() can
// prevent conflicts. The input interruptNumber is the number used
// with attachInterrupt. If SPI is used from a different interrupt
// (eg, a timer), interruptNumber should be 255.
static void usingInterrupt(uint8_t n) {
if (n == 3 || n == 4 || n == 24 || n == 33) {
usingInterrupt(IRQ_PORTA);
} else if (n == 0 || n == 1 || (n >= 16 && n <= 19) || n == 25 || n == 32) {
usingInterrupt(IRQ_PORTB);
} else if ((n >= 9 && n <= 13) || n == 15 || n == 22 || n == 23
|| (n >= 27 && n <= 30)) {
usingInterrupt(IRQ_PORTC);
} else if (n == 2 || (n >= 5 && n <= 8) || n == 14 || n == 20 || n == 21) {
usingInterrupt(IRQ_PORTD);
} else if (n == 26 || n == 31) {
usingInterrupt(IRQ_PORTE);
}
}
static void usingInterrupt(IRQ_NUMBER_t interruptName);
static void notUsingInterrupt(IRQ_NUMBER_t interruptName);

// Before using SPI.transfer() or asserting chip select pins,
// this function is used to gain exclusive access to the SPI bus
// and configure the correct settings.
inline static void beginTransaction(SPISettings settings) {
if (interruptMasksUsed) {
__disable_irq();
if (interruptMasksUsed & 0x01) {
interruptSave[0] = NVIC_ICER0 & interruptMask[0];
NVIC_ICER0 = interruptSave[0];
}
#if NVIC_NUM_INTERRUPTS > 32
if (interruptMasksUsed & 0x02) {
interruptSave[1] = NVIC_ICER1 & interruptMask[1];
NVIC_ICER1 = interruptSave[1];
}
#endif
#if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2)
if (interruptMasksUsed & 0x04) {
interruptSave[2] = NVIC_ICER2 & interruptMask[2];
NVIC_ICER2 = interruptSave[2];
}
#endif
#if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3)
if (interruptMasksUsed & 0x08) {
interruptSave[3] = NVIC_ICER3 & interruptMask[3];
NVIC_ICER3 = interruptSave[3];
}
#endif
__enable_irq();
}
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 1;
#endif
if (SPI1_CTAR0 != settings.ctar) {
SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI1_CTAR0 = settings.ctar;
SPI1_CTAR1 = settings.ctar| SPI_CTAR_FMSZ(8);
SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
}
}

// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
inline static uint8_t transfer(uint8_t data) {
SPI1_SR = SPI_SR_TCF;
SPI1_PUSHR = data;
while (!(SPI1_SR & SPI_SR_TCF)) ; // wait
return SPI1_POPR;
}
inline static uint16_t transfer16(uint16_t data) {
SPI1_SR = SPI_SR_TCF;
SPI1_PUSHR = data | SPI_PUSHR_CTAS(1);
while (!(SPI1_SR & SPI_SR_TCF)) ; // wait
return SPI1_POPR;
}
inline static void transfer(void *buf, size_t count) {
uint8_t *p = (uint8_t *)buf;
while (count--) {
*p = transfer(*p);
p++;
}
}

// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
inline static void endTransaction(void) {
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (!inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 0;
#endif
if (interruptMasksUsed) {
if (interruptMasksUsed & 0x01) {
NVIC_ISER0 = interruptSave[0];
}
#if NVIC_NUM_INTERRUPTS > 32
if (interruptMasksUsed & 0x02) {
NVIC_ISER1 = interruptSave[1];
}
#endif
#if NVIC_NUM_INTERRUPTS > 64 && defined(NVIC_ISER2)
if (interruptMasksUsed & 0x04) {
NVIC_ISER2 = interruptSave[2];
}
#endif
#if NVIC_NUM_INTERRUPTS > 96 && defined(NVIC_ISER3)
if (interruptMasksUsed & 0x08) {
NVIC_ISER3 = interruptSave[3];
}
#endif
}
}

// Disable the SPI bus
static void end();

// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
static void setBitOrder(uint8_t bitOrder);

// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
static void setDataMode(uint8_t dataMode);

// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
inline static void setClockDivider(uint8_t clockDiv) {
if (clockDiv == SPI_CLOCK_DIV2) {
setClockDivider_noInline(SPISettings(12000000, MSBFIRST, SPI_MODE0).ctar);
} else if (clockDiv == SPI_CLOCK_DIV4) {
setClockDivider_noInline(SPISettings(4000000, MSBFIRST, SPI_MODE0).ctar);
} else if (clockDiv == SPI_CLOCK_DIV8) {
setClockDivider_noInline(SPISettings(2000000, MSBFIRST, SPI_MODE0).ctar);
} else if (clockDiv == SPI_CLOCK_DIV16) {
setClockDivider_noInline(SPISettings(1000000, MSBFIRST, SPI_MODE0).ctar);
} else if (clockDiv == SPI_CLOCK_DIV32) {
setClockDivider_noInline(SPISettings(500000, MSBFIRST, SPI_MODE0).ctar);
} else if (clockDiv == SPI_CLOCK_DIV64) {
setClockDivider_noInline(SPISettings(250000, MSBFIRST, SPI_MODE0).ctar);
} else { /* clockDiv == SPI_CLOCK_DIV128 */
setClockDivider_noInline(SPISettings(125000, MSBFIRST, SPI_MODE0).ctar);
}
}
static void setClockDivider_noInline(uint32_t clk);

// These undocumented functions should not be used. SPI.transfer()
// polls the hardware flag which is automatically cleared as the
// AVR responds to SPI's interrupt
inline static void attachInterrupt() { }
inline static void detachInterrupt() { }

// Teensy 3.x can use alternate pins for these 3 SPI signals.
inline static void setMOSI(uint8_t pin) __attribute__((always_inline)) {
SPCR1.setMOSI(pin);
}
inline static void setMISO(uint8_t pin) __attribute__((always_inline)) {
SPCR1.setMISO(pin);
}
inline static void setSCK(uint8_t pin) __attribute__((always_inline)) {
SPCR1.setSCK(pin);
}
// return true if "pin" has special chip select capability
static bool pinIsChipSelect(uint8_t pin);
// return true if both pin1 and pin2 have independent chip select capability
static bool pinIsChipSelect(uint8_t pin1, uint8_t pin2);
// configure a pin for chip select and return its SPI_MCR_PCSIS bitmask
static uint8_t setCS(uint8_t pin);

private:
static uint8_t interruptMasksUsed;
static uint32_t interruptMask[(NVIC_NUM_INTERRUPTS+31)/32];
static uint32_t interruptSave[(NVIC_NUM_INTERRUPTS+31)/32];
#ifdef SPI_TRANSACTION_MISMATCH_LED
static uint8_t inTransactionFlag;
#endif
};
#endif




@@ -1475,5 +1672,7 @@ extern SPIClass SPI;
#if defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISL)
extern SPI1Class SPI1;
#endif

#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
extern SPI1Class SPI1;
#endif
#endif

Loading…
Cancel
Save