/*
    Title:    1,1 Zoll Pictiva mit ATMEGA32
	Version:  1.00
    Author:   Jens Dietrich
    Start:    06/01/2007
    Date:     07/01/2007
    Purpose:  3,3 Volt
    Software: AVR-GCC
    Hardware: ATMEGA32 + 3,3 Volt + 1 zoll SSD1303
    Note:     http://www.icplan.de
	File:     pictiva.c
*/

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "pictiva.h"
#include <compat/ina90.h>  


typedef struct{ 
unsigned char second;   //enter the current time, date, month, and year
unsigned char minute;
unsigned char hour;                                     
unsigned char date;       
unsigned char month;
unsigned int year;      
            }time;

time t;
unsigned char ticker;
unsigned char update;
unsigned char setup;
char weekday;
char wcase;
int temp0,temp1;  

int main(void)
{
	PORTB = 0x00;			    									/* port B ausgaenge auf low */
	DDRB =0xff;  				    								/* port B output */

	DDRD =0xc0;
	PORTD = 0x7F;	
	
	update = 1;
	setup = 0;

     
    for(temp0=0;temp0<0x0001;temp0++)   // Wait for external clock crystal to stabilize
    {
        for(temp1=0;temp1<0xFFFF;temp1++);
    }


	EICRA |= (1<<ISC01);					//Set Interupt on falling Edge
	EICRA &=~(1<<ISC00);
	EIMSK |= (1<<INT0);	
	
	EICRA |= (1<<ISC11);					//Set Interupt on falling Edge
	EICRA &=~(1<<ISC10);
	EIMSK |= (1<<INT1);	

	DDRB=0xFF;           
    
	TIMSK2 &=~((1<<TOIE2)|(1<<OCIE2B));  //Disable TC0 interrupt

 	ASSR |= (1<<AS2);           //set Timer/Counter0 to be asynchronous from the CPU clock 
                                //with a second external clock(32,768kHz)driving it.  
        
    TCNT2 = 0x00;
    TCCR2B = 0x05;                 //prescale the timer to be clock source / 128 to make it
                                //exactly 1 second for every overflow to occur
    while(ASSR&0x07);           //Wait until TC0 is updated
    TIMSK2 |= (1<<TOIE2);        //set 8-bit Timer/Counter0 Overflow Interrupt Enable                             
  // _SEI();                     //set the Global Interrupt Enable Bit  
    sei();       
       


	// DDRC = 0xfc;													/* bit 0,1 I2C rest ausgaenge */

	// PORTD = 0b00000010;		    									/* port c bit txd mit pulup */
	// DDRD =  0b11111100; 		                              		/* bit 2-7 output ; bit 0,1 input rs232 */
	// PORTD = 0x00;
	// DDRD =  0b11111110; 		                              		/* bit 1-7 output ; bit 0 input rs232 */

	// TWBR = 0x80;													/* i2c bitrate etwa 40 khz */
	// UBRRL = 103;													/* 51=8 103=16 mhz bei 9600 baud */
	// UCSRB = 0x98;													/* rx, tx + inter bei rx einschalten */
 	
	// TCCR1B = 0x03;	    											/* CK/8 = 0x02 */
	// TIMSK = 0x04;		    										/* timer 1 overflow enable */
    
    															
 	PORTB &= ~(0x01);											/* Set Reset (Clear)*/

    delay(200);
	
   	PORTB |= (0x01);											/* reset display (Set)*/
 	
 	delay(200);
   

// pictiva 1303 display laut herstellerangaben initialisieren

	send_data(0x01); 												/* Set Lower Column Address */
	send_data(0x10); 												/* Set Higher Column Address */

	send_data(0x40); 												/* Set Display Start Line */

	send_data(0x81); 												/* Set Contrast Control */
	send_data(0x8f); 												/* 0 ~ 127 */

	send_data(0xA1); 												/* [A0]:column address 0 is */
	send_data(0xC8); 												/* oben / unten */

	send_data(0xA4); 												/* A4=ON */

	send_data(0xA6);  												/* Normal Display*/

	send_data(0xA8); 												/* Set Multiplex Ratio */
	send_data(0x23);

	send_data(0xAD); 												/* Set DC-DC */
	send_data(0x8B); 												/* 8B=ON, 8A=Off */

	send_data(0xAF);  												/* AF=ON , AE=OFF*/

	send_data(0xD3); 												/* Set Display Offset */
	send_data(0x00); 												/* No offset */

	send_data(0xD5); 												/* Set Clock Divide */
	send_data(0xf1); 												/* Set to 80Hz */

	send_data(0xD8); 												/* Set Area Color On or Off */
	send_data(0x00); 												/* Mono Mode */

	send_data(0xDA); 												/* Set Pins Hardware */
	send_data(0x12);

	send_data(0xDB); 												/* Set VCOMH */
	send_data(0x20);

	send_data(0xD9); 												/* Set VP */
	send_data(0x22); 												/* P1=2 , P2=2 */
    

    	
    t.hour=17;
    t.minute=5;
    
    t.year=2007;
    t.month=8;
    t.date=21;
    
    	
	oled_clear();


//zeichen(0x7e,0,3);
//zeichen(0x7f,15,3);
/*zeichen(' ',4,2);
zeichen('S',5,2);
zeichen('e',6,2);
zeichen('t',7,2);
zeichen('u',8,2);
zeichen('p',9,2);
zeichen(' ',10,2);
*/

wcase=0;

ticker=0;

    for (;;)
	{
	
	
	switch (wcase)
{


	case 0:
	if (update==1)
	{
		zeichen('A',setup,4);
		
	zeichen('-',1,2);
	zeichen('-',2,2);
	zeichen('-',3,2);
	zeichen('-',4,2);
	zeichen('-',5,2);
	zeichen('-',6,2);
	zeichen('-',7,2);
	zeichen('-',8,2);
	zeichen('-',9,2);
	zeichen('-',10,2);
	zeichen('-',11,2);
	zeichen('-',12,2);
	zeichen('-',13,2);
	zeichen('-',14,2);
	
	zeichen_x(48+t.hour/10,1,0);
	zeichen_x(48+t.hour%10,3,0);
	zeichen('.',5,0);
	zeichen('.',5,1);
    zeichen_x(48+t.minute/10,6,0);
	zeichen_x(48+t.minute%10,8,0);
	zeichen('.',10,1);
	zeichen_x(48+t.second/10,11,0);
	zeichen_x(48+t.second%10,13,0);
	zeichen(48+t.date/10,5,3);
	zeichen(48+t.date%10,6,3);
	zeichen('.',7,3);
	zeichen(48+t.month/10,8,3);
	zeichen(48+t.month%10,9,3);
	zeichen('.',10,3);
	zeichen(48+t.year/1000,11,3);
	zeichen(48+t.year/100%10,12,3);
	zeichen(48+t.year/10%10,13,3);
	zeichen(48+t.year%10,14,3);
	

	weekday=dayofweek(t.year,t.month,t.date);
	zeichen(pgm_read_byte(&week_code[weekday*3]),1,3);
	zeichen(pgm_read_byte(&week_code[weekday*3+1]),2,3);
	zeichen(pgm_read_byte(&week_code[weekday*3+2]),3,3);
	
	send_data(0x81); 												/* Set Contrast Control */
	send_data(0x7f+((PIND&0x20)<<2)); 								/* 0 ~ 127 */
	
	update=0;
	}
	if (ticker>20) {wcase=3;};
	
	
	break;
	
		case 1:
	if (update==1)
	{
	
	zeichen_x(48+t.hour/10,1,1);
	zeichen_x(48+t.hour%10,3,1);
	zeichen('.',5,1);
	zeichen('.',5,2);
    zeichen_x(48+t.minute/10,6,1);
	zeichen_x(48+t.minute%10,8,1);
	zeichen('.',10,2);
	zeichen_x(48+t.second/10,11,1);
	zeichen_x(48+t.second%10,13,1);
	
	send_data(0x81); 												/* Set Contrast Control */
	send_data(0x7f+((PIND&0x20)<<2)); 								/* 0 ~ 127 */
	
	update=0;
	}

	break;
	
	case 2:
	oled_clear();
	update=1;
	wcase=1;
	
	break;
	
	//Go to sleep
	case 3:
	oled_clear();
	
	send_data(0xAE);  												/* AF=ON , AE=OFF*/
	
	// send_data(0xD8); 												/* Set Area Color On or Off */
	// send_data(0x05); 												/* Mono Mode */

	// send_data(0xA4); 												/* A4=OFF  */
	
	// send_data(0xAD); 												/* Set DC-DC */
	// send_data(0x8A); 												/* 8B=ON, 8A=Off */
	
	
	PORTD = 0xFF;													/* Turn Light Sensor OFF */
	
	wcase=4;
	
	break;
	
	// Sleep
	case 4:
	
	SMCR = 0x07;           //entering sleeping mode: power save mode
    _SLEEP();              //will wake up from time overflow interrupt    830uA / 5.6uA
    _NOP();
    
    
    TCCR2B=0x05;           // Write dummy value to Control register
    while(ASSR&0x07);     //Wait until TC0 is updated
	
	
	break;
	
	//Wake Up
	case 5:

	PORTD = 0x7F;	
	send_data(0xAF);  	
	
	ticker=0;
	wcase=0;
	break;
	
	
	
	
	}					//Switch
	
	};
}

