ADC and DMA

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

ADC and DMA

1,843 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lg_inetyx on Thu Jan 29 09:04:12 MST 2015
Hi everybody !

I need to acquire buffers of 512 sample to monitor a waveform using the LPC1549 ADC.

I am trying to have it working using the ADC1 in burst mode, triggering the DMA.

The ADC driver should work fine, as the sequence interrupt is properly fired and the value is correct.
(when I enable NVIS, which I should not do using dma).

Does anybody sees what I'm doing wrong while setting up dma ?

The DMA driver code :
/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

/* Size of the source and destination buffers in 32-bit words.
   Allowable values  = 128, 256, 512, or 1024 */
#define SIZE_BUFFER            (512)

/* Source and destination buffers */
uint32_t adc_buf[SIZE_BUFFER];

/* DMA completion flag */
static volatile bool dmaDone;


/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private functions
 ****************************************************************************/

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/**
 * @briefDMA Interrupt Handler
 * @returnNone
 */
void DMA_IRQHandler(void)
{
/* Error interrupt on channel 0? */
if ((Chip_DMA_GetIntStatus(LPC_DMA) & DMA_INTSTAT_ACTIVEERRINT) != 0) {
/* This shouldn't happen for this simple DMA example, so set the LED
   to indicate an error occurred. This is the correct method to clear
   an abort. */
Chip_DMA_DisableChannel(LPC_DMA, DMA_CH0);
while ((Chip_DMA_GetBusyChannels(LPC_DMA) & (1 << DMA_CH0)) != 0) {}
Chip_DMA_AbortChannel(LPC_DMA, DMA_CH0);
Chip_DMA_ClearErrorIntChannel(LPC_DMA, DMA_CH0);
Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
Board_LED_Set(0, true);
}

/* Clear DMA interrupt for the channel */
Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_CH0);

dmaDone = true;
}


void DMA_init( void ) {

DMA_CHDESC_T dmaDesc;

/* DMA initialization - enable DMA clocking and reset DMA if needed */
Chip_DMA_Init(LPC_DMA);

/* Enable DMA controller and use driver provided DMA table for current descriptors */
Chip_DMA_Enable(LPC_DMA);
Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));

/* Setup channel 0 for the following configuration:
   - High channel priority
   - Interrupt A fires on descriptor completion
   - trigger on ACD1 seq A completion
   - burst size is 1 */
Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
Chip_DMA_EnableIntChannel(LPC_DMA, DMA_CH0);
Chip_INMUX_SetDMATrigger(DMA_CH0, DMATRIG_ADC1_SEQA_IRQ);
Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_CH0,
(DMA_CFG_PERIPHREQEN | DMA_CFG_HWTRIGEN | DMA_CFG_TRIGTYPE_EDGE |
DMA_CFG_TRIGPOL_HIGH | DMA_CFG_BURSTPOWER_1 | DMA_CFG_CHPRIORITY(0)));

/* DMA descriptor for peripheral to memory operation - note that addresses must
   be the END address for src and destination, not the starting address.
     DMA operations moves from end to start. */
dmaDesc.source = DMA_ADDR(&(LPC_ADC1->DR[ADC1_IBUS_CH]));
dmaDesc.dest = DMA_ADDR(&adc_buf[SIZE_BUFFER - 1]) + 3;
dmaDesc.next = DMA_ADDR(0);

/* Setup transfer descriptor and validate it */
Chip_DMA_SetupTranChannel(LPC_DMA, DMA_CH0, &dmaDesc);
Chip_DMA_SetValidChannel(LPC_DMA, DMA_CH0);


NVIC_EnableIRQ(DMA_IRQn);// Enable DMA interrupt

/* Setup data transfer and software trigger in same call */
Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_CH0,
  (DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA  |
   DMA_XFERCFG_WIDTH_32 | DMA_XFERCFG_SRCINC_0 | DMA_XFERCFG_DSTINC_1 |
   DMA_XFERCFG_XFERCOUNT(SIZE_BUFFER)));

dmaDone = false;

}



The ADC driver code :
/**
 * Init ADC1 to perform periodical reads on channels 0 and 1, whith threshold detection
 */
