/***********************************************************************
Includes
***********************************************************************/
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/io.h>
#include <stdarg.h>
#include <math.h>
#include <avr/interrupt.h>


/***********************************************************************
Port, PINS and LCD defines 
***********************************************************************/
// Data port
#define LCDDATAPIN 	   PINC
#define LCDDATADDR     DDRC
#define LCDDATAPORT    PORTC
// Control port
#define LCDCTLPIN 	   PIND     
#define LCDCTLDDR	   DDRD
#define LCDCTLPORT	   PORTD
// Control pins
#define RST			   0x10
#define CS			   0x08  
#define RD             0x04
#define WR             0x02
#define A0             0x01  	  
// LCD display data
#define LCD_X_SIZE     320
#define LCD_Y_SIZE     240
#define LCD_XTAL       10000000


/***********************************************************************
Prototypes
***********************************************************************/
// General LCD functions
unsigned char lcd_read_data(void);
void lcd_write_data(unsigned char data);
void lcd_write_command(unsigned char command);
void lcd_init(void);
void lcd_delay(void);
// Text functions
void lcd_clear_text(void);
void lcd_goto(unsigned char column, unsigned char line);
void lcd_write_string(char *ptr);
void lcd_show_cursor(unsigned char show);
// Graphic functions
void lcd_clear_graphics(void);
void lcd_pixel(int x, int y, unsigned char show);
void lcd_rectangle(int x1, int y1, int x2, int y2, unsigned char show);
void lcd_line(int x1, int y1, int x2, int y2, unsigned char show);
void lcd_circle(int x, int y, int radius, unsigned char show);
void lcd_degree_line(int x, int y, int degree, int inner_radius, int outer_radius, unsigned char show);


/***********************************************************************
Makes a delay at approximate 2mS at 8MHz
***********************************************************************/
void lcd_delay(void)
{
  int i,x;
  for (i=0;i<180;i++)
  {
    for (x=1;x<20;x++);
    asm("WDR");
  }
}


/***********************************************************************
Write specified command to LCD panel.
This is an internal function...
***********************************************************************/
void lcd_write_command(unsigned char command)
{
  LCDDATAPORT = command;
  LCDCTLPORT |= A0;   
  LCDCTLPORT &= ~(WR);
  LCDCTLPORT |= WR;   
}


/***********************************************************************
Write specified data to LCD panel.
This is an internal function...
***********************************************************************/
void lcd_write_data(unsigned char data)
{
  LCDDATAPORT = data;
  LCDCTLPORT &= ~(A0);
  LCDCTLPORT &= ~(WR);
  LCDCTLPORT |= WR;   
}


/***********************************************************************
Read data from LCD panel at position given by lcd_pixel command.
This is an internal function...
***********************************************************************/
unsigned char lcd_read_data(void)
{
  unsigned char data;
  int i;
  LCDDATADDR = 0x00;    
  LCDCTLPORT |= A0;     
  LCDCTLPORT &= ~(RD);  
  data = LCDDATAPIN;    
  data = LCDDATAPIN;    
  data = LCDDATAPIN;    
  LCDCTLPORT |= RD;     
  LCDDATADDR = 0xff;    
  return data;
}


/***********************************************************************
Initialize the LCD controller. Read the documentation for the 
controller in use. If any other than SED1335 values can be wrong ! 
***********************************************************************/
void lcd_init(void)
{
  int i;
  // Setup LCD Data bus port as output
  LCDDATAPORT = 0x00;
  LCDDATADDR = 0xff;      
  // Setup LCD control bus port as output
  LCDCTLDDR |= 0xff;          
  LCDCTLPORT |= WR;           
  LCDCTLPORT |= RD;           
  // Reset the LCD
  LCDCTLPORT |= RST;
  lcd_delay();
  LCDCTLPORT &= ~RST;
  for (i=0; i<50; i++) lcd_delay();
  LCDCTLPORT |= RST; 
  // System
  lcd_write_command(0x40); 
  lcd_write_data(0x30);    
  lcd_write_data(0x87);    
  lcd_write_data(0x07);    
  lcd_write_data((LCD_X_SIZE/8)-1); 
  lcd_write_data((LCD_XTAL / 70 / LCD_Y_SIZE) / 9); 
  lcd_write_data(LCD_Y_SIZE - 1);  
  lcd_write_data(LCD_X_SIZE/8);    
  lcd_write_data(00);        
  // Scroll
  lcd_write_command(0x44); 
  lcd_write_data(0x00);    
  lcd_write_data(0x00);    
  lcd_write_data(LCD_Y_SIZE);
  lcd_write_data(0x00);      
  lcd_write_data(0x10);      
  lcd_write_data(LCD_Y_SIZE);
  // Horizontal scroll
  lcd_write_command(0x5a);  
  lcd_write_data(0x00);     
  // Overlay
  lcd_write_command(0x5b);  
  lcd_write_data(0x03);    
  // Display On/Off I
  lcd_write_command(0x58);
  lcd_write_data(0x14);     
  // Cursor format
  lcd_write_command(0x5d); 
  lcd_write_data(0x07);    
  lcd_write_data(0x87);    
  // Display On/Off II 
  lcd_write_command(0x59);   
  lcd_write_data(0x14);      
  // Curson direction  
  lcd_write_command(0x4c);          
  // CUrsor write  
  lcd_write_command(0x46);
  lcd_write_data(0x00); 
  lcd_write_data(0x00); 
}


