cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Timer Output Compare Interrupt all interrupt flags set at once

BKocs.1
Associate II

Hello!

I'm trying to set up the output compare channels of TIM3 of an STM32F103RB MCU. For now I only enabled channel 1, but later I'll need CH2 and CH3 as well. When CH1 triggers, the TIM3->SR register gets the value of 0x0000001f, i have looked at it in the dedugger. The other channels are not even set up, so their interrupts should be disabled right?

So it enters the TIM3_IRQHandler function and it executes the ISR of all the channels. Not only that, but it doesn't even reset the CCxIF bits when I tell it to. It just goes over the line and does nothing, according to the debugger, the TIM3->SR value remains unchanged.

Also, the TIM3->SR gets the value 0x0000001f when the counter enable CEN bit is set, and when I modify the prescaler or enable the CC1IE bit.

Have I misunderstood something about interrupt operation? What could be the problem? Why do all interrupt channel flags set at once? Could you provide some example as to how to properly setup output compare interrupts? Any help is much appreciated!

My timer setup function:

void TIM3_Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; //Enable timer 3 clock
 
    TIM3->PSC = 65536-1; // Set timer 3 prescaler
    TIM3->ARR = 7200-1; //Set timer 3 auto reload value
    TIM3->CR1 &= ~(3 << TIM_CR1_CMS_Pos); //selecting edge aligned PWM
    TIM3->CR1 |= TIM_CR1_ARPE; //Enable auto-reload preload
 
    TIM3->CCER &= ~TIM_CCER_CC1E;//Capture compare 1 disable
    TIM3->CCER &= ~TIM_CCER_CC1P;//Capture compare polarity active high
    TIM3->CCMR1 &=  ~(3 << TIM_CCMR1_CC1S_Pos);//CC1 channel is output
    TIM3->CCMR1 &= ~(7 << TIM_CCMR1_OC1M_Pos);//OC no output selected
    TIM3->CCER |= TIM_CCER_CC1E;//Capture compare 1 enable
}

The TIM3_IRQHandler function:

void TIM3_IRQHandler(void)
{
    //Determine which channel interrupted with the if statements
    if (TIM3->SR & (1 << 1))    //Channel 1 ISR
    {
        TIM3->SR &= ~(1 << 1); //Clear the interrupt flag, doesn't do anything...
        flag = 1;
    }
    if (TIM3->SR & (1 << 2))    //Channel 2 ISR
    {
        TIM3->SR &= ~(1 << 2); //Clear the interrupt flag, doesn't do anything...
        //Do stuff...
    }
    if (TIM3->SR & (1 << 3))    //Channel 3 ISR
    {
        TIM3->SR &= ~(1 << 3); //Clear the interrupt flag, doesn't do anything...
        //Do stuff...
    }
}

Main function and global variable flag:

volatile uint8_t flag = 0;
int main(void)
{
    SystemClockConfig();
    TIM3_Init();
 
    TIM_Start(TIM3);
    uint8_t counter = 0;
 
    TIM3->PSC = 2;
    TIM3->CCR1 = 5000;
    TIM3->CNT = 0;
    TIM3->DIER |= TIM_DIER_CC1IE;//Enable CH1 output compare interrupt bit
 
    NVIC_SetPriority(TIM3_IRQn, 2); //Set timer 3 ISR priority
    NVIC_EnableIRQ(TIM3_IRQn); //Enable timer 3 ISR
 
    while(counter < 10)
    {
        if (flag == 1)
        {
            counter++;
            flag = 0;
        }
    }
}

The TIM3 Start function:

void TIM_Start(TIM_TypeDef* TIM)
{
    TIM->EGR |= TIM_EGR_UG;//Generate update
    TIM->CR1 |= TIM_CR1_CEN; //Enable the counter
    TIM->SR = 0x00;
}

System clock configuration fuction:

void SystemClock_Config(void)
{
    RCC->CR |= RCC_CR_HSEON; //Enable the high speed external crystal
    while(!(RCC->CR & RCC_CR_HSERDY)); //Wait until clock stabilized
 
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
 
    FLASH->ACR |= FLASH_ACR_PRFTBE //Set up the flash memory (enable prefetch buffer, 
                            | FLASH_ACR_LATENCY_2; //set latency to two wait states)
 
    RCC->CFGR |= RCC_CFGR_PLLMULL9 //Set Phase locked loop multiplication
              | (1 << RCC_CFGR_PLLSRC_Pos) //Set PLL source to HSE
              | RCC_CFGR_PLLXTPRE_HSE //Set PLL HSE prescaler
              | RCC_CFGR_HPRE_DIV1 //Set AHB prescaler 
              | RCC_CFGR_PPRE1_DIV2 //Set APB1 peripheral prescaler 
              | RCC_CFGR_PPRE2_DIV1 //Set APB1 peripheral prescaler 
              | RCC_CFGR_ADCPRE_DIV6; //Set ADC prescaler
 
    RCC->CR |= RCC_CR_PLLON; //Turn on PLL
    while(!(RCC->CR & RCC_CR_PLLRDY)); //Wait until PLL locks
 
    RCC->CFGR |= RCC_CFGR_SW_PLL; //Set PLL as system clock source
}

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

