cancel
Showing results for 
Search instead for 
Did you mean: 

Is there a HAL_ADC_Stop_DMA() alternative to be able to start second transfer using HAL_ADC_Start_DMA() for single conversion?

PÖste.1
Associate II

Using a STM32F413 and ADC HAL drivers.

After a successfully completed single conversion initiated by HAL_ADC_Start_DMA() one apparently needs to call HAL_ADC_Stop_DMA() in order for next HAL_ADC_Start_DMA() to actually start a new transfer. There are no error condition in this use-case as .DMAContinuousRequests is disabled (DDS=0).

I would like to not have to call Stop_DMA as I may have an ongoing Injected conversion which then would be canceled as well. Based on RM0430 i think the real issue is that in this case the ADC_CR2_DMA bit must be cleared before set again in order for new regular channel conversions will be accepted (13.8.1).

There is however no HAL API function to handle this so I'm confused. But would this be a correct and working solution to this issue? If so perhaps LL function LL_ADC_REG_SetDMATransfer(LL_ADC_REG_DMA_TRANSFER_NONE) can be used?

3 REPLIES 3
berendi
Principal

The HAL API covers less than 10% of the functionality of the STM32 hardware. HAL is a collection of examples for getting started, but you are already beyond this point, having found its limitations. You are trying to do something that HAL was not designed to do, so don't try to solve it with HAL, it won't work.

The authorative documentation is the reference manual. If it states that you have to clear ADC_CR2_DMA to achieve what you want, then write

ADC->CR2 &= ~ADC_CR2_DMA;

and it will be done.

Moreover, it will be obvious to everyone maintaining the code later what it does, because the function of that bit can be looked up in the reference manual in seconds.

HAL functions don't have such detailed documentation, just a few example projects where they are used in isolation. Lacking proper documentation, you can either use them exactly as they are used in the examples, or you are on your own.

Using a LL function to clear a bit in a register has obviously the same effect as using the line above, but it adds a lot of unnecessary complexity if someone has to understand and maintain your code later (including you a few months after writing). To understand what a LL function does, one has to look up its source, then follow the macros therein to get to the CMSIS register and bit definitions, to finally be able to look it up in the reference manual. A lot of additional work that steers ones attention away from the actual problem.

GCA.1
Associate

Hi POste.1

I have exactly this problem with STM32F205.

The ADC conversion and DMA transfer works correctly only once. Further calls of 'HAL_ADC_Start_DMA()' causes 'HAL_ADC_ConvCpltCallback()' not being executed.

As you explained, there are no errors conditions, however, the 'hadc->Status' has set the 'HAL_ADC_STATE_REG_BUSY' bit and keeps hanged in that way for ever.

/* States of ADC group regular */
#define HAL_ADC_STATE_REG_BUSY          0x00000100U    /*!< A conversion on group regular is ongoing or can occur (either by continuous mode,
                                                                       external trigger, low power auto power-on (if feature available), multimode ADC master control (if feature available)) */

Calling 'HAL_ADC_Stop_DMA' or executing:

    ADC->CR2 &= ~ADC_CR2_DMA;

make further calls of 'HAL_ADC_Start_DMA()' to work again.

BTW. This is my ADC configuration:

static void MX_ADC1_Init(void)
{
 
    /* USER CODE BEGIN ADC1_Init 0 */
 
    /* USER CODE END ADC1_Init 0 */
 
    ADC_ChannelConfTypeDef sConfig = {0};
 
    /* USER CODE BEGIN ADC1_Init 1 */
 
    /* USER CODE END ADC1_Init 1 */
    /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ENABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 8;
    hadc1.Init.DMAContinuousRequests = DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
    if (HAL_ADC_Init(&hadc1) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_0;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 2;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_4;
    sConfig.Rank = 3;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_5;
    sConfig.Rank = 4;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_6;
    sConfig.Rank = 5;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_8;
    sConfig.Rank = 6;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_14;
    sConfig.Rank = 7;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
    sConfig.Channel = ADC_CHANNEL_9;
    sConfig.Rank = 8;
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /* USER CODE BEGIN ADC1_Init 2 */
 
    /* USER CODE END ADC1_Init 2 */
}

PÖste.1
Associate II

So same observation as me on F413. Would be nice if HAL IRQ handler could clear DMA flag for this use-case. I added clearing of DMA flag in the ConvCpltCallback() function and then it workt just fine for me. Now however I moved on and use a timer to trigger conversions instead of using software. I then also changed DMAContinuousRequests to ENABLE. This do not need DMA flag to be reset. However I now also use continuous circular DMA, so conversions never stops (now I must not clear DMA in the IRQ as DMA will stop). What could be an alternative for you is to try DMAContinuousRequests=ENABLE - but make sure your DMA transfer size is the correct size as overrun would be triggered (when continious is DISABLED the conversion ends with no error whenever DMA transfer is done), no need for continious circular DMA I think as you start new every time via _Start_DMA(). In any case HAL could very well support the use-case we both tried. As berendi pointed out HAL is not always best suited to handle the needs for all use-cases, but at least helps getting started faster.