Browse Source

DMAChannel stuff (work in progess - scatter/gather)

teensy4-core
PaulStoffregen 10 years ago
parent
commit
2e2ce610c3
3 changed files with 443 additions and 282 deletions
  1. +68
    -11
      teensy3/DMAChannel.cpp
  2. +374
    -270
      teensy3/DMAChannel.h
  3. +1
    -1
      teensy3/mk20dx128.h

+ 68
- 11
teensy3/DMAChannel.cpp View File

@@ -4,10 +4,9 @@
// so C-only code can reserve DMA channels
uint16_t dma_channel_allocated_mask = 0;

DMAChannel::DMAChannel(uint8_t channelRequest) : TCD(*(TCD_t *)0), channel(16)
void DMAChannel::init(void)
{
uint8_t next, ch=channelRequest;

uint32_t ch = 0;
__disable_irq();
while (1) {
if (!(dma_channel_allocated_mask & (1 << ch))) {
@@ -15,9 +14,10 @@ DMAChannel::DMAChannel(uint8_t channelRequest) : TCD(*(TCD_t *)0), channel(16)
__enable_irq();
break;
}
next = (ch + 1) & 15;
if (next == channelRequest) {
if (++ch >= DMA_NUM_CHANNELS) {
__enable_irq();
TCD = (TCD_t *)0;
channel = 16;
return; // no more channels available
// attempts to use this object will hardfault
}
@@ -30,12 +30,69 @@ DMAChannel::DMAChannel(uint8_t channelRequest) : TCD(*(TCD_t *)0), channel(16)
DMA_CERR = ch;
DMA_CEEI = ch;
DMA_CINT = ch;
TCD = (TCD_t *)(0x40009000 + ch * 32);
uint32_t *p = (uint32_t *)TCD;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 0;
}

void DMAChannel::release(void)
{
if (channel >= DMA_NUM_CHANNELS) return;
DMA_CERQ = channel;
__disable_irq();
dma_channel_allocated_mask &= ~(1 << channel);
__enable_irq();
channel = 16;
TCD = (TCD_t *)0;
}

static uint32_t priority(const DMAChannel &c)
{
uint32_t n;
n = *(uint32_t *)((uint32_t)&DMA_DCHPRI3 + (c.channel & 0xFC));
n = __builtin_bswap32(n);
return (n >> ((c.channel & 0x03) << 3)) & 0x0F;
}

static void swap(DMAChannel &c1, DMAChannel &c2)
{
uint8_t c;
DMABaseClass::TCD_t *t;

c = c1.channel;
c1.channel = c2.channel;
c2.channel = c;
t = c1.TCD;
c1.TCD = c2.TCD;
c2.TCD = t;
}

void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2)
{
if (priority(ch1) < priority(ch2)) swap(ch1, ch2);
}

TCD = *(TCD_t *)(0x40009000 + ch * 32);
TCD.CSR = 0;
TCD.ATTR = 0;
TCD.NBYTES = 0;
TCD.BITER = 0;
TCD.CITER = 0;
void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3)
{
if (priority(ch2) < priority(ch3)) swap(ch2, ch3);
if (priority(ch1) < priority(ch2)) swap(ch1, ch2);
if (priority(ch2) < priority(ch3)) swap(ch2, ch3);
}

void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3, DMAChannel &ch4)
{
if (priority(ch3) < priority(ch4)) swap(ch3, ch4);
if (priority(ch2) < priority(ch3)) swap(ch2, ch3);
if (priority(ch1) < priority(ch2)) swap(ch1, ch2);
if (priority(ch3) < priority(ch4)) swap(ch2, ch3);
if (priority(ch2) < priority(ch3)) swap(ch1, ch2);
if (priority(ch3) < priority(ch4)) swap(ch2, ch3);
}


+ 374
- 270
teensy3/DMAChannel.h View File

@@ -20,7 +20,9 @@

#ifdef __cplusplus

