LPC51U68 DMA ADC buffer to RAM buffer

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

LPC51U68 DMA ADC buffer to RAM buffer

Jump to solution
1,354 Views
castle
Contributor I

Hi everyone,

I am trying to do an ADC DMA reading to RAM buffer. I need to start DMA ADC conversion and fill my RAM buffer with 100 values. The point is to collect 100 audio input values to make calculations with them.

I found an example code of demo dma adc, but here only one value is saved.

I have tried to increase g_AdcConvResult buffer to 100 -> g_AdcConvResult[100] and change Dma transfer width sizeof(uint32_t) to 100*sizeof(uint32_t), but no success.

Could you please take a look what I have to modify in this demo code?

dma_handle_t g_DmaHandleStruct; /* Handler structure for using DMA. */
uint32_t g_AdcConvResult[1]; /* Keep the ADC conversion resulut moved from ADC data register by DMA. */
volatile bool g_DmaTransferDoneFlag; /* Flag of DMA transfer done trigger by ADC conversion. */
/* DMA descripter table used for ping-pong mode. */
DMA_ALLOCATE_LINK_DESCRIPTORS(s_dma_table, 2U);
const uint32_t g_XferConfig =
DMA_CHANNEL_XFER(true, /* Reload link descriptor after current exhaust, */
true, /* Clear trigger status. */
true, /* Enable interruptA. */
false, /* Not enable interruptB. */
sizeof(uint32_t), /* Dma transfer width. */
kDMA_AddressInterleave0xWidth, /* Dma source address no interleave */
kDMA_AddressInterleave0xWidth, /* Dma destination address no interleave */
sizeof(uint32_t) /* Dma transfer byte. */
);
const uint32_t g_Adc_12bitFullRange = 4096U;

/*******************************************************************************
* Code
******************************************************************************/

static void ADC_ClockPower_Configuration(void)
{
/* SYSCON power. */
POWER_DisablePD(kPDRUNCFG_PD_ADC0); /* Power on the ADC converter. */
POWER_DisablePD(kPDRUNCFG_PD_VD7_ENA); /* Power on the analog power supply. */
POWER_DisablePD(kPDRUNCFG_PD_VREFP_SW); /* Power on the reference voltage source. */
POWER_DisablePD(kPDRUNCFG_PD_TEMPS); /* Power on the temperature sensor. */

CLOCK_EnableClock(kCLOCK_Adc0); /* SYSCON->AHBCLKCTRL[0] |= SYSCON_AHBCLKCTRL_ADC0_MASK; */
}

/*!
* @brief Main function
*/
int main(void)
{
/* Initialize board hardware. */
/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();

/* Enable the power and clock for ADC. */
ADC_ClockPower_Configuration();
PRINTF("ADC DMA example.\r\n");

/* Configure peripherals. */
NVIC_EnableIRQ(DMA0_IRQn);
DMA_Configfuation();

#if !(defined(FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC) && FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC)
uint32_t frequency = 0U;
/* Calibration after power up. */
#if defined(FSL_FEATURE_ADC_HAS_CALIB_REG) && FSL_FEATURE_ADC_HAS_CALIB_REG
ADC0->CTRL |= ADC_CTRL_BYPASSCAL_MASK;
frequency = CLOCK_GetFreq(kCLOCK_BusClk);
if (true == ADC_DoOffsetCalibration(ADC0, frequency))
#else
#if defined(SYSCON_ADCCLKDIV_DIV_MASK)
frequency = CLOCK_GetFreq(DEMO_ADC_CLOCK_SOURCE) / CLOCK_GetClkDivider(kCLOCK_DivAdcClk);
#else
frequency = CLOCK_GetFreq(DEMO_ADC_CLOCK_SOURCE);
#endif /* SYSCON_ADCCLKDIV_DIV_MASK */
if (true == ADC_DoSelfCalibration(ADC0, frequency))
#endif /* FSL_FEATURE_ADC_HAS_CALIB_REG */
{
PRINTF("ADC Calibration Done.\r\n");
}
else
{
PRINTF("ADC Calibration Failed.\r\n");
}
#endif /* FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC */

ADC_Configuration();
PRINTF("Configuration Done.\r\n");
#if defined(FSL_FEATURE_ADC_HAS_CTRL_RESOL) & FSL_FEATURE_ADC_HAS_CTRL_RESOL
PRINTF("ADC Full Range: %d\r\n", g_Adc_12bitFullRange);
#endif /* FSL_FEATURE_ADC_HAS_CTRL_RESOL */
PRINTF("Type in any key to trigger the conversion ...\r\n");

while (1)
{
/* Get the input from terminal and trigger the converter by software. */
GETCHAR();

g_DmaTransferDoneFlag = false;
ADC_DoSoftwareTriggerConvSeqA(ADC0); /* Trigger the ADC and start the conversion. */

/* Wait for the converter & transfer to be done. */
while (!g_DmaTransferDoneFlag)
{
}
PRINTF("Conversion word : 0x%X\r\n", g_AdcConvResult[0]);
PRINTF("Conversion value: %d\r\n", (g_AdcConvResult[0] & ADC_DAT_RESULT_MASK) >> ADC_DAT_RESULT_SHIFT);
PRINTF("\r\n");
}
}

