// 16 X 5 LED Array
// By - Nandan Banerjee
//		20th of June, 2012

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

#define F_CPU 	11059200UL
#include <util/delay.h>

#include "font.h"
#include "uart.h"

#define true	1
#define false	0
#define _NOP()	{}

volatile uint8_t led_glow[5][16], size_command, read_comm, lastSize, spec_mode;
volatile char command[200];

void timerInit(void)
{
	TCCR0 = (1<<CS01) | (1<<CS00);
	TIMSK = (1<<TOIE0);
}

ISR(USART_RXC_vect)
{
    // USART RX interrupt
	unsigned char c;
	c = UDR;
	
	if (c == 'x')						// End of command
	{
		command[size_command++] = '\0';
		/*uart_putc(10);
		uart_putc(13);
		uart_puts(PSTR("Writing on the LED Array."));
		uart_putc(10);
		uart_putc(13);*/
		read_comm = true;
		lastSize = size_command;
		size_command = 0;
		//timerInit();
	}
	else
	{
		read_comm = false;
		if (c == 0x7F)
			size_command--;
		else
			command[size_command++] = c;
		//uart_putc(c);
		//TCCR0 = 0x00;
		//TIMSK = 0x00;
	}
}

ISR(TIMER0_OVF_vect)
{
	uint8_t row, col;
	
	for (row = 0; row < 8; row++)
	{
		PORTB = ~(1<<row);		
		for (col = 0; col < 16; col++)
		{
			if (led_glow[row][col])
			{	
				if (col > 7)
				{
					__asm__("nop\n\t");
					PORTC = (1<<(col - 8));
					PORTA = 0x00;
				}
				else
				{
					PORTA = (1<<col);
					PORTC = 0x00;
				}
			}
			else
			{
				PORTC = 0x00;
				PORTA = 0x00;
			}
		}
		PORTC = 0x00;
		PORTA = 0x00;
	}
}

void testAllLeds(void)
{
	uint8_t row, col;
	
	for (row = 0; row < 8; row++)
		for (col = 0; col < 16; col++)
		{
			led_glow[row][col] = true;
			_delay_ms(50);
		}
		
	for (row = 0; row < 8; row++)
		for (col = 0; col < 16; col++)
		{
			led_glow[row][col] = false;
			_delay_ms(50);
		}
}

void font_get(char match, char *buf) {
  // copies the character "match" into the buffer
  uint8_t i;
  PGM_P p;
  
  for(i=0; i<FONT_SIZE; i++) {
    memcpy_P(&p, &font[i], sizeof(PGM_P));
    
    if(memcmp_P(&match, p,1)==0) {
      memcpy_P(buf, p, 7);
      return;
    }
  }
  
  // NO MATCH?
  font_get('?', buf);
}

uint8_t font_width(char c) {
  char buf[7];
  buf[1] = 0;
  
  font_get(c, buf);
  
  return buf[1];
}

void printString(const char* str)
{
	uint8_t length, row, col = 15, count, count2;
	char temp, buf[7];
	
	while (pgm_read_byte(str) != 0x00)
	{
		count = 0;
		temp = pgm_read_byte(str++);
		font_get(temp, buf);
		length = font_width(temp);
		while (count < length)
		{
			for (row = 0; row < 5; row++)
				led_glow[row][col] = (buf[count + 2] & (1<<row));
			count++;
			col++;
			if (col == 16)
			{
				// Shift the columns by 1 column to the left
				_delay_ms(75);
				for (count2 = 0; count2 < 15; count2++)
					for (row = 0; row < 5; row++)
						led_glow[row][count2] = led_glow[row][count2 + 1];
				col = 15;
			}
		}
		for (row = 0; row < 5; row++)
			led_glow[row][col] = false;
		col++;
		if (col == 16)
		{
			// Shift the columns by 1 column to the left
			_delay_ms(75);
			for (row = 0; row < 5; row++)
				for (count2 = 0; count2 < 15; count2++)
					led_glow[row][count2] = led_glow[row][count2 + 1];
			col = 15;
		}
	}
}

void printCommand()
{
	uint8_t length, row, col = 15, count, count2, count3 = 0;
	char temp, buf[7];
	
	while (count3 < lastSize - 1)
	{
		count = 0;
		temp = command[count3++];
		font_get(temp, buf);
		length = font_width(temp);
		while (count < length)
		{
			for (row = 0; row < 5; row++)
				led_glow[row][col] = (buf[count + 2] & (1<<row));
			count++;
			col++;
			if (col == 16)
			{
				// Shift the columns by 1 column to the left
				_delay_ms(75);
				for (count2 = 0; count2 < 15; count2++)
					for (row = 0; row < 5; row++)
						led_glow[row][count2] = led_glow[row][count2 + 1];
				col = 15;
			}
		}
		for (row = 0; row < 5; row++)
			led_glow[row][col] = false;
		col++;
		if (col == 16)
		{
			// Shift the columns by 1 column to the left
			_delay_ms(75);
			for (row = 0; row < 5; row++)
				for (count2 = 0; count2 < 15; count2++)
					led_glow[row][count2] = led_glow[row][count2 + 1];
			col = 15;
		}
	}
}

void displaySpectrum()
{
	uint8_t row = 0, col = 0;
	
	for (col = 0; col < 16; col++)
		for (row = 0; row < 8; row++)
		{
			led_glow[row][col] = ((command[col + 1]) & (1<<row));
		}
}

int main(void)
{		
	uint8_t counter, row, col;
	
	MCUCSR |= (1<<7);
	MCUCSR |= (1<<7);
	
	DDRA = 0xFF;
	DDRC = 0xFF;
	DDRB = 0xFF;
	//DDRD = ((1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6));
	
	timerInit();
	uart_init();
	sei();

	for (row = 0; row < 5; row++)
		for (col = 0; col < 16; col++)
			led_glow[row][col] = false;
	printString(PSTR("HELLO WORLD!"));
	size_command = 0;
	
	while(1)
	{				
		if (read_comm == true)
		{
			if (command[0] == 's')
			{
				displaySpectrum();
			}
			else
			{
				printCommand();
				printString(PSTR("     "));
			}
		}
		else
		{
			//testAllLeds();
			//printString(PSTR(" PLEASE SET THE DESIRED DATA TO BE DISPLAYED USING THE SERIAL COMMUNICATION MODE! THANK YOU."));
			//printString(PSTR("     "));
		}
	}
		
	return 0;
}

// BLACK 	- GND
// BROWN 	- Vcc
// RED	 	- TxD
// ORANGE 	- RxD