//------------------------------------------------------------------
// qfixSoccerBoard.h
//
// This file contains the class SoccerBoard which represents the
// physical SoccerBoard with all its inputs and outputs.
//
// Copyright 2004-2006 by KTB mechatronics GmbH
// Author: Stefan Enderle
//------------------------------------------------------------------


#include "qfix.h"


#ifndef qfixSoccerBoard_h
#define qfixSoccerBoard_h


static int speedMotor0 = 0;
static int speedMotor1 = 0;
static int speedMotor2 = 0;
static int speedMotor3 = 0;
static int speedMotor4 = 0;
static int speedMotor5 = 0;


/**
* \class SoccerBoard
* \brief Represents the controller board "SoccerBoard".
* \author Stefan Enderle
*
* The class SoccerBoard represents the
* physical SoccerBoard with all its inputs and outputs.
* With this class it is possible to drive the motors,
* put on LEDs, check the buttons and get data from the
* analog and digital inputs.
*/

class SoccerBoard
{

public:
  /** Constructor for the SoccerBoard class.
  */
  SoccerBoard(); 

  /** Puts on LED i. i must be 0 or 1.
  */
  void ledOn(int i);

  /** Puts off LED i. i must be 0 or 1.
  */
  void ledOff(int i);

  /** Puts off all LEDs.
  */
  void ledsOff();

  /** Puts LED i on if state is true, else off.  i must be 0 or 1.
  */
  void led(int i, bool state);

  /** Puts the power output i on
  */
  void powerOn(int i);

  /** Puts the power output i off
  */
  void powerOff(int i);

  /** Puts the power output i on if state is true, else off.
  */
  void power(int i, bool state);

  /** Checks the state of button i. If it is pressed, true is returned,
   *  else false.
  */
  bool button(int i);

  /** Uses the four LEDs on the board to display the value i
   *  with 0 <= i <= 255
  */
  void ledMeter(int i);

  /** Sets motor i to the given speed. -255 <= speed <= 255.
  */
  void motor(int i, int speed);

  /** Puts off all motors.
  */
  void motorsOff();

  /** returns the value of the analog port i. 0 <= value <= 255.
  */
  int analog(int i);

  /** returns true if the digital port is logical high, else false.
  */
  bool digital(int i);

  /** Waits until button i is pressed and released again.
  */
  void waitForButton(int i);
};





// PWM routine //
SIGNAL (SIG_OVERFLOW0)
{
  const int OFFSET=50;    // motor does not work with very low ratio
  static int counter=255+OFFSET;

  if (speedMotor0==0)                          cbi(PORTB, 3); // enable1 = 0
  else if (abs(speedMotor0)+OFFSET >= counter) sbi(PORTB, 3); // enable1 = 1
  else                                         cbi(PORTB, 3); // enable1 = 0

  if (speedMotor1==0)                          cbi(PORTG, 2); // enable2 = 0
  else if (abs(speedMotor1)+OFFSET >= counter) sbi(PORTG, 2); // enable2 = 1
  else                                         cbi(PORTG, 2); // enable2 = 0

  if (speedMotor2==0)                          cbi(PORTB, 4); // enable3 = 0
  else if (abs(speedMotor2)+OFFSET >= counter) sbi(PORTB, 4); // enable3 = 1
  else                                         cbi(PORTB, 4); // enable3 = 0

  if (speedMotor3==0)                          cbi(PORTB, 6); // enable4 = 0
  else if (abs(speedMotor3)+OFFSET >= counter) sbi(PORTB, 6); // enable4 = 1
  else                                         cbi(PORTB, 6); // enable4 = 0

  if (speedMotor4==0)                          cbi(PORTB, 5); // enable5 = 0
  else if (abs(speedMotor4)+OFFSET >= counter) sbi(PORTB, 5); // enable5 = 1
  else                                         cbi(PORTB, 5); // enable5 = 0

  if (speedMotor5==0)                          cbi(PORTB, 7); // enable6 = 0
  else if (abs(speedMotor5)+OFFSET >= counter) sbi(PORTB, 7); // enable6 = 1
  else                                         cbi(PORTB, 7); // enable6 = 0

  if (counter==0) counter=255+OFFSET;
  else counter--;
}



void initTimer()
{
  TCCR0A=1;  // timer 0 for interrupt
  TIMSK0=1;
  sei();    // enable interrupts
}




