If, like us, you need to measure times with high accuracy over a relatively long period of time, timers with 32-bit accuracy are probably the way to go. Although the STM32F4 only offers two 32 bit timers, it is also possible to chain two 16 bit timers together to obtain 32 bit accuracy.
In the STM32F4 you can configure either TIM9 or TIM12 to act as the most significant word (MSW) of a 32-bit timer, which can be connected to another timer, which acts as the least significant word (LSW). The ST documentation refers to this as a "Synchronization circuit to control the timer with external signals and to interconnect several timers".
Below is an example using TIM3 as the master (to count the lower 16 bits) and TIM9 as the slave (to count the upper 16 bits):
/* enable the timer peripherals: */ RCC->APB1ENR |= RCC_APB1Periph_TIM3; RCC->APB2ENR |= RCC_APB2Periph_TIM9; /* Master - TIM3 counts the lower 16 bits */ /* TIM3 should use the clock as its input by default.*/ TIM3->PSC = 0x0000; /* no prescaler. */ /* set clock division to zero: */ TIM3->CR1 &= (uint16_t)(~TIM_CR1_CKD); TIM3->CR1 |= TIM_CKD_DIV1; TIM3->CR2 |= 0x20; /* MMS (6:4) */ /* slave - TIM9 counter the upper 16 bits */ TIM9->PSC = 0x0000; /* set clock division to zero: */ TIM9->CR1 &= (uint16_t)(~TIM_CR1_CKD); TIM9->CR1 |= TIM_CKD_DIV1; TIM9->SMCR |= (TIM_TS_ITR1 | TIM_SlaveMode_External1); /* enable the two counters: */ TIM9->CR1 |= TIM_CR1_CEN; TIM3->CR1 |= TIM_CR1_CEN;
In the example you can see that both timers need to have their prescaler set to zero and their clock division set to zero. The timer capturing the lower 16 bits (TIM3 in this case) needs to be configured in Master Mode (TIM3_CR2:MMS = 010, master is used as a prescaler for the slave). The timer capturing the high 16 bits (TIM9 in our example) needs to have the following configuration set:
- The Trigger Selection needs to be set to TIM3 (001) so that the output from TIM3 acts as the input to TIM9
TIM9_SMCR:TS = 001
- The Slave Mode Selection needs to be set to External Clock Mode 1 (111) - Rising edges of the selected trigger (TRGI) clock the counter. So the input causes a counter increment.
TIM9_SMCR:SMS = 111
The one challenge that this approach introduces is the risk of the LSW wrapping around between reading one 16-bit register and the other one (it doesn't matter what order they occur in). For example, if the timers have the following values
TIM9 (msw) | TIM3 (lsw) |
0x0100 | 0xffff |
If we read TIM9, then the clock increments before we read TIM3, we'll get a value that's about 216 ticks too low. If we read the registers in the other way, the resulting value is about 216 ticks too high. The following (rather simplistic) approach can handle rollover
lsw_1 = TIM3_CNT; msw_1 = TIM9_CNT; lsw_2 = TIM3_CNT; /* has TIM3 rolled over between its first and second reading? */ if (lsw_2 < lsw_1) { /* rollover has happened. lsw_2 is read post-rollover. * Not sure whether msw_1 was read pre- or post-rollover */ ret.s.lsw = lsw_2; ret.s.msw = TIM9_CNT; /* re-read MSW to be sure we've got it post-rollover */ } else { /* a rollover didn't occur between reading lsw_1 and lsw_2. We can use msw_1 safely */ ret.s.lsw = lsw_2; ret.s.msw = msw_1; }
So with a bit of simple setup, and a little pain when it comes to reading the result, you effectively have two more 32 bit timers on the STM32. Have fun.