/******************************************************************************
 Title:    encoder -> PWM
 Author:   rue_mohr
 Date:     Aug 19 2005
 Software: AVR-GCC 3.3 
 Hardware: atmega32
  
                         ATmega32 - 40 PIN DIP LAYOUT FOR DECODER AND PWM - 
                                      _______________________
                               ALI <- [ 1]PB0         PA0[40] 
                                      [ 2]PB1         PA1[39]
                                      [ 3]PB2         PA2[38] 
                               BHI <- [ 4]PB3         PA3[37] 
                                      [ 5]PB4         PA4[36] 
                                      [ 6]PB5         PA5[35] 
                                      [ 7]PB6         PA6[34] 
                                      [ 8]PB7         PA7[33] 
                             RESET -> [ 9]RST        AREF[32] <- +5V VCC
                            5V VCC -> [10]VCC         GND[31] <- GROUND
                            GROUND -> [11]GND        AVCC[30] <- +5V VCC
                       16 MHZ XTAL -> [12]XTAL1       PC7[29] 
                       16 MHZ XTAL -> [13]XTAL2       PC6[28] 
                                      [14]PD0         PC5[27] 
                                      [15]PD1         PC4[26] 
                         Encoder 1 -> [16]PD2         PC3[25] 
                         Encoder 2 -> [17]PD3         PC2[24] 
                                      [18]PD4         PC1[23] 
                                      [19]PD5         PC0[22]                  
                                      [20]PD6         PD7[21] -> BLI
                                      ______________________                         


                 +5V -> AHI
                 
what this should do:
when you turn the encoder one way, the motor shoudl go forward, at a variable speed
when you turn the encoder the other way, the motor shoudl go backward, at a variable speed
center is stopped, goes up as you get away from center
when it rolls over, it will suddenly switch directions
    
*******************************************************************************/

/****************************| INCLUDGEABLES |********************************/

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

/*****************************| DEFINIATIONS |********************************/

#define Forward            1
#define Reverse            0

#define OUTPUT             1
#define INPUT              0

/*****************************| VARIDABLES |********************************/


signed int   position;           // note that this is signed
unsigned char oldstate; // for quadrature decoding

#define ERROR 0
	signed char offsets[] = {
	/* 0000 */   0,
	/* 0001 */  +1,
	/* 0010 */  -1,
	/* 0011 */   ERROR,
	/* 0100 */  -1,
	/* 0101 */   0,
	/* 0110 */   ERROR,
	/* 0111 */  +1,
	/* 1000 */  +1,
	/* 1001 */   ERROR,
	/* 1010 */   0,
	/* 1011 */  -1,
	/* 1100 */   ERROR,
	/* 1101 */  -1,
	/* 1110 */  +1,
	/* 1111 */   0
};

/************************| FUNCTION DECLARATIONS |*************************/

void pwm_init     ( );
void SetPWM       ( uint8_t channel, uint8_t value) ;
void SetDir       ( uint8_t dir );
void SetSpeed     ( int Speed );
void setupEncoder ( void );
void updatePos    ( void );

/****************************| CODE... |***********************************/

int main (void)  {
  
  long error;
  
  DDRA = (INPUT << PA0 | INPUT << PA1 |INPUT << PA2 |INPUT << PA3 |INPUT << PA4 |INPUT << PA5 |INPUT << PA6 |INPUT << PA7);
  DDRB = (OUTPUT << PB0 | INPUT << PB1 |INPUT << PB2 |OUTPUT << PB3 |INPUT << PB4 |INPUT << PB5 |INPUT << PB6 |INPUT << PB7);
  DDRC = (INPUT << PC0 | INPUT << PC1 |INPUT << PC2 |INPUT << PC3 |INPUT << PC4 |INPUT << PC5 |INPUT << PC6 |INPUT << PC7);
  DDRD = (INPUT << PD0 | INPUT << PD1 |INPUT << PD2 |INPUT << PD3 |INPUT << PD4 |INPUT << PD5 |INPUT << PD6 |OUTPUT << PD7);        
         
  pwm_init();
  setupEncoder(); 
  sei();
  
  while(1) {
  
    error = position;
  	if (error > 255) error = 255;
    if (error < -255) error = -255;
    SetSpeed ( error  ); 
  }
  
}
    
