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]));
}
Solved! Go to Solution.
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
Hi,
Pls try to set the burst bit in SEQA_CTRL register.
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
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
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
Could you please take a look again at the code, maybe are additional changes are needed in code? Thank you very much!
Thank you for your time. This is what I needed and it works perfectly!
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.