class DMAChannel {

class DMABaseClass {
public:
typedef struct __attribute__((packed)) {
volatile const void * volatile SADDR;
int16_t SOFF;
@@ -37,123 +39,8 @@ class DMAChannel {
volatile uint16_t CSR;
union { volatile uint16_t BITER;
volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; };

} TCD_t;
public:
/*************************************************/
/** Channel Allocation **/
/*************************************************/

// Constructor - allocates which DMA channel each object actually uses
DMAChannel(uint8_t channelRequest=0);
// TODO: should the copy constructor be private?


/***************************************/
/** Triggering **/
/***************************************/

// Triggers cause the DMA channel to actually move data. Each
// trigger moves a single data unit, which is typically 8, 16 or 32 bits.

// Use a hardware trigger to make the DMA channel run
void attachTrigger(uint8_t source) {
volatile uint8_t *mux;
mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel;
*mux = 0;
*mux = (source & 63) | DMAMUX_ENABLE;
}

// Use another DMA channel as the trigger, causing this
// channel to trigger after each transfer is makes, except
// the its last transfer. This effectively makes the 2
// channels run in parallel.
void attachTriggerBeforeCompletion(DMAChannel &ch) {
ch.TCD.BITER = (ch.TCD.BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK)
| DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK;
ch.TCD.CITER = ch.TCD.BITER ;
}

// Use another DMA channel as the trigger, causing this
// channel to trigger when the other channel completes.
void attachTriggerAtCompletion(DMAChannel &ch) {
ch.TCD.CSR = (ch.TCD.CSR & ~DMA_TCD_CSR_MAJORLINKCH_MASK)
| DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK;
}

// Cause this DMA channel to be continuously triggered, so
// it will move data as rapidly as possible, without waiting.
// Normally this would be used with disableOnCompletion().
void attachTriggerContinuous(void) {
volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0;
mux[channel] = 0;
#if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS
mux[channel] = DMAMUX_SOURCE_ALWAYS0 + channel;
#else
// search for an unused "always on" source
unsigned int i = DMAMUX_SOURCE_ALWAYS0;
for (i = DMAMUX_SOURCE_ALWAYS0;
i < DMAMUX_SOURCE_ALWAYS0 + DMAMUX_NUM_SOURCE_ALWAYS; i++) {
unsigned int ch;
for (ch=0; ch < DMA_NUM_CHANNELS; ch++) {
if (mux[ch] == i) break;
}
if (ch >= DMA_NUM_CHANNELS) {
mux[channel] = i;
return;
}
}
#endif
}

// Manually trigger the DMA channel.
void trigger(void) {
DMA_SSRT = channel;
}


/***************************************/
/** Interrupts **/
/***************************************/

// An interrupt routine can be run when the DMA channel completes
// the entire transfer, and also optionally when half of the
// transfer is completed.
void attachInterrupt(void (*isr)(void)) {
_VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
TCD.CSR |= DMA_TCD_CSR_INTMAJOR;
}

void detachInterrupt(void) {
NVIC_DISABLE_IRQ(IRQ_DMA_CH0 + channel);
}

void interruptAtHalf(void) {
TCD.CSR |= DMA_TCD_CSR_INTHALF;
}

void clearInterrupt(void) {
DMA_CINT = channel;
}


/***************************************/
/** Enable / Disable **/
/***************************************/

void enable(void) {
DMA_SERQ = channel;
}

void disable(void) {
DMA_CERQ = channel;
}

void disableOnCompletion(void) {
TCD.CSR |= DMA_TCD_CSR_DREQ;
}

TCD_t *TCD;

/***************************************/
/** Data Transfer **/
@@ -163,29 +50,29 @@ public:
// for receiving data from one of the hardware peripherals is used.
void source(const signed char &p) { source(*(const uint8_t *)&p); }
void source(const unsigned char &p) {
TCD.SADDR = &p;
TCD.SOFF = 0;
TCD.ATTR_SRC = 0;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 1;
TCD.SLAST = 0;
TCD->SADDR = &p;
TCD->SOFF = 0;
TCD->ATTR_SRC = 0;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
TCD->SLAST = 0;
}
void source(const signed short &p) { source(*(const uint16_t *)&p); }
void source(const unsigned short &p) {
TCD.SADDR = &p;
TCD.SOFF = 0;
TCD.ATTR_SRC = 1;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 2;
TCD.SLAST = 0;
TCD->SADDR = &p;
TCD->SOFF = 0;
TCD->ATTR_SRC = 1;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
TCD->SLAST = 0;
}
void source(const signed int &p) { source(*(const uint32_t *)&p); }
void source(const unsigned int &p) { source(*(const uint32_t *)&p); }
void source(const signed long &p) { source(*(const uint32_t *)&p); }
void source(const unsigned long &p) {
TCD.SADDR = &p;
TCD.SOFF = 0;
TCD.ATTR_SRC = 2;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 4;
TCD.SLAST = 0;
TCD->SADDR = &p;
TCD->SOFF = 0;
TCD->ATTR_SRC = 2;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
TCD->SLAST = 0;
}

// Use a buffer (array of data) as the data source. Typically a
@@ -193,24 +80,24 @@ public:
void sourceBuffer(const signed char p[], unsigned int len) {
sourceBuffer((uint8_t *)p, len); }
void sourceBuffer(const unsigned char p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 1;
TCD.ATTR_SRC = 0;
TCD.NBYTES = 1;
TCD.SLAST = -len;
TCD.BITER = len;
TCD.CITER = len;
TCD->SADDR = p;
TCD->SOFF = 1;
TCD->ATTR_SRC = 0;
TCD->NBYTES = 1;
TCD->SLAST = -len;
TCD->BITER = len;
TCD->CITER = len;
}
void sourceBuffer(const signed short p[], unsigned int len) {
sourceBuffer((uint16_t *)p, len); }
void sourceBuffer(const unsigned short p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 2;
TCD.ATTR_SRC = 1;
TCD.NBYTES = 2;
TCD.SLAST = -len;
TCD.BITER = len / 2;
TCD.CITER = len / 2;
TCD->SADDR = p;
TCD->SOFF = 2;
TCD->ATTR_SRC = 1;
TCD->NBYTES = 2;
TCD->SLAST = -len;
TCD->BITER = len / 2;
TCD->CITER = len / 2;
}
void sourceBuffer(const signed int p[], unsigned int len) {
sourceBuffer((uint32_t *)p, len); }
@@ -219,37 +106,37 @@ public:
void sourceBuffer(const signed long p[], unsigned int len) {
sourceBuffer((uint32_t *)p, len); }
void sourceBuffer(const unsigned long p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 4;
TCD.ATTR_SRC = 2;
TCD.NBYTES = 4;
TCD.SLAST = -len;
TCD.BITER = len / 4;
TCD.CITER = len / 4;
TCD->SADDR = p;
TCD->SOFF = 4;
TCD->ATTR_SRC = 2;
TCD->NBYTES = 4;
TCD->SLAST = -len;
TCD->BITER = len / 4;
TCD->CITER = len / 4;
}

// Use a circular buffer as the data source
void sourceCircular(const signed char p[], unsigned int len) {
sourceCircular((uint8_t *)p, len); }
void sourceCircular(const unsigned char p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 1;
TCD.ATTR_SRC = ((31 - __builtin_clz(len)) << 3);
TCD.NBYTES = 1;
TCD.SLAST = 0;
TCD.BITER = len;
TCD.CITER = len;
TCD->SADDR = p;
TCD->SOFF = 1;
TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3);
TCD->NBYTES = 1;
TCD->SLAST = 0;
TCD->BITER = len;
TCD->CITER = len;
}
void sourceCircular(const signed short p[], unsigned int len) {
sourceCircular((uint16_t *)p, len); }
void sourceCircular(const unsigned short p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 2;
TCD.ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 1;
TCD.NBYTES = 2;
TCD.SLAST = 0;
TCD.BITER = len / 2;
TCD.CITER = len / 2;
TCD->SADDR = p;
TCD->SOFF = 2;
TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 1;
TCD->NBYTES = 2;
TCD->SLAST = 0;
TCD->BITER = len / 2;
TCD->CITER = len / 2;
}
void sourceCircular(const signed int p[], unsigned int len) {
sourceCircular((uint32_t *)p, len); }
@@ -258,42 +145,42 @@ public:
void sourceCircular(const signed long p[], unsigned int len) {
sourceCircular((uint32_t *)p, len); }
void sourceCircular(const unsigned long p[], unsigned int len) {
TCD.SADDR = p;
TCD.SOFF = 4;
TCD.ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 2;
TCD.NBYTES = 4;
TCD.SLAST = 0;
TCD.BITER = len / 4;
TCD.CITER = len / 4;
TCD->SADDR = p;
TCD->SOFF = 4;
TCD->ATTR_SRC = ((31 - __builtin_clz(len)) << 3) | 2;
TCD->NBYTES = 4;
TCD->SLAST = 0;
TCD->BITER = len / 4;
TCD->CITER = len / 4;
}

// Use a single variable as the data destination. Typically a register
// for transmitting data to one of the hardware peripherals is used.
void destination(signed char &p) { destination(*(uint8_t *)&p); }
void destination(unsigned char &p) {
TCD.DADDR = &p;
TCD.DOFF = 0;
TCD.ATTR_DST = 0;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 1;
TCD.DLASTSGA = 0;
TCD->DADDR = &p;
TCD->DOFF = 0;
TCD->ATTR_DST = 0;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 1;
TCD->DLASTSGA = 0;
}
void destination(signed short &p) { destination(*(uint16_t *)&p); }
void destination(unsigned short &p) {
TCD.DADDR = &p;
TCD.DOFF = 0;
TCD.ATTR_DST = 1;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 2;
TCD.DLASTSGA = 0;
TCD->DADDR = &p;
TCD->DOFF = 0;
TCD->ATTR_DST = 1;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 2;
TCD->DLASTSGA = 0;
}
void destination(signed int &p) { destination(*(uint32_t *)&p); }
void destination(unsigned int &p) { destination(*(uint32_t *)&p); }
void destination(signed long &p) { destination(*(uint32_t *)&p); }
void destination(unsigned long &p) {
TCD.DADDR = &p;
TCD.DOFF = 0;
TCD.ATTR_DST = 2;
if ((uint32_t)p < 0x40000000 || TCD.NBYTES == 0) TCD.NBYTES = 4;
TCD.DLASTSGA = 0;
TCD->DADDR = &p;
TCD->DOFF = 0;
TCD->ATTR_DST = 2;
if ((uint32_t)p < 0x40000000 || TCD->NBYTES == 0) TCD->NBYTES = 4;
TCD->DLASTSGA = 0;
}

// Use a buffer (array of data) as the data destination. Typically a
@@ -301,24 +188,24 @@ public:
void destinationBuffer(signed char p[], unsigned int len) {
destinationBuffer((uint8_t *)p, len); }
void destinationBuffer(unsigned char p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 1;
TCD.ATTR_DST = 0;
TCD.NBYTES = 1;
TCD.DLASTSGA = -len;
TCD.BITER = len;
TCD.CITER = len;
TCD->DADDR = p;
TCD->DOFF = 1;
TCD->ATTR_DST = 0;
TCD->NBYTES = 1;
TCD->DLASTSGA = -len;
TCD->BITER = len;
TCD->CITER = len;
}
void destinationBuffer(signed short p[], unsigned int len) {
destinationBuffer((uint16_t *)p, len); }
void destinationBuffer(unsigned short p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 2;
TCD.ATTR_DST = 1;
TCD.NBYTES = 2;
TCD.DLASTSGA = -len;
TCD.BITER = len / 2;
TCD.CITER = len / 2;
TCD->DADDR = p;
TCD->DOFF = 2;
TCD->ATTR_DST = 1;
TCD->NBYTES = 2;
TCD->DLASTSGA = -len;
TCD->BITER = len / 2;
TCD->CITER = len / 2;
}
void destinationBuffer(signed int p[], unsigned int len) {
destinationBuffer((uint32_t *)p, len); }
@@ -327,37 +214,37 @@ public:
void destinationBuffer(signed long p[], unsigned int len) {
destinationBuffer((uint32_t *)p, len); }
void destinationBuffer(unsigned long p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 4;
TCD.ATTR_DST = 1;
TCD.NBYTES = 4;
TCD.DLASTSGA = -len;
TCD.BITER = len / 4;
TCD.CITER = len / 4;
TCD->DADDR = p;
TCD->DOFF = 4;
TCD->ATTR_DST = 1;
TCD->NBYTES = 4;
TCD->DLASTSGA = -len;
TCD->BITER = len / 4;
TCD->CITER = len / 4;
}

// Use a circular buffer as the data destination
void destinationCircular(signed char p[], unsigned int len) {
destinationCircular((uint8_t *)p, len); }
void destinationCircular(unsigned char p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 1;
TCD.ATTR_DST = ((31 - __builtin_clz(len)) << 3);
TCD.NBYTES = 1;
TCD.DLASTSGA = 0;
TCD.BITER = len;
TCD.CITER = len;
TCD->DADDR = p;
TCD->DOFF = 1;
TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3);
TCD->NBYTES = 1;
TCD->DLASTSGA = 0;
TCD->BITER = len;
TCD->CITER = len;
}
void destinationCircular(signed short p[], unsigned int len) {
destinationCircular((uint16_t *)p, len); }
void destinationCircular(unsigned short p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 2;
TCD.ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 1;
TCD.NBYTES = 2;
TCD.DLASTSGA = 0;
TCD.BITER = len / 2;
TCD.CITER = len / 2;
TCD->DADDR = p;
TCD->DOFF = 2;
TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 1;
TCD->NBYTES = 2;
TCD->DLASTSGA = 0;
TCD->BITER = len / 2;
TCD->CITER = len / 2;
}
void destinationCircular(signed int p[], unsigned int len) {
destinationCircular((uint32_t *)p, len); }
@@ -366,32 +253,36 @@ public:
void destinationCircular(signed long p[], unsigned int len) {
destinationCircular((uint32_t *)p, len); }
void destinationCircular(unsigned long p[], unsigned int len) {
TCD.DADDR = p;
TCD.DOFF = 4;
TCD.ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 2;
TCD.NBYTES = 4;
TCD.DLASTSGA = 0;
TCD.BITER = len / 4;
TCD.CITER = len / 4;
TCD->DADDR = p;
TCD->DOFF = 4;
TCD->ATTR_DST = ((31 - __builtin_clz(len)) << 3) | 2;
TCD->NBYTES = 4;
TCD->DLASTSGA = 0;
TCD->BITER = len / 4;
TCD->CITER = len / 4;
}

/*************************************************/
/** Quantity of Data to Transfer **/
/*************************************************/

// Set the data size used for each triggered transfer
void size(unsigned int len) {
if (len == 4) {
TCD.NBYTES = 4;
if (TCD.SOFF != 0) TCD.SOFF = 4;
if (TCD.DOFF != 0) TCD.DOFF = 4;
TCD.ATTR = (TCD.ATTR & 0xF8F8) | 0x0202;
TCD->NBYTES = 4;
if (TCD->SOFF != 0) TCD->SOFF = 4;
if (TCD->DOFF != 0) TCD->DOFF = 4;
TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0202;
} else if (len == 2) {
TCD.NBYTES = 2;
if (TCD.SOFF != 0) TCD.SOFF = 2;
if (TCD.DOFF != 0) TCD.DOFF = 2;
TCD.ATTR = (TCD.ATTR & 0xF8F8) | 0x0101;
TCD->NBYTES = 2;
if (TCD->SOFF != 0) TCD->SOFF = 2;
if (TCD->DOFF != 0) TCD->DOFF = 2;
TCD->ATTR = (TCD->ATTR & 0xF8F8) | 0x0101;
} else {
TCD.NBYTES = 1;
if (TCD.SOFF != 0) TCD.SOFF = 1;
if (TCD.DOFF != 0) TCD.DOFF = 1;
TCD.ATTR = TCD.ATTR & 0xF8F8;
TCD->NBYTES = 1;
if (TCD->SOFF != 0) TCD->SOFF = 1;
if (TCD->DOFF != 0) TCD->DOFF = 1;
TCD->ATTR = TCD->ATTR & 0xF8F8;
}
}

@@ -399,15 +290,214 @@ public:
void count(unsigned int len) {
if (len > 32767) return;
if (len >= 512) {
TCD.BITER = len;
TCD.CITER = len;
TCD->BITER = len;
TCD->CITER = len;
} else {
TCD.BITER = (TCD.BITER & 0xFE00) | len;
TCD.CITER = (TCD.CITER & 0xFE00) | len;
TCD->BITER = (TCD->BITER & 0xFE00) | len;
TCD->CITER = (TCD->CITER & 0xFE00) | len;
}
}

/*************************************************/
/** Special Options / Features **/
/*************************************************/

void interruptAtCompletion(void) {
TCD->CSR |= DMA_TCD_CSR_INTMAJOR;
}

void interruptAtHalf(void) {
TCD->CSR |= DMA_TCD_CSR_INTHALF;
}

void disableOnCompletion(void) {
TCD->CSR |= DMA_TCD_CSR_DREQ;
}

void replaceSettingsOnCompletion(const DMABaseClass &settings) {
TCD->DLASTSGA = (int32_t)(settings.TCD);
TCD->CSR |= DMA_TCD_CSR_ESG;
}

protected:
// users should not be able to create instances of DMABaseClass, which
// require the inheriting class to initialize the TCD pointer.
DMABaseClass() {}

static inline void copy_tcd(TCD_t *dst, const TCD_t *src) {
const uint32_t *p = (const uint32_t *)src;
uint32_t *q = (uint32_t *)dst;
uint32_t t1, t2, t3, t4;
t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
*q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++;
*q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4;
}
};


