Browse Source

Add SPI Buffer functions

Merged in the SPI.transfer(buf, retbuf, cnt)  version.

Also merged in the SPI.transfer(buf, retbuf, cnt, event_responder) version that does DMA access.
main
Kurt Eckhardt 7 years ago
parent
commit
c20bf368a5
3 changed files with 629 additions and 58 deletions
  1. +549
    -57
      SPI.cpp
  2. +74
    -1
      SPI.h
  3. +6
    -0
      keywords.txt

+ 549
- 57
SPI.cpp View File

@@ -27,6 +27,8 @@ uint8_t SPIClass::interruptSave = 0;
#ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t SPIClass::inTransactionFlag = 0;
#endif
uint8_t SPIClass::_transferWriteFill = 0;


void SPIClass::begin()
{
@@ -138,16 +140,63 @@ void SPIClass::usingInterrupt(uint8_t interruptNumber)
SREG = stmp;
}

void SPIClass::transfer(const void * buf, void * retbuf, uint32_t count) {
if (count == 0) return;

const uint8_t *p = (const uint8_t *)buf;
uint8_t *pret = (uint8_t *)retbuf;
uint8_t in;

uint8_t out = p ? *p++ : _transferWriteFill;
SPDR = out;
while (--count > 0) {
if (p) {
out = *p++;
}
while (!(SPSR & _BV(SPIF))) ;
in = SPDR;
SPDR = out;
if (pret)*pret++ = in;
}
while (!(SPSR & _BV(SPIF))) ;
in = SPDR;
if (pret)*pret = in;
}


/**********************************************************/
/* 32 bit Teensy 3.x */
/**********************************************************/

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

#ifndef TRANSFER_COUNT_FIXED
inline void DMAChanneltransferCount(DMAChannel * dmac, unsigned int len) {
// note does no validation of length...
DMABaseClass::TCD_t *tcd = dmac->TCD;
if (!(tcd->BITER & DMA_TCD_BITER_ELINK)) {
tcd->BITER = len & 0x7fff;
} else {
tcd->BITER = (tcd->BITER & 0xFE00) | (len & 0x1ff);
}
tcd->CITER = tcd->BITER;
}
#else
inline void DMAChanneltransferCount(DMAChannel * dmac, unsigned int len) {
dmac->transferCount(len);
}
#endif
#endif


#if defined(__MK20DX128__) || defined(__MK20DX256__)
void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/}
#ifdef SPI_HAS_TRANSFER_ASYNC
void _spi_dma_rxISR0(void) {SPI.dma_rxisr();}
#else
void _spi_dma_rxISR0(void) {;}
#endif

const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0,
32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX,
@@ -165,9 +214,15 @@ const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SPIClass SPI((uintptr_t)&KINETISK_SPI0, (uintptr_t)&SPIClass::spi0_hardware);

#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/}
void _spi_dma_rxISR1(void) {/*SPI1.dma_rxisr();*/}
void _spi_dma_rxISR2(void) {/*SPI2.dma_rxisr();*/}
#ifdef SPI_HAS_TRANSFER_ASYNC
void _spi_dma_rxISR0(void) {SPI.dma_rxisr();}
void _spi_dma_rxISR1(void) {SPI1.dma_rxisr();}
void _spi_dma_rxISR2(void) {SPI2.dma_rxisr();}
#else
void _spi_dma_rxISR0(void) {;}
void _spi_dma_rxISR1(void) {;}
void _spi_dma_rxISR2(void) {;}
#endif
const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0,
32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX,
@@ -473,73 +528,367 @@ void SPIClass::setSCK(uint8_t pin)
}
}

void SPIClass::transfer(void *buf, size_t count)
void SPIClass::transfer(const void * buf, void * retbuf, size_t count)
{

if (count == 0) return;
uint8_t *p_write = (uint8_t *)buf;
uint8_t *p_read = p_write;
size_t count_read = count;
bool lsbfirst = (port().CTAR0 & SPI_CTAR_LSBFE) ? true : false;
uint32_t sr, full_mask;

// Lets clear the reader queue
port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);

// Now lets loop while we still have data to output
if (count & 1) {
if (count > 1)
port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
else
port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0);
count--;
}

