/************************************************************************/
/*                                                                      */
/*                      Motorpotiplatine Steuerung                      */
/*                                                                      */
/*              Author : Frank Link                                     */
/*                       franklink61@aol.com                            */
/*              Version: 1.1                                            */
/************************************************************************/
#include "main.h"
#include <stdio.h>
#include <string.h>
#include <avr/pgmspace.h>

#include "motor.h"
#include "timer.h"
#include "eprom.h"
#include "buttons.h"


/******************************	Interrupts *******************************/

/*
	Hochsetzen des Timers und gegebenenfalls stoppen des Motors
*/
ISR(TIMER1_OVF_vect)
{
  timerHighByte++;
  if ( ( timerHighByte >= MOTOR_ON_TIME ) &&
       ( ( ( MotorRightIsOn == MOTOR_ON ) || ( MotorLeftIsOn != MOTOR_ON ) ) ) )
  {
	stopTimer();						// Der Timer wird ausgeschaltet
  	MotorOff();							// Der Motor wird ausgeschaltet
  }
}

/*
	RC5 Routine
*/
ISR( TIMER0_OVF_vect )
{
  uint tmp = rc5_tmp;						// for faster access

  TCNT0 = -2;								// 2 * 256 = 512 cycle

  if( ++rc5_time > PULSE_MAX ){				// count pulse time
    if( !(tmp & 0x4000) && tmp & 0x2000 )	// only if 14 bits received
      rc5_data = tmp;
    tmp = 0;
  }

  if( (rc5_bit ^ xRC5_IN) & 1<<xRC5 ){		// change detect
    rc5_bit = ~rc5_bit;						// 0x00 -> 0xFF -> 0x00

    if( rc5_time < PULSE_MIN )				// to short
      tmp = 0;

    if( !tmp || rc5_time > PULSE_1_2 ){		// start or long pulse time
      if( !(tmp & 0x4000) )					// not to many bits
        tmp <<= 1;							// shift
      if( !(rc5_bit & 1<<xRC5) )			// inverted bit
        tmp |= 1;							// insert new bit
      rc5_time = 0;							// count next pulse time
    }
  }

  rc5_tmp = tmp;
  f_timer_tick = 1;
}



/******************************	Motor ************************************/

/*
	Motor für Linkslauf einschalten
*/
void motor_left_on( void )
{
	stopTimer();						// Der Timer wird ausgeschaltet
	if ( MotorRightIsOn == MOTOR_ON )   // dreht der Motor in die andere Richtung
	{
		MotorOff();						// Motor ausschalten
	}
	if ( MotorLeftIsOn != MOTOR_ON )	// Dreht der Motor bereit in diese Richtung
	{
		MotorLeftOn();					// wenn nein, dann einschalten
	}
	restartTimer();						// Timer neu starten
}

/*
	Motor für Rechtslauf einschalten
*/
void motor_right_on( void )
{
	stopTimer();						// Der Timer wird ausgeschaltet
	if ( MotorLeftIsOn == MOTOR_ON )    // dreht der Motor in die andere Richtung
	{
		MotorOff();						// Motor ausschalten
	}
	if ( MotorRightIsOn != MOTOR_ON )	// Dreht der Motor bereit in diese Richtung
	{
		MotorRightOn();					// wenn nein, dann einschalten
	}
	restartTimer();						// Timer neu starten
}

/*
	Alle Ports werden abgeschaltet
*/
void devices_off( void )
{
	DDRD  = ALL_OFF;
	PORTD = ALL_OFF;
	MotorOff();
}

static unsigned char ascii2byte(char *val)
{
	unsigned char temp = *val;
	
	if(temp > 0x60) temp -= 39;  // convert chars a-f
	temp -= 48;  // convert chars 0-9
	temp *= 16;
	
	temp += *(val+1);
	if(*(val+1) > 0x60) temp -= 39;  // convert chars a-f
	temp -= 48;  // convert chars 0-9	

	return temp;

}

void serial_putc(unsigned char data)
{
	// wait for USART to become available
		while ( (UCSRA & _BV(UDRE)) != _BV(UDRE));
		UDR = data; 									// send character
}; //end usart_putc

void serial_put_byte2ascii(unsigned char val)
{
	unsigned char ascii1=val;
	serial_putc( ((ascii1>>4) < 10) ? (ascii1>>4) + 48 : (ascii1>>4) + 55 );  // upper nibble
	serial_putc( ((val&0x0f) < 10) ? (val&0x0f) + 48 : (val&0x0f) + 55 );  // lower nibble
}

void serial_puts_P(const char *s)
{
	while( pgm_read_byte(&*s)) serial_putc( pgm_read_byte(&*s++));// send string char by char
}


/*************************** Initializierung **********************/


