Skip to main content
RParf.1
Associate
April 15, 2021
Solved

How to configure a 64 bit quadrature encoder interface

  • April 15, 2021
  • 7 replies
  • 1763 views

I'm currently using TIM2 on an STM32F373 as a 32bit quadrature encoder interface, however I need to extend this to 64 bits. Can I cascade a second 32bit counter (e.g. TIM5) as a slave whilst still maintaining the ability to count up & down ? Thanks

This topic has been closed for replies.
Best answer by waclawek.jan

No.

You'll have to extend it in software.

JW

7 replies

waclawek.jan
waclawek.janBest answer
Super User
April 15, 2021

No.

You'll have to extend it in software.

JW

RParf.1
RParf.1Author
Associate
April 16, 2021

Thanks for that. So amongst the registers associated with TIM2 are there flag bits which indicate a recent overflow or underflow condition?

waclawek.jan
Super User
April 16, 2021

TIMx_SR.UIF as for any other mode of timer. However, I wouldn't use that to extend the counter; rather, I would observe the counter's value periodically and count overflows/underflows based on comparison of previous and new value.

JW

RParf.1
RParf.1Author
Associate
April 16, 2021

OK, thanks again. I'll do that.

RParf.1
RParf.1Author
Associate
April 16, 2021

I've been trying to develop a function to do what you suggest and read the TIM2 CNT register at a regular fast rate and extend its 32 bit value into 64 bits whilst coping with rollovers in both the up and down directions. The code I have so far is shown below, where enc and enc_old are both int32, whilst enc_long is the intended int64 output value, all defined and initialized to zero elsewhere. It works for rollovers around zero, but messes up at the higher order rollovers. I think the problem is that I don't fully understanding how C is handling the different sized integers. So is there a simpler and more elegant way to do this job compared to the mess I am currently making of it?

void encoder(void)
{
		int64_t		diff;
	
		enc = htim2.Instance->CNT;
	
		diff = abs(enc - enc_old);
	
		enc_long = enc_long & 0xFFFFFFFF00000000;
	
		if (diff < 1000000 && enc < 0 && enc_old >= 0) {
				enc_long = enc_long - 0x0000000100000000;
		}
		
		if (diff < 1000000 && enc >= 0 && enc_old < 0) {
				enc_long = enc_long + 0x0000000100000000;
		}
		
		enc_long = enc_long | enc;
	
		enc_old = enc;
	
		return;
}

waclawek.jan
Super User
April 16, 2021

I would do something along these lines:

uint64_t get_encoder(void) {
 uint32_t enc;
 static uint32_t enc_old, enc_hi;
 
 enc = TIM2->CNT;
 
 if ((enc_old > 0xc0000000) && (enc < 0x40000000)) enc_hi++;
 if ((enc_old < 0x40000000) && (enc > 0xc0000000)) enc_hi--;
 
 enc_old = enc;
 
 return (((uint64_t)enc_hi) << 32) + enc;
}

and I would call this whenever I would need the 64-bit encoder value, making sure it's called at least once within the minimum time it takes to make 0x4000'0000 increments to the encoder.

JW

RParf.1
RParf.1Author
Associate
April 17, 2021

Thanks JW. I can see you've used unsigned integers throughout and I think this is where I am getting confused as I would need the function to return a signed 64 bit result which could run positive and negative either side of zero.