full_mask = (hardware().queue_size-1) << 12;
while (count > 0) {
// Push out the next byte
uint16_t w = (*p_write++) << 8;
w |= *p_write++;
if (lsbfirst) w = __builtin_bswap16(w);
if (count == 2)
port().PUSHR = w | SPI_PUSHR_CTAS(1);
else
port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1);
count -= 2; // how many bytes to output.
// Make sure queue is not full before pushing next byte out
do {
if (!(port().CTAR0 & SPI_CTAR_LSBFE)) {
// We are doing the standard MSB order
const uint8_t *p_write = (const uint8_t *)buf;
uint8_t *p_read = (uint8_t *)retbuf;
size_t count_read = count;

// Lets clear the reader queue
port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);

uint32_t sr;

// Now lets loop while we still have data to output
if (count & 1) {
if (p_write) {
if (count > 1)
port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
else
port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0);
} else {
if (count > 1)
port().PUSHR = _transferWriteFill | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
else
port().PUSHR = _transferWriteFill | SPI_PUSHR_CTAS(0);
}
count--;
}

uint16_t w = (uint16_t)(_transferWriteFill << 8) | _transferWriteFill;

while (count > 0) {
// Push out the next byte;
if (p_write) {
w = (*p_write++) << 8;
w |= *p_write++;
}
uint16_t queue_full_status_mask = (hardware().queue_size-1) << 12;
if (count == 2)
port().PUSHR = w | SPI_PUSHR_CTAS(1);
else
port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1);
count -= 2; // how many bytes to output.
// Make sure queue is not full before pushing next byte out
do {
sr = port().SR;
if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) {
if (p_read) {
*p_read++ = w; // Read any pending RX bytes in
}
count_read--;
} else {
if (p_read) {
*p_read++ = w >> 8;
*p_read++ = (w & 0xff);
}
count_read -= 2;
}
}
} while ((sr & (15 << 12)) > queue_full_status_mask);

}

// now lets wait for all of the read bytes to be returned...
while (count_read) {
sr = port().SR;
if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) {
*p_read++ = w; // Read any pending RX bytes in
if (p_read)
*p_read++ = w; // Read any pending RX bytes in
count_read--;
} else {
if (lsbfirst) w = __builtin_bswap16(w);
*p_read++ = w >> 8;
*p_read++ = (w & 0xff);
if (p_read) {
*p_read++ = w >> 8;
*p_read++ = (w & 0xff);
}
count_read -= 2;
}
}
} while ((sr & (15 << 12)) > full_mask);
}
} else {
// We are doing the less ofen LSB mode
const uint8_t *p_write = (const uint8_t *)buf;
uint8_t *p_read = (uint8_t *)retbuf;
size_t count_read = count;

// Lets clear the reader queue
port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);

uint32_t sr;

// Now lets loop while we still have data to output
if (count & 1) {
if (p_write) {
if (count > 1)
port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
else
port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0);
} else {
if (count > 1)
port().PUSHR = _transferWriteFill | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
else
port().PUSHR = _transferWriteFill | SPI_PUSHR_CTAS(0);
}
count--;
}

uint16_t w = _transferWriteFill;

while (count > 0) {
// Push out the next byte;
if (p_write) {
w = *p_write++;
w |= ((*p_write++) << 8);
}
uint16_t queue_full_status_mask = (hardware().queue_size-1) << 12;
if (count == 2)
port().PUSHR = w | SPI_PUSHR_CTAS(1);
else
port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1);
count -= 2; // how many bytes to output.
// Make sure queue is not full before pushing next byte out
do {
sr = port().SR;
if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) {
if (p_read) {
*p_read++ = w; // Read any pending RX bytes in
}
count_read--;
} else {
if (p_read) {
*p_read++ = (w & 0xff);
*p_read++ = w >> 8;
}
count_read -= 2;
}
}
} while ((sr & (15 << 12)) > queue_full_status_mask);

}

// now lets wait for all of the read bytes to be returned...
while (count_read) {
sr = port().SR;
if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) {
if (p_read)
*p_read++ = w; // Read any pending RX bytes in
count_read--;
} else {
if (p_read) {
*p_read++ = (w & 0xff);
*p_read++ = w >> 8;
}
count_read -= 2;
}
}
}
}
}
//=============================================================================
// ASYNCH Support
//=============================================================================
//=========================================================================
// Try Transfer using DMA.
//=========================================================================
#ifdef SPI_HAS_TRANSFER_ASYNC
static uint8_t bit_bucket;
#define dontInterruptAtCompletion(dmac) (dmac)->TCD->CSR &= ~DMA_TCD_CSR_INTMAJOR

