|
|
|
|
|
|
|
|
/*************************************************/ |
|
|
/*************************************************/ |
|
|
|
|
|
|
|
|
// Set the data size used for each triggered transfer |
|
|
// Set the data size used for each triggered transfer |
|
|
void size(unsigned int len) { |
|
|
|
|
|
|
|
|
void transferSize(unsigned int len) { |
|
|
if (len == 4) { |
|
|
if (len == 4) { |
|
|
TCD->NBYTES = 4; |
|
|
TCD->NBYTES = 4; |
|
|
if (TCD->SOFF != 0) TCD->SOFF = 4; |
|
|
if (TCD->SOFF != 0) TCD->SOFF = 4; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Set the number of transfers (number of triggers until complete) |
|
|
// Set the number of transfers (number of triggers until complete) |
|
|
void count(unsigned int len) { |
|
|
|
|
|
|
|
|
void transferCount(unsigned int len) { |
|
|
if (len > 32767) return; |
|
|
if (len > 32767) return; |
|
|
if (len >= 512) { |
|
|
if (len >= 512) { |
|
|
TCD->BITER = len; |
|
|
TCD->BITER = len; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void replaceSettingsOnCompletion(const DMABaseClass &settings) { |
|
|
void replaceSettingsOnCompletion(const DMABaseClass &settings) { |
|
|
TCD->DLASTSGA = (int32_t)(settings.TCD); |
|
|
TCD->DLASTSGA = (int32_t)(settings.TCD); |
|
|
|
|
|
TCD->CSR &= ~DMA_TCD_CSR_DONE; |
|
|
TCD->CSR |= DMA_TCD_CSR_ESG; |
|
|
TCD->CSR |= DMA_TCD_CSR_ESG; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************/ |
|
|
/***************************************/ |
|
|
|
|
|
|
|
|
// Triggers cause the DMA channel to actually move data. Each |
|
|
// Triggers cause the DMA channel to actually move data. Each |
|
|
// trigger moves a single data unit, which is typically 8, 16 or 32 bits. |
|
|
|
|
|
|
|
|
// trigger moves a single data unit, which is typically 8, 16 or |
|
|
|
|
|
// 32 bits. If a channel is configured for 200 transfers |
|
|
|
|
|
|
|
|
// Use a hardware trigger to make the DMA channel run |
|
|
// Use a hardware trigger to make the DMA channel run |
|
|
void attachTrigger(uint8_t source) { |
|
|
|
|
|
|
|
|
void triggerAtHardwareEvent(uint8_t source) { |
|
|
volatile uint8_t *mux; |
|
|
volatile uint8_t *mux; |
|
|
mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel; |
|
|
mux = (volatile uint8_t *)&(DMAMUX0_CHCFG0) + channel; |
|
|
*mux = 0; |
|
|
*mux = 0; |
|
|
|
|
|
|
|
|
// Use another DMA channel as the trigger, causing this |
|
|
// Use another DMA channel as the trigger, causing this |
|
|
// channel to trigger after each transfer is makes, except |
|
|
// channel to trigger after each transfer is makes, except |
|
|
// the its last transfer. This effectively makes the 2 |
|
|
// the its last transfer. This effectively makes the 2 |
|
|
// channels run in parallel. |
|
|
|
|
|
void attachTriggerBeforeCompletion(DMABaseClass &ch) { |
|
|
|
|
|
|
|
|
// channels run in parallel until the last transfer |
|
|
|
|
|
void triggerAtTransfersOf(DMABaseClass &ch) { |
|
|
ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK) |
|
|
ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK) |
|
|
| DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK; |
|
|
| DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK; |
|
|
ch.TCD->CITER = ch.TCD->BITER ; |
|
|
ch.TCD->CITER = ch.TCD->BITER ; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Use another DMA channel as the trigger, causing this |
|
|
// Use another DMA channel as the trigger, causing this |
|
|
// channel to trigger when the other channel completes. |
|
|
// channel to trigger when the other channel completes. |
|
|
void attachTriggerAtCompletion(DMABaseClass &ch) { |
|
|
|
|
|
ch.TCD->CSR = (ch.TCD->CSR & ~DMA_TCD_CSR_MAJORLINKCH_MASK) |
|
|
|
|
|
|
|
|
void triggerAtCompletionOf(DMABaseClass &ch) { |
|
|
|
|
|
ch.TCD->CSR = (ch.TCD->CSR & ~(DMA_TCD_CSR_MAJORLINKCH_MASK|DMA_TCD_CSR_DONE)) |
|
|
| DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK; |
|
|
| DMA_TCD_CSR_MAJORLINKCH(channel) | DMA_TCD_CSR_MAJORELINK; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Cause this DMA channel to be continuously triggered, so |
|
|
// Cause this DMA channel to be continuously triggered, so |
|
|
// it will move data as rapidly as possible, without waiting. |
|
|
// it will move data as rapidly as possible, without waiting. |
|
|
// Normally this would be used with disableOnCompletion(). |
|
|
// Normally this would be used with disableOnCompletion(). |
|
|
void attachTriggerContinuous(void) { |
|
|
|
|
|
|
|
|
void triggerContinuously(void) { |
|
|
volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0; |
|
|
volatile uint8_t *mux = (volatile uint8_t *)&DMAMUX0_CHCFG0; |
|
|
mux[channel] = 0; |
|
|
mux[channel] = 0; |
|
|
#if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS |
|
|
#if DMAMUX_NUM_SOURCE_ALWAYS >= DMA_NUM_CHANNELS |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Manually trigger the DMA channel. |
|
|
// Manually trigger the DMA channel. |
|
|
void trigger(void) { |
|
|
|
|
|
|
|
|
void triggerManual(void) { |
|
|
DMA_SSRT = channel; |
|
|
DMA_SSRT = channel; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void enable(void) { |
|
|
void enable(void) { |
|
|
DMA_SERQ = channel; |
|
|
DMA_SERQ = channel; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void disable(void) { |
|
|
void disable(void) { |
|
|
DMA_CERQ = channel; |
|
|
DMA_CERQ = channel; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************/ |
|
|
/***************************************/ |
|
|
/** Status **/ |
|
|
/** Status **/ |
|
|
/***************************************/ |
|
|
/***************************************/ |
|
|
|
|
|
|
|
|
// TODO: "get" functions, to read important stuff, like SADDR & DADDR... |
|
|
|
|
|
// error status, etc |
|
|
|
|
|
|
|
|
bool complete(void) { |
|
|
|
|
|
if (TCD->CSR & DMA_TCD_CSR_DONE) return true; |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
void clearComplete(void) { |
|
|
|
|
|
DMA_CDNE = channel; |
|
|
|
|
|
} |
|
|
|
|
|
bool error(void) { |
|
|
|
|
|
if (DMA_ERR & (1<<channel)) return true; |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
void clearError(void) { |
|
|
|
|
|
DMA_CERR = channel; |
|
|
|
|
|
} |
|
|
|
|
|
void * sourceAddress(void) { |
|
|
|
|
|
return (void *)(TCD->SADDR); |
|
|
|
|
|
} |
|
|
|
|
|
void * destinationAddress(void) { |
|
|
|
|
|
return (void *)(TCD->DADDR); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/***************************************/ |
|
|
/***************************************/ |
|
|
/** Direct Hardware Access **/ |
|
|
/** Direct Hardware Access **/ |
|
|
|
|
|
|
|
|
// 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. |
|
|
uint8_t channel; |
|
|
uint8_t channel; |
|
|
|
|
|
// TCD is accessible due to inheritance from DMABaseClass |
|
|
|
|
|
|
|
|
/* usage cases: |
|
|
/* usage cases: |
|
|
|
|
|
|