/******************************	Main *****************************/
int main( void )
{
 	uint i;									// Variable mit gültigem RC5-Code
	uint key;								// Key-Code der gedrückten Taste
	uint device;							// Device-Code der gedrückten Taste
	int toggle;								// Toggle-Bit der gedrückten Taste
	int oldtoggle;							// vorheriger Zustand des Toggle-Bit
	uint ir_on;								// Zustand des Device 0 = ausgeschaltet / 1 = eingeschaltet
	int oldkey;	    						// Zuletzt verwendete Taste, dient zur Entprellung der Eingabe. 
   	volatile uint8_t channel;       		// Zwischenspeicher für den letzten Kanal. 
   											// Diese Variable wird im EEPROM gespeichert

	TCCR0  = 1<<CS02;			         	// divide by 256
	TCCR1B = 1<<CS10;       			 	// divide by 256
  	TIMSK  = 1<<TOIE0^1<<TOIE1;	         	// enable timer interrupt
	stopTimer();

  	key = 0x01;								// Initialisierung für Key
	device = 0x01;							// Initialisierung für Device
  	oldkey = key;		
  	oldtoggle = -1;      					// Initialisierung für Togglebit

  	ir_on = IR_INIT;							
  	DDRD  = CHANNEL1_INIT; 					// Bei Type1 wird automatisch Channel 1 eingeschaltet um sicherzustellen,
	PORTD = CHANNEL1_INIT;  				// dass die Platine in einem definierten Zustand ist.
											// Bei Type2 sind alle Ports ausgeschaltet und die Platine muss explizit
											// eingeschaltet werden
											// Type2 und Type3 verhalten sich identisch

	initArrays();							// Initialisierung der Tastatur Arrays

	channel = 0;	    					// Kanal 1 soll aktiviert werden, wenn das Gerät über die Ein-Austaste eingeschaltet wird.
  	channel = eeprom_read_byte(&eeChannel); // die letzte Einstellung wird aus dem EEPROM gelesen
	if ( channel < 1 )						// zur Sicherheit wird geprüft,
	{										// ob der Inhalt des EEPROMS korrekt ist
		channel = 1;
	}
	if ( channel > MAX_CHANNELS )
	{
		channel = MAX_CHANNELS;
	}
	currentChannel = channel;				// Zwischenspeicher für EEPROM Ablage. nur wenn channel
											// und currentChannel unterschiedlich sind,
											// wird ins EEPROM geschrieben
  	sei();									// Interrupts global freigeben


  	/* Main-Loop */
  	while( 1 )
  	{
    	cli();
    	i = rc5_data;						// lese die zwei Bytes für die RC5-Codierung
    	rc5_data = 0;
    	sei();
    	if( i )
    	{
			device = i >> 6 & 0x1F;			// Device-Code auslesen
			key = (i & 0x3F);				// Key-Code auslesen

		
			toggle = ( i >> 11 & 1);		// Toggle-Bit auslesen


			if ( ( isKey_on_off( device, key ) == 1 ) &&
			     ( ir_on == IR_OFF ) && ( oldtoggle != toggle ) )
			{
				ir_on = IR_ON;

				oldtoggle = toggle;
	      		sendCode( channel );			
			} else
			if ( ( isKey_on_off( device, key ) == 1 ) &&
			     ( ir_on == IR_ON ) && ( oldtoggle != toggle ) )
			{
				ir_on = IR_OFF;
				oldtoggle = toggle;
				devices_off();	
			}
			if ( ir_on == IR_ON )
			{
		      	if ( isKey_Motor_Left( device, key ) == 1 )
		      	{
		      		motor_left_on();
		      	}
	    		if ( isKey_Motor_Right( device, key ) == 1 )
	    		{
		      		motor_right_on();
		      	}
				if ( isKey_Channel_Up( device, key ) == 1 )
				{
	      			if ( oldkey != i )
	      			{
	      				channel++;
	      				if ( channel > MAX_CHANNELS )
	      				{
	      					channel = 1;
	      				};
	      				channel = sendCode( channel );			
	      				oldkey = i;
	      			}
				}
				if ( isKey_Channel_Down( device, key ) == 1 )
				{
	      			if ( oldkey != i )
	      			{
	      				channel--;
	      				if ( channel == 0 )
	      				{
	      					channel = MAX_CHANNELS;
	      				};
	      				channel = sendCode( channel );			
	      				oldkey = i;
	      			}
				}
			}
			if( isKey_1( device, key ) == 1 )
			{
				if ( ir_on == IR_OFF )
				{
					ir_on = IR_ON;
					oldtoggle = -1;
				}
				channel = sendCode( 0x01 );
			}
			if( isKey_2( device, key ) == 1 )
			{
				if ( ir_on == IR_OFF )
				{
					ir_on = IR_ON;
					oldtoggle = -1;
				}
				channel = sendCode( 0x02 );
			}
			if( isKey_3( device, key ) == 1 )
			{
				if ( ir_on == IR_OFF )
				{
					ir_on = IR_ON;
					oldtoggle = -1;
				}
				channel = sendCode( 0x03 );
			}
			if( isKey_4( device, key ) == 1 )
			{
				if ( ir_on == IR_OFF )
				{
					ir_on = IR_ON;
					oldtoggle = -1;
				}
				channel = sendCode( 0x04 );
			}
			if( isKey_5( device, key ) == 1 )
			{
				if ( ir_on == IR_OFF )
				{
					ir_on = IR_ON;
					oldtoggle = -1;
				}
				channel = sendCode( 0x05 );
			}
			// wenn der Kanal gewechselt wurde, wird der neue Wert ins EEPROM
			// geschrieben
			if ( currentChannel != channel )
			{
				eeprom_write_byte(&eeChannel, channel);
				currentChannel = channel;
			}
		}
  	}
}