//=========================================================================
// Init the DMA channels
//=========================================================================
bool SPIClass::initDMAChannels() {
// Allocate our channels.
_dmaTX = new DMAChannel();
if (_dmaTX == nullptr) {
return false;
}

// now lets wait for all of the read bytes to be returned...
while (count_read) {
sr = port().SR;
if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) {
*p_read++ = w; // Read any pending RX bytes in
count_read--;
} else {
if (lsbfirst) w = __builtin_bswap16(w);
*p_read++ = w >> 8;
*p_read++ = (w & 0xff);
count_read -= 2;
_dmaRX = new DMAChannel();
if (_dmaRX == nullptr) {
delete _dmaTX; // release it
_dmaTX = nullptr;
return false;
}

// Let's setup the RX chain
_dmaRX->disable();
_dmaRX->source((volatile uint8_t&)port().POPR);
_dmaRX->disableOnCompletion();
_dmaRX->triggerAtHardwareEvent(hardware().rx_dma_channel);
_dmaRX->attachInterrupt(hardware().dma_rxisr);
_dmaRX->interruptAtCompletion();

// We may be using settings chain here so lets set it up.
// Now lets setup TX chain. Note if trigger TX is not set
// we need to have the RX do it for us.
_dmaTX->disable();
_dmaTX->destination((volatile uint8_t&)port().PUSHR);
_dmaTX->disableOnCompletion();

if (hardware().tx_dma_channel) {
_dmaTX->triggerAtHardwareEvent(hardware().tx_dma_channel);
} else {
// Serial.printf("SPI InitDMA tx triger by RX: %x\n", (uint32_t)_dmaRX);
_dmaTX->triggerAtTransfersOf(*_dmaRX);
}


_dma_state = DMAState::idle; // Should be first thing set!
return true;
}

//=========================================================================
// Main Aync Transfer function
//=========================================================================

bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) {
uint8_t dma_first_byte;
if (_dma_state == DMAState::notAllocated) {
if (!initDMAChannels())
return false;
}

if (_dma_state == DMAState::active)
return false; // already active

event_responder.clearEvent(); // Make sure it is not set yet
if (count < 2) {
// Use non-async version to simplify cases...
transfer(buf, retbuf, count);
event_responder.triggerEvent();
return true;
}

// Now handle the cases where the count > then how many we can output in one DMA request
if (count > hardware().max_dma_count) {
_dma_count_remaining = count - hardware().max_dma_count;
count = hardware().max_dma_count;
} else {
_dma_count_remaining = 0;
}

// Now See if caller passed in a source buffer.
_dmaTX->TCD->ATTR_DST = 0; // Make sure set for 8 bit mode
uint8_t *write_data = (uint8_t*) buf;
if (buf) {
dma_first_byte = *write_data;
_dmaTX->sourceBuffer((uint8_t*)write_data+1, count-1);
_dmaTX->TCD->SLAST = 0; // Finish with it pointing to next location
} else {
dma_first_byte = _transferWriteFill;
_dmaTX->source((uint8_t&)_transferWriteFill); // maybe have setable value
DMAChanneltransferCount(_dmaTX, count-1);
}
if (retbuf) {
// On T3.5 must handle SPI1/2 differently as only one DMA channel
_dmaRX->TCD->ATTR_SRC = 0; //Make sure set for 8 bit mode...
_dmaRX->destinationBuffer((uint8_t*)retbuf, count);
_dmaRX->TCD->DLASTSGA = 0; // At end point after our bufffer
} else {
// Write only mode
_dmaRX->TCD->ATTR_SRC = 0; //Make sure set for 8 bit mode...
_dmaRX->destination((uint8_t&)bit_bucket);
DMAChanneltransferCount(_dmaRX, count);
}

_dma_event_responder = &event_responder;
// Now try to start it?
// Setup DMA main object
yield();
port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS(0x1F);

port().SR = 0xFF0F0000;

// Lets try to output the first byte to make sure that we are in 8 bit mode...
port().PUSHR = dma_first_byte | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;

if (hardware().tx_dma_channel) {
port().RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS;
_dmaRX->enable();
// Get the initial settings.
_dmaTX->enable();
} else {
//T3.5 SP1 and SPI2 - TX is not triggered by SPI but by RX...
port().RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS ;
_dmaTX->triggerAtTransfersOf(*_dmaRX);
_dmaTX->enable();
_dmaRX->enable();
}

_dma_state = DMAState::active;
return true;
}