/***********************************************************************
Clears the text layer / screen.
***********************************************************************/
void lcd_clear_text(void)
{
  int i;
  lcd_write_command(0x46);
  lcd_write_data(0x00);
  lcd_write_data(0x00);
  lcd_write_command(0x42); 
  for (i=0; i<((LCD_X_SIZE/8)*(LCD_Y_SIZE/8)); i++) lcd_write_data(' ');
  lcd_write_command(0x46); 
  lcd_write_data(0x00);
  lcd_write_data(0x00);
}


/***********************************************************************
Goto specified column and line. 1,1 is the upper left corner.
***********************************************************************/
void lcd_goto(unsigned char column, unsigned char line)
{
  int addr;
  unsigned char lo;
  unsigned char hi;
  column--;
  line--;
  addr = (line * (LCD_X_SIZE/8)) + column;
  lo = (unsigned char) (addr & 0x00ff);
  hi = (unsigned char) ((addr & 0xff00) >> 8);
  lcd_write_command(0x46);
  lcd_write_data(lo);
  lcd_write_data(hi);
}


/***********************************************************************
Write strings to the text layer. Set position with lcd_goto.
Text will wrap if to long to show on one line.
***********************************************************************/
void lcd_write_string(char *ptr)
{
  int i;
  lcd_write_command(0x42);
  while (*ptr != 0x00) lcd_write_data(*ptr++);
}


/***********************************************************************
Show or hide cursor. 0=Hide, 1=Show.
***********************************************************************/
void lcd_show_cursor(unsigned char show)
{
  lcd_write_command(0x59); 
  if (show == 0) lcd_write_data(0x14);
  else lcd_write_data(0x16);
}


/***********************************************************************
Clears the graphic layer / screen.
***********************************************************************/
void lcd_clear_graphics(void)
{
  int i;
  lcd_write_command(0x46);
  lcd_write_data(0x00);
  lcd_write_data(0x10);
  lcd_write_command(0x42); 
  for (i=0; i<((LCD_X_SIZE/8)*LCD_Y_SIZE); i++) lcd_write_data(0x00);
  lcd_write_command(0x46); 
  lcd_write_data(0x00);
  lcd_write_data(0x10);
}


/***********************************************************************
Write pixel data to the display at specified position.
Set show to 1 to draw pixel, set to 0 to hide pixel.
***********************************************************************/
void lcd_pixel(int x, int y, unsigned char show)
{
  unsigned int Address;
  unsigned char Offset;
  unsigned char low;
  unsigned char high;
  unsigned char byte;
  x--;
  y--;
  Address = (y * (LCD_X_SIZE/8)) + (x / 8);
  Offset = x - ((x / 8) * 8);
  low = (unsigned char) (Address & 0x00ff);
  high = (unsigned char) (((Address & 0xff00) >> 8) + 0x10);
  lcd_write_command(0x46);   
  lcd_write_data(low);       
  lcd_write_data(high);      
  lcd_write_command(0x43);   
  byte = lcd_read_data();    
  if (show != 0) byte |= (0x80 >> Offset);
  else byte &= (~(0x80 >> Offset));
  lcd_write_command(0x46); 
  lcd_write_data(low);    
  lcd_write_data(high);   
  lcd_write_command(0x42);
  lcd_write_data(byte); 
}


/***********************************************************************
Draws a rectangle from x1,y1 to x2,y2.
Set show to 1 to draw pixel, set to 0 to hide pixel.
***********************************************************************/
void lcd_rectangle(int x1, int y1, int x2, int y2, unsigned char show)
{
  int i;
  for (i=x1; i<=x2; i++) lcd_pixel(i,y1,show);
  for (i=x1; i<=x2; i++) lcd_pixel(i,y2,show); 
  for (i=y1; i<=y2; i++) lcd_pixel(x1,i,show);
  for (i=y1; i<=y2; i++) lcd_pixel(x2,i,show);
}


/***********************************************************************
Draws a line from x,y at given degree from inner_radius to outer_radius.
Set show to 1 to draw pixel, set to 0 to hide pixel.
***********************************************************************/
void lcd_degree_line(int x, int y, int degree, int inner_radius, int outer_radius, unsigned char show)
{
  int fx,fy,tx,ty;
  fx = x + round(inner_radius * sin(degree * 3.14 / 180));
  fy = y - round(inner_radius * cos(degree * 3.14 / 180));
  tx = x + round(outer_radius * sin(degree * 3.14 / 180));
  ty = y - round(outer_radius * cos(degree * 3.14 / 180));
  lcd_line(fx,fy,tx,ty,show);
}


