cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F411: APB1 Timer to fast when using APB1 divisor > 2

mfe
Visitor

Hello,

i have a STM32F114 (Black Pill V3.0) and a strange timer problem. We all know that when setting the APB1 clock divisior is > 1, the APB1 timers gets twice as fast (2x, TIMPRE bit is not set). However, my timers are 4x when the divisor is > 2 (RCC_CFGR_PPRE1_DIV4, RCC_CFGR_PPRE1_DIV8, ...). It doesn't matter if i use HSI, HSE or PLL as sys clock source. My timer interrupt is twice as fast compared to a APB1 divisor of 1 or 2:

APB1 divisor = 1: timer interrupt each 0.1 ms (10 KHz)

APB1 divisor = 2: timer interrupt each 0.1 ms

APB1 divisor = 4: timer interrupt each 0.05 ms (20 KHz)

...

I checked different frequencies (16 MHz, 48 MHz and 100 MHz sysclock). AHB and APB2 divisors are 1. I used two STM32F114 devices for testing. I double checked everything.

Unbenannt.png

 

Code:

// reset state assumed, HSI is 16 MHz
unsigned long cfgr = RCC->CFGR;
cfgr = (cfgr & ~RCC_CFGR_HPRE) | RCC_CFGR_HPRE_DIV1; // AHB clock = 16 MHz
cfgr = (cfgr & ~RCC_CFGR_PPRE1) | RCC_CFGR_PPRE1_DIV8; // APB1 clock  = 16 Mhz / 8 = 2 Mhz
RCC->CFGR = cfgr;

...

RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->CR1 &= ~TIM_CR1_CEN;
RCC->APB1RSTR |= RCC_APB1RSTR_TIM2RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_TIM2RST;
TIM2->PSC = 4 - 1; // 2 MHz (APB1 clock) x 2 = 4 MHz
TIM2->ARR = 100 - 1; // Timer should be called each 0.1 ms
TIM2->DIER |= TIM_DIER_UIE; // Enable timer interrupt generation
TIM2->SR &= ~TIM_SR_UIF;
TIM2->EGR |= TIM_EGR_UG;
NVIC_SetPriority(TIM2_IRQn, 1);
NVIC_EnableIRQ(TIM2_IRQn);
TIM2->CR1 |= (1<<0); // Enable timer

 

3 REPLIES 3
TDK
Super User

The timer tick frequency is working as intended here, with your code. You are probably misinterpreting something.

> My timer interrupt is twice as fast compared to a APB1 divisor of 1 or 2:

How are you determining this?

 

TDK_0-1767318389325.png

 

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

Hello,

the tick frequency is fine, i use the same values as yours. However, the timer interrupt is called twice when using divisor > 1. For example, when using a divisor of 4 (_DIV4), i setup the timer TIM2 as follow:

 

TIM2->PSC = 8 - 1; // 4 MHz (APB1 clock) x 2 = 8 MHz
TIM2->ARR = 100 - 1; // Timer should be called each 0.1 ms

 

Timer interrupt handler:

void TIM2_IRQHandler(void) {
  cnt++;
  cnt_us += 100;
  TIM2->SR &= ~(1<<0); // Clear UIF update interrupt flag
}

 

cnt is the number of 0.1 ms, e.g. cnt = 10 -> 1 ms, cnt = 10000 -> 1s.

cnt_us are the number of microseconds counted so far. Both variables are volatile declared to prevent compiler caching.

A simple wait function for testing:

void delay_ms(volatile uint32_t ms) {
  unsigned int old = cnt;

  while (old + (ms * 10) > cnt) {
    __WFI();
  }
}

int main() {
  // ... setup timer here ...
  while(1) {
    delay_ms(1000);
    printf("%i\n", cnt); // outputs to USART2 for debugging
  }
}

Output when using a divisor > 2 is twice as fast as using a divisor < 2.

TDK
Super User

Ahh:

Check and clear flags as the first step in the ISR. Technically, the interrupt is allowed to fire without a relevant ISR bit being set. That is what's happening here because you are clearing it at the last step. When it returns, the IPSR bit is still set and it immediately fires again. Things take a few clocks to propagate within the system. When you slow down the relevant bus that handles this, the issue crops up.

E.g.:

void TIM2_IRQHandler(void) {
  if (TIM2->SR & TIM_SR_UIF) {
    TIM2->SR = ~TIM_SR_UIF;
    cnt++;
    cnt_us += 100;
  }
}

 

 

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