void oled_clear(void)
{
  unsigned char a, b;
  for(a=0;a<5;a++)
	for(b=0;b<16;b++)zeichen(' ',b,a);
}

// zeichenausgabe vierfache höhe an bestimmte displayposition - display hat jetzt nur 4 spalten/zeichen und 2 zeilen
// z=zeichen 0x20-0x7f ; spalte=0-3 ; zeile=0-1

void zeichen_big2 (unsigned char z, unsigned char spalte, unsigned char zeile)
{
  unsigned char a, b, c, d, e, f;
  unsigned int stelle;

  if((z<0x20)||(z>0x7f))z=0x20;
  stelle = 6*(z-0x20);
  
  a = 24*(spalte&0x03)+36;
  e = (a&0x0f);
  send_data(e);														// low spalte setzen
  f = ((((a&0xf0)>>4)+0x10));
  send_data(f);						  								// high spalte setzen
  d = 4*(zeile&0x01);

  for(c=0;c<6;c++)
  {
    a = pgm_read_byte(&z_code[stelle++]); 
    send_data(0xb0 + d);											// erste zeile setzen
    b = 0;
    if(a&0x01)b|=0x0f;												// die vier doppelten punkte setzen
    if(a&0x02)b|=0xf0;
    send_char(b);
    send_char(b);
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl
    send_data(e);													// low spalte setzen
    send_data(f);													// high spalte setzen
    send_data(0xb1 + d);											// zweite zeile setzen
	b = 0;
    if(a&0x04)b|=0x0f;
    if(a&0x08)b|=0xf0;
    send_char(b);
    send_char(b);
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl
    send_data(e);													// low spalte setzen
    send_data(f);													// high spalte setzen
    send_data(0xb2 + d);											// dritte zeile setzen
	b = 0;
    if(a&0x10)b|=0x0f;
    if(a&0x20)b|=0xf0;
    send_char(b);
    send_char(b);
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl
    send_data(e);													// low spalte setzen
    send_data(f);													// high spalte setzen
    send_data(0xb3 + d);											// vierte zeile setzen
	b = 0;
    if(a&0x40)b|=0x0f;
    if(a&0x80)b|=0xf0;
    send_char(b);
    send_char(b);
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl

	e = e + 4;														// spaltenadresse 4 fach erhöhen
	if(e>0x0f)
	{
	  e = e & 0x0f;
	  f = f + 1;
	}
  }
}