static void ADC_Configuration(void)
{
adc_config_t adcConfigStruct;
adc_conv_seq_config_t adcConvSeqConfigStruct;
adc_result_info_t adcResultInfoStruct;

/* Configure the converter. */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE) & FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE
adcConfigStruct.clockMode = kADC_ClockSynchronousMode; /* Using sync clock source. */
#endif /* FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE */
adcConfigStruct.clockDividerNumber = 1U;
#if defined(FSL_FEATURE_ADC_HAS_CTRL_RESOL) & FSL_FEATURE_ADC_HAS_CTRL_RESOL
adcConfigStruct.resolution = kADC_Resolution12bit;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_RESOL */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL) & FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL
adcConfigStruct.enableBypassCalibration = false;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_TSAMP) & FSL_FEATURE_ADC_HAS_CTRL_TSAMP
adcConfigStruct.sampleTimeNumber = 0U;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_TSAMP */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE) & FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE
adcConfigStruct.enableLowPowerMode = false;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE */
#if defined(FSL_FEATURE_ADC_HAS_TRIM_REG) & FSL_FEATURE_ADC_HAS_TRIM_REG
adcConfigStruct.voltageRange = kADC_HighVoltageRange;
#endif /* FSL_FEATURE_ADC_HAS_TRIM_REG */
ADC_Init(ADC0, &adcConfigStruct);

#if !(defined(FSL_FEATURE_ADC_HAS_NO_INSEL) && FSL_FEATURE_ADC_HAS_NO_INSEL)
/* Use the temperature sensor input to channel 0. */
ADC_EnableTemperatureSensor(ADC0, true);
#endif /* FSL_FEATURE_ADC_HAS_NO_INSEL. */

/* Enable channel 4U's conversion in Sequence A. */
adcConvSeqConfigStruct.channelMask =
(1U << 4U); /* Includes channel 4U. */
adcConvSeqConfigStruct.triggerMask = 0U;
adcConvSeqConfigStruct.triggerPolarity = kADC_TriggerPolarityPositiveEdge;
adcConvSeqConfigStruct.enableSingleStep = false;
adcConvSeqConfigStruct.enableSyncBypass = false;
adcConvSeqConfigStruct.interruptMode = kADC_InterruptForEachSequence; /* Enable the interrupt/DMA trigger. */
ADC_SetConvSeqAConfig(ADC0, &adcConvSeqConfigStruct);
ADC_EnableConvSeqA(ADC0, true); /* Enable the conversion sequence A. */

/* Clear the result register. */
ADC_DoSoftwareTriggerConvSeqA(ADC0);
while (!ADC_GetChannelConversionResult(ADC0, 4U, &adcResultInfoStruct))
{
}
ADC_GetConvSeqAGlobalConversionResult(ADC0, &adcResultInfoStruct);

