Skip to main content
Louie88
Senior
July 24, 2021
Solved

How to reset STM32H7 timer/counter and how to set the encoder mode?

Hi,

I am new with the STM32H7 timer/counter. I found the LPTIM_PulseCounter example which was good starting point. But I have a couple of questions:

  1. How can a reset the counter manually (not via automatic reload) of LPTIMER1? I could not find any HAL_LPTIM_CounterClear(), HAL_LPTIM_Counter_Reset(), HAL_LPTIM_Counter_Write(0x0000) function in HAL LPTIM Generic Driver.
  2. As far as I think the LPTIMER1 is capable to count quadrature encoded signals. It has two inputs, I can set the ENCODER mode and I can start the encoder mode with HAL_LPTIM_Encoder_Start (LPTIM_HandleTypeDef * hlptim, uint32_t Period) function. But I guess the period parameter should be set to 0xFFFF in order not to allow autoreload function after PERIOD count.
  3. Does ENCODER mode work with 32-bit timers? No need to be LPTIMER.
  4.  I have STM32H747I-DISCO board. The input1 of the LPTIMER1 is in the Arduino connector PD12 (pin 10 of CN5). Is the Input2 of LPTIMER1 in the same CN5 connector but the next pin: pin 9 of CN5 (PD13)?
  5. Is there any example how to use LPTIMER1 as an encoder in STM32H747I-DISCO board?

Thanks for your help!

Louis

This topic has been closed for replies.
TDK
TDKBest answer
July 28, 2021

It doesn't look like HAL_TIM_Encoder_Start_IT actually enable the update interrupt. You can do this manually after starting the encoder:

/* Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);

HAL_TIM_Encoder_Start_IT does enable the CC1 and CC2 interrupts, which are going to cause HAL_TIM_IC_CaptureCallback to get called. It looks like this is the way HAL intends you to use the encoder in timer mode. Set CCR1 to 0 and CCR2 to half-range to be able to detect overflows. Check for htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 or HAL_TIM_ACTIVE_CHANNEL_2 to see which is getting called.

"If you feel a post has answered your question, please click ""Accept as Solution""."

20 replies

TDK
July 24, 2021

> How can a reset the counter manually (not via automatic reload) of LPTIMER1? I could not find any HAL_LPTIM_CounterClear(), HAL_LPTIM_Counter_Reset(), HAL_LPTIM_Counter_Write(0x0000) function in HAL LPTIM Generic Driver.

You can write directly to the CNT register to set it to 0, but it won't reset the prescaler. To reset the counter including the prescaler, generate an update event with the EGR register, which will also reset the CNT value.

> As far as I think the LPTIMER1 is capable to count quadrature encoded signals. It has two inputs, I can set the ENCODER mode and I can start the encoder mode with HAL_LPTIM_Encoder_Start (LPTIM_HandleTypeDef * hlptim, uint32_t Period) function. But I guess the period parameter should be set to 0xFFFF in order not to allow autoreload function after PERIOD count.

Setting period to 0xFFFF is fine, and IMO the right thing to do. It still rolls over when it reaches the max value.

> Does ENCODER mode work with 32-bit timers? No need to be LPTIMER.

Yes

> I have STM32H747I-DISCO board. The input1 of the LPTIMER1 is in the Arduino connector PD12 (pin 10 of CN5). Is the Input2 of LPTIMER1 in the same CN5 connector but the next pin: pin 9 of CN5 (PD13)?

Not sure, but the answer will be in the user manual. Be aware of solder bridges sometimes needing to be closed/opened to expose or free up pins.

>Is there any example how to use LPTIMER1 as an encoder in STM32H747I-DISCO board?

Doesn't look like it. Only one example and it's on the H743-EVAL. However, the code is going to be similar on your board.

https://github.com/STMicroelectronics/STM32CubeH7/search?q=HAL_LPTIM_Encoder_Start

"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 25, 2021

Hi TDK,

Thanks for the very quick reply. I can't create quadrature modulated signal at home but I will test the suggested (https://github.com/STMicroelectronics/STM32CubeH7/blob/ccb11556044540590ca6e45056e6b65cdca2deb2/Projects/STM32H743I-EVAL/Examples/LPTIM/LPTIM_Encoder/Src/main.c) sample project on Monday morning. It seems to be very close to STM320H7474I-DISCO board. Thank you for your help!!!

Best regards,

Louis

Louie88
Louie88Author
Senior
July 26, 2021

Hi TDK,

Thanks for the suggestions. The STM32H743I-EVAL (Single core version of STM32H7474I) LPTIM_Encoder example project was the closest example to me (how to configure Encoder Timer/Counter). The exmaple used LPTIM1 for encoder but I had problems with accessing to the defined INPUT1 and INPUT2 pins of LPTIMER1 in 747I-DISCO board. In the example the LPTIM1_IN1 = PD12, and LPTIM_IN2 = PE1 port pin. In STM32H747I-DISCO board the PD12 pin can be found in the STMOD+ pin7, which is accessible from the little “fanout�? board. The LPTIM1_IN2 = PE1, but I could not find PE1 nether in ARDUINO not in the STMOD+ fanout board. So, this example cannot be directly used.

However I found (in the STM32H747I-DISCO user manual) that the STMod+ fanout connector has PD12 and PD13, which is the INPUT1 and INPUT2 of TIMER4. The TIMER4 is 32-bit counter and does support Encoder. Great, I just need to use TIMER4 instead of LPTIMER1. It seemed not to difficult job. (See attached file)

Now the problem is if a use STM32CubeMX with a new project for STM32H747I-DISCO board and if I enable TIM4 (for CM7 core) then it specifies different pins for TIMER4 in Encoder Mode, other than the STM32H747I-DISCO user manual… For TIM4 IN1 CubeMX specifies PB7 (instead of PD12) and IN2 = PD12 (Instead of PD13). I feel disturbance in the Force. Which is right?

Thank you for your help.

Best regards,

Louis

TDK
July 26, 2021

> For TIM4 IN1 CubeMX specifies PB7 (instead of PD12) and IN2 = PD12 (Instead of PD13). I feel disturbance in the Force. Which is right?

TIM4_CH2 is an option for both PB7 and PD13. If you want to change it to a different pin, click on the pin and select TIM4_CH2.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 26, 2021
Hi TDK!

Thanks, nice idea and even it works!

However, there should be a cross reference table which shows the PIN numbers and its function(s)/alternate functions. And reverse: a cross table which shows (for example) TIMER4_CH1 can be assigned to the following GPIO pins. Do you know where can I find these tables? I could not find in the STM327H7xx manual.

Many thanks for your help
Louis
TDK
July 26, 2021
The datasheet will have that info.
"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 26, 2021

Thanks TDK, I should have known. I will test the TIMER4 based encoder in STM7474I-DISCO board tomorrow.

Louis

Louie88
Louie88Author
Senior
July 27, 2021

Hi TDK,

I do hope you can answer how in the hell I can read the counted pulses form TIMER4? There is no HAL_TIM_Encoder_Read(Counter) function. The example you found just reads the count direction flags and blink LED1/3 to inform the user whether the encoder counts up or down. The closest function I found are:

/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_TIM4_Init();
 
	//+++++
	// 20210727 Start encoder TIMER4. But which channel?
	//	TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_ALL
	//-----
	if(HAL_OK != HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL))
	{
		Error_Handler();
	}
 
	uint32_t encoderPulses = 0;
	uint32_t encoderPulsesAll = 0;
	uint32_t encoderPulses1 = 0;
	uint32_t encoderPulses2 = 0;
	while (1)
	{
		HAL_Delay(50);
		//+++++
		// TFS20210727	Read counter of TIMER4
		//-----
		encoderPulses = __HAL_TIM_GetCounter(&htim4);
 
		encoderPulses1 = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_1);
		encoderPulses2 = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_2);
 
		encoderPulsesAll = HAL_TIM_ReadCapturedValue(&htim4, TIM_CHANNEL_ALL);
	}

All of those returns 0 even I see the Encoder pulses on my scope. Maybe I start encoder with the wrong channel TIM_CHANNEL_ALL? Where can I found a description how I can use the Encoder mode? The "STM32H7-GPTIM" application notes is a nice document but it does not explain the usage of the Encoder. (Init, Start, Stop, Read counted pulses). I wonder how others could use encoder?

Thanks for your help.

Louis

TDK
July 27, 2021

The encoder value is stored in CNT, which can be read by __HAL_TIM_GET_COUNTER(&htim4), or directly with TIM4->CNT.

The details of how to use the encoder at a register level will be in the reference manual, if that's what you're looking for.

For HAL, just enable clock, call HAL_TIM_Encoder_Init, then call HAL_TIM_Encoder_Start.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 27, 2021

Hi TDK,

Thanks, its working now. But, the next problem is the 16-bit encoder counter overflows and I could not find a interrupt handler (callback!!!) which would handle the encoder over/underflow. I drive the encoder from 10kHz pulses for 10 seconds. Counting on CH1/CH2 rising edges are configured so the encoder would contain 200 000 pulses. But it contains 3392, which is right because it overflows 3 times (3 * 65536) + 3392 = 200 000. I could handle it except this stupid way:

uint32_t lastEncoderPulses = 0;
	while (1)
	{
		HAL_Delay(50);
		//+++++
		// TFS20210727	Read counter of TIMER4
		// Take care about overflow manually because no interrupt provided for counter overflow...
		//-----
		encoderPulses = __HAL_TIM_GetCounter(&htim4);
		if (encoderPulses < lastEncoderPulses)
			encoderPulsesOvf++;
		lastEncoderPulses = encoderPulses;
	}

I found the following callbacks for TIMER4 in the "Description of STM32G4 HAL and low-layer drivers" manual. Please note that this is for STM32G4 not STM32H7 device...

58.2.9 TIM Callbacks functions

This section provides TIM callback functions:

• TIM Period elapsed callback

• TIM Output Compare callback

• TIM Input capture callback

• TIM Trigger callback

• TIM Error callback

• TIM Index callback (Not found or not implemented)

• TIM Direction change callback (Not found or not implemented)

• TIM Index error callback (Not found or not implemented)

• TIM Transition error callback

This section contains the following APIs:

• HAL_TIM_PeriodElapsedCallback

• HAL_TIM_PeriodElapsedHalfCpltCallback

• HAL_TIM_OC_DelayElapsedCallback

• HAL_TIM_IC_CaptureCallback

• HAL_TIM_IC_CaptureHalfCpltCallback

• HAL_TIM_PWM_PulseFinishedCallback

• HAL_TIM_PWM_PulseFinishedHalfCpltCallback

• HAL_TIM_TriggerCallback

• HAL_TIM_TriggerHalfCpltCallback

• HAL_TIM_ErrorCallback

Which one is the counter over/underflow? None.

Yes, the reference manual contains the description of the registers and registers bit. But, what I am looking for is an Example APP or a document which describes what I need to do and why (initialization, start, stop, clear, overflow handling) in order to be able to use Encoder. I could not find such document. I do not think it even exists.

Thank you for your help. I appreciate it very much.

Louie

TDK
July 27, 2021
The update event happens on overflow/underflow, which is piped out to HAL_TIM_PeriodElapsedCallback. If your encoder can change directions, a better method would be to poll the CNT/UIFCPY directly in order to handle underflow/overflows.
The reference manual is the best source of information on the details of the encoder. Not sure the documentation you're looking for exists.
"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 28, 2021

Thanks TDK for the explanations, but the HAL_TIM_PeriodElapsedCallback() is never called back (in my case). The TIMER4 encoder gets 200 000 pulses under 10 seconds. The TIMER4 counter should overflow 3 times. But, if I set a breakpoint into the HAL_TIM_PeriodElapsedCallback() - where I increment the ecnoderPulsefOvf (Overflow) variable - then then the breakpoint is never hit.

Maybe I am doing something wrong. In main.c: enable TIMER4 interrupt:

// Set the TIM4 priority
	HAL_NVIC_SetPriority(TIM4_IRQn, 0, 1);
 
	// Enable the TIM4 global Interrupt
	HAL_NVIC_EnableIRQ(TIM4_IRQn);

In main.c: start encoder with interrupt:

if(HAL_OK != HAL_TIM_Encoder_Start_IT(&htim4, TIM_CHANNEL_ALL))
	{
		Error_Handler();
	}

In main.c: Handling the callback event:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	// Works?
	encoderPulsesOvf++;
}

In stm32h7xx_it.c: defining TIMER4 interrupt handler:

void TIM4_IRQHandler(void)
{
 HAL_TIM_IRQHandler(&htim4);
}

So what do you think? What am I doing wrong?

Thanks for your help!

Louis

TDK
TDKBest answer
July 28, 2021

It doesn't look like HAL_TIM_Encoder_Start_IT actually enable the update interrupt. You can do this manually after starting the encoder:

/* Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);

HAL_TIM_Encoder_Start_IT does enable the CC1 and CC2 interrupts, which are going to cause HAL_TIM_IC_CaptureCallback to get called. It looks like this is the way HAL intends you to use the encoder in timer mode. Set CCR1 to 0 and CCR2 to half-range to be able to detect overflows. Check for htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 or HAL_TIM_ACTIVE_CHANNEL_2 to see which is getting called.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 29, 2021

Hi TDK,

Genius! That solved my interrupt problem. Many thanks for the suggestion.

However, as I mentioned it earlier, if I enable TIMER4 CH1 = PD12 and CH2 = PD13 then the touch screen initialization returns error. I checked it and I found this is because the I2C controller of the touch screen uses PD12/13 as data/clock line. That means TIMER4 can't be used with touch screen at the same time. Too bad!

I also found that TIMER8 can be used. The CH1 and CH2 of TIMER8 can be PC6/7 ports. PC6/7 are used in camera driver which is not needed. The problem is when I try to enable the over/underflow interrupts for TIMER8 I get an error : TIM8_IRQn is not defined:

// Set the TIM8 priority
HAL_NVIC_SetPriority(TIM8_IRQn, 0, 1);
 
/* TDK: Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE);
 
// Enable the TIM4 global Interrupt
HAL_NVIC_EnableIRQ(TIM8_IRQn);

I checked it and it is true. I have the following IRQ definitions only:

 TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */
 TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */
 TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */
 TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */

My stupid question is which one I can use instead of TIM8_IRQn? Why does the HAL support different the TIMER8 and TIMER4?

Many thanks for your help.

Best regards,

Louis

TDK
July 29, 2021

It's in an interrupt handler that you didn't define so it defaults to Default_Handler.

Ensure TIM8_UP_TIM13_IRQHandler is defined appropriately.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Louie88
Louie88Author
Senior
July 29, 2021

Hi TDK!

Super, as always, you are right, I used void TIM8_IRQHandler(void) which does not exist. Defining void TIM8_UP_TIM13_IRQHandler(void) solved the problem. One note: I still have to call __HAL_TIM_ENABLE_IT(&hTimer8, TIM_IT_UPDATE). Without this the callback function is never hit.

Both the ENCODER (TIMER8) and the touch screen works. Many thanks for your help.

Now the only thing left: how to measure the frequency of signals counted by TIMER8? Can I attach another timer (in order to measure the frequency) to the same pins (PC6/PC7) which are already used by TIMER8? Or I have to wire the signal connected to PC6/PC7 to other pins?

Thanks,

Louis

TDK
July 29, 2021
You attach those signals to another timer and use the other timer in input capture mode. Should be quite a few examples around of code which measure frequency.
"If you feel a post has answered your question, please click ""Accept as Solution""."