How to implement DMA ping pong buffering with two interrupts

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
josc_1234441
Level 1
Level 1
First like received

In the application note AN52705, Example 5 shows how two separate buffers can be used. I would like to implemented a ping-pong DMA for the SAR ADC but with an interrupt after each buffer is full. i.e. An ISR should be generated when Destination 1 is full and when Destination 2 is full.

Is it correct to assume 2 DMAs are needed? If so how would these be configured so that one triggers the next?

UPDATE: I have it working with the following flag settings: I would  now like to know if Dma_1_TD[0] or Dma_1_TD[1] triggered the ISR? i.e. is adc_buffer1 or adc_buffer2 full.

CyDmaTdSetConfiguration(DMA_1_TD[0], TFR_CNT, DMA_1_TD[1],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | TD_TERMOUT0_EN | DMA_1__TD_TERMOUT_EN);

  CyDmaTdSetConfiguration(DMA_1_TD[1], TFR_CNT, DMA_1_TD[0],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | TD_TERMOUT0_EN | DMA_1__TD_TERMOUT_EN);

#define NSAMPLES 32

#define TFR_CNT NSAMPLES * 2

#define DMA_BYTES_PER_BURST 2

#define DMA_REQUEST_PER_BURST 1

#define DMA_SRC_BASE (CYDEV_PERIPH_BASE)

#define DMA_DST_BASE (CYDEV_SRAM_BASE)

uint8 DMA_1_Chan; //TODO: use the int assigned in DMA_1_dma.h

uint8 DMA_1_TD[2];

uint16_t adc_buffer1[NSAMPLES] ={0};

uint16_t adc_buffer2[NSAMPLES] ={0};

void init_adc_dma(void) {

  //Init DMA channel

  DMA_1_Chan = DMA_1_DmaInitialize(DMA_BYTES_PER_BURST, DMA_REQUEST_PER_BURST, HI16(DMA_SRC_BASE), HI16(DMA_DST_BASE));

 

  //Create TD instances

  DMA_1_TD[0] = CyDmaTdAllocate();

  DMA_1_TD[1] = CyDmaTdAllocate();

 

  //Configure each TD

  CyDmaTdSetConfiguration(DMA_1_TD[0], TFR_CNT, DMA_1_TD[1],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | TD_TERMOUT1_EN | TD_TERMOUT0_EN);

  CyDmaTdSetConfiguration(DMA_1_TD[1], TFR_CNT, DMA_1_TD[0],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | TD_TERMOUT1_EN | TD_TERMOUT0_EN);

 

  //Set TD source and destination address

  CyDmaTdSetAddress(DMA_1_TD[0], LO16((uint32)ADC_SAR_1_SAR_WRK0_PTR), LO16((uint32)&adc_buffer1[0]));

  CyDmaTdSetAddress(DMA_1_TD[1], LO16((uint32)ADC_SAR_1_SAR_WRK0_PTR), LO16((uint32)&adc_buffer2[0]));

 

  //Set the channel's initial TD

  CyDmaChSetInitialTd(DMA_1_Chan, DMA_1_TD[0]);

 

  //Enable the DMA channel

  CyDmaChEnable(DMA_1_Chan, 1);

}

/*

*Called when the adc_buffer1 or adc_buffer2 is full

*/

CY_ISR(ISR_DMA_DONE_ADC)

{

  //Toggle LED

  LED_BLUE_DR ^= LED_BLUE_MASK;

  CyDmaClearPendingDrq(DMA_1_Chan);

  //Process the buffer

}

pastedImage_0.png

0 Likes
1 Solution

user_27...,

something like that (DMA1 is the instance name of the DMA component)

// Variable declarations for DMA2

// uint8_t DMA_Chan;

// uint8_t DMA_TD[2];

...

CyDmaTdSetConfiguration(DMA_TD[0], TRANSFER_COUNT1, DMA_TD[1], TD_INC_DST_ADR | DMA1__TD_TERMOUT_EN);

CyDmaTdSetConfiguration(DMA_TD[1], TRANSFER_COUNT2, DMA_TD[0], TD_INC_DST_ADR | DMA1__TD_TERMOUT_EN);

/odissey1

View solution in original post

0 Likes
4 Replies
odissey1
Level 9
Level 9
First comment on KBA 1000 replies posted 750 replies posted

user_27...,

Did you test the original code from the AN52705 ex. #5? Please explain how would you like to modify it.

/odissey1

0 Likes
RaAl_264636
Level 6
Level 6
50 sign-ins 25 sign-ins 10 solutions authored

Hi,

if I understand AN52705 correct, there's no need to use two DMAs. According to table 1, DMA completion event can be set for a transaction descriptor. So, each TD will trigger the IRQ when this flag is set this on both TDs.

Regards

0 Likes

Do you know how to set the DMA completion event for a TD? In the API there doesn't appear to be a specific flag for this. I just see:

TD_TERMOUT0_EN

TD_TERMOUT1_EN

DMA__TD_TERMOUT_EN

Should all of these flags be used? e.g.

CyDmaTdSetConfiguration(DMA_1_TD[0], TFR_CNT, DMA_1_TD[1],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | DMA_1__TD_TERMOUT_EN | TD_TERMOUT1_EN | TD_TERMOUT0_EN);

  CyDmaTdSetConfiguration(DMA_1_TD[1], TFR_CNT, DMA_1_TD[0],

  TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT | DMA_1__TD_TERMOUT_EN | TD_TERMOUT1_EN | TD_TERMOUT0_EN);

0 Likes

user_27...,

something like that (DMA1 is the instance name of the DMA component)

// Variable declarations for DMA2

// uint8_t DMA_Chan;

// uint8_t DMA_TD[2];

...

CyDmaTdSetConfiguration(DMA_TD[0], TRANSFER_COUNT1, DMA_TD[1], TD_INC_DST_ADR | DMA1__TD_TERMOUT_EN);

CyDmaTdSetConfiguration(DMA_TD[1], TRANSFER_COUNT2, DMA_TD[0], TD_INC_DST_ADR | DMA1__TD_TERMOUT_EN);

/odissey1

0 Likes