> The other channels are not even set up, so their interrupts should be disabled right?

Don't confuse "not set up" with "set up with the default configuration" which means they're in output mode and CCRx is 0. If you read the description of these bits, it explains why they are set:

1: The content of the counter TIMx_CNT matches the content of the TIMx_CCR1 register.

When the contents of TIMx_CCR1 are greater than the contents of TIMx_ARR, the CC1IF

bit goes high on the counter overflow (in upcounting and up/down-counting modes) or

underflow (in downcounting mode)

One way to address this with a generic IRQ handler is to check both the flag and the interrupt enable flag and only act if both are set. This is what HAL does.

https://github.com/STMicroelectronics/STM32CubeF1/blob/c750eab6990cac35ab05020793b0221ecc1a8ce5/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c#L3751

> TIM3->SR &= ~(1 << 1); //Clear the interrupt flag, doesn't do anything...

If you want to clear the flag, write a 0 to the corresponding register. Don't read-modify-write which can have other effects and is slower.

TIM3->SR = ~TIM_SR_CC1IF;

> Not only that, but it doesn't even reset the CCxIF bits when I tell it to. It just goes over the line and does nothing, according to the debugger, the TIM3->SR value remains unchanged.

The timer continues to run while debugging. Likely these bits get set again before the debugger reads them. You can change this behavior if desired.

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

View solution in original post

4 REPLIES 4
TDK
Guru

> The other channels are not even set up, so their interrupts should be disabled right?

Don't confuse "not set up" with "set up with the default configuration" which means they're in output mode and CCRx is 0. If you read the description of these bits, it explains why they are set:

1: The content of the counter TIMx_CNT matches the content of the TIMx_CCR1 register.

When the contents of TIMx_CCR1 are greater than the contents of TIMx_ARR, the CC1IF

bit goes high on the counter overflow (in upcounting and up/down-counting modes) or

underflow (in downcounting mode)

One way to address this with a generic IRQ handler is to check both the flag and the interrupt enable flag and only act if both are set. This is what HAL does.

https://github.com/STMicroelectronics/STM32CubeF1/blob/c750eab6990cac35ab05020793b0221ecc1a8ce5/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c#L3751

> TIM3->SR &= ~(1 << 1); //Clear the interrupt flag, doesn't do anything...

If you want to clear the flag, write a 0 to the corresponding register. Don't read-modify-write which can have other effects and is slower.

TIM3->SR = ~TIM_SR_CC1IF;

> Not only that, but it doesn't even reset the CCxIF bits when I tell it to. It just goes over the line and does nothing, according to the debugger, the TIM3->SR value remains unchanged.

The timer continues to run while debugging. Likely these bits get set again before the debugger reads them. You can change this behavior if desired.

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

Thank you for the answer.

>One way to address this with a generic IRQ handler is to check both the flag and the interrupt enable flag and only act if both are set. This is what HAL does.

But if I want to enable CH2 of output compare as well, then its ISR will still be incorrectly executed if the TIM3->SR register sets all interrupt channel flags at once, will it not? Is there no way to make it only set the channel interrupt flag that actually triggers?

Edit:

>Don't confuse "not set up" with "set up with the default configuration" which means they're in output mode and CCRx is 0. If you read the description of these bits, it explains why they are set:

But if the reset value of CCRx is 0, then it will never be larger than TIMx_ARR, so then why does it still trigger?

Edit 2:

Wait, so are you saying that there is no way to prevent the interrupt flags from triggering? Because if the CCRx always has a value then it will either intersect the CNT at some point or just turns on permanently because it is higher than TIMx_ARR?

Independent of whether I enabled the interrupt enable bit in TIM3->DIER register?

TDK
Guru

> But if I want to enable CH2 of output compare as well, then its ISR will still be incorrectly executed if the TIM3->SR register sets all interrupt channel flags at once, will it not?

Only if the corresponding interrupt enable flag is set. If you don't care about that interrupt, don't set it and don't check for it in the IRQ handler.

> But if the reset value of CCRx is 0, then it will never be larger than TIMx_ARR, so then why does it still trigger?

The flag gets set when CNT matches CCRx, which is 0. Per the description above:

1: The content of the counter TIMx_CNT matches the content of the TIMx_CCR1 register.

> Wait, so are you saying that there is no way to prevent the interrupt flags from triggering?

Flags getting set that you don't care about and don't have interrupt enabled for should not be causing you distress, but if you really want them to not be set, you could set them to input mode assuming the line is idle.

But again, flags getting set doesn't mean the ISR is triggered. That only happens if the corresponding interrupt enable bit is also set.

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

I understand now. Thank you very much, you explained it very well! :)