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

#ifdef SPI_TRANSACTION_MISMATCH_LED #ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t SPIClass::inTransactionFlag = 0; uint8_t SPIClass::inTransactionFlag = 0;
#endif #endif
uint8_t SPIClass::_transferWriteFill = 0;



void SPIClass::begin() void SPIClass::begin()
{ {
SREG = stmp; 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 */ /* 32 bit Teensy 3.x */
/**********************************************************/ /**********************************************************/


#elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISK) #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__) #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 = { const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0, SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0,
32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX, 32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX,
SPIClass SPI((uintptr_t)&KINETISK_SPI0, (uintptr_t)&SPIClass::spi0_hardware); SPIClass SPI((uintptr_t)&KINETISK_SPI0, (uintptr_t)&SPIClass::spi0_hardware);


#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) #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 = { const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0, SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0,
32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX, 32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX,
} }
} }


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

if (count == 0) return; 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; sr = port().SR;
if (sr & 0xF0) { if (sr & 0xF0) {
uint16_t w = port().POPR; // Read any pending RX bytes in uint16_t w = port().POPR; // Read any pending RX bytes in
if (count_read & 1) { 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--; count_read--;
} else { } 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; 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




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


#elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISL) #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 = { const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
SIM_SCGC4, SIM_SCGC4_SPI0, SIM_SCGC4, SIM_SCGC4_SPI0,
0, // BR index 0 0, // BR index 0
}; };
SPIClass SPI((uintptr_t)&KINETISL_SPI0, (uintptr_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 = { const SPIClass::SPI_Hardware_t SPIClass::spi1_hardware = {
SIM_SCGC4, SIM_SCGC4_SPI1, SIM_SCGC4, SIM_SCGC4_SPI1,
1, // BR index 1 in SPI Settings 1, // BR index 1 in SPI Settings
return 0; 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



#include <Arduino.h> #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(), // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode) // usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
#define SPI_HAS_TRANSACTION 1 #define SPI_HAS_TRANSACTION 1
// on if any mismatch is ever detected. // on if any mismatch is ever detected.
//#define SPI_TRANSACTION_MISMATCH_LED 5 //#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 #ifndef LSBFIRST
#define LSBFIRST 0 #define LSBFIRST 0
#endif #endif
while (!(SPSR & _BV(SPIF))) ; while (!(SPSR & _BV(SPIF))) ;
*p = SPDR; *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 // After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus // signal, this function allows others to access the SPI bus
static uint8_t interruptSave; // temp storage, to restore state static uint8_t interruptSave; // temp storage, to restore state
#ifdef SPI_TRANSACTION_MISMATCH_LED #ifdef SPI_TRANSACTION_MISMATCH_LED
static uint8_t inTransactionFlag; static uint8_t inTransactionFlag;
static uint8_t _transferWriteFill;
#endif #endif
}; };


static const SPI_Hardware_t spi0_hardware; static const SPI_Hardware_t spi0_hardware;
static const SPI_Hardware_t spi1_hardware; static const SPI_Hardware_t spi1_hardware;
static const SPI_Hardware_t spi2_hardware; static const SPI_Hardware_t spi2_hardware;

enum DMAState { notAllocated, idle, active, completed};
public: public:
constexpr SPIClass(uintptr_t myport, uintptr_t myhardware) constexpr SPIClass(uintptr_t myport, uintptr_t myhardware)
: port_addr(myport), hardware_addr(myhardware) { : port_addr(myport), hardware_addr(myhardware) {
while (!(port().SR & SPI_SR_TCF)) ; // wait while (!(port().SR & SPI_SR_TCF)) ; // wait
return port().POPR; 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 // After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus // signal, this function allows others to access the SPI bus
void endTransaction(void) { void endTransaction(void) {
#ifdef SPI_TRANSACTION_MISMATCH_LED #ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t inTransactionFlag = 0; uint8_t inTransactionFlag = 0;
#endif #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
}; };




} SPI_Hardware_t; } SPI_Hardware_t;
static const SPI_Hardware_t spi0_hardware; static const SPI_Hardware_t spi0_hardware;
static const SPI_Hardware_t spi1_hardware; static const SPI_Hardware_t spi1_hardware;
enum DMAState { notAllocated, idle, active, completed};
public: public:
constexpr SPIClass(uintptr_t myport, uintptr_t myhardware) constexpr SPIClass(uintptr_t myport, uintptr_t myhardware)
: port_addr(myport), hardware_addr(myhardware) { : port_addr(myport), hardware_addr(myhardware) {
*p = port().DL; *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 // After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus // signal, this function allows others to access the SPI bus
void endTransaction(void) { void endTransaction(void) {
#ifdef SPI_TRANSACTION_MISMATCH_LED #ifdef SPI_TRANSACTION_MISMATCH_LED
uint8_t inTransactionFlag = 0; uint8_t inTransactionFlag = 0;
#endif #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

setMOSI KEYWORD2 setMOSI KEYWORD2
setMISO KEYWORD2 setMISO KEYWORD2
setSCK KEYWORD2 setSCK KEYWORD2
setCS KEYWORD2
usingInterrupt KEYWORD2 usingInterrupt KEYWORD2
beginTransaction KEYWORD2 beginTransaction KEYWORD2
endTransaction KEYWORD2 endTransaction KEYWORD2
SPISettings KEYWORD2 SPISettings KEYWORD2
pinIsChipSelect KEYWORD2
pinIsMOSI KEYWORD2
pinIsMISO KEYWORD2
pinIsSCK KEYWORD2



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

Loading…
Cancel
Save