// DMASetting represents settings stored only in memory, which can be
// applied to any DMA channel.

class DMASetting : public DMABaseClass {
public:
DMASetting() {
TCD = &tcddata;
}
DMASetting(const DMASetting &c) {
TCD = &tcddata;
*this = c;
}
DMASetting(const DMABaseClass &c) {
TCD = &tcddata;
*this = c;
}
DMASetting & operator = (const DMABaseClass &rhs) {
copy_tcd(TCD, rhs.TCD);
return *this;
}
private:
TCD_t tcddata __attribute__((aligned(32)));
};


// DMAChannel reprents an actual DMA channel and its current settings

class DMAChannel : public DMABaseClass {
public:
/*************************************************/
/** Channel Allocation **/
/*************************************************/

DMAChannel() {
init();
}
DMAChannel(const DMAChannel &c) {
TCD = c.TCD;
channel = c.channel;
}
DMAChannel(const DMASetting &c) {
init();
copy_tcd(TCD, c.TCD);
}
DMAChannel & operator = (const DMAChannel &rhs) {
if (channel != rhs.channel) {
release();
TCD = rhs.TCD;
channel = rhs.channel;
}
return *this;
}
DMAChannel & operator = (const DMASetting &rhs) {
copy_tcd(TCD, rhs.TCD);
return *this;
}
~DMAChannel() {
release();
}
private:
void init(void);
void release(void);

public:
/***************************************/
/** Triggering **/
/***************************************/

// Triggers cause the DMA channel to actually move data. Each
// trigger moves a single data unit, which is typically 8, 16 or 32 bits.

// Use a hardware trigger to make the DMA channel run
void attachTrigger(uint8_t source) {
volatile uint8_t *mux;
mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel;
*mux = 0;
*mux = (source & 63) | DMAMUX_ENABLE;
}

// Use another DMA channel as the trigger, causing this
// channel to trigger after each transfer is makes, except
// the its last transfer. This effectively makes the 2
// channels run in parallel.
void attachTriggerBeforeCompletion(DMABaseClass &ch) {
ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK)
| DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK;
ch.TCD->CITER = ch.TCD->BITER ;
}

