At Rapita Systems, we find the STM32 Discovery F4 board to be a cost-effective and versatile development board. We have a number of them that we use for training courses and demonstrations of our tools. Since we’ve started using them, we’ve had to work out for ourselves how to operate some of the on-chip peripherals. In a previous blog post I explained how to configure the I/O port. Another peripheral we use often is a high-resolution free running timer.
In this post I’ll show you how to set up a timer on the STM32. The STM32 board has 3 32-bit and 10 16-bit timers available. Below is an example that uses the peripheral libraries supplied by STMicroelectronics to deploy a 32-bit timer. This example shows the selected timer (TIM2) set up to be a free running up counter running at the main clock speed. Use stm32fxxx_tim.h
- library file from ST that adds APIs for accessing on chip timers. Also stm32fxxx_rcc.h
- used to enable the timer to use (in this example TIM2).
TIM_TimeBaseInitTypeDef SetupTimer; /* Enable timer 2, using the Reset and Clock Control register */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); SetupTimer.TIM_Prescaler = 0x0000; SetupTimer.TIM_CounterMode = TIM_CounterMode_Up; SetupTimer.TIM_Period = 0xFFFFFFFF; SetupTimer.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &SetupTimer); TIM_Cmd(TIM2, ENABLE); /* start counting by enabling CEN in CR1 */
This code snippet enables timer 2 to be an upcounter, going at maximum speed (Prescaler = 0 and ClockDivision = TIM_CLD_DIV1
) which will wrap at 0xFFFFFFFF. To retrieve the counter value, do the following:
/* get the timer value. */ unsigned long count = TIM_GetCounter(TIM2);
This method relies on using the peripheral libraries supplied by STMicroelectronics. If you don’t have them they are available here.
Triggering with a set period
Below is an example where we increment count
every 60 microseconds.
TIM_TimeBaseInitTypeDef SetupTimer; /* Enable timer 2, using the Reset and Clock Control register */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // Get the clock frequencies RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); SetupTimer.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency/1000000) - 1; // Prescale to 1Mhz SetupTimer.TIM_CounterMode = TIM_CounterMode_Up; SetupTimer.TIM_Period = 60 - 1; // 60 microsecond period SetupTimer.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &SetupTimer); TIM_Cmd(TIM2, ENABLE); // start counting by enabling CEN in CR1 */ TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // enable so we can track each period int count = 0; while(1){ if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); count += 1;// Insert your 60 microsecond event here } }
To do this we first fetch the clock frequency's using:
RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks);
This lets us to prescale the clock to 1Mhz:
SetupTimer.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency/1000000) - 1; // Prescale to 1Mhz
We then set the period to 60 microseconds:
SetupTimer.TIM_Period = 60 - 1;
We then enable the update events for each period of the timer using:
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // enable so we can track each period
And finally test for those update events with:
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)