//-------------------------------------------------------------------------
// DMA RX ISR
//-------------------------------------------------------------------------
void SPIClass::dma_rxisr(void) {
_dmaRX->clearInterrupt();
_dmaTX->clearComplete();
_dmaRX->clearComplete();

uint8_t should_reenable_tx = true; // should we re-enable TX maybe not if count will be 0...
if (_dma_count_remaining) {
// What do I need to do to start it back up again...
// We will use the BITR/CITR from RX as TX may have prefed some stuff
if (_dma_count_remaining > hardware().max_dma_count) {
_dma_count_remaining -= hardware().max_dma_count;
} else {
DMAChanneltransferCount(_dmaTX, _dma_count_remaining-1);
DMAChanneltransferCount(_dmaRX, _dma_count_remaining);
if (_dma_count_remaining == 1) should_reenable_tx = false;

_dma_count_remaining = 0;
}
// In some cases we need to again start the TX manually to get it to work...
if (_dmaTX->TCD->SADDR == &_transferWriteFill) {
if (port().CTAR0 & SPI_CTAR_FMSZ(8)) {
port().PUSHR = (_transferWriteFill | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT);
} else {
port().PUSHR = (_transferWriteFill | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT);
}
} else {
if (port().CTAR0 & SPI_CTAR_FMSZ(8)) {
// 16 bit mode
uint16_t w = *((uint16_t*)_dmaTX->TCD->SADDR);
_dmaTX->TCD->SADDR = (volatile uint8_t*)(_dmaTX->TCD->SADDR) + 2;
port().PUSHR = (w | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT);
} else {
uint8_t w = *((uint8_t*)_dmaTX->TCD->SADDR);
_dmaTX->TCD->SADDR = (volatile uint8_t*)(_dmaTX->TCD->SADDR) + 1;
port().PUSHR = (w | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT);
}
}
_dmaRX->enable();
if (should_reenable_tx)
_dmaTX->enable();
} else {

port().RSER = 0;
//port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); // clear out the queue
port().SR = 0xFF0F0000;
port().CTAR0 &= ~(SPI_CTAR_FMSZ(8)); // Hack restore back to 8 bits

_dma_state = DMAState::completed; // set back to 1 in case our call wants to start up dma again
_dma_event_responder->triggerEvent();

}
}
#endif // SPI_HAS_TRANSFER_ASYNC


/**********************************************************/
@@ -548,7 +897,14 @@ void SPIClass::transfer(void *buf, size_t count)

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

void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/}
#ifdef SPI_HAS_TRANSFER_ASYNC
void _spi_dma_rxISR0(void) {SPI.dma_isr();}
void _spi_dma_rxISR1(void) {SPI1.dma_isr();}
#else
void _spi_dma_rxISR0(void) {;}
void _spi_dma_rxISR1(void) {;}
#endif

const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC4, SIM_SCGC4_SPI0,
0, // BR index 0
@@ -565,7 +921,6 @@ const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
};
SPIClass SPI((uintptr_t)&KINETISL_SPI0, (uintptr_t)&SPIClass::spi0_hardware);

void _spi_dma_rxISR1(void) {/*SPI1.dma_rxisr();*/}
const SPIClass::SPI_Hardware_t SPIClass::spi1_hardware = {
SIM_SCGC4, SIM_SCGC4_SPI1,
1, // BR index 1 in SPI Settings
@@ -752,8 +1107,145 @@ uint8_t SPIClass::setCS(uint8_t pin)
return 0;
}

void SPIClass::transfer(const void * buf, void * retbuf, size_t count) {
if (count == 0) return;
const uint8_t *p = (const uint8_t *)buf;
uint8_t *pret = (uint8_t *)retbuf;
uint8_t in;

while (!(port().S & SPI_S_SPTEF)) ; // wait
uint8_t out = p ? *p++ : _transferWriteFill;
port().DL = out;
while (--count > 0) {
if (p) {
out = *p++;
}
while (!(port().S & SPI_S_SPTEF)) ; // wait
__disable_irq();
port().DL = out;
while (!(port().S & SPI_S_SPRF)) ; // wait
in = port().DL;
__enable_irq();
if (pret)*pret++ = in;
}
while (!(port().S & SPI_S_SPRF)) ; // wait
in = port().DL;
if (pret)*pret = in;
}
//=============================================================================
// ASYNCH Support
//=============================================================================
//=========================================================================
// Try Transfer using DMA.
//=========================================================================
#ifdef SPI_HAS_TRANSFER_ASYNC
static uint8_t _dma_dummy_rx;

