/********************************
*     AK Rotating LED POV       *
*********************************
* Author: Aki Korhonen          *
* First version: 20-07-2008     *
* Current:       26-07-2008     *
*********************************/

// ------------------------------------------------ //

// ------------------------------------------
// Configuration and Security bits for ATmega
// ------------------------------------------
// WDTON		= 1
// SPIEN		= 0 [X]
// CKOPT		= 0 [X]
// EESAVE		= 1
// BOOTSZ1	= 0 [X]
// BOOTSZ0	= 0 [X]
// BOOTRST	= 1
// BODLEVEL	= 1
// BODEN		= 1
// SUT1		= 1
// SUT0		= 1
// CKSEL3		= 1
// CKSEL2		= 0 [X]
// CKSEL1		= 1
// CKSEL0		= 1
// ------------------------------------------

// ------------------------------------------------ //

#define XTAL 14000000UL	// 14 MHz crystal
#define F_CPU XTAL // F_CPU is needed by delay.h etc
#define BAUD 9600 // UART speed
#define BUFFER_SIZE 12 // UART buffer size

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

// 74HC595 data lines
#define SR_595_PORT	PORTB
#define SR_595_SER	0 // Serial data
#define SR_595_RCK	1 // Latch
#define SR_595_SCK	2 // Serial clock
#define SR_595_CLR	3 // Clear

// Misc. lines
#define MISC_PORT	PORTD
#define IR_TRIGGER	2
#define PAD34		3
#define OK_IR_LED	5
#define PAD38		6
#define PAD36		7

// UART buffer, fill with 0x00
uint8_t cmd_buffer[BUFFER_SIZE] = {0};

// Misc
unsigned int i = 0;
unsigned int o = 0;
unsigned int p = 0;
unsigned int second_counter = 0;
int counter = 0;

static int values[32] = {0};

// Display buffer
#define DISP_BUF_SIZE 76

uint8_t display_data_red[] = {
0x00, 0x00, 0xFE, 0x7F, 0x02, 0x40, 0x02, 0x40, 
0xF2, 0x5F, 0x82, 0x40, 0x82, 0x40, 0x02, 0x5F, 
0x02, 0x40, 0x02, 0x40, 0x02, 0x4F, 0x82, 0x54, 
0x82, 0x54, 0x02, 0x57, 0x02, 0x40, 0x02, 0x40, 
0x02, 0x40, 0x02, 0x40, 0x12, 0x50, 0xF2, 0x5F, 
0x02, 0x50, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 
0x12, 0x50, 0xF2, 0x5F, 0x02, 0x50, 0x02, 0x40, 
0x02, 0x40, 0x02, 0x40, 0x02, 0x4F, 0x82, 0x50, 
0x82, 0x50, 0x02, 0x4F, 0x02, 0x40, 0x02, 0x40, 
0xFE, 0x7F, 0x00, 0x00
};

uint8_t display_data_green[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 
0xFF, 0xFF, 0xFF, 0xFF
};

// ------------------------------------------------ //

void SR_595_delay(void)
{
	uint8_t x = 0;
	
	while(x != 1)
	{
		x++;
	}
}

void SR_595_reset(void)
{
	SR_595_PORT &= ~(_BV(SR_595_CLR));
	SR_595_delay();
	SR_595_PORT |= _BV(SR_595_CLR);
}

void SR_595_clock(void)
{
	SR_595_PORT |= _BV(SR_595_SCK);
	//SR_595_delay();
	SR_595_PORT &= ~(_BV(SR_595_SCK));
}

void SR_595_send_bit(int bit)
{
	if(bit == 1)
	{
		SR_595_PORT |= _BV(SR_595_SER);
	}
	else
	{
		SR_595_PORT &= ~(_BV(SR_595_SER));
	}
	
	//SR_595_delay();
	
	SR_595_clock();
}

void SR_595_latch(void)
{
	SR_595_PORT |= _BV(SR_595_RCK);
	//SR_595_delay();
	SR_595_PORT &= ~(_BV(SR_595_RCK));
}

// ------------------------------------------------ //

SIGNAL(SIG_INTERRUPT0)
{
	// Disable interrupts
	cli();
	
	// Start of display area
	
	uint8_t i = 0;
	uint8_t o = 0;
	
	uint8_t value = 0;
	
	for(o=0 ; o<DISP_BUF_SIZE ; o+=2)
	{
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_red[o] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_red[o+1] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_green[o] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_green[o+1] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		
		SR_595_latch();
		
		_delay_ms(0.4);
	}
	
	// Clear the display
	for(i=0 ; i<32 ; i++)
	{
		SR_595_send_bit(0);
	}
	
	SR_595_latch();
	
	// Enable interrupts
	sei();
}

// ------------------------------------------------ //

ISR(TIMER0_OVF_vect)
{
	if(counter == 1)
	{
		counter = 0;
	}
	else
	{
		counter++;
	}
	
	// When second_counter reaches 800, about one second has passed
	second_counter++;
	
	if(second_counter == 800)
	{
		// One second passed, do what ever needed
		second_counter = 0;
	}
}

// ------------------------------------------------ //

int Uart_putchar(uint8_t character)
{
	// Send characters to the serial line
	
	loop_until_bit_is_set(UCSRA, UDRE); // Wait until the last character is sent
	UDR = character;
	return 0;
}

SIGNAL(SIG_UART_RECV)
{
	// UART interrupt function to receive characters from the serial line
	
	uint8_t character;
	character = UDR;
	
	cmd_buffer[o] = character;
	//Uart_putchar(character);
	
	o++;
	if(o == BUFFER_SIZE) o = 0;
}

void Init_USART(void)
{
	// Initialize UART
	
	// USART Mode: Asynchronous
	UBRRH = 0x00; // write high byte first
	UBRRL = ((XTAL/16)/BAUD)-1; // baud rate 9600
	UCSRA = 0x00;
	UCSRB |= (1 << 3); // TXEN ON, TX ON,        xxxx 1xxx
	UCSRB |= (1 << 4); // RXEN, RX ON,           xxx1 1xxx
	UCSRB |= (1 << 7); // RXIE, RX interrupt ON, 1xx1 1xxx
	UCSRC = 0x86; // 8 Data, 1 Stop, No Parity   1xxx x11x
	//SREG = (1 << 7); // global interrupt ON      1xxx xxxx
}

// ------------------------------------------------ //

int main(void)
{
	DDRC = 0x00;	// Port C input
	PORTC = 0xFF;	// Enable internal pull-ups
	
	DDRB = 0xFF;	// Port B output
	PORTB = 0x00;	// Set all outputs low
	
	DDRD = 0b00100000;
	PORTD = 0b00001100;
	
	SR_595_PORT |= _BV(SR_595_CLR); // Set 595 clear high
	
	// Disable global interrupts
	cli();
	
	// Initialize timers
	TCCR0 = 0x00;
	TCNT0 = 0x00;
	TCCR0 = 0b00000011;
	
	MCUCR |= _BV(ISC01); // INT0 on falling edge
	GICR |= _BV(INT0); // Enable INT0
	TIMSK = 0b00000001;
	
	// Initialize UART
	Init_USART();
	
	// Enable global interrupts
	sei();
	
	// Start
	
	for(o=0 ; o<32 ; o++)
	{
		SR_595_send_bit(0);
	}
	SR_595_latch();
	
	while(1)
	{
		//
	}
}
