cancel
Showing results for 
Search instead for 
Did you mean: 

Using Timer 2 to Capture Timer 3 via internal Interrupts

MHerz.1
Associate II

Hey,

I'm using the Timer 2 in Encoder Mode TI1 and TI2. The Trigger Output of the Timer is set to Master/Slave Mode Enabled and the Trigger Event is set to Compare Pulse (OC1).

As far as I know: This Timer will fire an internal interrupt via TRGO everytime the value changes.

The timer 3 is configured as following: He is used as a normal Timer with an internal clock and the auto-reload preloader turned off. The timer is configured as slave in reset Mode. The Trigger is set to ITR1 and the Ch1 is configured as Input Capture triggered by TRC.

So again. As far as i know. This timer 3 is just counting until the timer 2 interrupts him via TRGO. This will capture the current timer 3 CNT (because of the Ch1 Input Capture) and reset the Timer 3 (because of the Reset Mode).

The internal connection between the timer 2 and timer 3 works. The timer 3 will get reset as soon as timer 2 got a changing edge from its channels. But unfortunatly the timer 3 will not write the CNT value into the CC1 Register.

Is there anything i haven't understood so far? Is something missing? Is it even possible to capture the current timestamp and reset the timer in slave mode?

Markus

5 REPLIES 5

>The Trigger Output of the Timer is set to Master/Slave Mode Enabled

Contrary to its name, the MSM but does not do much, just shifts one of the internal signals by one or two cycles.

> and the Trigger Event is set to Compare Pulse (OC1).

Well, but you don't have Output compare set on CH1, do you? If yes, then the timer is not working in Encoder mode...

> The timer 3 will get reset as soon as timer 2 got a changing edge from its channels

How do you know it is reset exactly at that edge?

Also, read out and put the TIMs registers content.

JW

MHerz.1
Associate II

> Contrary to its name, the MSM but does not do much, just shifts one of the internal signals by one or two cycles.

"The effect of an event on the trigger input (TRGI) is delayed to allow a perfect synchronization between the current timer and its slaves (through TRGO). It is useful if we want to synchronize several timers on a single external event." - Okay, I see. So i dont even need to set MSM to enable internal communication between two timers.

> Well, but you don't have Output compare set on CH1, do you? If yes, then the timer is not working in Encoder mode...

The Channels 1 and 2 are automatically grayed out in STM32CubeIDE as soon as i activate encoder mode. The encoder mode functionallity works as intended os far. If I dont set the Compare Pulse (OC1) in timer 2 i wont get an interrupt in timer 3 to reset the timer.

> How do you know it is reset exactly at that edge?

To be honest, I assumed it. Even if the timer 2 only sends a trigger every rising edge or falling edge. Thats fine.

Is there a way to configure timer 3 to write the CNT value into a CC Register after getting reset by the ITR1?

Edit: To further clarify what I'm trying to achieve:

STM32F4 RM00090 Rev 18 Reference manual - p554/1749

"The timer, when configured in Encoder Interface mode provides information on the sensor’s current position.The user can obtain dynamic information (speed, acceleration, deceleration) by measuring the period between two encoder events using a second timer configured in capture mode. The output of the encoder which indicates the mechanical zero can be used for this purpose. Depending on the time between two events, the counter can also be read at regular times. This can be done by latching the counter value into a third input capture register if available (then the capture signal must be periodic and can be generated by another timer). when available, it is also possible to read its value through a DMA request generated by a real-time clock."

Markus

Which STM32?

Read out and post the TIMs registers' content.

(I did not realize that ST calls the CC1-> TRGO as "Compare Pulse", even if, contrary to other TRGO outputs, it is *not* bound to Compare... )

JW

MHerz.1
Associate II

µC: STM32F446RETx LQFP64 via NUCLEO F446RE

Config Timer 2

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 4095;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

Config Timer 3

htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 65535;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  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();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  sSlaveConfig.InputTrigger = TIM_TS_ITR1;
  if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != 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_TRC;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

Hi,

I know that this post is very old, so I assume you fixed it 😅, but this might be helpful to someone else.

