Example 1: ahb freq. 48MHz apb1 freq. 24MHz i.e. a /2 prescaler ahb -> apb1 apb2 freq. 48MHz i.e. same as ahb freq. tim5 is on apb1 which has a /2 prescaler => inp.freq. to timer is 2x apb1 freq. tim1 is on apb2 which has no prescaling => inp.freq. to timer is 1x apb2 freq. which means that both tim1 (apb2) and tim5 (apb1) has the same input clock frequency. tim5 has an input freq. 2*apb1 = 2*24MHz = 48MHz 48 = 6*8, so with a prescaler of 6 will get you a timer input freq. of 8MHz and an integer ts = 125ns (If you used a prescaler of 3, the inp. freq. would be 16MHz and the ts would be 62.5ns wich isn't integer, so no luck with that with theese routines wich has ns as the smallest resolution.) Now if timer inp. freq. = 8MHz, then by dividing by 8000 you get 1kHz so we use time_steps = 8000 to that end, and time_cnth will then have a timestep of 1ms giving you a variable with the time in ms. which can be handy. If a counters input freq. is f, then the first bit of the counter will change att half the freq., i.e. f/2, the second bit f/4, third f/8 etc. So by doing: int main(void) { // set sysclk = 48MHz, ahb = sysclk, apb1 = ahb/2, apb2 = ahb // set GPIO_CRH(GPIOE) to all CNF_OPP50 time_init(5, 6, 8000, 125, 2); while (1) { time_get(); // "<< 8" so we get the value in the high byte of gpioe GPIO_ODR(GPIOE) = time_cnth << 8; } you can measure that PE8 has a freq. of 500Hz, PE9 250Hz, PE10 125Hz etc. since time_cnth is incremented at 1kHz. Doning so I got PE8 freq. to 500.05 kHz wich is in the 100ppm region. If instead you do: while (1) { GPIO_ODR(GPIOE) = (TIM_CNT(time_timer) & 0x0ff) << 8; } You'll see that each bit has a period time (T): bit pin T f 7 PE15 32us ~31kHz 6 PE14 16us ~62kHz 5 PE13 8us 125kHz 4 PE12 4us 250kHz 3 PE11 2us 500kHz 2 PE10 1us 1MHz though irregular 1 PE9 500ns 2MHz though irregular 0 PE8 1.75us ~570kHz in bit 1 and 2 you see that the mcu isn't quick enough to read the counter and set the output bits. In bit 0 you might be fooled into thinking that the counter is at the wrong frequency. To verify that the sysclk is 48MHz, you can acess the MCO signal with: GPIO_CRH(GPIOA) |= GPIOCNF( CNF_APP50, 8); // PA8 MCO and setting the MCO bits in RCC_CFGR to output sysclk, pllclk/2 or what you want to check. To check time_cntl, do: while (1) { time_get(); GPIO_ODR(GPIOE) = (time_cntl & 0x0ff) << 8; } and see what pins have a correct freq. Above I set the shift to 2, so hopefully we will see a T of 1us (1MHz) in bit 0, but I get 9us, bit 1 I get 2us. In all bits you'll see jitter. Soo, time_cntl is useful to measure times greater than say 5us. To get better resolution you have to use hw triggers.