cancel
Showing results for 
Search instead for 
Did you mean: 

ADC with DMA and external Trigger on a STM32G431

user 143
Associate III

Hi guys,

I want to use the ADC with DMA and external trigger. Sounds easy, and its working, but not to 100%.

The ADC should measure 2 channels, Channel 3 and Vrefint. Every measurement should be repeated 50 times, that I get 100 values. The external trigger is on PA11, internal PullUp and a falling edge should be detected. I'm using an mixture of HAL functions and direct register access.

Here is the code for the intialization of the ADC:

HAL_StatusTypeDef adc_custom_init(void)
{
	/* configure ADC1 */
 
	hadc1.Instance = ADC1;
	hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
	hadc1.Init.Resolution = ADC_RESOLUTION_12B;
	hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
	hadc1.Init.GainCompensation = 0;
	hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
	hadc1.Init.LowPowerAutoWait = DISABLE;
	hadc1.Init.ContinuousConvMode = ENABLE;
	hadc1.Init.DiscontinuousConvMode = DISABLE;
	hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_EXT_IT11;
	hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_FALLING;
	hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
	hadc1.Init.OversamplingMode = ENABLE;
	hadc1.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_256;
	hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_8;
	hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
	hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
	hadc1.Init.DMAContinuousRequests = DISABLE;
	hadc1.Init.NbrOfConversion = 2;
	hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
 
	if(HAL_ADC_Init(&hadc1) != HAL_OK)
		return HAL_ERROR;
 
	/* Configure the ADC multi-mode */
	multimode.Mode = ADC_MODE_INDEPENDENT;
 
	if(HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
		return HAL_ERROR;
 
 
	/* Configure Regular Channel */
	sConfig.Channel = ADC_CHANNEL_4;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
	sConfig.SingleDiff = ADC_SINGLE_ENDED;
	sConfig.OffsetNumber = ADC_OFFSET_NONE;
	sConfig.Offset = 0;
 
	/* Rank 1 = Channel 4, Rank 2 = Vrefint */
	if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
		return HAL_ERROR;
 
	/* vrefint */
	sConfig.Channel = ADC_CHANNEL_VREFINT;
	sConfig.Rank = ADC_REGULAR_RANK_2;
 
	if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
		return HAL_ERROR;
 
	return HAL_OK;
}

To start the ADC with DMA I use the following function:

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_results, 100);

After firing the external trigger the Transfer Complete Interrupt of the DNA is executed. So far so good. I also get 100 values (value[0] = channel 4, value[1] = vrefint, value[2] = channel 4, ...).

But when I fire the external trigger again, nothing is happening.

What I already tried?

After the Transfer Complete interrupt is set, I clear the pending interrupt and set a flag. I'm checking this flag in the main-loop.

In the main-loop (when the flag is set) I'm clearing the pending exti flag:

EXTI->PR1 |= EXTI_PR1_PIF11;	// clear exti flag

(For testing I enabled the exti interrupt, and the interrupt is always executed, when I fire the trigger).

Which Bits and Flags in the ADC and DMA registers do I have to set, that I can fire both again?

I compared the ADC and DMA registers, once before I fired the trigger, and one after one successful run.

In the ADC registers are only two register different: ISR and CR (and of course DR).

In the ISR register the EOS bit and the EOSMP bit are set.

In the CR register only the ADSTART bit has changed to 0.

I tried to set the ADSTART bit. The bit is set, but its not working. When I try to clear the EOS and EOSMP bits (by writing 1 to it) the ADRDY bit in the ISR register is cleared (I dont know why?) and its not working. Why is the ADRDY bit cleared?

In the DMA registers only the CNDTR3 (number of data items to transfer) is set to 0, other registers did not change (yeah, the TC interrupt flag is set, but I clear it in the interrupt routine).

So I set the CNDTR3 to 100. But nothing is happening.

My complete "restart" routine:

if(restart_adc == 1)
{
	restart_adc = 0;
 
	EXTI->PR1 |= EXTI_PR1_PIF11;	// clear exti flag
 
	ADC1->CR |= ADC_CR_ADSTART;
 
	DMA1_Channel3->CNDTR = 100;
}

Does anyone here have an idea where my mistake is?

Thank you.

6 REPLIES 6
TDK
Guru

What happens if you restart the transfer with HAL_ADC_Start_DMA instead of what you're doing?

If you feel a post has answered your question, please click "Accept as Solution".
user 143
Associate III

The function returns with HAL_BUSY, because the ADC_CR_ADSTART bit is set.

When I call first HAL_ADC_Stop_DMA() and after that again HAL_ADC_Start_DMA() it work fine. But it takes so long, that can't be the solution.

BrianG
Associate II

I'm having the exact same issue using the STM32L071 with 6 inputs. I'm using DMA and a timer as a trigger. It works the first time, but never fires again. The sample values are also correct values.

Did you find the cause or solution?

BrianG
Associate II

I may have just discovered the issue. However, I'm still testing.

hadc.Init.DMAContinuousRequests = ENABLE;

BrianG
Associate II

And... it appears that DMA request mode must be circular.

user 143
Associate III

Yes, I found a way, now it works.

I changed my ADC configuration:

HAL_StatusTypeDef adc_custom_init(void)
{
	/* configure ADC1 */
 
	hadc1.Instance = ADC1;
	hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
	hadc1.Init.Resolution = ADC_RESOLUTION_12B;
	hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
	hadc1.Init.GainCompensation = 0;
	hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
	hadc1.Init.LowPowerAutoWait = DISABLE;
	hadc1.Init.ContinuousConvMode = ENABLE;
	hadc1.Init.DiscontinuousConvMode = DISABLE;
	hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_EXT_IT11;
	hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_FALLING;
	hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
	hadc1.Init.OversamplingMode = DISABLE;
	hadc1.Init.DMAContinuousRequests = DISABLE;
	hadc1.Init.NbrOfConversion = 2;
	hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
 
	if(HAL_ADC_Init(&hadc1) != HAL_OK)
		return HAL_ERROR;
 
	/* Configure the ADC multi-mode */
	multimode.Mode = ADC_MODE_INDEPENDENT;
 
	if(HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
		return HAL_ERROR;
 
 
	/* Configure Regular Channel */
	sConfig.Channel = ADC_CHANNEL_4;
	sConfig.Rank = ADC_REGULAR_RANK_1;
	sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
	sConfig.SingleDiff = ADC_SINGLE_ENDED;
	sConfig.OffsetNumber = ADC_OFFSET_NONE;
	sConfig.Offset = 0;
 
	/* Rank 1 = Channel 4, Rank 2 = Vrefint */
	if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
		return HAL_ERROR;
 
	/* vrefint */
	sConfig.Channel = ADC_CHANNEL_VREFINT;
	sConfig.Rank = ADC_REGULAR_RANK_2;
 
	if(HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
		return HAL_ERROR;
 
	return HAL_OK;
}

Then I changed the DMA mode into Normal:

hdma_adc1.Init.Mode = DMA_NORMAL;

For the first start I use the HAL function:

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_results, 100);

In the Transfer Complete Callback I set a flag, which I check in the main-loop:

if(restart_adc == 1)
{
	restart_adc = 0;
 
	EXTI->PR1 |= EXTI_PR1_PIF11;	// clear exti flag
 
	/* reset DMA counting register */
	DMA1_Channel3->CCR &= ~DMA_CCR_EN;	// disable DMA
	DMA1_Channel3->CNDTR = 100;			// write the amount of data to read
	DMA1_Channel3->CCR |= DMA_CCR_EN;	// enable DMA
 
	/* reenable the ADC */
	ADC1->CR |= ADC_CR_ADSTART;
}

And now is everything working as I want it.

When you only want to read the 6 channels once, then you have to change the following line:

hadc1.Init.ContinuousConvMode = DISABLE;

I hope its working for you to.