AnsweredAssumed Answered

Memory to DAC using eDMA  (Kinetis K28)

Question asked by Eldar Firing on Dec 19, 2017
Latest reply on Dec 21, 2017 by Mark Butcher

Hi,

I'm using Kinetis K28-FRDM and SDK 2.3. I have a test application where I want to feed the DAC at 500kHz sampling rate using eDMA and PDB modules.  The data to be converted is a large pre-stored buffer in RAM. So far, I am able to do so only if  I transfer 16 words (size of DAC data registers) at at time, and manually increase the source pointer and setup a new DMA TCD for every 16 words in the eDMA callback (interrupt). But what I would like to achieve is to setup the eDMA, PDB and DAC to convert the entire source buffer without the need handle the eDMA IRQ every 2 x 16 us. It leaves very little time to do anything else. That should be possible or what ? 

 

If I change this that works : 

/* DAC_DATL_COUNT  = 16 */

EDMA_PrepareTransfer(&m_transferConfig, (void *) (s_dmaBuffer), sizeof(uint16_t), (void *) DAC_DATA_REG_ADDR, sizeof(uint16_t), DAC_DATL_COUNT * sizeof(uint16_t), DAC_DATL_COUNT * sizeof(uint16_t), EDMA_MemoryToMemory );

to :

#define SOURCE_BUFF_SIZE 1024*16

EDMA_PrepareTransfer(&m_transferConfig, (void *) (s_dmaBuffer),sizeof(uint16_t), (void *) DAC_DATA_REG_ADDR, sizeof(uint16_t),DAC_DATL_COUNT * sizeof(uint16_t), SOURCE_BUFF_SIZE * sizeof(uint16_t), EDMA_MemoryToMemory );

...there is no eDMA interrupt and DAC output is 0 v all the time (major loop > 1)

 

If I change EDMA_MemoryToMemory  to kEDMA_MemoryToPeripheral,  the eDMA interrupts come as expected when the entire source buffer is completed.  The source pointer is automatically increased in-between by eDMA,  but it seems that only the first DAC data register is converted, the rest (15) are all 0v.

 

What did I miss ?

 

Initialization Code : 


void MyGenerator::initDMAMux()
{
/* Configure DMAMUX */
DMAMUX_Init(DMAMUX_BASEADDR);
DMAMUX_SetSource(DMAMUX_BASEADDR, DMA_CHANNEL, DMA_DAC_SOURCE); /* Map ADC source to channel 0 */
DMAMUX_EnableChannel(DMAMUX_BASEADDR, DMA_CHANNEL);
}

void MyGenerator::initProgrammableDelayBlock()
{

pdb_config_t pdbConfigStruct;
pdb_dac_trigger_config_t pdbDacTriggerConfigStruct;
const uint16_t PDB_DAC_INTERVAL_VALUE = 150U; /* delay count (hold time) for each DAC level */

PDB_GetDefaultConfig (&pdbConfigStruct);
pdbConfigStruct.prescalerDivider = kPDB_PrescalerDivider1;
pdbConfigStruct.dividerMultiplicationFactor = kPDB_DividerMultiplicationFactor1;
pdbConfigStruct.enableContinuousMode = true;
pdbConfigStruct.triggerInputSource = kPDB_TriggerInput4; /* PIT0 */

PDB_Init(PDB_BASEADDR, &pdbConfigStruct);
#if 0
PDB_EnableInterrupts(PDB_BASEADDR, kPDB_DelayInterruptEnable);
#endif
PDB_EnableDMA(PDB_BASEADDR, true);
PDB_SetModulusValue(PDB_BASEADDR, PDB_MODULUS_VALUE );
PDB_SetCounterDelayValue(PDB_BASEADDR, PDB_DELAY_VALUE);

/* Set DAC trigger. */
pdbDacTriggerConfigStruct.enableExternalTriggerInput = false;
pdbDacTriggerConfigStruct.enableIntervalTrigger = true;
PDB_SetDACTriggerConfig(PDB_BASEADDR, PDB_DAC_CHANNEL, &pdbDacTriggerConfigStruct);
PDB_SetDACTriggerIntervalValue(PDB_BASEADDR, PDB_DAC_CHANNEL, PDB_DAC_INTERVAL_VALUE);

/* Load PDB values. */
PDB_DoLoadValues(PDB_BASEADDR);

}

