cancel
Showing results for 
Search instead for 
Did you mean: 

How to use the input capture feature

Sarra.S
ST Employee

Introduction 

Input capture is one of the advanced timer features available on the STM32 microcontrollers. This article guides you through the theoretical aspects of input capture, provide a practical example, and discuss troubleshooting common issues. In this article we use the STM32H563 microcontroller. 

1. Input capture basics

Input capture is a feature that allows the timer to record the time at which an external event occurs. This is particularly useful for measuring an input signal's frequency, period, or pulse width. The timer captures the value of its counter at the moment an edge (rising or falling) is detected on a specific input pin.

2. Key components

  • Timer counter: A register that increments (or decrements) based on the timer clock.
  • Capture/compare register (CCR): Stores the counter value when an input capture event occurs.
  • Input capture channel: The specific pin and associated circuitry used to detect the input signal.

3. Configuration parameters

  • Edge selection: Determines whether the capture occurs on a rising edge, falling edge, or both.
  • Prescaler: Divides the timer clock to adjust the counter frequency.
  • Filter: Filters out noise by requiring a stable signal for a specified duration before capturing.

4. Practical example 

This example aims to measure the frequency of an external signal using timer 2 on the STM32H563.

Hardware setup: Connect the external signal to the timer 2 channel 1 input pin (PA0).

Step 1: Configure the clock 

In this example, the HCLK clock is set to 125MHz. Timer2 is connected to APB1 (always refer to the block diagram in the datasheet to check the timer clock).

Clock configurationClock configuration

Step 2: Configure the timer settings

  • Click on the Configuration tab.
  • Select TIM2 and choose Input capture direct mode for Channel 1.

Choose input capture modeChoose input capture mode

  • Set the Prescaler to adjust the timer clock frequency.
  • Set the Counter Mode to Up.
  • Set the Period value.
  • In the Channel 1 settings, set the IC Polarity to Rising (or Falling depending on your requirement).
  • Set the IC Selection to Direct.
  • Set the IC Prescaler to No division.
  • Set the IC Filter to 0 (or adjust as needed to filter noise).

TIM2 configurationTIM2 configuration

The formula below determines the timer's counter clock frequency (CK_CNT):

CK_CNT= HCLK / (PSC+1)

Where: 

  • HCLK is the system clock frequency (125 MHz in this case).
  • PSC is the prescaler value.

The timer period is determined by the auto-reload register (ARR) value and the counter clock frequency, the formula is:

Period = (ARR+1) * (1/ CK_CNT)

Where: 

  • ARR is the auto-reload register value.
  • CK_CNT is the counter clock frequency.

For a desired period of 1 ms for example, and CK_CNT=1MHz, we can choose PSC=124, this divides the clock by 125. Using the period formula, ARR=1000-1 =999

Step 3: Enable the Timer interrupt

TIM2 interruptTIM2 interrupt

Step 4: Configure GPIO pins

  • In the Pinout & Configuration tab, select the pin connected to the input signal (PA0 for TIM2_CH1).
  • Ensure that the pin is configured as Alternate Function PP and is set to the correct timer channel.

Step 5: Generate the project code

Start Input Capture in Interrupt mode 

void TIM2_Start_IC(void) {
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
}

The variable we used are: 

  • captureValue: Stores the current captured value of the timer counter.
  • previousCaptureValue: Stores the previously captured value of the timer counter. It is used to calculate the time difference between two consecutive capture events.
  • frequency: Stores the calculated frequency of the input signal based on the captured values.

Handle Input Capture Interrupt

uint32_t captureValue = 0;
uint32_t previousCaptureValue = 0;
uint32_t frequency = 0;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
        captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        frequency = HAL_RCC_GetPCLK1Freq() / (captureValue - previousCaptureValue);
        previousCaptureValue = captureValue;
    }
}

5. Troubleshooting known issues

The following issues are the most likely encountered by users, with the most probable root causes.

5.1. No capture event is detected

  • Check pin configuration: Ensure the input pin is correctly configured as an alternate function for the timer.
  • Verify signal integrity: Ensure the input signal is clean and meets the voltage levels required by the microcontroller. If the signal is noisy, consider using a filter.