void ADC1_init( void )
{

/* --- ADC Init --- */

/* Setup ADC for 12-bit mode and normal power */
Chip_ADC_Init(ADC, 0);

/* Setup for maximum ADC clock rate */
Chip_ADC_SetClockRate(ADC, ADC_MAX_SAMPLE_RATE);

/* For ADC1, sequencer A will be used with threshold events.
   It will run continuously in burst mode and will monitor the CH0 & CH1 input. */
Chip_ADC_SetupSequencer(ADC, ADC_SEQA_IDX,(ADC_SEQ_CTRL_CHANSEL(ADC1_VBUS_CH) |
 ADC_SEQ_CTRL_CHANSEL(ADC1_IBUS_CH) |
 ADC_SEQ_CTRL_BURST   |
 ADC_SEQ_CTRL_MODE_EOS));
/* Need to do a calibration after initialization and trim */
Chip_ADC_StartCalibration(ADC);
while (!(Chip_ADC_IsCalibrationDone(ADC))) {}

/* ACD clock must be set after calibration !! (bug in lib) */
Chip_ADC_SetClockRate(ADC, ADC_MAX_SAMPLE_RATE);

/* setup thresholds so that they are never crossed.
 * They will be set by application, through ADC1_set_thr(...)
 */
// ch 0
Chip_ADC_SelectTH0Channels(ADC, ADC_SEQ_CTRL_CHANSEL(ADC1_VBUS_CH));
Chip_ADC_SetThrLowValue(ADC, 0,  0x000);
Chip_ADC_SetThrHighValue(ADC, 0, 0xFFF);

// ch 1
Chip_ADC_SelectTH1Channels(ADC, ADC_SEQ_CTRL_CHANSEL(ADC1_IBUS_CH));
Chip_ADC_SetThrLowValue(ADC, 1,  0x000);
Chip_ADC_SetThrHighValue(ADC, 1, 0xFFF);

/* Clear all pending interrupts */
Chip_ADC_ClearFlags(ADC, Chip_ADC_GetFlags(ADC));

/* Enable sequence A completion and threshold crossing interrupts for ADC1_0 and ADC 1_1 */
Chip_ADC_EnableInt(ADC, ADC_INTEN_CMP_ENABLE(ADC_INTEN_CMP_CROSSTH, ADC1_VBUS_CH) |
ADC_INTEN_CMP_ENABLE(ADC_INTEN_CMP_CROSSTH, ADC1_IBUS_CH) |
ADC_INTEN_SEQA_ENABLE);
Chip_ADC_SetThresholdInt(ADC, ADC1_VBUS_CH, ADC_INTEN_THCMP_CROSSING);
Chip_ADC_SetThresholdInt(ADC, ADC1_IBUS_CH, ADC_INTEN_THCMP_CROSSING);

// DO NOT ACTIVATE NVIC (for dma operation)
//_NVIC_ClearEnableIRQ(ADC1_SEQA_IRQn);

/* Enable sequencers */
Chip_ADC_EnableSequencer(ADC, ADC_SEQA_IDX);

}

/**
 * ADC Mux PIN enable
 */
void ADC1_init_PinMux( void )
{
/* Disables pullups/pulldowns and disable digital mode */
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 9, (IOCON_MODE_INACT | IOCON_ADMODE_EN));
Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 32+1, (IOCON_MODE_INACT | IOCON_ADMODE_EN));

/* Assign ADC1_1 to PIO0_9 via SWM (fixed pin) */
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_0);/*!< ADC0_0 fixed pin enable/disable on pin P1_1 */
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC1_1);/*!< ADC0_1 fixed pin enable/disable on pin P0_9 */

}
Labels (1)
4 Replies

1,041 Views
martinlorenz
Contributor II

Hi Tom,

Did you have success in the meantime?

I have a rather similar use case and still cannot find any examples / application notes on this topic :-(

Regards, Martin

0 Kudos

1,041 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by tomg on Sat Oct 10 05:00:47 MST 2015
As I need somehow the same functionality from ADC and DMA - seems as a quiet common scenario to me - I started investigating your code. I'm using a 1549.

The (first?) issue seems to be, that there is no triggering of DMA from finished ADC sampling interrupt signal.

I just figured out, that there is an interrupt generated by the adc upon Chip_ADC_EnableSequencer(ADC, ADC_SEQA_IDX); This is bad, because there is no ISR related to it and so, this bit won't be cleared and therefore any successive ADC_INTEN_SEQA Interrrupts are masked as DMA triggers only on rising edge.

I inserted a Chip_ADC_ClearFlags(LPC_ADC0, Chip_ADC_GetFlags(LPC_ADC0)); request at the end of DMA_init and now, the DMA is triggered and saves a valid sample into the buffer (My sample code is rewritten due to investigation and only does one conversion). This seems to fix it, but of course is only a bloody workaround which may not be used in production code:)

I hope this helps.

Still I don't know, why the ADC_SEQA interrupt is set on EnablerSequencer. There seem to be no valid sample in ADC DR (bit 1<<31 is not set).

Also have to make the burst conversion stuff working (one trigger should sample n times in burst mode and store raw DR in memory for later investigation). Someone, into this already?

greetings tom
0 Kudos

1,041 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by jdesbonnet on Sat Jun 20 08:57:47 MST 2015
Unfortunately there are no examples of reading the ADC using DMA that I can find. I'm attempting this with the LPC824 and having no success yet.  Googling the topic only yields people asking the same (unanswered) question.   I would have thought it an important enough use case to make a code example.

1,041 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by xianghuiwang on Thu May 07 20:31:57 MST 2015
Hi, you can reference the LPCOpen sample code:

http://www.lpcware.com/content/nxpfile/lpcopen-software-development-platform-lpc15xx-packages

Regards!
0 Kudos