// Use another DMA channel as the trigger, causing this
// channel to trigger when the other channel completes.
void attachTriggerAtCompletion(DMABaseClass &ch) {
ch.TCD->CSR = (ch.TCD->CSR & ~DMA_TCD_CSR_MAJORLINKCH_MASK)
| DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK;
}

// Cause this DMA channel to be continuously triggered, so
// it will move data as rapidly as possible, without waiting.
// Normally this would be used with disableOnCompletion().
void attachTriggerContinuous(void) {
volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0;
mux[channel] = 0;
#if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS
mux[channel] = DMAMUX_SOURCE_ALWAYS0 + channel;
#else
// search for an unused "always on" source
unsigned int i = DMAMUX_SOURCE_ALWAYS0;
for (i = DMAMUX_SOURCE_ALWAYS0;
i < DMAMUX_SOURCE_ALWAYS0 + DMAMUX_NUM_SOURCE_ALWAYS; i++) {
unsigned int ch;
for (ch=0; ch < DMA_NUM_CHANNELS; ch++) {
if (mux[ch] == i) break;
}
if (ch >= DMA_NUM_CHANNELS) {
mux[channel] = i;
return;
}
}
#endif
}

// Manually trigger the DMA channel.
void trigger(void) {
DMA_SSRT = channel;
}


