Make 48 bit Counter by Cascading Timers
This is the 8th tutorial in the STM32 Timer series, and today we will see how to cascade 3 16 bit counters into a single 48 bit counter. This is basically another tutorial covering the timer synchronization and today we will see how to synchronize the timers using the External Clock mode.
For the purpose of the application, I will combine the 2 16bit timers to make a 32 bit counter, and use this counter to measure the wide range of frequencies. It will overcome the disadvantage of using a 16 bit counter, where we could only measure up to a certain bandwidth because of the counter size.
Similarly you can combine 3 16bit timers to make a 48bit counter, or 4 timers to make 64bit counter.
In this tutorial, I am going to use 2 timers, TIM1 and TIM3. TIM3 is the slave to TIM1 and is being controlled by the trigger ITR0. This is as per the internal trigger connection table in the F446RE reference manual.
When the timers are used in the slave mode (External Clock mode), the output of the master timer will be used as the input clock for the slave timer. By doing so, the APB clock is still running at max clock speed and so does the master timer’s counter. But the slave timer slows down, which helps us measure the lower frequencies.
We will see this in application. Let’s see the setup first.
CubeMX Setup
First of all let’s take a look at the clock setup
Here I have configured the external crystal, which is 8 MHz, to provide the clock such that the system will run at 90 MHz. This is to make sure that both the APB timer clocks have the same speeds and so all the timers will have the same base clock.
The Timer 1 config
Below is the image showing the configuration for the TIM1.
TIM1 will be used as the master timer.
- I have enabled the input capture on the channel 1 to measure the input signal.
- The timer is clocked by the APB clock, so the base frequency is at 90MHz.
- Using the prescalar of 9 will bring down the frequency to 10MHz.
- This 10MHz is also the counter 1’s Frequency. Basically the counter 1 will count at this rate.
- The ARR is set to 10000. This makes the counter 1’s period equal to 1ms. (check the image below)
- The trigger event selection is set to update event, so that we can cascade another timer with this.
- The input capture configuration is kept to default, i.e. capture on rising edge. Also the interrupt for the input capture has been enabled.
The TIM1’s output frequency will be equal to 1KHz. This will be used as the base clock for the slave timer.
TIM3 config (Slave Timer)
TIM3 is the slave to the TIM1.
- I have set the slave mode (External Clock Mode 1), and the Trigger source is set to ITR0.
- When the Update Event from the master timer is combined with the External Clock Mode 1, the output of the master timer is used as the clock for the slave timer.
- Since the prescalar is set to 1, the counter 3 will run at 1KHz, so each count take 1ms.
- The ARR is set to 65535. This makes the counter 3 period equal to 65 Seconds.
So by using the combination of these 2 counters, we can measure the Frequencies from 10MHz(Theoretically) to 0.015Hz.
Some insight into the code
The main function
/* USER CODE BEGIN 2 */
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
HAL_TIM_Base_Start(&htim3);
/* USER CODE END 2 */
- In the main function, we will start the TIM1 in the input capture interrupt mode.
- Also we will simply start the TIM3 so that it’s counter can also count.
Once the rising edge of the input signal is detected, the Input capture callback will be called. We will write the rest of the code inside it.
Input Capture callback
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if (Is_First_Captured==0) // if the first rising edge is not captured
{
TIM1->CNT = 0;
TIM3->CNT = 0;
Is_First_Captured = 1; // set the first captured as true
}
else // If the first rising edge is captured, now we will capture the second edge
{
counter1 = TIM1->CNT;
counter3 = TIM3->CNT;
frequency = ((float)10000000)/((counter3*10000)+counter1);
Is_First_Captured = 0; // set it back to false
}
}
}
- In the callback, we will first check if the detected edge is the first or the second edge.
- If it is the first edge, then we will reset both the counters and set the is_first_captured to 1 indicating that the first edge has been captured.
- When the second edge arrives the callback will be called again.
- Here we will first read the counters of the respective timers.
- Now we will combine all the counters to make a final counter value.
- Each count in the counter 3 is equivalent of 10000 counts in counter 1.
- so to make them equal, we need to multiply the counter 3 with 10000.
- Now we have the final counter value in terms of the counter 1, so to calculate the frequency, we will divide the counter 1 clock by the counter value.
- Finally reset the is_first_captured, so that the whole process can begin again.
Result
For the purpose of testing, I am using the NE555 module which can output different frequency ranges.
Although we are using multiple timers, only TIM1 is being used as the input capture mode. So there is only one input pin as shown in the image below
Below are the images showing the lowest and highest frequency
As shown in the image above, the STM32 can measure the 0.57 Hertz, as well as 222KHz. Even though we are configuring the TIM1 to run at 10MHz, the fact that it can measure the frequency as low as 0.5Hz is only possible by cascading the timers.
Although I didn’t use the 48bit counter in this tutorial, you can still use the same logic to make one.
You need to cascade 3 16bit timers with one timer clocking the second, and second clocking the third, TIM1->TIM2->TIM3.
With this configuration, the formula to calculate the frequency is shown below