Browse Source

Merge pull request #16 from KurtE/K66-SPI2

SPI2 Support
main
Paul Stoffregen 8 years ago
parent
commit
f6c294f804
2 changed files with 324 additions and 0 deletions
  1. +135
    -0
      SPI.cpp
  2. +189
    -0
      SPI.h

+ 135
- 0
SPI.cpp View File

@@ -317,6 +317,9 @@ uint8_t SPIClass::setCS(uint8_t pin)
case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTD6
case 22: CORE_PIN22_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTC1
case 15: CORE_PIN15_CONFIG = PORT_PCR_MUX(2); return 0x10; // PTC0
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
case 26: CORE_PIN26_CONFIG = PORT_PCR_MUX(2);return 0x01;
#endif
}
return 0;
}
@@ -445,6 +448,138 @@ uint8_t SPI1Class::setCS(uint8_t pin)
}
return 0;
}

// SPI2 Class
SPI2Class SPI2;

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

void SPI2Class::begin()
{
SIM_SCGC3 |= SIM_SCGC3_SPI2;
SPI2_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI2_CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
SPI2_CTAR1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
SPI2_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
SPCR2.enable_pins(); // pins managed by SPCRemulation in avr_emulation.h
}

void SPI2Class::end() {
SPCR2.disable_pins();
SPI2_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
}

void SPI2Class::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 SPI2Class::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 updateCTAR2(uint32_t ctar)
{
if (SPI2_CTAR0 != ctar) {
uint32_t mcr = SPI2_MCR;
if (mcr & SPI_MCR_MDIS) {
SPI2_CTAR0 = ctar;
SPI2_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
} else {
SPI2_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI2_CTAR0 = ctar;
SPI2_CTAR1 = ctar | SPI_CTAR_FMSZ(8);
SPI2_MCR = mcr;
}
}
}

void SPI2Class::setBitOrder(uint8_t bitOrder)
{
SIM_SCGC3 |= SIM_SCGC3_SPI2;
uint32_t ctar = SPI2_CTAR0;
if (bitOrder == LSBFIRST) {
ctar |= SPI_CTAR_LSBFE;
} else {
ctar &= ~SPI_CTAR_LSBFE;
}
updateCTAR2(ctar);
}

void SPI2Class::setDataMode(uint8_t dataMode)
{
SIM_SCGC3 |= SIM_SCGC3_SPI2;

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

void SPI2Class::setClockDivider_noInline(uint32_t clk)
{
SIM_SCGC3 |= SIM_SCGC3_SPI2;
uint32_t ctar = SPI2_CTAR0;
ctar &= (SPI_CTAR_CPOL | SPI_CTAR_CPHA | SPI_CTAR_LSBFE);
if (ctar & SPI_CTAR_CPHA) {
clk = (clk & 0xFFFF0FFF) | ((clk & 0xF000) >> 4);
}
ctar |= clk;
updateCTAR2(ctar);
}

uint8_t SPI2Class::pinIsChipSelect(uint8_t pin)
{
switch (pin) {
case 43: return 0x01; // CS0
case 54: return 0x02; // CS1
case 55: return 0x01; // CS0
}
return 0;
}

bool SPI2Class::pinIsChipSelect(uint8_t pin1, uint8_t pin2)
{
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;
}

uint8_t SPI2Class::setCS(uint8_t pin)
{
switch (pin) {
case 43: CORE_PIN43_CONFIG = PORT_PCR_MUX(2); return 0x01; // CS0
case 54: CORE_PIN54_CONFIG = PORT_PCR_MUX(2); return 0x02; // CS1
case 55: CORE_PIN55_CONFIG = PORT_PCR_MUX(2); return 0x01; // CS0
}
return 0;
}


#endif

/**********************************************************/

+ 189
- 0
SPI.h View File

@@ -397,6 +397,7 @@ private:
friend class SPIClass;
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
friend class SPI1Class;
friend class SPI2Class;
#endif
};

@@ -783,6 +784,194 @@ public:
// 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
};
class SPI2Class {
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 (SPI2_CTAR0 != settings.ctar) {
SPI2_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
SPI2_CTAR0 = settings.ctar;
SPI2_CTAR1 = settings.ctar| SPI_CTAR_FMSZ(8);
SPI2_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) {
SPI2_SR = SPI_SR_TCF;
SPI2_PUSHR = data;
while (!(SPI2_SR & SPI_SR_TCF)) ; // wait
return SPI2_POPR;
}
inline static uint16_t transfer16(uint16_t data) {
SPI2_SR = SPI_SR_TCF;
SPI2_PUSHR = data | SPI_PUSHR_CTAS(1);
while (!(SPI2_SR & SPI_SR_TCF)) ; // wait
return SPI2_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)) {
SPCR2.setMOSI(pin);
}
inline static void setMISO(uint8_t pin) __attribute__((always_inline)) {
SPCR2.setMISO(pin);
}
inline static void setSCK(uint8_t pin) __attribute__((always_inline)) {
SPCR2.setSCK(pin);
}
// return true if "pin" has special chip select capability
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
static uint8_t setCS(uint8_t pin);

private:
static uint8_t interruptMasksUsed;
static uint32_t interruptMask[(NVIC_NUM_INTERRUPTS+31)/32];

Loading…
Cancel
Save