Need help with Danni's encoder code

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been looking at rotary encoder strategies, and came across danni's solution for reading a rotary encoder:

 

https://www.avrfreaks.net/commen...

https://www.mikrocontroller.net/...

https://www.mikrocontroller.net/...

 

I'm having trouble understanding the last part of it.  This is what I understand so far:

 

/************************************************************************/
/*                                                                      */
/*                      Reading rotary encoder                   	*/
/*                      one, two and four step encoders supported	*/
/*                                                                      */
/*              Author: Peter Dannegger                                 */
/*                                                                      */
/************************************************************************/
#include
#include 

				// target: ATmega16
//------------------------------------------------------------------------

#define	XTAL		8e6			// 8MHz

#define PHASE_A		(PINA & 1<<PA1)
#define PHASE_B		(PINA & 1<<PA3)

#define LEDS_DDR	DDRC
#define	LEDS		PORTC			// LEDs against VCC

int8_t enc_delta;				// -128 ... 127

void encode_init( void )
{
  TCCR0 = 1<<WGM01^1<<CS01^1<<CS00;		// CTC, XTAL / 64
  OCR0 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5);	// 1ms
  TIMSK |= 1<<OCIE0;
}

ISR( TIMER0_COMP_vect )				// 1ms for manual movement
{
  static int8_t last;
  int8_t new, diff;

  new = 0;
  if( PHASE_A )
    new = 3;
  if( PHASE_B )
    new ^= 1;					// convert gray to binary
  diff = last - new;				// difference last - new
  if( diff & 1 ){				// bit 0 = value (1)
    last = new;					// store new as next last
    enc_delta += (diff & 2) - 1;		// bit 1 = direction (+/-)
  }
}

 

encode_init is just to set up the timer, and the interrupt runs every 1ms to capture any changes in encoder state (and assumes that the encoder isn't turning so fast that 1ms isn't enough).  diff takes values between -3 to +3, and of those +2 and -2 are invalid transitions because they skip a state.  If the transition is +3, +1, -1 or -3, the "if" block updates enc_delta, the cumulative change in encoder steps since the main block last looked, with the change +1 or -1.

 

 

int8_t encode_read1( void )			// read single step encoders
{
  int8_t val;

  cli();
  val = enc_delta;
  enc_delta = 0;
  sei();
  return val;					// counts since last call
}

int8_t encode_read2( void )			// read two step encoders
{
  int8_t val;

  cli();
  val = enc_delta;
  enc_delta &= 1;
  sei();
  return val >> 1;
}

int8_t encode_read4( void )			// read four step encoders
{
  int8_t val;

  cli();
  val = enc_delta;
  enc_delta &= 3;
  sei();
  return val >> 2;
}

int main( void )
{
  int32_t val = 0;

  LEDS_DDR = 0xFF;
  encode_init();
  sei();

  for(;;){
    val += encode_read1();			// read a single step encoder
    LEDS = val;
  }
}

 

encode_read1 to encode_read4 are called from the main loop to update val with the change stored in enc_delta.  Which one is picked depends on the number of encoder states per detent?

 

encode_read1 is straightforward.  It returns the value by which val has changed since last called and resets enc_delta to 0.  So far so good.

 

Where I now get stuck is encode_read2 and encode_read4.  Particularly the line:

 

enc_delta &= 1;

 

I assume the intent is to keep the remnant of enc_delta if the number of steps traveled in the interim is not a multiple of 2.  But say we are slowly turning the knob in the decrementing direction, and enc_delta is -1 when this function is called.  Doesn't this mean enc_delta takes the value 0b11111111 & 0b00000001, ie changing -1 to 1?  The next interrupt call with the knob continuing to turn in the same direction would add -1 to enc_delta bringing it to 0, and so even though the knob has turned two steps, the code doesn't see it.

 

What have I missed here?

 

Last Edited: Sun. May 10, 2020 - 04:07 AM