// zeichenausgabe doppelte höhe an bestimmte displayposition - display hat jetzt nur 8 spalten/zeichen und 4 zeilen
// z=zeichen 0x20-0x7f ; spalte=0-7 ; zeile=0-3

void zeichen_big (unsigned char z, unsigned char spalte, unsigned char zeile)
{
  unsigned char a, b, c, d, e, f;
  unsigned int stelle;

  if((z<0x20)||(z>0x7f))z=0x20;
  stelle = 6*(z-0x20);
  
  a = 12*(spalte&0x07)+36;
  e = (a&0x0f);
  send_data(e);														// low spalte setzen
  f = ((((a&0xf0)>>4)+0x10));
  send_data(f);						  								// high spalte setzen
  d = 2*(zeile&0x03);

  for(c=0;c<6;c++)
  {
    a = pgm_read_byte(&z_code[stelle++]); 
    send_data(0xb0 + d);											// obere zeile setzen
    b = 0;
    if(a&0x01)b|=0x03;												// die vier doppelten punkte setzen
    if(a&0x02)b|=0x0c;
    if(a&0x04)b|=0x30;
    if(a&0x08)b|=0xc0;
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl
    send_data(e);													// low spalte setzen
    send_data(f);													// high spalte setzen
    send_data(0xb1 + d);											// untere zeile setzen
    b = 0;
    if(a&0x10)b|=0x03;												// die vier doppelten punkte setzen
    if(a&0x20)b|=0x0c;
    if(a&0x40)b|=0x30;
    if(a&0x80)b|=0xc0;
    send_char(b);
    send_char(b);
    send_data(0xe3);												// nop befehl
	
	e = e + 2;														// spaltenadresse 2 fach erhöhen
	if(e>0x0f)
	{
	  e = e & 0x0f;
	  f = f + 1;
	}
  }
}

// zeichenausgabe an bestimmte displayposition - display hat 16 spalten/zeichen und 8 zeilen
// z=zeichen 0x20-0x7f ; spalte=0-15 ; zeile=0-7

void zeichen (unsigned char z, unsigned char spalte, unsigned char zeile)
{
  unsigned char a;
  unsigned int stelle;

  if((z<0x20)||(z>0x7f))z=0x20;
  stelle = 6*(z-0x20);
  
  a = 6*(spalte&0x0f)+36;
  send_data(a&0x0f);												// low spalte setzen
  send_data((((a&0xf0)>>4)+0x10));  								// high spalte setzen
  a = (zeile&0x07);
  send_data(0xb0 + a);												// zeile setzen
  
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  a = pgm_read_byte(&z_code[stelle++]); 
  send_char(a);
  send_data(0xe3);													// nop befehl
}

