//Distributed under the GPL v 2.0
//by fenn 02/15/07
//optical encoder demonstration using only one external interrupt
//this code was written for an attiny26 and hasnt been thoroughly tested

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile unsigned char enc0_pos=0;

#define SETBIT(PORT, BIT)  PORT |= (1<<BIT)
#define CLRBIT(PORT, BIT)  PORT &= ~(1<<BIT)

//==0 is a quick instruction that returns a logical result
//otherwise isset/isnset return a bitmask which can cause probs
#define ISNSET(PORT, BIT)  (PORT & (1<<BIT)) == 0 
#define ISSET(PORT, BIT)  !ISNSET(PORT, BIT) 
//if (ISSET(PIND, 2)) ...


/* pinout */
#define LED PA0
#define ENC0_A PB4
#define ENC0_B PB5
#define ENC0_PORT PINB

SIGNAL(SIG_INTERRUPT0)
{ //update quadrature state
  //this routine gets called when PB6 changes state. PB6 should be hooked to
  //the B quadrature channel. this provides 2x counting, not full quadrature. 
	if((ISSET(ENC0_PORT, ENC0_A) == (ISSET(ENC0_PORT, ENC0_B))))
	{ enc0_pos++; } //if same, we are going right
	else
	{ enc0_pos--; } //if different we are going left
	//PORTA ^= 1<<LED; //toggle the LED pin
}

int main(void)
{    	char old_state=0;
	DDRA=	(1<<LED) |
			(1<<PA4) |
			(1<<PA5) |
			(1<<PA6) |
			(1<<PA7); 
	//DDRA=0xFF; //set port A to output
	sei(); //global interrupt enable
	GIMSK |= (1<<INT0); // turn on interrupt 0
	MCUCR |= (0<<ISC01) | (1<<ISC00); //interrupt on any change of int0 (pb6)
	//MCUCR |= (1<<ISC01) | (1<<ISC00); //interrupt on rising edge of int0 (pb6); gives half the resolution
	while (1)
	{
		//PORTA = enc0_pos; //show our current position
		PORTA=(enc0_pos<<4 & 0xF0); //show position in binary on PA4-PA7
	}
}