//------------------------| FUNCTIONS |------------------------

void SetSpeed ( int Speed ) {

  if (Speed > 0) {
      SetDir( Forward ); // set direction
      SetPWM( 0, Speed); // set other side
    } else {
      SetDir( Reverse );
      SetPWM( 0, -Speed);
    }
}

void _SetSpeed ( int Speed ) {

  if (Speed > 0) {
      SetPWM( 0, 0);     // make sure one side is stopped
      SetDir( Forward ); // set direction
      SetPWM( 1, Speed); // set other side
    } else {
      SetPWM( 1, 0);
      SetDir( Reverse );
      SetPWM( 0, -Speed);
    }
}

void SetDir  (  uint8_t dir ) {

   if (dir == 0) {
     PORTB |= 0x01;
       TCCR0 = 0 << FOC0  | 
          1 << WGM00 | /* fast pwm */
          1 << COM01 | /* inverted */
          0 << COM00 | /*   this bit 1 for inverted, 0 for normal  */
          1 << WGM01 | /* fast pwm */
          1 << CS02  | /* prescale by 1024 */
          0 << CS01  |
          1 << CS00  ;
   } else {
     PORTB &= (~0x01);
       TCCR0 = 0 << FOC0  | 
          1 << WGM00 | /* fast pwm */
          1 << COM01 | /* inverted */
          1 << COM00 | /*   this bit 1 for inverted, 0 for normal  */
          1 << WGM01 | /* fast pwm */
          1 << CS02  | /* prescale by 1024 */
          0 << CS01  |
          1 << CS00  ;
   }
}


void SetPWM( uint8_t channel, uint8_t value) {
   if (channel == 0)   
      OCR0 = value;
   if (channel == 1)   
      OCR2 = value;
}

void pwm_init() {
  // clear pwm levels
  OCR0 = 0; 
  OCR2 = 0;
  
  // set up WGM, clock, and mode for timer 0
  TCCR0 = 0 << FOC0  | 
          1 << WGM00 | /* fast pwm */
          1 << COM01 | /* inverted */
          0 << COM00 | /*   this bit 1 for inverted, 0 for normal  */
          1 << WGM01 | /* fast pwm */
          1 << CS02  | /* prescale by 1024 */
          0 << CS01  |
          1 << CS00  ;
  
  // set up WGM, clock, and mode for timer 2
  TCCR2 = 0 << FOC2  | 
          1 << WGM20 |
          1 << COM21 |
          0 << COM20 | /*   this bit 1 for inverted, 0 for normal  */
          1 << WGM21 |
          1 << CS22  |
          0 << CS21  |
          1 << CS20  ;
  
 }

void setupEncoder() {
	// clear position
	position = 0; 
 
	// we need to set up int0 and int1 to 
	// trigger interrupts on both edges
	MCUCR = (1<<ISC10) | (1<<ISC00);
 
	// then enable them.
	GICR  = (1<<INT0) | (1<<INT1);
}
 

 
SIGNAL (SIG_INTERRUPT0) {  // fix this signal name!!! INT0
	updatePos();
}
 
SIGNAL (SIG_INTERRUPT1) { // fix this signal name!!! INT1
	updatePos();
}
 
void updatePos() {
/*	get the bits        PIND     = XXXXiiXX
  	clear space       & 0x0C     = 0000ii00
  	or in old status  | oldstate = XX00iijj
 	clean up          & 0x0F     = 0000iijj  */  
	oldstate = oldstate | (PIND & 0x0C); // Update Oldstate 
	position += offsets[oldstate]; 	     // Update Position
	oldstate = oldstate >> 2; 
}

