Today we will try to learn the STM32 Input Capture Mode with the simplest code example to learn how to use STM32 Input Capture Mode? Before we dive into today’s tutorial you may want to check out our previous STM32 tutorials. For today’s example, I’ll connect a push button to one of the timers on the STM32 board. I’ll then configure it as an Input Capture source.
In embedded systems, accurately measuring time intervals between events is crucial for many applications, such as pulse-width measurement, frequency counting, and event timing. The STM32 microcontroller family offers a versatile timer peripheral that can be used to capture these time intervals with high precision. This blog post will guide you through configuring STM32 Timer 3 in input capture mode using direct register-level programming without relying on interrupts.
what input capture mode does?
It allows you to precisely measure the time between events by capturing the timer’s value when those events occur. You can imagine a stopwatch which we start when we see red traffic light and stop when traffic signal goes green. The measured time between the traffic light red event to green event or the measured time between red event and the next light goes red event with stopwatch is an example of input capture mode.
In case of STM32, instead of a green and red light, it looks for changes in voltage (like a rising or falling edge) on a specific pin. When it sees this change, it records the exact value of its internal counter. This value represents the time elapsed since the timer started.
In Input Capture mode, the STM32 microcontroller records a timestamp in memory when an input signal is received. This is different from the Output Compare mode, where a timestamp is used to trigger an output event.
In STM32, input capture mode is used to latch the value of the counter after a transition detected by the corresponding ICx signal. The Capture/Compare Registers (TIMx_CCRx) are used for this purpose. When a capture occurs, the corresponding CCXIF flag (TIMx_SR register) is set and an interrupt or a DMA request can be sent if they are enabled1. To use input capture direct mode for timer in STM32, you need to use the Timer in input capture mode and configure Timer_Pin for external interrupt trigger.
STM32 Timer 3 Input Capture
Here is the quick configuration of the STM32 Timer 3 for the Input capture mode using the Register Level Programming. You can see the overall process of how to configure the STM32 Timer 3 for input capture mode.
/*Enable clock access to GPIOA*/
RCC->AHB1ENR |=GPIOAEN;
/*Set PA6 mode to alternate function*/
GPIOA->MODER &=~(1U<<12);
GPIOA->MODER |=(1U<<13);
/*Set PA6 alternate function type to TIM3_CH1 (AF02)*/
GPIOA->AFR[0]|=AFR6_TIM;
/*Enable clock access to tim3*/
RCC->APB1ENR |=TIM3EN;
/*Set Prescaler*/
TIM3->PSC = 16000 -1; // 16 000 000 /16 000
/*Set CH1 to input capture*/
TIM3->CCMR1 = CCER_CC1S;
/*Set CH1 to capture at rising edge*/
TIM3->CCER = CCER_CC1E;
/*Enable TIM3*/
TIM3->CR1 = CR1_CEN;
Code language: C++ (cpp)
The code configures STM32’s Timer 3 in input capture mode to measure the time between rising edges of a signal on PA6. It starts by enabling clocks for GPIOA and TIM3, then configures PA6 as an input pin connected to TIM3’s channel 1. The timer’s prescaler is set to divide the system clock by 16000, effectively reducing the timer’s counting speed. Finally, it enables input capture on channel 1 to capture the timer’s value when a rising edge occurs on PA6 and starts the timer.
Input Capture Mode Code without Interrupt
The code below directly accesses and manipulates the STM32 registers to configure TIM3 for input capture mode on channel 1 (PA6). It does not use the interrupt mechanism; instead, the captured value is polled directly.
#include "stm32f4xx.h"
void GPIO_Config(void);
void TIM3_Config(void);
int main(void)
{
// Enable clock for GPIOA
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// Configure PA6 as alternate function (AF2) for TIM3_CH1
GPIOA->MODER &= ~(GPIO_MODER_MODE6); // Clear mode bits
GPIOA->MODER |= GPIO_MODER_MODE6_1; // Set mode to Alternate function
GPIOA->AFR[0] |= 2 << 24; // Set AF2 for PA6 (TIM3_CH1)
// Timer 3 configuration
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // Enable clock for TIM3
TIM3->PSC = 16000 - 1; // Prescaler value (16MHz/16000 = 1kHz)
TIM3->ARR = 0xFFFF; // Set auto-reload register to max value
TIM3->CCMR1 |= TIM_CCMR1_CC1S_0; // Configure CC1 as input, mapped to TI1
TIM3->CCER &= ~(TIM_CCER_CC1P); // Capture on rising edge
TIM3->CCER |= TIM_CCER_CC1E; // Enable capture on channel 1
TIM3->CR1 |= TIM_CR1_CEN; // Enable TIM3
uint32_t lastCapture = 0;
uint32_t currentCapture = 0;
uint32_t timeInterval = 0;
while (1)
{
if (TIM3->SR & TIM_SR_CC1IF) // Check if capture occurred
{
currentCapture = TIM3->CCR1; // Read captured value
timeInterval = currentCapture - lastCapture;
lastCapture = currentCapture;
TIM3->SR &= ~(TIM_SR_CC1IF); // Clear capture flag
}
}
}
Code language: C++ (cpp)
This code configures an STM32 microcontroller’s Timer 3 to measure the time between rising edges of a signal. First, the necessary clock sources for the GPIO and timer are enabled. Then, the GPIO pin is configured as an input and connected to the timer’s input channel. The timer is set up to count at a specific frequency and to capture the time value when a rising edge is detected on the input pin. Once a capture occurs, the code calculates the time elapsed since the previous capture by comparing the captured values.
Conclusion
This tutorial demonstrated how to configure STM32’s Timer 3 for input capture in direct mode using register-level programming. By avoiding interrupts, we directly poll the status register to determine when a capture has occurred. This approach is beneficial in simpler applications where you want to avoid the overhead of interrupt handling.
This method provides a solid foundation for understanding input capture in STM32 and can be expanded to include more complex timing and frequency measurement tasks in future projects.