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

// so C-only code can reserve DMA channels // so C-only code can reserve DMA channels
uint16_t dma_channel_allocated_mask = 0; 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(); __disable_irq();
while (1) { while (1) {
if (!(dma_channel_allocated_mask & (1 << ch))) { if (!(dma_channel_allocated_mask & (1 << ch))) {
__enable_irq(); __enable_irq();
break; break;
} }
next = (ch + 1) & 15;
if (next == channelRequest) {
if (++ch >= DMA_NUM_CHANNELS) {
__enable_irq(); __enable_irq();
TCD = (TCD_t *)0;
channel = 16;
return; // no more channels available return; // no more channels available
// attempts to use this object will hardfault // attempts to use this object will hardfault
} }
DMA_CERR = ch; DMA_CERR = ch;
DMA_CEEI = ch; DMA_CEEI = ch;
DMA_CINT = 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



#ifdef __cplusplus #ifdef __cplusplus


class DMAChannel {

class DMABaseClass {
public:
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
volatile const void * volatile SADDR; volatile const void * volatile SADDR;
int16_t SOFF; int16_t SOFF;
volatile uint16_t CSR; volatile uint16_t CSR;
union { volatile uint16_t BITER; union { volatile uint16_t BITER;
volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; }; volatile uint16_t BITER_ELINKYES; volatile uint16_t BITER_ELINKNO; };

} TCD_t; } 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 **/ /** Data Transfer **/
// for receiving data from one of the hardware peripherals is used. // 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 signed char &p) { source(*(const uint8_t *)&p); }
void source(const unsigned char &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 signed short &p) { source(*(const uint16_t *)&p); }
void source(const unsigned short &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 signed int &p) { source(*(const uint32_t *)&p); }
void source(const unsigned 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 signed long &p) { source(*(const uint32_t *)&p); }
void source(const unsigned long &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 // Use a buffer (array of data) as the data source. Typically a
void sourceBuffer(const signed char p[], unsigned int len) { void sourceBuffer(const signed char p[], unsigned int len) {
sourceBuffer((uint8_t *)p, len); } sourceBuffer((uint8_t *)p, len); }
void sourceBuffer(const unsigned char p[], unsigned int 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) { void sourceBuffer(const signed short p[], unsigned int len) {
sourceBuffer((uint16_t *)p, len); } sourceBuffer((uint16_t *)p, len); }
void sourceBuffer(const unsigned short p[], unsigned int 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) { void sourceBuffer(const signed int p[], unsigned int len) {
sourceBuffer((uint32_t *)p, len); } sourceBuffer((uint32_t *)p, len); }
void sourceBuffer(const signed long p[], unsigned int len) { void sourceBuffer(const signed long p[], unsigned int len) {
sourceBuffer((uint32_t *)p, len); } sourceBuffer((uint32_t *)p, len); }
void sourceBuffer(const unsigned long p[], unsigned int 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 // Use a circular buffer as the data source
void sourceCircular(const signed char p[], unsigned int len) { void sourceCircular(const signed char p[], unsigned int len) {
sourceCircular((uint8_t *)p, len); } sourceCircular((uint8_t *)p, len); }
void sourceCircular(const unsigned char p[], unsigned int 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) { void sourceCircular(const signed short p[], unsigned int len) {
sourceCircular((uint16_t *)p, len); } sourceCircular((uint16_t *)p, len); }
void sourceCircular(const unsigned short p[], unsigned int 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) { void sourceCircular(const signed int p[], unsigned int len) {
sourceCircular((uint32_t *)p, len); } sourceCircular((uint32_t *)p, len); }
void sourceCircular(const signed long p[], unsigned int len) { void sourceCircular(const signed long p[], unsigned int len) {
sourceCircular((uint32_t *)p, len); } sourceCircular((uint32_t *)p, len); }
void sourceCircular(const unsigned long p[], unsigned int 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 // Use a single variable as the data destination. Typically a register
// for transmitting data to one of the hardware peripherals is used. // for transmitting data to one of the hardware peripherals is used.
void destination(signed char &p) { destination(*(uint8_t *)&p); } void destination(signed char &p) { destination(*(uint8_t *)&p); }
void destination(unsigned char &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(signed short &p) { destination(*(uint16_t *)&p); }
void destination(unsigned short &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(signed int &p) { destination(*(uint32_t *)&p); }
void destination(unsigned 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(signed long &p) { destination(*(uint32_t *)&p); }
void destination(unsigned long &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 // Use a buffer (array of data) as the data destination. Typically a
void destinationBuffer(signed char p[], unsigned int len) { void destinationBuffer(signed char p[], unsigned int len) {
destinationBuffer((uint8_t *)p, len); } destinationBuffer((uint8_t *)p, len); }
void destinationBuffer(unsigned char p[], unsigned int 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) { void destinationBuffer(signed short p[], unsigned int len) {
destinationBuffer((uint16_t *)p, len); } destinationBuffer((uint16_t *)p, len); }
void destinationBuffer(unsigned short p[], unsigned int 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) { void destinationBuffer(signed int p[], unsigned int len) {
destinationBuffer((uint32_t *)p, len); } destinationBuffer((uint32_t *)p, len); }
void destinationBuffer(signed long p[], unsigned int len) { void destinationBuffer(signed long p[], unsigned int len) {
destinationBuffer((uint32_t *)p, len); } destinationBuffer((uint32_t *)p, len); }
void destinationBuffer(unsigned long p[], unsigned int 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 // Use a circular buffer as the data destination
void destinationCircular(signed char p[], unsigned int len) { void destinationCircular(signed char p[], unsigned int len) {
destinationCircular((uint8_t *)p, len); } destinationCircular((uint8_t *)p, len); }
void destinationCircular(unsigned char p[], unsigned int 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) { void destinationCircular(signed short p[], unsigned int len) {
destinationCircular((uint16_t *)p, len); } destinationCircular((uint16_t *)p, len); }
void destinationCircular(unsigned short p[], unsigned int 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) { void destinationCircular(signed int p[], unsigned int len) {
destinationCircular((uint32_t *)p, len); } destinationCircular((uint32_t *)p, len); }
void destinationCircular(signed long p[], unsigned int len) { void destinationCircular(signed long p[], unsigned int len) {
destinationCircular((uint32_t *)p, len); } destinationCircular((uint32_t *)p, len); }
void destinationCircular(unsigned long p[], unsigned int 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 // Set the data size used for each triggered transfer
void size(unsigned int len) { void size(unsigned int len) {
if (len == 4) { 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) { } 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 { } 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;
} }
} }


void count(unsigned int len) { void count(unsigned int len) {
if (len > 32767) return; if (len > 32767) return;
if (len >= 512) { if (len >= 512) {
TCD.BITER = len;
TCD.CITER = len;
TCD->BITER = len;
TCD->CITER = len;
} else { } 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 **/ /** Status **/
/***************************************/ /***************************************/
// functions, the Transfer Control Descriptor (TCD) and channel number // functions, the Transfer Control Descriptor (TCD) and channel number
// can be used directly. This leads to less portable and less readable // can be used directly. This leads to less portable and less readable
// code, but direct control of all parameters is possible. // code, but direct control of all parameters is possible.
TCD_t &TCD;
uint8_t channel; uint8_t channel;


/* usage cases: /* usage cases:
DMA_CERQ = 3; DMA_CERQ = 3;


// DMA channel #1 sets WS2811 high at the beginning of each cycle // 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.source(ones);
dma1.destination(GPIOD_PSOR); dma1.destination(GPIOD_PSOR);
dma1.size(1); dma1.size(1);
dma1.count(bufsize); dma1.count(bufsize);
dma1.disableOnCompletion();


// DMA channel #2 writes the pixel data at 20% of the cycle // 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.source(frameBuffer, sizeof(frameBuffer));
dma2.destination(GPIOD_PDOR); 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 channel #3 clear all the pins low at 48% of the cycle
DMA_TCD3_SADDR = &ones; DMA_TCD3_SADDR = &ones;
DMA_TCD3_DLASTSGA = 0; DMA_TCD3_DLASTSGA = 0;
DMA_TCD3_CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR; DMA_TCD3_CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR;
DMA_TCD3_BITER_ELINKNO = bufsize; DMA_TCD3_BITER_ELINKNO = bufsize;
dma3.source(ones);
dma3.destination(GPIOD_PCOR);
dma3.size(1);
dma3.count(bufsize);
dma3.disableOnCompletion();


************************ ************************
Audio, DAC Audio, DAC


}; };


// 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" { extern "C" {
#endif #endif

+ 1
- 1
teensy3/mk20dx128.h View File

#define DMA_TCD_ATTR_SIZE_16BIT 1 #define DMA_TCD_ATTR_SIZE_16BIT 1
#define DMA_TCD_ATTR_SIZE_32BIT 2 #define DMA_TCD_ATTR_SIZE_32BIT 2
#define DMA_TCD_ATTR_SIZE_16BYTE 4 #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(n) (((n) & 0x3) << 14)
#define DMA_TCD_CSR_BWC_MASK 0xC000 #define DMA_TCD_CSR_BWC_MASK 0xC000
#define DMA_TCD_CSR_MAJORLINKCH(n) (((n) & 0xF) << 8) #define DMA_TCD_CSR_MAJORLINKCH(n) (((n) & 0xF) << 8)

Loading…
Cancel
Save