Skip to main content
AMak.2
Associate
April 12, 2021
Solved

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

  • April 12, 2021
  • 6 replies
  • 2128 views

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)));

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

> 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

6 replies

waclawek.jan
Super User
April 12, 2021

> 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
AMak.2Author
Associate
April 13, 2021
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;

waclawek.jan
Super User
April 13, 2021

>> 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
AMak.2Author
Associate
April 13, 2021

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;
 }
}

waclawek.jan
waclawek.janBest answer
Super User
April 13, 2021

> 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

AMak.2
AMak.2Author
Associate
April 13, 2021

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

Thanks again for help.