DMA Ping-Pong DAC Output

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

DMA Ping-Pong DAC Output

710 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Wed May 07 12:48:58 MST 2014
I am trying to output procedurally generated audio using the GPDMA and the DAC in a ping-pong transfer scheme.  To do this, I've created to linked list items and configured DMA Ch. 0 to output them:

dac_transfer_a - Source dac_buffer_a, Next item dac_transfer_b
dac_transfer_b - Source dac_buffer_b, Next item dac_transfer_a

I can set the contents of the dac_buffers before enabling the DMA, and I can see the expected voltages at the DAC.

In order to update the DAC buffers, I've enabled the Terminal Count Interrupt, and determine which buffer is free by looking at the next linked list item.  I thought that since I have just finished transfering, the buffer for the next linked item (DMACCLLI) is busy, and the current transfer's buffer is available.

void DMA_IRQHandler() {
uint32_t* free_dac_buffer;

if (LPC_GPDMA->DMACIntTCStat & (1<<0)) {

if (LPC_GPDMACH0->DMACCLLI == &dac_transfer_a) {
free_dac_buffer = dac_buffer_b;
} else {
free_dac_buffer = dac_buffer_a;
}

               update_buffer(free_dac_buffer);

LPC_GPDMA->DMACIntTCClear = 1 << 0;
}
}


However, I realized I'm assuming that DMACCLLI points at the transfer just completed, and is not immediatly advanced.  I couldn't find anything definitive in the datasheet, and was hoping to get confirmation on the right way to handle this kind of transfer.

Thank you,
Louis
Labels (1)
0 Kudos
Reply
1 Reply

561 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lsimons on Thu May 08 06:35:46 MST 2014
I haven't found a way to halt the DMA during emulation breakpoints, but I was able to get a better look at the state of DMACCLLI via setting/clearing a test point.

void DMA_IRQHandler() {
uint32_t* free_dac_buffer;

if (LPC_GPDMA->DMACIntTCStat & (1<<0)) {
// Linked list has already advanced, e.g. if DMACCLLI points at
// dac_transfer_a, it is performing dac_transfer_b, and dac_buffer_a
// is free.
if ((dma_linked_list_t *)LPC_GPDMACH0->DMACCLLI == &dac_transfer_a) {
free_dac_buffer = dac_buffer_a;
debug_set_tp1();
} else {
free_dac_buffer = dac_buffer_b;
debug_clear_tp1();
}

load_dac_buffer(free_dac_buffer, DAC_BUFFER_LEN);

LPC_GPDMA->DMACIntTCClear = 1 << 0;
}
}


By hard-coding two distinct waveforms into dac_buffer_a and dac_buffer_b, I'm fairly confident that DMACCLLI has already advanced to the next linked item, i.e. it is no longer in the same state it was prior to the interrupt.  On the scope, I can see that when TP1 is set, dac_buffer_b is being transferred.

I'm still hoping for a confirmation on this behavior, because I'd hate for a race condition or some other ambiguous timing lead to modifying a buffer that's in use.
0 Kudos
Reply