Can't get DMA transfers for ADC working correctly on a LPC1768

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

Can't get DMA transfers for ADC working correctly on a LPC1768

2,267 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by eTLA on Mon May 05 01:42:46 MST 2014
I bet I'm missing something obvious here, but I've been banging my head against this problem for days, read tons of code found on the net and I simply can't get it to work.

What I would like to do is handling DMA transfers for conversions of channel 0-2 but the errata for the LPC17xx gave me the impression that this won't work due to a bug so for now I'll settle for one channel at a time. Right now I'm trying to get it to work with channel 0 but I must be missing something (probably/hopefully obvious)...

This is the code:

#define SAMPLE_BUFFER_LENGTH 32

uint32_t adcInputBuffer[SAMPLE_BUFFER_LENGTH];
uint32_t holdingInputBuffer[SAMPLE_BUFFER_LENGTH];

void hw_adc_setup(void)
{
SC->PCONP |= PCONP_PCAD | PCONP_PCGPDMA;
SC->PCLKSEL0 |= 1<<24;
PINCON->PINSEL1 &= (uint32_t) ~(0x3f << 14);
PINCON->PINSEL1 |= 0x15 << 14;
ADC->ADCR = (uint32_t) ((1 << 21) | (1 << 8) | 0x01);

/* Enable DMA */
GPDMA->DMACConfig = 0x01;

/* Clear errors etc */
GPDMA->DMACIntTCClear = 0xff;
GPDMA->DMACIntErrClr  = 0xff;

/* Set up DMA channel */
GPDMACH0->DMACCConfig   = 0;
GPDMACH0->DMACCControl  =
(SAMPLE_BUFFER_LENGTH << 0) |   /* Transfer size */
(0x00 << 12) |  /* Source burst size */
(0x00 << 15) |  /* Destination burst size */
(0x02 << 18) |  /* 32 bit word source transfer width */
(0x02 << 21) |  /* 32 bit word destination transfer width */
(0x01 << 27) |  /* Destination increment */
(0x01UL << 31); /* Terminal count interrupt enable bit */
GPDMACH0->DMACCLLI      = (uint32_t) NULL;
GPDMACH0->DMACCDestAddr = (uint32_t) adcInputBuffer;
GPDMACH0->DMACCSrcAddr  = ADC->ADDR0;

/* Fixme: Don't know if this is needed but it was in the example... */
while((GPDMA->DMACConfig & 0x01) == 0);

GPDMACH0->DMACCConfig =
(4 << 1)  | /* Source peripheral ADC */
(2 << 11) | /* Transfer type; P->M */
(1 << 14) | /* Interrupt error mask */
(1 << 15);  /* Terminal count interrupt mask */

ADC->ADINTEN = 1<<8;

NVIC_EnableIRQ(DMA_IRQn);
NVIC_SetPriority(DMA_IRQn, 5);
}

void hw_adc_trig(void)
{
/* Set buffer address */
GPDMACH0->DMACCDestAddr = (uint32_t) adcInputBuffer;

/* Enable DMA-channel*/
GPDMACH0->DMACCConfig |= 0x01;

/* Burst mode */
ADC->ADCR |= 1 << 16;

hw_gpio_set_testpin_1(true);
}

void DMA_IRQHandler (void)
{
uint32_t IntTCStat = GPDMA->DMACRawIntTCStat;
uint32_t IntErrStat = GPDMA->DMACRawIntErrStat;

memcpy(holdingInputBuffer, adcInputBuffer, SAMPLE_BUFFER_LENGTH * 4);

GPDMA->DMACIntErrClr = IntTCStat;
GPDMA->DMACIntErrClr = IntErrStat;

hw_gpio_set_testpin_1(false);

/* Turn off burst mode */
ADC->ADCR &= ~(1UL << 16);

/* Disable DMA-channel*/
GPDMACH0->DMACCConfig &= ~0x01UL;
}


The ADC conversion and DMA transfer should start when hw_adc_trig is called and then 32 conversions should be done, ie it should take approximately 160 us. However, when I measure testpin 1 I get only 7.2 us the first time trig is called (2.6 us every other call after the first) which leads me to the conclusion that the DMA transfer only transfer the first ADC sample 32 times instead of transferring 32 unique samples.

This is, of course, not what I desire.

It's probably just a very stupid mistake I've done, but I seem unable to figure this out on my own. Sooo... Help?
Labels (1)
0 Kudos
6 Replies

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by _Lutz_ on Fri Jan 22 04:13:36 MST 2016
Hi,

I can confirm this observation with a LPC4078. This part seems to have the same GPDMA peripheral.

In my application the QEI (quadrature encoder) interrupt triggered the ADC and the ADC data are transferred to memory via DMA.
It also only works OK when I disable the synchronization logic by setting the corresponding bit in the DMA Synchronization Register.

Thank you for posting your observation!
It helps when you don't feel alone ...

Lutz Hoerl
0 Kudos

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by eTLA on Thu May 15 06:57:01 MST 2014
Found a solution.Thought I'd mention it here since I personally hate when people ask questions and there's never any answer.

Turns out that DMACSync was the culprit. The documentation claims:

0 - synchronization logic for the corresponding DMA request signals are enabled.
1 - synchronization logic for the corresponding request line signals are disabled.

The bits are 0 by default so I never thought more about it, but in pure desperation I tested to change that to 1 and voilá! The DMA transfers are now synced with the ADC! If this is because DMACSync turns on a different sync (that breaks the ADC-connection), the documentation is wrong or there's a very well hidden odd behavior in the silicon I don't know.

However, there's still a minor inconvenience in the tranfer; the DMA always transfer two initial words before the ADC is ready, after that all the data is valid. Something's still amiss, but for now it's good enough.

So I'm still interested in a "real" solution.
0 Kudos

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by eTLA on Thu May 08 05:50:20 MST 2014
So I've been banging my head a bit more on this problem, but I dont' seem to get closer to a solution. I've tried different transfer sizes and flipped around different settings. No difference.

Also found another minor bug (I didn't clear the right register when the IRQ-function was called which lead to an infinite IRQ-loop) but no matter how I try I can't get the DMA to wait for the ADC to finish. See attached image, all values below zero are values that don't have the DONE-flag set.

The input is a square wave so the samples are correct, but there's several (like ~5) "dummysamples" between every correct sample. Ie, the DMA happily transfers data from the ADC without waiting for the DONE-bit to go true.

Surely someone must have ran into this problem before? No?
0 Kudos

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by leluno on Tue May 06 00:41:12 MST 2014
try to shorten the transfersize
0 Kudos

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by leluno on Tue May 06 00:08:18 MST 2014
ADCready interrupt + LPC_GPDMA->DMACSoftSReq ?
0 Kudos

1,667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by eTLA on Mon May 05 07:54:25 MST 2014
Ok, that was stupid. I found one bug: Giving the value, rather than the address, of ADDR0 bordered to sheer stupidity. So the line:

GPDMACH0->DMACCSrcAddr  = ADC->ADDR0;

now reads:

GPDMACH0->DMACCSrcAddr  = (uint32_t) &(ADC->ADDR0);

The data in the buffer now reads:

2147490192, 6544, 6544, 6544, 6544, 6544, 2147490240, 6592, 6592, 6592, 6592, 6592, 6592, 214749019...

Which makes sense. 2147490192 is DONE-bit set + 0x199 as ADC-result and 6544 is, of course, the same result minus the DONE-bit.

So now I have a working DMA-transfer from the ADC, but it just burst the data without waiting for the ADC to complete.

Anyone have an idea?
0 Kudos