void zeichen_x (unsigned char z, unsigned char spalte, unsigned char zeile)
{
  unsigned char a,i;
  unsigned int stelle;

  if((z<0x30)||(z>0x39))z=0x30;
  stelle = 24*(z-0x30);
  
  a = 6*(spalte&0x0f)+36;
  send_data(a&0x0f);												// low spalte setzen
  send_data((((a&0xf0)>>4)+0x10));  								// high spalte setzen
  a = (zeile&0x07);
  send_data(0xb0 + a);												// zeile setzen
  
  for(i=0;i<12;i++)
  {
  a = pgm_read_byte(&x_code[stelle++]); 
  send_char(a);
  }
  send_data(0xe3);	
  
  a = 6*(spalte&0x0f)+36;
  send_data(a&0x0f);												// low spalte setzen
  send_data((((a&0xf0)>>4)+0x10));  								// high spalte setzen
  a = ((zeile+1)&0x07);
  send_data(0xb0 + a);												// zeile setzen
  
    for(i=0;i<12;i++)
  {
   a = pgm_read_byte(&x_code[stelle++]); 
  send_char(a);
	}
  send_data(0xe3);	
  
  // nop befehl
}

void send_char(unsigned char zeichen)
{
  PORTB |= (0x02);													// Set Char (Set D/C)		
  senden_spi(zeichen);
}

void send_data(unsigned char zeichen)
{
  PORTB &= ~(0x02);													// Set Data (Clear D/C)	
  senden_spi(zeichen);
  PORTB |= (0x02);													// Clear D/C
}

void senden_spi(unsigned char zeichen)
{
  unsigned char a=0, b=0b10000000;
  PORTB &= ~(0x04);													// Clear Chipselect 
  PORTB |= (0x20);													// Set Clock		

  for(a=0;a<8;a++)
  { 
    if(zeichen&b)PORTB |= (0x08);									// Set Data
    else PORTB &= ~(0x08);											// Clear Data
	PORTB &= ~(0x20);												// clock low
    PORTB |= (0x20);												// clock high 
    b=b>>1;
  }
  PORTB |= (0x04);													// cs high
}




//interrupt [TIMER2_OVF_vect] void counter(void) //overflow interrupt vector
ISR(TIMER2_OVF_vect)
//void counter(void)
{ 
    ticker+=1;
    if (++t.second==60)        //keep track of time, date, month, and year
    {
        t.second=0;
        if (++t.minute==60) 
        {
            t.minute=0;
            if (++t.hour==24)
            {
                t.hour=0;
                if (++t.date==32)
                {
                    t.month++;
                    t.date=1;
                }
                else if (t.date==31) 
                {                    
                    if ((t.month==4) || (t.month==6) || (t.month==9) || (t.month==11)) 
                    {
                        t.month++;
                        t.date=1;
                    }
                }
                else if (t.date==30)
                {
                    if(t.month==2)
                    {
                       t.month++;
                       t.date=1;
                    }
                }              
                else if (t.date==29) 
                {
                    if((t.month==2) && (not_leap()))
                    {
                        t.month++;
                        t.date=1;
                    }                
                }                          
                if (t.month==13)
                {
                    t.month=1;
                    t.year++;
                }
            }
        }
    }  
 	update=1;
}  


ISR(INT0_vect)
{

if (wcase==4) {wcase=5;}

if ((setup==1) && (update==0)) 
{t.minute+=1;
if (t.minute>59) {t.minute=0;} update=1;}
if ((setup==2) && (update==0)) 
{t.hour+=1;
if (t.hour>23) {t.hour=0;} update=1;}
if ((setup==3) && (update==0)) 
{t.date+=1;
if (t.date>32) {t.date=0;} update=1;}
if ((setup==4) && (update==0)) 
{t.month+=1;
if (t.month>12) {t.month=0;} update=1;}
if ((setup==5) && (update==0)) {t.year+=1;update=1;}


}

ISR(INT1_vect)
{

setup=setup+1;
if (setup>5) {setup=0;}

}



char not_leap(void)      //check for leap year
{
    if (!(t.year%100))
        return (char)(t.year%400);
    else
        return (char)(t.year%4);
}         

char dayofweek(int y, int m, int d)      /* 0 = Sonntag */ 
                                    /* 1 <= m <= 12,  y > 1752 oder so */ 
{ 
  static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; 

    y -= m < 3; 

  return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; 
}

 
 
void delay(unsigned char d)										// zeitschleife
{
  unsigned char i, j;
  for (i=0; i<d; i++)
	for (j=0; j<255; j++);
}




