2025-08-20 2:23 PM
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();
}
Solved! Go to Solution.
2025-08-20 2:24 PM
> 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.
2025-08-20 2:24 PM
> 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.
2025-08-20 2:38 PM
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!