/* Enable DMA trigger when Seq A conversion done. */
ADC_EnableInterrupts(ADC0, kADC_ConvSeqAInterruptEnable);
}

/* Software ISR for DMA transfer done. */
void ADC_DMA_Callback(dma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
if (true == transferDone)
{
g_DmaTransferDoneFlag = true;
}
}

static void DMA_Configfuation(void)
{
dma_channel_config_t dmaChannelConfigStruct;
dma_channel_trigger_t dmaChannelTriggerStruct;

/* Init DMA. This must be set before INPUTMUX_Init() for DMA peripheral reset will clear the mux setting. */
DMA_Init(DMA0);

/* Configure DMAMUX. */
INPUTMUX_Init(INPUTMUX);
INPUTMUX_AttachSignal(INPUTMUX, 0U, kINPUTMUX_Adc0SeqaIrqToDma);

/* Configure DMA. */
DMA_EnableChannel(DMA0, 0U);
DMA_CreateHandle(&g_DmaHandleStruct, DMA0, 0U);
DMA_SetCallback(&g_DmaHandleStruct, ADC_DMA_Callback, NULL);
/*
* Configure the DMA trigger:
* The DATAVALID of ADC will trigger the interrupt. This signal is also for thie DMA triger, which is changed 0 ->
* 1.
*/
dmaChannelTriggerStruct.burst = kDMA_EdgeBurstTransfer1;
dmaChannelTriggerStruct.type = kDMA_RisingEdgeTrigger;
dmaChannelTriggerStruct.wrap = kDMA_NoWrap;

/* Prepare and submit the transfer. */
DMA_PrepareChannelTransfer(&dmaChannelConfigStruct, /* DMA channel transfer configurationstructure. */
(void *)(uint32_t)(&(ADC0->DAT[4U])), /* DMA transfer source address. */
(void *)g_AdcConvResult, /* DMA transfer destination address. */
g_XferConfig, /* Xfer configuration */
kDMA_MemoryToMemory, /* DMA transfer type. */
&dmaChannelTriggerStruct, /* DMA channel trigger configurations. */
(dma_descriptor_t *)&(s_dma_table[0]) /* Address of next descriptor. */
);
DMA_SubmitChannelTransfer(&g_DmaHandleStruct, &dmaChannelConfigStruct);

/* Set two DMA descripters to use ping-pong mode. */
DMA_SetupDescriptor(&(s_dma_table[0]), g_XferConfig, (void *)(uint32_t)(&(ADC0->DAT[4U])), (void *)g_AdcConvResult,
(dma_descriptor_t *)&(s_dma_table[1]));
DMA_SetupDescriptor(&(s_dma_table[1]), g_XferConfig, (void *)(uint32_t)(&(ADC0->DAT[4U])), (void *)g_AdcConvResult,
(dma_descriptor_t *)&(s_dma_table[0]));
}

0 Kudos
Reply
1 Solution
1,166 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I just modified the adc_dma code based on LPC51U68 and MCUXPresso tools, it appears it works.

I attach the example code

BR

XiangJun Rong

View solution in original post

0 Kudos
Reply
7 Replies
1,335 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

Pls try to set the burst bit in SEQA_CTRL register.

xiangjun_rong_0-1691574612095.png

When the clear the burst bit, each software triggering only triggers ADC to sample once, the DMA can only be triggered once, that is why you can NOT get 100 samples, so the DMA never generate interrupt.

Secondly, pls disable ADC interrupt when you use DMA to transfer data.

For the original example, the ADC is triggered by a timer, so the ADC can be triggered multiple times, so the DMA can be triggered multiple times, the example works fine.

Hope it can help you

BR

XiangJun Rong

 

0 Kudos
Reply
1,325 Views
castle
Contributor I

As I understand I should modify my code:

Update these parts of code:

1.

