cancel
Showing results for 
Search instead for 
Did you mean: 

Can DMA CR TCIE bit be changed, when DMA is enabled?

AMak.2
Associate II

Hello.

I'm testing ADC with DMA on STM32G071. I wanted to have interrupt on transfer complete, but I had trouble clearing TCIF flag in ICFR register. Even if I cleared the bit, ISR was entered again immediately. I don't have circular mode set neither on ADC nor on DMA.

I checked, how it is done in HAL drivers. First, TCIE is cleared and only then TCIF is cleared, although Reference says, that TCIE must not be written, when channel is enabled.

Can the bit be cleared, or is there some better way how to stop ISR to be called immediately again and again?

STM32G071 reference, section 10.6.3

"Bit 3 TEIE: transfer error interrupt enable

0: disabled

1: enabled

Note: this bit is set and cleared by software.

It must not be written when the channel is enabled (EN = 1).

It is not read-only when the channel is enabled (EN = 1)."

stm32g0xx_hal_dma.c

else if ((0U != (flag_it & (DMA_FLAG_TC1 << (hdma->ChannelIndex & 0x1cU)))) && (0U != (source_it & DMA_IT_TC)))
  {
    if ((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U)
    {
      /* Disable the transfer complete and error interrupt */
      __HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);
 
      /* Change the DMA state */
      hdma->State = HAL_DMA_STATE_READY;
    }
    /* Clear the transfer complete flag */
    __HAL_DMA_CLEAR_FLAG(hdma, (DMA_FLAG_TC1 << (hdma->ChannelIndex & 0x1cU)));

1 ACCEPTED SOLUTION

Accepted Solutions

> CLEAR_BIT(DMA1->IFCR, DMA_IFCR_CGIF2);

IFCR bits work by writing one into them, i.e. don't use this macro (which is a RMW) but write plainly 1 into CGIF2. Read the DMA chapter.

JW

View solution in original post

6 REPLIES 6

> I had trouble clearing TCIF flag in ICFR register.

> Even if I cleared the bit,

How exactly, and in which part of ISR?

> ISR was entered again immediately

Because of TCIF?

JW

AMak.2
Associate II
void DMA1_Channel2_3_IRQHandler(void) {
  if (READ_BIT(DMA1->ISR, DMA_ISR_TCIF2)) {
    CLEAR_BIT(ADC_DMA->CCR, DMA_CCR_TCIE);   // if I don't clear this bit, ISR is called again and again
    CLEAR_BIT(DMA1->IFCR, DMA_IFCR_CGIF2);
    adcDone = 1;
  }
}

This is my IRQHandler.

I want to use ADC single conversion mode with DMA triggered by timer to take samples until ADC buffer is full. Then I want to do some processing, so ADC should be stopped and DMA should not transfer any data to buffer until ADC is started again.

Here is my ADC setup:

  ADC1->CFGR1 &= ~(ADC_CFGR1_CONT | ADC_CFGR1_DISCEN); // single conversion mode
  ADC1->CFGR1 |= (1 << ADC_CFGR1_EXTEN_Pos) | (5 << ADC_CFGR1_EXTSEL_Pos)m| ADC_CFGR1_DMAEN; // timer trigger
  ADC1->CHSELR |= ADC_CHSELR_CHSEL1;
 
  CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR_DIR); // peripheal to memory
  DMA1_Channel2->CCR = DMA_CCR_CIRC | DMA_CCR_MINC | (0x1 << DMA_CCR_PSIZE_Pos) | (0x1 << DMA_CCR_MSIZE_Pos) | DMA_CCR_PL_1; // memory increment, half word
  DMA1_Channel2->CNDTR = ADC_BUFFER_SIZE;
  DMA1_Channel2->CPAR = (uint32_t) &ADC1->DR;
  DMA1_Channel2->CMAR = (uint32_t) adcBuffer;
  NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); // enable interrupt
 
  DMAMUX1_Channel1->CCR = (0x5 << DMAMUX_CxCR_DMAREQ_ID_Pos); // connect adc to DMA
 
  DMA1_Channel2->CCR |= DMA_CCR_EN; // enable DMA
 
  TIM6->CR1 = TIM_CR1_ARPE;
  TIM6->CR2 = TIM_CR2_MMS_1;
  TIM6->PSC = 0;
  TIM6->ARR = SYSTEM_CLOCK / settings.adcSamplingFreq;
  TIM6->CR1 |= TIM_CR1_CEN;

I start conversion with this code:

  ADC1->CR |= ADC_CR_ADSTART;
  DMA1_Channel2->CCR |= DMA_CCR_TCIE;

>> I don't have circular mode set neither on ADC nor on DMA.

> DMA1_Channel2->CCR = DMA_CCR_CIRC | DMA_CCR_MINC | (0x1 << DMA_CCR_PSIZE_Pos) | (0x1 << DMA_CCR_MSIZE_Pos) | DMA_CCR_PL_1; // memory increment, half word

ADC is triggered externally from the timer, so you would need to stop the timer to stop the ADC to keep converting.

Also

> CLEAR_BIT(DMA1_Channel2->CCR, DMA_CCR_DIR); // peripheal to memory

is redundant as you subsequently directly load DMAx_CCR; but this is just a minor thing not causing any problem.

JW

AMak.2
Associate II

Jan, Thanks for your answers.

>> I don't have circular mode set neither on ADC nor on DMA.

Sorry, my bad. Circular mode on DMA shoudn't cause this, should it?

If I understand ADC DMA correctly, it works like this:

  1. Timer triggers ADC conversion
  2. ADC generates DMA request on conversion completion
  3. DMA transfers data register to memory and increment address
  4. When DMA reaches the end of provided memory, it generates TC interrupt.

I tried to stop timer, adc, both timer and adc, but interrupt still wasn't cleared.

void DMA1_Channel2_3_IRQHandler(void) {
  if (READ_BIT(DMA1->ISR, DMA_ISR_TCIF2)) {
    //CLEAR_BIT(ADC_DMA->CCR, DMA_CCR_TCIE);
    CLEAR_BIT(TIM6->CR1, TIM_CR1_CEN);
    CLEAR_BIT(ADC1->CR, ADC_CR_ADSTP);
 
    CLEAR_BIT(DMA1->IFCR, DMA_IFCR_CGIF2);
    adcDone = 1;
  }
}

> CLEAR_BIT(DMA1->IFCR, DMA_IFCR_CGIF2);

IFCR bits work by writing one into them, i.e. don't use this macro (which is a RMW) but write plainly 1 into CGIF2. Read the DMA chapter.

JW

This is the cause. As expected, the problem was on my side.

Thanks again for help.