void SPIClass::dma_isr(void) {
// Serial.println("_spi_dma_rxISR");
_dmaRX->clearInterrupt();
port().C2 = 0;
uint8_t tmp __attribute__((unused)) = port().S;
_dmaTX->clearComplete();
_dmaRX->clearComplete();

_dma_state = DMAState::completed; // set back to 1 in case our call wants to start up dma again
_dma_event_responder->triggerEvent();
}

bool SPIClass::initDMAChannels() {
//Serial.println("First dma call"); Serial.flush();
_dmaTX = new DMAChannel();
if (_dmaTX == nullptr) {
return false;
}

_dmaTX->disable();
_dmaTX->destination((volatile uint8_t&)port().DL);
_dmaTX->disableOnCompletion();
_dmaTX->triggerAtHardwareEvent(hardware().tx_dma_channel);

#endif

_dmaRX = new DMAChannel();
if (_dmaRX == NULL) {
delete _dmaTX;
_dmaRX = nullptr;
return false;
}
_dmaRX->disable();
_dmaRX->source((volatile uint8_t&)port().DL);
_dmaRX->disableOnCompletion();
_dmaRX->triggerAtHardwareEvent(hardware().rx_dma_channel);
_dmaRX->attachInterrupt(hardware().dma_isr);
_dmaRX->interruptAtCompletion();

_dma_state = DMAState::idle; // Should be first thing set!
//Serial.println("end First dma call");
return true;
}

//=========================================================================
// Main Aync Transfer function
//=========================================================================
bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) {
if (_dma_state == DMAState::notAllocated) {
if (!initDMAChannels()) {
return false;
}
}
if (_dma_state == DMAState::active)
return false; // already active

event_responder.clearEvent(); // Make sure it is not set yet

if (count < 2) {
// Use non-async version to simplify cases...
transfer(buf, retbuf, count);
event_responder.triggerEvent();
return true;
}
//_dmaTX->destination((volatile uint8_t&)port().DL);
//_dmaRX->source((volatile uint8_t&)port().DL);
_dmaTX->CFG->DCR = (_dmaTX->CFG->DCR & ~DMA_DCR_DSIZE(3)) | DMA_DCR_DSIZE(1);
_dmaRX->CFG->DCR = (_dmaRX->CFG->DCR & ~DMA_DCR_SSIZE(3)) | DMA_DCR_SSIZE(1); // 8 bit transfer

// Now see if the user passed in TX buffer to send.
uint8_t first_char;
if (buf) {
uint8_t *data_out = (uint8_t*)buf;
first_char = *data_out++;
_dmaTX->sourceBuffer(data_out, count-1);
} else {
first_char = (_transferWriteFill & 0xff);
_dmaTX->source((uint8_t&)_transferWriteFill); // maybe have setable value
_dmaTX->transferCount(count-1);
}

if (retbuf) {
_dmaRX->destinationBuffer((uint8_t*)retbuf, count);
} else {
_dmaRX->destination(_dma_dummy_rx); // NULL ?
_dmaRX->transferCount(count);
}

_dma_event_responder = &event_responder;

//Serial.println("Before DMA C2");
// Try pushing the first character
while (!(port().S & SPI_S_SPTEF));
port().DL = first_char;

port().C2 |= SPI_C2_TXDMAE | SPI_C2_RXDMAE;

// Now make sure SPI is enabled.
port().C1 |= SPI_C1_SPE;
_dmaRX->enable();
_dmaTX->enable();
_dma_state = DMAState::active;
return true;
}
#endif //SPI_HAS_TRANSFER_ASYNC

#endif

+ 74
- 1
SPI.h View File

@@ -15,6 +15,16 @@

#include <Arduino.h>

#if defined(__arm__) && defined(TEENSYDUINO)
#if defined(__has_include) && __has_include(<EventResponder.h>)
// SPI_HAS_TRANSFER_ASYNC - Defined to say that the SPI supports an ASYNC version
// of the SPI_HAS_TRANSFER_BUF
#define SPI_HAS_TRANSFER_ASYNC 1
#include <DMAChannel.h>
#include <EventResponder.h>
#endif
#endif

// SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
#define SPI_HAS_TRANSACTION 1
@@ -25,6 +35,12 @@
// on if any mismatch is ever detected.
//#define SPI_TRANSACTION_MISMATCH_LED 5

// SPI_HAS_TRANSFER_BUF - is defined to signify that this library supports
// a version of transfer which allows you to pass in both TX and RX buffer
// pointers, either of which could be NULL
#define SPI_HAS_TRANSFER_BUF 1