SoccerBoard::SoccerBoard()
{
  // PORT A: Digital In (PA0, PA1) //
  DDRA= 255 - (1+2);   			// all bits output (motor direction) except PA0,PA1 (digital in)
  PORTA=1+2;           				// set pullups for digital in

  // PORT B:  LEDs (PB0, PB2), Motor enable (PB3-PB7) //
  DDRB  = 1+4;                     	// PB0 + PB4 = LEDs -> output
  DDRB |= 8+16+32+64+128;      	// PB3 - PB7 = Motor enable -> output
  PORTB |= 1+4;             		// set bits 0 and 2 -> LEDs off
  PORTB &= 255-(8+16+32+64+128); 	// clear bits 3-7 -> motor disable

  // PORT C: Power Output //
  DDRC  = 255;             			// direction port D, all bits output
  PORTC = 0;               			// clear all bits -> power on

  // PORT D: I2C, USB, CAN //
  DDRD  = 0;                 		// all bits input
  PORTD = 1+2;               		// set bits 0,1 -> I2C pullUps

  // PORT E: Digital In (PE2 - PE7) //
  DDRE=0;		              		// all bits input
  PORTE=4+8+16+32+64+128;    		// set pullups for digital in

  // PORT F: Analog In //
  DDRF=0;		   					// all bits input
  ADCSRA=128;              			// set A/D enable bit (ADEN)

  // PORT G: Buttons (PG3, PG4), motor enable (PG2)
  DDRG = BV(PG2);            // PG2 output
  PORTG= BV(PG3)+BV(PG4);    // set pullups for buttons
  cbi(PORTG,PG2);            // clear PG2 -> motor disable

  initTimer();
}


void SoccerBoard::ledOn(int i)
{
  if (i==0) cbi(PORTB, PB0);         // clear bit -> LED on
  else if (i==1) cbi(PORTB, PB2);	  // clear bit -> LED on  
}


void SoccerBoard::ledOff(int i)
{
  if (i==0) sbi(PORTB, PB0);         // set bit -> LED off
  else if (i==1) sbi(PORTB, PB2);	  // set bit -> LED off  
}

void SoccerBoard::ledsOff()
{
  PORTB|=BV(PB0)+BV(PB2);          // set bits -> LEDs off
}


void SoccerBoard::led(int i, bool state)
{
  if (state) ledOn(i); else ledOff(i);
}


void SoccerBoard::powerOn(int i)
{
  if ((i<0) || (i>7)) return;
  cbi(PORTC, i);
}


void SoccerBoard::powerOff(int i)
{
  if ((i<0) || (i>7)) return;
  sbi(PORTC, i);
}


void SoccerBoard::power(int i, bool state)
{
  if (state) powerOn(i); else powerOff(i);
}


bool SoccerBoard::button(int i)
{
  if (i==0) return ( (PING & BV(PG4)) == 0);
  else if (i==1) return ( (PING & BV(PG3)) == 0);
  else return false;    // bad approach...
}


void SoccerBoard::ledMeter(int i)
{
  led(0, (i>100));
  led(1, (i>200));
}


void SoccerBoard::motor(int i, int speed)
{
  if ((i<0) || (i>5)) return;

  if (i==0) {
    speedMotor0 = speed;
	if (speed>0) sbi(PORTA, 3);   // input1  = 1
    else         cbi(PORTA, 3);   // input1  = 0
  }

  else if (i==1) {
    speedMotor1 = speed;
	if (speed>0) sbi(PORTA, 2);   // input2  = 1
    else         cbi(PORTA, 2);   // input2  = 0
  }

  else if (i==2) {
    speedMotor2 = speed;
	if (speed>0) sbi(PORTA, 5);   // input3  = 1
    else         cbi(PORTA, 5);   // input3  = 0
  }

  else if (i==3) {
    speedMotor3 = speed;
	if (speed>0) sbi(PORTA, 4);   // input4  = 1
    else         cbi(PORTA, 4);   // input4  = 0
  }

  else if (i==4) {
    speedMotor4 = speed;
	if (speed>0) sbi(PORTA, 7);   // input5  = 1
    else         cbi(PORTA, 7);   // input5  = 0
  }

  else if (i==5) {
    speedMotor5 = speed;
	if (speed>0) sbi(PORTA, 6);   // input6  = 1
    else         cbi(PORTA, 6);   // input6  = 0
  }
}


void SoccerBoard::motorsOff()
{
  motor(0,0);
  motor(1,0);
  motor(2,0);
  motor(3,0);
  motor(4,0);
  motor(5,0);
}


// return 0-255 //
int SoccerBoard::analog(int i)
{
  if ((i<0) || (i>7)) return -1;

  ADMUX=i;                       // select analog input and start A/D
  sbi(ADMUX, ADLAR);             // left adjust -> we use only ADCH
  sbi(ADCSRA, ADSC);             // start conversion
  while (ADCSRA & 64);           // wait until ADSC is low again
  int value = ADCH;              // read 8 bit value fom ADCH
  return value;
}


bool SoccerBoard::digital(int i)
{
  if ((i<0) || (i>7)) return false;  // bad solution...

  if (i==0) return (PINA & 1);
  else if (i==1) return (PINA & 2);
  else return (PINE & (1<<i)) ;
}


void SoccerBoard::waitForButton(int i)
{
  if ((i<0) || (i>3)) return;  // bad solution...
  while (!button(i))  { /* do nothing */ }
  while (button(i))   { /* do nothing */ }
}


#endif

