cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure a 64 bit quadrature encoder interface

RParf.1
Associate II

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

1 ACCEPTED SOLUTION

Accepted Solutions

No.

You'll have to extend it in software.

JW

View solution in original post

7 REPLIES 7

No.

You'll have to extend it in software.

JW

RParf.1
Associate II

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

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
Associate II

OK, thanks again. I'll do that.

RParf.1
Associate II

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;
}

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
Associate II

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.