/***********************************************************************
Draws a circle with center at x,y with given radius.
Set show to 1 to draw pixel, set to 0 to hide pixel.
***********************************************************************/
void lcd_circle(int x, int y, int radius, unsigned char show)
{
  int xc = 0;
  int yc = radius;
  int p = 3 - (radius<<1);
  while (xc <= yc)  
  {
    lcd_pixel(x + xc, y + yc, show);
    lcd_pixel(x + xc, y - yc, show);
    lcd_pixel(x - xc, y + yc, show);
    lcd_pixel(x - xc, y - yc, show);
    lcd_pixel(x + yc, y + xc, show);
    lcd_pixel(x + yc, y - xc, show);
    lcd_pixel(x - yc, y + xc, show);
    lcd_pixel(x - yc, y - xc, show);
    if (p < 0)
      p += (xc++ << 2) + 6;
    else
      p += ((xc++ - yc--)<<2) + 10;
  }
}


/***********************************************************************
Draws a line from x1,y1 go x2,y2. Line can be drawn in any direction.
Set show to 1 to draw pixel, set to 0 to hide pixel.
***********************************************************************/
void lcd_line(int x1, int y1, int x2, int y2, unsigned char show) 
{
  int dy = y2 - y1;
  int dx = x2 - x1;
  int stepx, stepy, fraction;
  if (dy < 0) 
  {
    dy = -dy;
    stepy = -1;
  }
  else 
  {
    stepy = 1;
  }
  if (dx < 0)
  {
    dx = -dx;
    stepx = -1;
  }
  else
  {
    stepx = 1;
  }
  dy <<= 1;
  dx <<= 1;
  lcd_pixel(x1,y1,show);
  if (dx > dy)
  {
    fraction = dy - (dx >> 1); 
    while (x1 != x2)
    {
      if (fraction >= 0)
      {
        y1 += stepy;
        fraction -= dx;
      }
      x1 += stepx;
      fraction += dy;  
      lcd_pixel(x1,y1,show);
    }
  }
  else
  {
    fraction = dx - (dy >> 1);
    while (y1 != y2)
    {
      if (fraction >= 0)
      {
        x1 += stepx;
        fraction -= dy;
      }
      y1 += stepy;
      fraction += dx;
      lcd_pixel(x1,y1,show);
    }
  }
}

/***********************************************************************

***********************************************************************/


/***********************************************************************
Defines and globals 
***********************************************************************/
#define ClockX		   160
#define ClockY		   120
#define ClockOR		   40
#define ClockIR		   2
int sec = 0;
int min = 0;


/***********************************************************************
Main 
***********************************************************************/
void main()
{
  int i = 0;
  int o = 0;
  // Init the display
  lcd_init();
  lcd_clear_text();
  lcd_clear_graphics();
  // Disable all interrupts
  cli();
  // Set up timer 1 for about 1 second 
  TCCR1B = 0x00;
  TCNT1H = 0xC2;
  TCNT1L = 0xF7;
  OCR1AH = 0x3D;
  OCR1AL = 0x09;
  OCR1BH = 0x3D;
  OCR1BL = 0x09;
  TCCR1A = 0x00;
  TCCR1B = 0x04;
  MCUCR = 0x00;
  GICR = 0x00;
  TIMSK = 0x80;
 
  // Draw a frame
  lcd_rectangle(1,1,320,240,1);
  lcd_rectangle(3,3,318,238,1);
  // Write some text
  lcd_goto(10,4);
  lcd_write_string("  Baardsen Software");
  lcd_goto(10,5);
  lcd_write_string("   Svennahaugen 39");
  lcd_goto(10,6);
  lcd_write_string("5516 Haugesund, Norway");
  // DrawClockOutline();
  lcd_circle(ClockX,ClockY,ClockIR,1);
  lcd_circle(ClockX,ClockY,ClockOR,1);
  i = 0;
  while (i < 360)
  {
    lcd_degree_line(ClockX,ClockY,i,ClockOR-5,ClockOR,1);
	i = i + 30;
  }
  // Ok, then we enable interrupts and timer which will simulate
  // a good old analog clock. Enjoy !
  sei();
}


/***********************************************************************
Interrupt handler for Timer 1 - 1 Second 
***********************************************************************/
#pragma interrupt_handler timer1_ovf_isr:7
void timer1_ovf_isr(void)
{
  TCNT1H = 0xC0;
  TCNT1L = 0x00;
  // Draw seconds
  lcd_degree_line(ClockX,ClockY,sec,ClockIR+1,ClockOR-7,0); 
  sec = sec + 6;
  lcd_degree_line(ClockX,ClockY,sec,ClockIR+1,ClockOR-7,1); 
  // Draw minute
  lcd_degree_line(ClockX,ClockY,min,ClockIR+1,ClockOR-15,1); 
  // Some simple checking....
  if (sec >= 360) 
  {
    sec = 0;
    lcd_degree_line(ClockX,ClockY,min,ClockIR+1,ClockOR-15,0);
    min = min + 6;
    lcd_degree_line(ClockX,ClockY,min,ClockIR+1,ClockOR-15,1);
  }
  if (min >= 360)
  {
    min = 0;
  }
}


