2021-04-12 08:26 AM
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)));
Solved! Go to Solution.
2021-04-13 08:54 AM
> 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
2021-04-12 03:30 PM
> 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
2021-04-12 11:20 PM
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;
2021-04-13 12:04 AM
>> 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
2021-04-13 08:04 AM
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:
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;
}
}
2021-04-13 08:54 AM
> 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
2021-04-13 09:41 AM
This is the cause. As expected, the problem was on my side.
Thanks again for help.