void MyGenerator::initEDMA()
{
edma_config_t userConfig;

EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(DMA0, &userConfig);
EDMA_CreateHandle(&m_EDMA_Handle, DMA0, DMA_CHANNEL);

EDMA_SetCallback(&m_EDMA_Handle, Edma_Callback, this);

EDMA_PrepareTransfer(&m_transferConfig, (void *) (s_dmaBuffer),
sizeof(uint16_t), (void *) DAC_DATA_REG_ADDR, sizeof(uint16_t),
DAC_DATL_COUNT * sizeof(uint16_t),
SINE_BUFF_SIZE * sizeof(uint16_t), kEDMA_MemoryToPeripheral );


EDMA_SubmitTransfer(&m_EDMA_Handle, &m_transferConfig);
/* Enable interrupt when transfer is done. */
EDMA_EnableChannelInterrupts(DMA0, DMA_CHANNEL,kEDMA_MajorInterruptEnable);

#if defined(FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT) && FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT
/* Enable async DMA request. */
EDMA_EnableAsyncRequest(DMA0, DMA_CHANNEL, true);

#endif /* FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT */

/* Enable transfer. */
EDMA_StartTransfer(&m_EDMA_Handle);
}

void MyGenerator::initDAC()
{
dac_config_t dacConfigStruct;
dac_buffer_config_t dacBufferConfigStruct;

DAC_GetDefaultConfig(&dacConfigStruct);
DAC_Init(DAC_BASEADDR, &dacConfigStruct);
DAC_Enable(DAC_BASEADDR, true); /* Enable output. */

/* Configure the DAC buffer. */
DAC_EnableBuffer(DAC_BASEADDR, true);
DAC_GetDefaultBufferConfig(&dacBufferConfigStruct);
dacBufferConfigStruct.triggerMode = kDAC_BufferTriggerByHardwareMode;


DAC_SetBufferConfig(DAC_BASEADDR, &dacBufferConfigStruct);
DAC_SetBufferReadPointer(DAC_BASEADDR, 0U); /* Make sure the read pointer to the start. */

/* Enable DMA. */

DAC_EnableBufferInterrupts(DAC_BASEADDR,kDAC_BufferReadPointerBottomInterruptEnable);
DAC_EnableBufferDMA(DAC_BASEADDR, true);
}


void MyGenerator::eDMAcallback(edma_handle_t *handle, bool transferDone, uint32_t tcds)
{

/* Clear Edma interrupt flag. */
EDMA_ClearChannelStatusFlags(DMA0, DMA_CHANNEL, kEDMA_InterruptFlag);

EDMA_PrepareTransfer(&m_transferConfig, (void *) (s_dmaBuffer),
sizeof(uint16_t), (void *) DAC_DATA_REG_ADDR, sizeof(uint16_t),
DAC_DATL_COUNT * sizeof(uint16_t),
SINE_BUFF_SIZE * sizeof(uint16_t), kEDMA_MemoryToPeripheral );

EDMA_SetTransferConfig(DMA0, DMA_CHANNEL, &m_transferConfig, NULL);
/* Enable transfer. */
EDMA_StartTransfer(&m_EDMA_Handle);
}


static void Edma_Callback(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds)
{
GPIO_WritePinOutput(BOARD_INITPINS_SIGCodeTime_GPIO, BOARD_INITPINS_SIGCodeTime_GPIO_PIN, 1U);
(static_cast<MyGenerator*>(userData))->eDMAcallback(handle, transferDone,tcds);
GPIO_WritePinOutput(BOARD_INITPINS_SIGCodeTime_GPIO, BOARD_INITPINS_SIGCodeTime_GPIO_PIN, 0U);
}

Outcomes