#ifndef LSBFIRST
#define LSBFIRST 0
#endif
@@ -235,6 +251,8 @@ public:
while (!(SPSR & _BV(SPIF))) ;
*p = SPDR;
}
static void setTransferWriteFill(uint8_t ch ) {_transferWriteFill = ch;}
static void transfer(const void * buf, void * retbuf, uint32_t count);

// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
@@ -290,6 +308,7 @@ private:
static uint8_t interruptSave; // temp storage, to restore state
#ifdef SPI_TRANSACTION_MISMATCH_LED
static uint8_t inTransactionFlag;
static uint8_t _transferWriteFill;
#endif
};

@@ -432,6 +451,8 @@ public:
static const SPI_Hardware_t spi0_hardware;
static const SPI_Hardware_t spi1_hardware;
static const SPI_Hardware_t spi2_hardware;

enum DMAState { notAllocated, idle, active, completed};
public:
constexpr SPIClass(uintptr_t myport, uintptr_t myhardware)
: port_addr(myport), hardware_addr(myhardware) {
@@ -519,7 +540,23 @@ public:
while (!(port().SR & SPI_SR_TCF)) ; // wait
return port().POPR;
}
void transfer(void *buf, size_t count);

void inline transfer(void *buf, size_t count) {transfer(buf, buf, count);}
void setTransferWriteFill(uint8_t ch ) {_transferWriteFill = ch;}
void transfer(const void * buf, void * retbuf, size_t count);

// Asynch support (DMA )
#ifdef SPI_HAS_TRANSFER_ASYNC
bool transfer(const void *txBuffer, void *rxBuffer, size_t count, EventResponderRef event_responder);

friend void _spi_dma_rxISR0(void);
friend void _spi_dma_rxISR1(void);
friend void _spi_dma_rxISR2(void);

inline void dma_rxisr(void);
#endif


// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
void endTransaction(void) {
@@ -622,6 +659,18 @@ private:
#ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t inTransactionFlag = 0;
#endif

uint8_t _transferWriteFill = 0;

// DMA Support
#ifdef SPI_HAS_TRANSFER_ASYNC
bool initDMAChannels();
DMAState _dma_state = DMAState::notAllocated;
uint32_t _dma_count_remaining = 0; // How many bytes left to output after current DMA completes
DMAChannel *_dmaTX = nullptr;
DMAChannel *_dmaRX = nullptr;
EventResponder *_dma_event_responder = nullptr;
#endif
};


@@ -766,6 +815,7 @@ public:
} SPI_Hardware_t;
static const SPI_Hardware_t spi0_hardware;
static const SPI_Hardware_t spi1_hardware;
enum DMAState { notAllocated, idle, active, completed};
public:
constexpr SPIClass(uintptr_t myport, uintptr_t myhardware)
: port_addr(myport), hardware_addr(myhardware) {
@@ -851,6 +901,18 @@ public:
*p = port().DL;
}

void setTransferWriteFill(uint8_t ch ) {_transferWriteFill = ch;}
void transfer(const void * buf, void * retbuf, size_t count);

// Asynch support (DMA )
#ifdef SPI_HAS_TRANSFER_ASYNC
bool transfer(const void *txBuffer, void *rxBuffer, size_t count, EventResponderRef event_responder);

friend void _spi_dma_rxISR0(void);
friend void _spi_dma_rxISR1(void);
inline void dma_isr(void);
#endif

// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
void endTransaction(void) {
@@ -945,6 +1007,17 @@ private:
#ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t inTransactionFlag = 0;
#endif
uint8_t _transferWriteFill = 0;
#ifdef SPI_HAS_TRANSFER_ASYNC
// DMA Support
bool initDMAChannels();

DMAState _dma_state = DMAState::notAllocated;
uint32_t _dma_count_remaining = 0; // How many bytes left to output after current DMA completes
DMAChannel *_dmaTX = nullptr;
DMAChannel *_dmaRX = nullptr;
EventResponder *_dma_event_responder = nullptr;
#endif
};



+ 6
- 0
keywords.txt View File

@@ -23,10 +23,16 @@ setClockDivider KEYWORD2
setMOSI KEYWORD2
setMISO KEYWORD2
setSCK KEYWORD2
setCS KEYWORD2
usingInterrupt KEYWORD2
beginTransaction KEYWORD2
endTransaction KEYWORD2
SPISettings KEYWORD2
pinIsChipSelect KEYWORD2
pinIsMOSI KEYWORD2
pinIsMISO KEYWORD2
pinIsSCK KEYWORD2


#######################################
# Constants (LITERAL1)

Loading…
Cancel
Save