/***************************************/
/** Interrupts **/
/***************************************/

// An interrupt routine can be run when the DMA channel completes
// the entire transfer, and also optionally when half of the
// transfer is completed.
void attachInterrupt(void (*isr)(void)) {
_VectorsRam[channel + IRQ_DMA_CH0 + 16] = isr;
NVIC_ENABLE_IRQ(IRQ_DMA_CH0 + channel);
}

void detachInterrupt(void) {
NVIC_DISABLE_IRQ(IRQ_DMA_CH0 + channel);
}

void clearInterrupt(void) {
DMA_CINT = channel;
}


/***************************************/
/** Enable / Disable **/
/***************************************/

void enable(void) {
DMA_SERQ = channel;
}

void disable(void) {
DMA_CERQ = channel;
}



/***************************************/
/** Status **/
/***************************************/
@@ -423,7 +513,6 @@ public:
// functions, the Transfer Control Descriptor (TCD) and channel number
// can be used directly. This leads to less portable and less readable
// code, but direct control of all parameters is possible.
TCD_t &TCD;
uint8_t channel;

/* usage cases:
@@ -441,36 +530,40 @@ OctoWS2811:
DMA_CERQ = 3;

// DMA channel #1 sets WS2811 high at the beginning of each cycle
DMA_TCD1_SADDR = &ones;
DMA_TCD1_SOFF = 0;
DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
DMA_TCD1_NBYTES_MLNO = 1;
DMA_TCD1_SLAST = 0;
DMA_TCD1_DADDR = &GPIOD_PSOR;
DMA_TCD1_DOFF = 0;
DMA_TCD1_CITER_ELINKNO = bufsize;
DMA_TCD1_DLASTSGA = 0;
DMA_TCD1_CSR = DMA_TCD_CSR_DREQ;
DMA_TCD1_BITER_ELINKNO = bufsize;
DMA_TCD1_SADDR = &ones;
DMA_TCD1_SOFF = 0;
DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
DMA_TCD1_NBYTES_MLNO = 1;
DMA_TCD1_SLAST = 0;
DMA_TCD1_DADDR = &GPIOD_PSOR;
DMA_TCD1_DOFF = 0;
DMA_TCD1_CITER_ELINKNO = bufsize;
DMA_TCD1_DLASTSGA = 0;
DMA_TCD1_CSR = DMA_TCD_CSR_DREQ;
DMA_TCD1_BITER_ELINKNO = bufsize;
dma1.source(ones);
dma1.destination(GPIOD_PSOR);
dma1.size(1);
dma1.count(bufsize);
dma1.disableOnCompletion();

// DMA channel #2 writes the pixel data at 20% of the cycle
DMA_TCD2_SADDR = frameBuffer;
DMA_TCD2_SOFF = 1;
DMA_TCD2_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
DMA_TCD2_NBYTES_MLNO = 1;
DMA_TCD2_SLAST = -bufsize;
DMA_TCD2_DADDR = &GPIOD_PDOR;
DMA_TCD2_DOFF = 0;
DMA_TCD2_CITER_ELINKNO = bufsize;
DMA_TCD2_DLASTSGA = 0;
DMA_TCD2_CSR = DMA_TCD_CSR_DREQ;
DMA_TCD2_BITER_ELINKNO = bufsize;
DMA_TCD2_SADDR = frameBuffer;
DMA_TCD2_SOFF = 1;
DMA_TCD2_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);
DMA_TCD2_NBYTES_MLNO = 1;
DMA_TCD2_SLAST = -bufsize;
DMA_TCD2_DADDR = &GPIOD_PDOR;
DMA_TCD2_DOFF = 0;
DMA_TCD2_CITER_ELINKNO = bufsize;
DMA_TCD2_DLASTSGA = 0;
DMA_TCD2_CSR = DMA_TCD_CSR_DREQ;
DMA_TCD2_BITER_ELINKNO = bufsize;
dma2.source(frameBuffer, sizeof(frameBuffer));
dma2.destination(GPIOD_PDOR);
dma2.size(1);
dma2.count(bufsize);
dma2.disableOnCompletion();

// DMA channel #3 clear all the pins low at 48% of the cycle
DMA_TCD3_SADDR = &ones;
@@ -484,6 +577,11 @@ OctoWS2811:
DMA_TCD3_DLASTSGA = 0;
DMA_TCD3_CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR;
DMA_TCD3_BITER_ELINKNO = bufsize;
dma3.source(ones);
dma3.destination(GPIOD_PCOR);
dma3.size(1);
dma3.count(bufsize);
dma3.disableOnCompletion();

************************
Audio, DAC
@@ -675,6 +773,12 @@ SmartMatrix

};

// arrange the relative priority of 2 or more DMA channels
void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2);
void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3);
void DMAPriorityOrder(DMAChannel &ch1, DMAChannel &ch2, DMAChannel &ch3, DMAChannel &ch4);



extern "C" {
#endif

+ 1
- 1
teensy3/mk20dx128.h View File

@@ -678,7 +678,7 @@ extern "C" {
#define DMA_TCD_ATTR_SIZE_16BIT 1
#define DMA_TCD_ATTR_SIZE_32BIT 2
#define DMA_TCD_ATTR_SIZE_16BYTE 4
#define DMA_TCD_ATTR_SIZE_32BYTE 5
#define DMA_TCD_ATTR_SIZE_32BYTE 5 // caution: this might not be supported in newer chips?
#define DMA_TCD_CSR_BWC(n) (((n) & 0x3) << 14)
#define DMA_TCD_CSR_BWC_MASK 0xC000
#define DMA_TCD_CSR_MAJORLINKCH(n) (((n) & 0xF) << 8)

Loading…
Cancel
Save