5.2. Incorrect frequency measurement

  • Adjust prescaler: If the measured frequency is too high or too low, adjust the prescaler to bring the counter value within a measurable range.
  • Debounce the input signal: Use the input filter feature to debounce noisy signals.

5.3. Interrupt not triggering

  • Enable NVIC: Ensure that the NVIC is configured to handle the timer interrupt.
  • Check the timer clock: Verify that the timer clock is enabled and running.

5.4. Overflows and wraparounds

  • Handle overflows: Implement logic to handle counter overflows if the input signal period is longer than the timer period.

Related links

Comments

I don't understand why is this posed as 'H5-specific - this is a pretty generic feature which will work across the *whole* STM32 range. Maybe the pictures in CubeMX are different.

---

> CK_CNT= HCLK / (ARR+1)

should read

CK_CNT= HCLK / (PSC+1)

[EDIT] These 2 issues have been fixed. [/EDIT]

----

For this application there's no reason to set any other ARR than maximum. Nonetheless, whatever ARR value is, unless in 32-bit timers, it has to be taken into consideration in the final calculation of the frequency (using modulo arithmetics, either explicitly or through casts), as  captureValue may have been captured after timer rollover.

JW

 

BarryWhit
Lead II

Elaborating:

 

1. Whether you're using a 16 bit timer, a 32 bit timer, or something else (is dithering applicable here? don't remember), the calculation of the time/tick difference between two capture events must deal with the fact that a timer rollover might have occurred between the two capture events, as this could produce the surprising situation where T2<T1. When a rollover does occur, the calculation of the time/tick interval depends on the value of ARR (the actual value at which the counter rolls over, i.e. the timer period).

2. There is a special case, however. If ARR=UINTx_MAX (where x equals the width of the timer) and you perform the subtraction using UINTx types, you don't have to explicitly deal with the overflow case because this case is "baked" in to the "two's complement" binary representation used by the processor's arithmetic unit. For example, If you use a 32 bit counter, and ARR is set to UINT32_MAX=2**32-1 value, then if you calculate t2-t1 using uint32_t types all over, you will get the correct result whether a (single) rollover occurred between the two events or not.

3. The design (by choosing the frequency of the timer's clock source) must guarantee that at most one (*) rollover event occurs between two consecutive capture events. In other words, using a W-bit timer, you can only measure an interval of at most 2**W-1 ticks (2**W if you cared enough). 

4. You can think of handling the rollover case as doing modulo arithmetic, if you really want to, just as long as you don't forget (3) .

 

(*) Not strictly true but, in practice, true enough.

MM..1
Chief III

I mean good is offload MCU here and leave hw do measure period without any wasting interrupts or instructions. Add to TIM config slave triggered reset on input capture event and maybe too DMA store more as one measured periods for next calculations...

BarryWhit
Lead II

True. You can configure the Timer to reset after each capture (on the opposite edge for example), so that every captured value is actually the absolute time which has elapsed since the previous capture event.

Gaetan
Associate

Hi,

Something seems to be wrong with STMCubeIDE Version: 1.16.0 Build: 21983_20240628_1741 (UTC) with an STM32G070RBT6

After generation of the code, the Timer code is like this:

/**
* @brief TIM3 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM3_Init(void)
{

/* USER CODE BEGIN TIM3_Init 0 */
// HAL_TIM_IC_DeInit(&htim3); // GDE

/* USER CODE END TIM3_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};

/* USER CODE BEGIN TIM3_Init 1 */

/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}
 

When we call HAL_TIM_IC_Init() function, the htim->State is already set to HAL_TIM_STATE_READY   instead of HAL_TIM_STATE_RESET, so HAL_TIM_IC_MspInit(htim) is never called.

So the interrupt is not triggered, HAL_TIM_IC_CaptureCallback() is never called too.

Is my analysis right?

Ahmet Yasin CİVAN
Associate III

Thank you for sharing this article. It has been a very helpful resource for understanding how to use the Input Capture feature. I really appreciate your support!

Version history
Last update:
‎2024-08-08 05:44 AM
Updated by: