ソースを参照

Merge pull request #45 from KurtE/T4_Async_Support

T4 async support
main
Paul Stoffregen 5年前
コミット
70c832a49b
コミッターのメールアドレスに関連付けられたアカウントが存在しません
2個のファイルの変更208行の追加7行の削除
  1. +202
    -3
      SPI.cpp
  2. +6
    -4
      SPI.h

+ 202
- 3
SPI.cpp ファイルの表示

@@ -11,7 +11,7 @@
#include "SPI.h"
#include "pins_arduino.h"

//#define DEBUG_DMA_TRANSFERS

/**********************************************************/
/* 8 bit AVR-based boards */
@@ -1282,7 +1282,7 @@ void SPIClass::begin()

uint32_t fastio = IOMUXC_PAD_SRE | IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3);
//uint32_t fastio = IOMUXC_PAD_DSE(3) | IOMUXC_PAD_SPEED(3);
Serial.printf("SPI MISO: %d MOSI: %d, SCK: %d\n", hardware().miso_pin[miso_pin_index], hardware().mosi_pin[mosi_pin_index], hardware().sck_pin[sck_pin_index]);
//Serial.printf("SPI MISO: %d MOSI: %d, SCK: %d\n", hardware().miso_pin[miso_pin_index], hardware().mosi_pin[mosi_pin_index], hardware().sck_pin[sck_pin_index]);
*(portControlRegister(hardware().miso_pin[miso_pin_index])) = fastio;
*(portControlRegister(hardware().mosi_pin[mosi_pin_index])) = fastio;
*(portControlRegister(hardware().sck_pin[sck_pin_index])) = fastio;
@@ -1385,8 +1385,11 @@ void SPIClass::setDataMode(uint8_t dataMode)
//SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
}

void _spi_dma_rxISR0(void) {SPI.dma_rxisr();}

const SPIClass::SPI_Hardware_t SPIClass::spiclass_lpspi4_hardware = {
CCM_CCGR1, CCM_CCGR1_LPSPI4(CCM_CCGR_ON),
DMAMUX_SOURCE_LPSPI4_TX, DMAMUX_SOURCE_LPSPI4_RX, _spi_dma_rxISR0,
12,
3 | 0x10,
11,
@@ -1435,7 +1438,7 @@ void SPIClass::transfer(const void * buf, void * retbuf, size_t count)

// Pass 1 keep it simple and don't try packing 8 bits into 16 yet..
// Lets clear the reader queue
//port().CR = LPSPI_CR_RRF;
port().CR = LPSPI_CR_RRF | LPSPI_CR_MEN; // clear the queue and make sure still enabled.

while (count > 0) {
// Push out the next byte;
@@ -1465,6 +1468,202 @@ void SPIClass::transfer(const void * buf, void * retbuf, size_t count)

void SPIClass::end(){}

//=============================================================================
// 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;
}

_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().RDR);
_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().TDR);
_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 Async Transfer function
//=========================================================================
#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
#ifdef DEBUG_DMA_TRANSFERS
void dumpDMA_TCD(DMABaseClass *dmabc)
{
Serial4.printf("%x %x:", (uint32_t)dmabc, (uint32_t)dmabc->TCD);

Serial4.printf("SA:%x SO:%d AT:%x NB:%x SL:%d DA:%x DO: %d CI:%x DL:%x CS:%x BI:%x\n", (uint32_t)dmabc->TCD->SADDR,
dmabc->TCD->SOFF, dmabc->TCD->ATTR, dmabc->TCD->NBYTES, dmabc->TCD->SLAST, (uint32_t)dmabc->TCD->DADDR,
dmabc->TCD->DOFF, dmabc->TCD->CITER, dmabc->TCD->DLASTSGA, dmabc->TCD->CSR, dmabc->TCD->BITER);
}
#endif

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;
}

// Now handle the cases where the count > then how many we can output in one DMA request
if (count > MAX_DMA_COUNT) {
_dma_count_remaining = count - MAX_DMA_COUNT;
count = 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) {
_dmaTX->sourceBuffer((uint8_t*)write_data, count);
_dmaTX->TCD->SLAST = 0; // Finish with it pointing to next location
if ((uint32_t)write_data >= 0x20200000u) arm_dcache_flush(write_data, count);
} else {
_dmaTX->source((uint8_t&)_transferWriteFill); // maybe have setable value
DMAChanneltransferCount(_dmaTX, count);
}
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
if ((uint32_t)retbuf >= 0x20200000u) arm_dcache_delete(retbuf, count);
} 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();

#ifdef DEBUG_DMA_TRANSFERS
// Lets dump TX, RX
dumpDMA_TCD(_dmaTX);
dumpDMA_TCD(_dmaRX);
#endif

// Make sure port is in 8 bit mode and clear watermark
port().TCR = (port().TCR & ~(LPSPI_TCR_FRAMESZ(31))) | LPSPI_TCR_FRAMESZ(7);
port().FCR = 0;

// Lets try to output the first byte to make sure that we are in 8 bit mode...
port().DER = LPSPI_DER_TDDE | LPSPI_DER_RDDE; //enable DMA on both TX and RX
port().SR = 0x3f00; // clear out all of the other status...

_dmaRX->enable();
_dmaTX->enable();

_dma_state = DMAState::active;
return true;
}


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

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 > MAX_DMA_COUNT) {
_dma_count_remaining -= MAX_DMA_COUNT;
} else {
DMAChanneltransferCount(_dmaTX, _dma_count_remaining);
DMAChanneltransferCount(_dmaRX, _dma_count_remaining);

_dma_count_remaining = 0;
}
_dmaRX->enable();
_dmaTX->enable();
} else {

port().FCR = LPSPI_FCR_TXWATER(15); // _spi_fcr_save; // restore the FSR status...
port().DER = 0; // DMA no longer doing TX (or RX)

port().CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; // actually clear both...
port().SR = 0x3f00; // clear out all of the other status...

_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



#endif

+ 6
- 4
SPI.h ファイルの表示

@@ -1034,7 +1034,7 @@ private:
//#include "debug/printf.h"

// TODO......
#undef SPI_HAS_TRANSFER_ASYNC
//#undef SPI_HAS_TRANSFER_ASYNC

class SPISettings {
public:
@@ -1100,6 +1100,9 @@ public:
typedef struct {
volatile uint32_t &clock_gate_register;
const uint32_t clock_gate_mask;
uint8_t tx_dma_channel;
uint8_t rx_dma_channel;
void (*dma_rxisr)();
const uint8_t miso_pin[CNT_MISO_PINS];
const uint32_t miso_mux[CNT_MISO_PINS];
const uint8_t mosi_pin[CNT_MOSI_PINS];
@@ -1228,9 +1231,6 @@ public:
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

@@ -1335,6 +1335,8 @@ private:
// DMA Support
#ifdef SPI_HAS_TRANSFER_ASYNC
bool initDMAChannels();
enum DMAState { notAllocated, idle, active, completed};
enum {MAX_DMA_COUNT=32767};
DMAState _dma_state = DMAState::notAllocated;
uint32_t _dma_count_remaining = 0; // How many bytes left to output after current DMA completes
DMAChannel *_dmaTX = nullptr;

読み込み中…
キャンセル
保存