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

This discussion is locked. Please start a new topic to ask your question.
1 ACCEPTED SOLUTION

Accepted Solutions
waclawek.jan
Super User

No.

You'll have to extend it in software.

JW

View solution in original post

7 REPLIES 7
waclawek.jan
Super User

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?

waclawek.jan
Super User

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

waclawek.jan
Super User

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.