Skip to main content
Graduate
October 8, 2024
Solved

ADC-DMA transfer complete interrupt firing without ADC triggering

  • October 8, 2024
  • 3 replies
  • 2371 views

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.

    This topic has been closed for replies.
    Best answer by waclawek.jan

    > 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

    3 replies

    Super User
    October 8, 2024

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

    JW

    rakib2023Author
    Graduate
    October 8, 2024

    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".

    Super User
    October 8, 2024

    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.

    rakib2023Author
    Graduate
    October 8, 2024

    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".

    Super User
    October 8, 2024

    > 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

    rakib2023Author
    Graduate
    October 8, 2024

    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.