uint32_t g_AdcConvResult[100]; /* Keep the ADC conversion resulut moved from ADC data register by DMA. */

 

2.

DMA_CHANNEL_XFER(true, /* Reload link descriptor after current exhaust, */
true, /* Clear trigger status. */
true, /* Enable interruptA. */
false, /* Not enable interruptB. */
sizeof(uint32_t), /* Dma transfer width. */
kDMA_AddressInterleave0xWidth, /* Dma source address no interleave */
kDMA_AddressInterleave0xWidth, /* Dma destination address no interleave */
100*sizeof(uint32_t) /* Dma transfer byte. */
);

 

3.

while (1)
{
/* Get the input from terminal and trigger the converter by software. */
GETCHAR();

ADC_DisableInterrupts(ADC0, kADC_ConvSeqAInterruptEnable); //disable ADC interrupt

g_DmaTransferDoneFlag = false;
ADC_DoSoftwareTriggerConvSeqA(ADC0); /* Trigger the ADC and start the conversion. */

ADC_EnableConvSeqABurstMode(ADC0); //burst mode start

/* Wait for the converter & transfer to be done. */
while (!g_DmaTransferDoneFlag)
{
}

for(int i=0; i<100; i++){
PRINTF("Conversion word : 0x%X\r\n", g_AdcConvResult[i]);
PRINTF("Conversion value: %d\r\n", (g_AdcConvResult[i] & ADC_DAT_RESULT_MASK) >> ADC_DAT_RESULT_SHIFT);

}
PRINTF("\r\n");
}

 

Please correct me

0 Kudos
Reply
1,285 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

Yes, I suppose that your code is almost correct, but you have to remain the software trigger line, pls try to use the following lines:

while (1)
{
/* Get the input from terminal and trigger the converter by software. */
GETCHAR();

ADC_EnableConvSeqABurstMode(ADC0); //burst mode start

ADC_DisableInterrupts(ADC0, kADC_ConvSeqAInterruptEnable); //disable ADC interrupt

g_DmaTransferDoneFlag = false;
ADC_DoSoftwareTriggerConvSeqA(ADC0); /* Trigger the ADC and start the conversion. */

 

/* Wait for the converter & transfer to be done. */
while (!g_DmaTransferDoneFlag)
{
}

for(int i=0; i<100; i++){
PRINTF("Conversion word : 0x%X\r\n", g_AdcConvResult[i]);
PRINTF("Conversion value: %d\r\n", (g_AdcConvResult[i] & ADC_DAT_RESULT_MASK) >> ADC_DAT_RESULT_SHIFT);

}
PRINTF("\r\n");
}

Note that the ADC_DoSoftwareTriggerConvSeqA(ADC0); can start ADC to sample, because you have configured bust mode, so after once software triggering, the ADC will continue to sample one by one.

If the bust mode does not meet your requirement, you can enable a timer, in the ISR of timer, execute the ADC_DoSoftwareTriggerConvSeqA(ADC0); each triggering will launch ADC to sample once. so 100 times software triggering will start ADC to sample 100 times, the DMA will transfer 100 times.

Hope it is helpful

BR

XiangJun rong

 

0 Kudos
Reply
1,183 Views
castle
Contributor I

Could you please take a look again at the code, maybe are additional changes are needed in code? Thank you very much!

0 Kudos
Reply
1,167 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

I just modified the adc_dma code based on LPC51U68 and MCUXPresso tools, it appears it works.

I attach the example code

BR

XiangJun Rong

0 Kudos
Reply
1,159 Views
castle
Contributor I

Thank you for your time. This is what I needed and it works perfectly!

0 Kudos
Reply
1,264 Views
castle
Contributor I

Tried this code, but program hangs on a while (!g_DmaTransferDoneFlag).

Burst mode meets my requirements. All I need is to trigger conversion and get 100 DMA ADC values directly to RAM buffer. I believe this method is fastest so I can get up to 5MSamples/s. For audio signal ADC speed is very important.

0 Kudos
Reply