2024-03-01 02:58 AM
Hello,
I'm using ADC together with DMA.
At first start of ADC and DMA all worked like expected.
At 2nd and following starts, the first value received from DMA is always zero / invalid.
Now I have found out that it works if between two DMA transfers I use
LL_ADC_Disable(ADC2);
and afterwards
LL_ADC_Enable(ADC2);
//code snippet:
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1, 100U);
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
LL_ADC_REG_StartConversion(ADC2);
while (LL_DMA_GetDataLength(DMA2, LL_DMA_CHANNEL_1) > 0U)
{
}
LL_ADC_REG_StopConversion(ADC2);
LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
LL_ADC_Disable(ADC2);
while(LL_ADC_IsDisableOngoing(ADC2))
{
}
LL_ADC_Enable(ADC2);
Now my questions:
Is disable/enable of the ADC absolutely necessary?
Or is there a less brute way to reuse the ADC together with DMA?
Thanks in advance.
Best regards
Johann
Solved! Go to Solution.
2024-03-05 02:37 AM - edited 2024-03-05 04:13 AM
Hello @jhoerd,
In circular mode, the result array is normally updated indefinitely. Please use this line to use the circular mode :
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
If I resume :
I advise you to download the package STM32Cube_FW_G4 by clicking here and to have a look to the project under the root "Projects\NUCLEO-G474RE\Examples_LL\ADC\ADC_GroupsRegularInjected_Init". It's an interesting example on how to use the DMA to perform a continuous data stream. Also, DMA is configured to transfer conversion data in an array.
Can you enable the transfer complete DMA transfer interrupt ?
Best Regards,
Pierre
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-03-01 06:57 AM - edited 2024-03-01 06:57 AM
Hello @jhoerd,
Thank you for your question !
Here some questions/remarks to help your investigation :
Best Regards,
Pierre
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-03-03 10:02 PM
Hello Pierre,
here my code to intitialize ADC2/DMA
//ADC2
LL_ADC_SetGainCompensation(ADC2, 0);
LL_ADC_SetOverSamplingScope(ADC2, LL_ADC_OVS_DISABLE);
LL_ADC_DisableDeepPowerDown(ADC2);
LL_ADC_EnableInternalRegulator(ADC2);initTypeDef.Resolution = LL_ADC_RESOLUTION_12B;
initTypeDef.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
initTypeDef.LowPowerMode = LL_ADC_LP_MODE_NONE;
if (LL_ADC_Init(ADC2, &initTypeDef) != SUCCESS)
{
//@what: error ADC_Init
}
regInitTypeDef.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
regInitTypeDef.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE; //means one Channel
regInitTypeDef.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
regInitTypeDef.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS; //LL_ADC_REG_CONV_SINGLE
regInitTypeDef.DMATransfer = LL_ADC_REG_DMA_TRANSFER_LIMITED;//LL_ADC_REG_DMA_TRANSFER_NONE;
regInitTypeDef.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
if (LL_ADC_REG_Init(ADC2, ®InitTypeDef) != SUCCESS)
{
//@what: error ADC_Reg_Init
}
LL_ADC_REG_SetSequencerRanks(ADC2, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_3);
LL_ADC_SetChannelSingleDiff(ADC2, LL_ADC_CHANNEL_3, LL_ADC_SINGLE_ENDED);
LL_ADC_StartCalibration(ADC2, LL_ADC_SINGLE_ENDED);
while (LL_ADC_IsCalibrationOnGoing(ADC2) != 0)
{
//check Timeout
}
LL_ADC_SetChannelSamplingTime(ADC2, static_cast<uint32_t>(LL_ADC_CHANNEL_3),
LL_ADC_SAMPLINGTIME_6CYCLES_5); //PA6
//Now initialization of DMA:
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC2);
LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, //(uint32_t)&ADC2->DR,
LL_ADC_DMA_GetRegAddr(ADC2, LL_ADC_DMA_REG_REGULAR_DATA),
reinterpret_cast<uint32_t>(&ReceiveBuffer[0]),
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
//Now cyclic start/restart of DMA request:
LL_ADC_ClearFlag_EOC(ADC2);
LL_ADC_ClearFlag_EOS(ADC2);
LL_ADC_ClearFlag_EOSMP(ADC2);
LL_ADC_ClearFlag_OVR(ADC2);
LL_ADC_ClearFlag_ADRDY(ADC2);
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1, 100U);
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
LL_ADC_REG_StartConversion(ADC2);
while (LL_DMA_GetDataLength(DMA2, LL_DMA_CHANNEL_1) > 0U)
{
}LL_ADC_REG_StopConversion(ADC2);
LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
//OVRFlag:
Like the code above, i think, I am already resetting OVR flag before
next start:
LL_ADC_ClearFlag_OVR(ADC2);
ADC and DMA registers before first call:
ADC2->ISR: 0x0
ADC2->IER: 0x0
ADC2->CR: 0x10000001
ADC2->CFGR: 0x80003001
ADC2->CFGR2: 0x0
ADC2->SQR1: 0xc0
ADC2->SQR2: 0x0
ADC2->SQR3: 0x0
ADC2->SQR4: 0x0
ADC2->SMPR1: 0x200
ADC2->SMPR2: 0x0
ADC2->JSQR: 0x0
ADC2->JDR1: 0x0
ADC2->JDR2: 0x0
ADC2->JDR3: 0x0
ADC2->JDR4: 0x0
DMA2_Channel1->CCR: 0x2580
DMA2_Channel1->CPAR: 0x50000140
DMA2_Channel1->CMAR: 0x200003b4
ADC12->CSR: 1
ADC12->CCR: c30000
ADC12->CDR: 0
ADC2->DR: 0
Registers before 2ndCall without disable/enable ADC2
ADC2->ISR: 0x0
ADC2->IER: 0x0
ADC2->CR: 0x10000001
ADC2->CFGR: 0x80003001
ADC2->CFGR2: 0x0
ADC2->SQR1: 0xc0
ADC2->SQR2: 0x0
ADC2->SQR3: 0x0
ADC2->SQR4: 0x0
ADC2->SMPR1: 0x200
ADC2->SMPR2: 0x0
ADC2->JSQR: 0x0
ADC2->JDR1: 0x0
ADC2->JDR2: 0x0
ADC2->JDR3: 0x0
ADC2->JDR4: 0x0
DMA2_Channel1->CCR: 0x2580
DMA2_Channel1->CPAR: 0x50000140
DMA2_Channel1->CMAR: 0x200003b4
ADC12->CSR: 1
ADC12->CCR: c30000
ADC12->CDR: fc20000
ADC2->DR: fc2
As you can see in the code above, there is configured a limited transfer.
So I think, when DMA reached his limit he stopps transfer, but adc2
continous conversion, until ovr-Flag is set. But I think, I'm clearing
OVR-Flag before every start.
Thanks for your help.
2024-03-04 02:20 AM
Hello @jhoerd,
Try to configure your DMA in circular mode (DMACFG bit is equal to 1 in ADC_CFGR). In this mode, the ADC generates a DMA transfer request each time a new conversion data is available in the data register, even if the DMA has reached the last DMA transfer. This allows configuring the DMA in circular mode to handle a continuous analog input data stream.
FYI : If an overrun occurs (OVR = 1) because the DMA could not serve the DMA transfer request in time, the ADC stops generating DMA requests and the data corresponding to the new conversion is not transferred by the DMA. Which means that all the data transferred to the RAM can be considered as valid.
Best Regards,
Pierre
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-03-04 02:54 AM
Hello Pierre,
I changed Flag to DMACFG to 1 (means Circular Mode) but this doesn't show any effect.
First call: with CMACFG=1:
ADC2->ISR: 0x0
ADC2->IER: 0x0
ADC2->CR: 0x10000001
ADC2->CFGR: 0x80003003
ADC2->CFGR2: 0x0
ADC2->SQR1: 0xc0
ADC2->SQR2: 0x0
ADC2->SQR3: 0x0
ADC2->SQR4: 0x0
ADC2->SMPR1: 0x200
ADC2->SMPR2: 0x0
ADC2->JSQR: 0x0
ADC2->JDR1: 0x0
ADC2->JDR2: 0x0
ADC2->JDR3: 0x0
ADC2->JDR4: 0x0
DMA2_Channel1->CCR: 0x2580
DMA2_Channel1->CPAR: 0x50000140
DMA2_Channel1->CMAR: 0x200003b4
ADC12->CSR: 1
ADC12->CCR: c30000
ADC12->CDR: 0
ADC2->DR: 0
Item0: 4034
Item1: 4034
Item2: 4035
Item3: 4035
I receive first Item0 mit correct value from adc/dma.
At 2nd Call, also with DMACFG=1
ADC2->ISR: 0x0
ADC2->IER: 0x0
ADC2->CR: 0x10000001
ADC2->CFGR: 0x80003003
ADC2->CFGR2: 0x0
ADC2->SQR1: 0xc0
ADC2->SQR2: 0x0
ADC2->SQR3: 0x0
ADC2->SQR4: 0x0
ADC2->SMPR1: 0x200
ADC2->SMPR2: 0x0
ADC2->JSQR: 0x0
ADC2->JDR1: 0x0
ADC2->JDR2: 0x0
ADC2->JDR3: 0x0
ADC2->JDR4: 0x0
DMA2_Channel1->CCR: 0x2580
DMA2_Channel1->CPAR: 0x50000140
DMA2_Channel1->CMAR: 0x200003b4
ADC12->CSR: 1
ADC12->CCR: c30000
ADC12->CDR: fc30000
ADC2->DR: fc3
Item0: 0
Item1: 4035
Item2: 4035
My first received value stays at zero.
(Again: if I disable/enalbe ADC2 between measurements,
the first value will be valid at all calls:)
//firstCall:
ADC12->CDR: 0
ADC2->DR: 0
Item0: 4033
Item1: 4035
Item2: 4035
//2nd and following calls:
ADC2->DR: fc3
Timespan: 478
Item0: 4035
Item1: 4035
Item2: 4035
Have you any other idea?
Best Regards
johann
2024-03-05 02:37 AM - edited 2024-03-05 04:13 AM
Hello @jhoerd,
In circular mode, the result array is normally updated indefinitely. Please use this line to use the circular mode :
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
If I resume :
I advise you to download the package STM32Cube_FW_G4 by clicking here and to have a look to the project under the root "Projects\NUCLEO-G474RE\Examples_LL\ADC\ADC_GroupsRegularInjected_Init". It's an interesting example on how to use the DMA to perform a continuous data stream. Also, DMA is configured to transfer conversion data in an array.
Can you enable the transfer complete DMA transfer interrupt ?
Best Regards,
Pierre
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-03-06 05:19 AM
2024-03-06 10:17 PM
Hello Pierre,
I have solved the problem with ADC3/4 in dual interleaved mode with dma restart.
I have to change "MultiDmaTransfer" of ADC345 from LL_ADC_MULTI_REG_DMA_LIMIT_RES12_10B
to:
LL_ADC_MULTI_REG_DMA_UNLMT_RES12_10B.
The behaviour of ADC345_COMMON is identical to ADC2 from the
previous example.
Many thanks again for your support
Best regards
Johann