cancel
Showing results for 
Search instead for 
Did you mean: 

ADC with DMA restart

jhoerd
Associate II

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

 

1 ACCEPTED SOLUTION

Accepted Solutions
Pierre_Paris
ST Employee

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 :

  • 1st call : "first Item0 mit correct value from adc/dma."
  • 2nd call : "My first received value stays at zero."
  • 3rd call : What happens ? Is the two first value stays at zero and so on for next calls ? If yes, there is an incremental address somewhere in your code.

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.

View solution in original post

7 REPLIES 7
Pierre_Paris
ST Employee

Hello @jhoerd,

Thank you for your question !

Here some questions/remarks to help your investigation :

  • Disable/Enable is not a normal behavior.
  • Can you please share ADC/DMA handler ?
  • Is an overrun occurs ? (OVR = 1) If 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. The DMA transfer requests are blocked until the software clears the OVR bit.
  • What is the value of DMACFG ? Please, share your registers values.

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.

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, &regInitTypeDef) != 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.

Pierre_Paris
ST Employee

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.

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

 

 

Pierre_Paris
ST Employee

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 :

  • 1st call : "first Item0 mit correct value from adc/dma."
  • 2nd call : "My first received value stays at zero."
  • 3rd call : What happens ? Is the two first value stays at zero and so on for next calls ? If yes, there is an incremental address somewhere in your code.

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.

Hello Pierre,
 
I tried out your advices. and I see the following context:
 
LL_ADC_REG_SetDMATransfer(ADC2, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
 
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_IsActiveFlag_TC1(DMA2))
{
}
/* ---- LL_ADC_REG_StopConversion(ADC2); --*/
LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
LL_DMA_ClearFlag_TC1(DMA2);
 
If not calling LL_StopConversion at the end of conversion sequence,
all works as expected. When calling it at the end of conversion,
the first recorded item at next dma start is zero.
 
As far as good. But my actual goal is to use ADC3, ADC4 in dual interleaved mode.
One dma request, when both finished on measure cycle.
 
It shows the same error as usage of single ADC2.
1st start of measurement:
Item0: 4092
Item1: 4092
Item2: 4093
Item3: 4092
Item4: 4093
Item5: 4092
 
2nd and following starts of measurement:
Item0: 1
Item1: 0
Item2: 4093
Item3: 4092
Item4: 4093
Item5: 4092
(two items zero because of one dma reqeust for concated result of adc3 and adc4)
 
Here the relevant part of my configuration:
//ADC3 and Common:
    commonInitTypeDef.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
    commonInitTypeDef.Multimode = LL_ADC_MULTI_DUAL_REG_INTERL;
    commonInitTypeDef.MultiDMATransfer = LL_ADC_MULTI_REG_DMA_LIMIT_RES12_10B;
    commonInitTypeDef.MultiTwoSamplingDelay = LL_ADC_MULTI_TWOSMP_DELAY_1CYCLE;
    LL_ADC_CommonInit(ADC345_COMMON, &commonInitTypeDef)
 
  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; 
  regInitTypeDef.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
  regInitTypeDef.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
 
//ADC4:
  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;
  regInitTypeDef.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE; 
  regInitTypeDef.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
 
//DMA:
          LL_DMA_ConfigTransfer(DMA1, LL_DMA_CHANNEL_5,
                                LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_VERYHIGH | LL_DMA_MODE_CIRCULAR 
                                | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD
                                | LL_DMA_MDATAALIGN_WORD);
 
          LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_5, LL_DMAMUX_REQ_ADC3);
      LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_5, (uint32_t)&ADC345_COMMON->CDR, 
                       reinterpret_cast<uint32_t>(&ReceiveBuffer[0]),
                               LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_5));
 
//start of adc/dma
        LL_DMA_ClearFlag_TC5(DMA1);
        LL_ADC_ClearFlag_EOC(ADC3);
        LL_ADC_ClearFlag_EOC(ADC4);
        LL_ADC_ClearFlag_EOS(ADC3);
        LL_ADC_ClearFlag_EOS(ADC4);
        LL_ADC_ClearFlag_EOSMP(ADC3);
        LL_ADC_ClearFlag_EOSMP(ADC4);
        LL_ADC_ClearFlag_OVR(ADC3);
        LL_ADC_ClearFlag_OVR(ADC4);
        LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, 100U);
        LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
        LL_ADC_REG_StartConversion(ADC3);
        while(!LL_DMA_IsActiveFlag_TC5(DMA1))
        {
        }
        LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);
        LL_DMA_ClearFlag_TC5(DMA1);
 
I try to use it the same way as adc2 in the above example.
Do you have any idea, what I have to change in the dual interleaved example?
 
Many thanks in advance.
Best regards
Johann

 

 

 

 

 

 

 

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