On the STM32F105, I used TIM1 and TIM4 in the very same configuration of you to read 2 hall sensors (that produce the very same signals of a quadrature encoder):

TIM1 in encoder mode, and  trigger output on Compare Pulse (OC1).

TIM4 as slave in reset mode with channel 1 as Input Capture triggered by TRC.

Here my timers config (extracted from the STM32CUBEMX):

TIM1.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE
TIM1.EncoderMode=TIM_ENCODERMODE_TI12
TIM1.IC1Filter=1
TIM1.IC1Polarity=TIM_ICPOLARITY_RISING
TIM1.IC2Filter=1
TIM1.IC2Polarity=TIM_ICPOLARITY_RISING
TIM1.IPParameters=EncoderMode,AutoReloadPreload,Period,IC1Polarity,IC2Polarity,IC2Filter,TIM_MasterOutputTrigger,TIM_MasterSlaveMode,IC1Filter
TIM1.Period=65535
TIM1.TIM_MasterOutputTrigger=TIM_TRGO_OC1
TIM1.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE


TIM4.Channel-Input_Capture1_from_TRC=TIM_CHANNEL_1
TIM4.Channel-Output\ Compare2\ No\ Output=TIM_CHANNEL_2
TIM4.IPParameters=Channel-Input_Capture1_from_TRC,Prescaler,Channel-Output Compare2 No Output,Pulse-Output Compare2 No Output,OCMode_2
TIM4.OCMode_2=TIM_OCMODE_ACTIVE
TIM4.Prescaler=\ 72-1
TIM4.Pulse-Output\ Compare2\ No\ Output=65535

Here the screenshots:

TIM1.png

TIM4.png

As all the work is done by the peripherals, so the code is very simple:

Timers are initialized as:

HAL_TIM_Encoder_Start_IT(&htim1, TIM_CHANNEL_ALL);
HAL_TIM_Base_Start_IT(&htim1);
HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_2);

 

When I need to get the encoder measure I simply call:

bool bRead_speed = Shaft_Measure();
Shaft_Speed(bRead_speed);

 

Where these functions are defined as:

bool Shaft_Measure(void)
{
	static int32_t aShaft_old = 0;
	/* Get the current shaft rotation direction */
	uint8_t bGet_dir = (uint8_t)__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1);
	/* Get the current shaft position - consider the possible overflow */
	int32_t aShaft = NOverflow_cnt + (int32_t)htim1.Instance->CNT;

	/*
	 * Check for the speed calculation
	 * As the TIM4 rolls over when the speed is too low...
	 * I set a limit (~20 rpm), to avoid to return wrong values
	 * aShaft - aShaft_old > 1
	 */
	if (ABS(aShaft - aShaft_old) > 1){
		bRead_speed = true;
	}
	else{
		bRead_speed = false;
	}
	aShaft_old = aShaft;
	return bRead_speed;
}



void Shaft_Speed(bool bRead_speed)
{
	if (bRead_speed){
		nShaft = (uint16_t)((1000000.0)/(float)(htim4.Instance->CCR1));
	}
	else{
		nShaft = 0 ;
	}
}

While the code for the ISR (I needed to use only TIM1) is:

void TIM1_UP_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_UP_IRQn 0 */

	// TIM1 counter counting UP
	if (!__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1)){
		// TIM1 counter just started
		if (0 == aShaft){
			// 32 bit counter in the range 0 to 65535
			NOverflow_cnt = 0;
		}
		else{
			NOverflow_cnt += MAX_UNSIGNED_16_BIT;
		}
	}
	// TIM1 counter counting DOWN
	else {
		NOverflow_cnt -= MAX_UNSIGNED_16_BIT;
	}

  /* USER CODE END TIM1_UP_IRQn 0 */
  HAL_TIM_IRQHandler(&htim1);
  /* USER CODE BEGIN TIM1_UP_IRQn 1 */

  /* USER CODE END TIM1_UP_IRQn 1 */
}

 

Doing that all work like a charm.

I hope that this might help.

AGA