cancel
Showing results for 
Search instead for 
Did you mean: 

ADC-DMA transfer complete interrupt firing without ADC triggering

rakib2023
Associate III

I have come across a rather weird problem and would like to get some direction.

I am using STM32F756 nucleo board. I set ADC1 to trigger by TIM1 (the complete project uses some other peripherals). TIM1 is set as PWM generation. All these are set using CubeMX in CubeIDE. In ADC setup, continuous DMA request is enabled, continuous conversion disabled. Trigger set to TIM1 update event (also tried with external trigger and shorting TIM1 output to EXTI pin with jumper). ADC1-DMA is set to circular mode. In the code, I start ADC with "HAL_ADC_StartDMA" before starting the timer. To start the timer, I copied "HAL_TIM_StartPWM" and made another method, let's say, "Private_TIM_StartPWM" which is almost identical to the HAL method except it doesn't have the "__HAL_TIM_ENABLE" call at the end. So, when calling this method, TIM1 is set up for PWM generation but the counter remains idle and will be enabled separately later.

Now onto the problem, in the code, before TIM1 can be enabled, the DMA transfer complete interrupt is fired. I have checked that the SR register has only TCIF flag set in the interrupt. However, checking the timer output with oscilloscope, the timer is not outputting any pulse, so, the ADC should not be triggering at all. When I checked the DMA buffer, there is no data there.

I set up a separate minimal project with only ADC1 and TIM1 set the same way to reproduce the problem but in this case, it works fine as expected. So, back in my main project, I debugged with breakpoint in every IRQHandler (only 4) and the DMA transfer complete interrupt is the first one being fired, so, it probably is not a case of another interrupt messing with things. I have repeatedly debugged the interrupt, it is always only TCIF flag, no PWM in timer output, no data in buffer memory.

I am quite stumped as to what is causing this and would like some guidance as to what should I look into. Thank you for your precious time.

1 ACCEPTED SOLUTION

Accepted Solutions

> CR->EN bits are 0. I set these bits to 0 right after calling "HAL_ADC_StartDMA".

Why do you do that?

This is the reason for the DMA TCIF.

waclawekjan_0-1728390374741.png

JW

View solution in original post

7 REPLIES 7

Read out and check/post TIM, ADC and the relevant DMA stream registers content.

JW

Register contents before the interrupt

ADC TIM1 DMA 
SR0x0CR10x80CR0x3551F
CR10x4000000CR20x20ISR0x0
CR20x39000701SR0x1FCR0xA0
HTR0xfffCCMR10x6c  
SQR30x3BDTR0x200A000  
  DMAR0x80  

At the IRQHandler breakpoint, almost none of these changes except ADC's CR2->ADON bit is 0, DMA's CR->HTIE and CR->EN bits are 0. I set these bits to 0 right after calling "HAL_ADC_StartDMA".

I think I have figured out what happened. Apparently, disabling DMA sets the TCIF flag and fires the interrupt. I did not know about it. Now, the problem is that in my project, I need to disable DMA to change the buffer pointer and data size and this process needs to be done fast (2us), so, there is not enough time to stop and start DMA again. And for some unknown reason, if DMA is in normal mode, it doesn't fire the interrupt after the first time even if the TCIE bit is set.

TDK
Guru

If you show more of your code, the issue could likely be spotted. The problem description is a little hard to follow--you're using HAL functions, so which IRQHandler is the breakpoint in?

Showing code and screenshots of register values or variables is going to be better than a text description. Things can be lost in translation.

These are well-tested chips, there is unlikely to be a hardware bug in modules as common as ADC and DMA.

I do not believe that disabling DMA sets TCIF.

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

> CR->EN bits are 0. I set these bits to 0 right after calling "HAL_ADC_StartDMA".

Why do you do that?

This is the reason for the DMA TCIF.

waclawekjan_0-1728390374741.png

JW

I have been disabling the DMA after calling HAL method. This is what has been setting the interrupt. This I just tested. So, it is not a bug, I just didn't know this information. Found it from the below discussion-

https://community.st.com/t5/stm32-mcus-products/restarting-dma-with-new-configuration/td-p/455057

Another interesting thing I found is that if DMA is disabled while within interrupt routine, it doesn't fire any additional interrupt, probably because the TCIF flag is already set.

In my project and tests, I have only added code in the main function as follows-

main in project

 

	Private_TIM_StartPWM (&htim1, TIM_CHANNEL_1);
	Private_TIM_StartPWM (&htim8, TIM_CHANNEL_1);
	Private_SwitchTimer_Start(&htim5);

	//Start peripherals
	Private_Start_Receive(&huart3, (uint8_t*)ReceiveBuffer_p, 2);
	HAL_ADC_Start_DMA(&hadc1, VoltageBuffer_p, 1000);
	HAL_ADC_Start_DMA(&hadc2, &TempBuffer, 1);
	HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_3, TimestampBuffer_p, 1000);
	HAL_DAC_Start(&hdac, DAC_CHANNEL_2);
	hdac.Instance->CR &= ~DAC_CR_EN2;

	// Disable DMA and half transfer interrupts
	hadc1.Instance->CR2 &= ~ADC_CR2_ADON;
	hadc2.Instance->CR2 &= ~ADC_CR2_ADON;
	hdma_adc1.Instance->CR &= ~(DMA_SxCR_EN | DMA_SxCR_HTIE);
	hdma_adc2.Instance->CR &= ~(DMA_SxCR_EN | DMA_SxCR_HTIE);
	htim2.Instance->DIER &= ~TIM_DIER_CC3DE;
	htim2.Instance->CR1 &= ~TIM_CR1_CEN;
	htim2.Instance->CNT = 0;
	hdma_tim2_up_ch3.Instance->CR &= ~(DMA_SxCR_EN | DMA_SxCR_HTIE | DMA_SxCR_TCIE);
	while(1)
	{
	}

 

Private_TIM_StartPWM method

 

HAL_StatusTypeDef Private_TriggerTimer_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
	/* Set the TIM channel state */
	TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);

	/* Enable the Capture compare channel */
	TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

	if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
	{
		/* Enable the main output */
		__HAL_TIM_MOE_ENABLE(htim);
	}

	htim->Instance->SR = 0;
	return HAL_OK;
}

 

 

For a minimal test, I set only ADC1 and TIM1 from CubeMX and added the following code in main

 

  Private_TIM_StartPWM(&htim1, TIM_CHANNEL_1);
  htim1.Instance->PSC = 5400-1;
  htim1.Instance->ARR = 10000-1;
  htim1.Instance->CCR1 = 5000;
  HAL_ADC_Start_DMA(&hadc1, buff1, 40);
  hdma_adc1.Instance->CR &= ~DMA_SxCR_HTIE;
  hdma_adc1.Instance->CR &= ~DMA_SxCR_EN; //Interrupt fires here
  hdma_adc1.Instance->CR |=  DMA_SxCR_EN;
  htim1.Instance->CR1 |= TIM_CR1_CEN;

 

 

Edit: Breakpoint in "DMA2_Stream0_IRQHandler".

In my project, I need to sample some analog signal at different sampling rate and store the data in different memory location. The signal is in one channel only. So, I need to get one set of data; tweak timer frequency, change DMA buffer location and size and re-enable DMA. But these operations have to be done in a very short time, 2~3 us. So, I was planning to use HAL methods to start of all the peripherals and DMA, then disable everything and only enable when it starts sampling. Now, I am thinking of other options.