cancel
Showing results for 
Search instead for 
Did you mean: 

Input timer setup halves period after 1st overflow and then remains in that state

Mystmified
Visitor

Hi there, Ive gone around in circles for hours on this. 

I'm just trying to read a frequency, around 150Hz typ. using the 32 bit timers, for now just TIM2, 4 channel but behaviour same on all channels. Using 16MHz clock the timer overflow happens around 4:40 mins, the read of the period and calc of frequency is fine, matches input signal, until the first overflow.

After first overflow the period halves, frequency doubles, and stays like this for every subsequent overflow (confirmed by making a smaller ARR so overflows more frequency). I am trying to increment the high side of a 64bit counter-measurement value with the overflows and then populate the low side with the TIM2->CCR1. Below is my code, just one channel for brevity.


As far as I can see it looks relatively textbook and it's just a simple task, it seems so odd that it just doubles the frequency reading on the first overflow but on no subsequent. Any help very gratefully received...

 

// 

uint8_t TIM2_CH1_NEW_DATA = 0;

volatile uint32_t TIM2_overflows = 0;

 

// 64 bit values so overflows can be handled nicely.

uint64_t TIM2_CH1_val = 0;

uint64_t TIM2_CH1_PREV_val = 0;

uint64_t TIM2_CH1_Measured_Period;

 

void init_TIM2(void){

 

    // Timer 2 CH 1~4 = GPIOB 8 9 10 11

     UART2_SendString("TIM2 Initialising\n");

 

    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

 

    // Clock GPIOB

    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

 

    // clear MODERS

    GPIOB->MODER &= ~GPIO_MODER_MODER8;

 

    //set MODERS to alt function

    MODIFY_FIELD(GPIOB->MODER, GPIO_MODER_MODER8, 0b10);

 

    // select alt function 1

    MODIFY_FIELD(GPIOB->AFR[1], GPIO_AFRH_AFSEL8, 0b01);

 

    // Set auto reload value to max for 32bit.  

    // TIM2->ARR = 0xffffffff; // desired count

    TIM2->ARR = 0x4000000; // quick count for debug

 

    // slave mode control register, these are all set to 0 for basic operation

    TIM2->SMCR = 0;

    // all control register 1 values set to default

    TIM2->CR1 = 0;

    // all control register 2 values set to default

    TIM2->CR2 = 0;

 

    // No pre-scaling, use full clock

    TIM2->PSC = 1;

 

    // Set each channel to input capture mode

    MODIFY_FIELD(TIM2->CCMR1, TIM_CCMR1_CC1S, 0b01);

 

    //enable capture, via edge detection, rising by default

    TIM2->CCER |= TIM_CCER_CC1E;

 

    //enable interrupts for capture and overflow/update

    TIM2->DIER |= TIM_DIER_CC1IE;

 

    // state register cleared of any flags that exist

    TIM2->SR = 0;

 

    // initialise the first 'previous val'

    TIM2_CH1_PREV_val = ((uint64_t)TIM2_overflows << 32) | TIM2->CCR1;

 

    NVIC_SetPriority(TIM2_IRQn, 1);

    NVIC_ClearPendingIRQ(TIM2_IRQn);

    NVIC_EnableIRQ(TIM2_IRQn);

 

    // enable Tim 2

    TIM2->CR1 |= TIM_CR1_CEN;

 

     UART2_SendString("TIM2 Initialised\n");

 

}

 

void TIM2_IRQHandler(void){

    // ----------------------------

    // Check if an overflow has occurred

    // ----------------------------

    if (TIM2->SR & TIM_SR_UIF) {     // Update/Timer overflow

        TIM2->SR &= ~TIM_SR_UIF;

        TIM2_overflows++;

    }

 

    // ----------------------------

    // Check for interrupt flag for each channel

    // ----------------------------

    if (TIM2->SR & TIM_SR_CC1IF) { // Rising edge

        TIM2->SR &= ~TIM_SR_CC1IF; // clear flag

        TIM2_CH1_val = ((uint64_t)TIM2_overflows << 32) | TIM2->CCR1; // capture the val in CCR1 and overflows

        TIM2_CH1_Measured_Period = TIM2_CH1_val - TIM2_CH1_PREV_val; // get elapsed ticks since prev val

        TIM2_CH1_PREV_val = TIM2_CH1_val; // Prepare prev val for next call

        TIM2_CH1_NEW_DATA = 1; // flag for new data

    }

}

 

 

int main(void){

 

    HAL_Init();

    init_UART2();

    init_TIM2();

    init_TIM5();

    init_heater_ctrl();

 

    printf("SYSCLK: %" PRIu32 " Hz\n", HAL_RCC_GetSysClockFreq());

    printf("APB1 : %" PRIu32 " Hz\n", HAL_RCC_GetPCLK1Freq());

    printf("TIM2 : %" PRIu32 " Hz\n", HAL_RCC_GetPCLK1Freq());

 

    while(1){

 

        if((HAL_GetTick() - LAST_PRINT_TIME) > 500) {

            if (TIM2_CH1_NEW_DATA){

            TIM2_CH1_NEW_DATA = 0;

            TIM2_CH1_Frequency = (float)CLCK_SPEED / TIM2_CH1_Measured_Period;

            printf("TIM2_CH1_Freq = %.2f Hz\n", TIM2_CH1_Frequency);

        }

    LAST_PRINT_TIME = HAL_GetTick();

}

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Super User

> TIM2->PSC = 1;

The prescaler is preloaded and doesn't take effect until the next update event.

Generate an update event using the EGR register to have it take effect immediately.

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

View solution in original post

2 REPLIES 2
TDK
Super User

> TIM2->PSC = 1;

The prescaler is preloaded and doesn't take effect until the next update event.

Generate an update event using the EGR register to have it take effect immediately.

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

Oh my word, THANK YOU, yes, I was using prescale as 1, I needed it as 0, on the first update event my psc=0 goes to psc=1 and halved the clock tick count per interrupt so my maths in the main thought everything was going twice as fast.. Cheers!