//------------------------------ Digitální hodiny s alarmem ------------------------------
#define F_CPU 16000000					// frekvence krystalu
#include <avr/io.h>						// I/O vstupy ATmega16
#include <util/delay.h>					// knihovna časů
#include <avr/interrupt.h>				// knihovna přerušení
#define LCD				
#include "mojelib1.h"					// knihovna pro LCD
#include "twi_lib.h"					// knihovna pro I2C
#include "twi_lib.c"					
//*************************************************************************************
//   prototypy funkcí -----
void nacti (void);						// načte stav hodin a alarmu do paměti
void time (void);						// vypíše stav hodin na LCD
void readRTC (unsigned char adresa);						// čtení RTC
void writeRTC(unsigned char adresa,unsigned char data);		// zápis do RTC
void klavesnice (void);					// čtení z klávesnice
void lcd_alarm(void);					// výpis LCD pro nastavení alarmu
void poplach (void);					// spustí poplach - generování tónu
void load_alarm(void);					// nahraje do RTC nastavení alarmu
void error_time(void);					// vypíše na LCD varování o špatně nastaveném čase
unsigned char kontrola_formatu (void);	// kontroluje správnost zadané hodnoty hodin
void zmena_casu (void);					// podprogram pro zmenu času
void lcd_zmena (void);					// výpis na LCD pro změnu času
//*************************************************************************************
// definice konstant pro práci s RTC
#define READ_adresa 0xA3 	// adresa pro čtení (0xA2+1)
#define WRITE_adresa 0xA2	// adresa pro zápis
// adresace uvnitř PCF8563T - výběr požadované položky
#define sekundy_A 0x02	
#define minuty_A 0x03
#define hodiny_A 0x04
#define dny_A 0x05
#define den_A 0x06				// neděle až sobota (0 ... 6)
#define mesic_A 0x07
#define rok_A 0x08
#define alarm_hodiny_A 0x0A
#define alarm_minuty_A 0x09
#define control_status_1 0x01
#define alarm_den_A 0x0C		// den v týdnu (po - Ne)

// proměné pro čas --------------------------------
unsigned char sekundy;
unsigned char minuty;
unsigned char hodiny;
unsigned int hodnota;
unsigned char dny;
unsigned char mesic;
unsigned char rok;
unsigned char den;
// proměné pro alarm ------------------------------
unsigned char alarm_temp;		// vypne a zapne alarm
unsigned char alarm_hodiny;		
unsigned char alarm_minuty;		
unsigned char alarm_den;		// opakování - den v týdnu
unsigned char temp;				// temp =1 - nastav alarm, temp =2 nastav čas
unsigned char status;			// uložení status registru RTC
// proměné pro čtení z klávesnice
unsigned char key;				// stisknutá klávesa
unsigned char alarm[4];			// 4 znaky
unsigned int pocitadlo;			// indexace znaků
unsigned int stare_poc;

//*******************************************************************************
// hlavní program----------------------------------------------------------------
int main (void)
{
	DDRC = 0xff;
	PORTC = 0b11000010;
	DDRD=0b10110000;

	init_lcd();			//inicializace LCD
	_delay_ms(10);

	cls();				// smazání LCD displeje
	_delay_ms(10);

	i2c_init();			// inicializace I2C

	// nastavení přerušení INT0, INT1 na sestupnou hranu
	// INT0 - změna času, INT1 - nastavení alarmu
	MCUCR = 0b00001010;
	GICR = 0b11000000;

/* zápis data, provede se pouze jednou při spuštění programu
	writeRTC(dny_A,9);
	writeRTC(mesic_A,4);
	writeRTC(rok_A,11);
	writeRTC(den_A,6);
*/
	// při každém spuštění smaže příznak alarmu
	writeRTC(control_status_1,0x00);
	sei();				// povolení přerušení

	while(1)
	{
	if (temp==1) 			// pokud temp ==1 potom probíhá nastavení alarmu
		{
		klavesnice();		// načtení z klávesnice
		lcd_alarm();		// výpis obrazovky nastavení alarmu
		}
	else
		if (temp==2)			// pokud temp ==2 potom probíhá nastavení času
			{
			klavesnice();
			lcd_zmena();		
			}
		else
			{
			PORTB = 0x00;
			nacti();			// vyčte hodnoty z RTC
			TWCR =0x00;			// vypnutí I2C sběrnice

			// vyčtení posledního alarmu z RTC (hodiny a minuty)
			alarm[3]=alarm_minuty%10;
			alarm[2]=alarm_minuty/10;
			alarm[1]=alarm_hodiny%10;
			alarm[0]=alarm_hodiny/10;
			time();				// výpis hodin na LCD displej


			// vypnutí alarmu		
			if (status&0x08)	// je-li nastaven příznak alarmu
				if ((PIND&0b00000010)==0)		// je-li stisknuto 2. tlačítk
				{								
				writeRTC(control_status_1,0x00);// vymaž příznak alarmu
				TCCR2 = 0x00;		// nastavení děličky 1024, CTC mód, změna OC2 při komparaci

				  
   				}
			}
		

	}
}


void writeRTC(unsigned char adresa,unsigned char data)
{
	// data je nutné převést do BCD kódu pro zařízení
	// převod => každá číslice v desítkové soustavě odpovídá číslici v binární podobě
	// př. 23 => 2 = 0b0010; 3 = 0b0011; v BCD tedy platí 23 = 0b0010 0011
	// jednoduchá úprava s dělením 10
	data = ( ((((data/10)<<4)&0b01110000)|((data%10)&0b00001111)));

	i2c_start_wait(WRITE_adresa);	// adresace zařízení RTC
	i2c_write(adresa);				// adresace položky v zařízení RTC
	i2c_write(data);				// zapíši na sběrnici data
	i2c_stop();						// ukončím komunikaci na sběrnici
}

void readRTC (unsigned char adresa)		// pošlu adresaci položky k vyčtení z RTC
{
	i2c_start_wait(WRITE_adresa);		// adresace zařízení pro zápis
	i2c_write(adresa);					// adresace položky na danném zařízení
	i2c_rep_start(READ_adresa);			// načtení adresy pro čtení
	hodnota = i2c_readNak();
	i2c_stop();
}



void nacti(void)
{
	// vyčtení hodnot z RTC
	readRTC(hodiny_A);
	hodiny = (	(((hodnota&0x30)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(minuty_A);
	minuty = ((((hodnota&0x70)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(sekundy_A);
	sekundy = ((((hodnota&0x70)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(dny_A);
	dny = ((((hodnota&0x30)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(mesic_A);
	mesic = ((((hodnota&0x10)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(rok_A);
	rok = ((((hodnota&0xF0)>>4)*10)	+(hodnota&0x0F)	);
	readRTC(den_A);
	den = (hodnota&0x07);

	readRTC(control_status_1);	
	status = hodnota;			

	// alarm -------------------
	readRTC(alarm_minuty_A);
	alarm_minuty = ((((hodnota&0x70)>>4)*10)	+(hodnota&0x0F)	);
	if(!(hodnota&0x80)) alarm_temp=1;		// je li alarm zapnut, nastaví alarm
	
	readRTC(alarm_hodiny_A);
	alarm_hodiny = ((((hodnota&0x30)>>4)*10)	+(hodnota&0x0F)	);
	if(!(hodnota&0x80)) alarm_temp=1;		// je li alarm zapnut, nastaví alarm

	readRTC(alarm_den_A);
	if(!(hodnota&0x80)) 					// je-li denní alarm používán
		{
		alarm_temp=1;						// nastaví alarm
		alarm_den= (hodnota&0x07);			// vyčte hodnotu (0-6)
		}
	else alarm_den = 0x07;					// pokud je den nepoužíván
}


void time (void)				// výpis hodin na LCD displej
{	
 

	DDRC = 0xff;
	PORTC = 0b11000010;

	cls();						// smaže displej
	_delay_ms(2);

	//1. řádek----------------
	gotoxy(1,11);	
	lcd_byt(sekundy);
	gotoxy(1,8);
	lcd_byt(minuty);
	gotoxy(1,5);
	lcd_byt(hodiny);
	gotoxy(1,8);
	lcd_text(":");
	gotoxy(1,11);
	lcd_text(":");
	gotoxy(1,9);
	if (minuty<10) lcd_text("0");
	gotoxy(1,12);
	if (sekundy<10) lcd_text("0");
	gotoxy(1,1);
	lcd_text("Time:");
	
	//2. řádek ----------------
	gotoxy(2,3);
	lcd_byt(dny);
	gotoxy(2,6);
	lcd_byt(mesic);
	gotoxy(2,11);
	lcd_byt(rok);
	gotoxy(2,6);
	lcd_text(".");
	gotoxy(2,9);
	lcd_text(".20");
	gotoxy(2,7);
	if (mesic<10) lcd_text("0");

	gotoxy(2,1);		// výběr dne v týdnu (řazeno dle katalogu)
	switch (den)
	{
		case 0:	lcd_text("Ne");
				break;
		case 1:	lcd_text("Po");
				break;
		case 2: lcd_text("Ut");
				break;
		case 3: lcd_text("St");
				break;
		case 4: lcd_text("Ct");
				break;
		case 5: lcd_text("Pa");
				break;
		case 6: lcd_text("So");
				break;
	}

	gotoxy(1,16);
	if (alarm_temp==1)	lcd_text("A");	// oznámení alarmu

	// vyhodnocení poplachu
	if (status & 0b00001000) poplach();	// generování tónu

	// časová prodleva 900 ms 
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);
	_delay_ms(100);

}

ISR (INT1_vect)
{
	TWCR =0x00;			// vypne I2C sběrnici
	temp=1;				// nastaví pro změnu alarmu
	
	cls();				// vymaže displej
	_delay_ms(2);
}

ISR (INT0_vect)
{
	TWCR =0x00;
	temp=2;				// nastaví pro změnu času

	// zapíše aktuální hodnotu času do pole znaků
	alarm[3]=minuty%10;
	alarm[2]=minuty/10;
	alarm[1]=hodiny%10;
	alarm[0]=hodiny/10;

	cls();				// vymaže displej
	_delay_ms(2);
}

void klavesnice (void)
{

	DDRA=0x00;		//nastavení registrů pro klávesnici
	DDRB=0xff;
	DDRD=0b10110000;

	// ostatní sloupce krom prvního musí být v logické 1
	PORTD |= 0x30;

	
	PORTB &= 0b11111100;	//chci 1 sloupec
	_delay_ms(2);
		if (!(PINA&0b00000100)) 
			{
			key=1;
			pocitadlo++;
			while (!(PINA&0b00000100));	// dokud nepustím talčítko
			}
		if (!(PINA&0b00001000))
			{
			 key=4;
			 pocitadlo++;
			 while (!(PINA&0b00001000));	
			 }
	
		if (!(PINA&0b00100000))
			{
			key=7;
			pocitadlo++;
			while (!(PINA&0b00100000));
			}
		if (!(PINA&0b01000000))
			{
			if (temp ==2) temp =0;
			else
			{
			pocitadlo = 0;
			for(int i=0;i<4;i++) alarm[i]=0;
			stare_poc = pocitadlo;
			}
			}


	PORTB|=0b00000010;  // první sloupec nastavit opět do logické 1
	PORTD&=0b11011111;	//chci 2. sloupec
	_delay_ms(2);
	   	if (!(PINA&0b00000100))
			{
			 key=2;
			 pocitadlo++;
			 while (!(PINA&0b00000100));
			 }
		if (!(PINA&0b00001000))
			{
			key=5;
			pocitadlo++;
			while (!(PINA&0b00001000));
			}
		if (!(PINA&0b00100000))
			{
			key=8;
			pocitadlo++;
			while (!(PINA&0b00100000));
			}
		if (!(PINA&0b01000000))
			{ 
			key=0;
			pocitadlo++;
			while (!(PINA&0b01000000));
			}

	PORTD |=0b00100000;

	
	PORTD&=0b11101111;	//chci 3. sloupec
	_delay_ms(2);
		if (!(PINA&0b00000100)) 		
			{
			key=3;
			pocitadlo++;
			while (!(PINA&0b00000100));
			}
		if (!(PINA&0b00001000)) 
			{
			key=6;
			pocitadlo++;
			while (!(PINA&0b00001000));
			}
		if (!(PINA&0b00100000))
			{
			key=9;
			pocitadlo++;
			while (!(PINA&0b00100000));
			}

		if (!(PINA&0b01000000)) 	
			{
			// otestuje správnost času, případně vypíše varování
			if (kontrola_formatu())	 error_time();
			else
				if (temp==2)	// provede při změně času
					{
					temp=0;
					zmena_casu(); // nahraje nový čas
				}
				else
					{
					temp=0;
					load_alarm(); // nahraje čas alarmu
				}
			}

	PORTD |=0b000100000;

	// ukládání do pole, zapsání klávesy ------------------
	if (stare_poc<pocitadlo) 
		{
			if(pocitadlo == 5) 
				{
				pocitadlo = 1;
				for(int i=0;i<4;i++) alarm[i]=0;
				}
			alarm [pocitadlo-1] = key;
			stare_poc = pocitadlo;
		}	

	// změna hodnoty alarmu (zapnuto - vypnuto)
	if ((PIND&0b00000001)==0)		
		{								
			if (alarm_temp==0) alarm_temp=1;
				else alarm_temp=0;	  
			_delay_ms(10);
   		}

	// nastavení dne alarmu
	if ((PIND&0b00000010)==0)		
		{								
			alarm_den++;
			if (alarm_den>7) alarm_den=0;
			_delay_ms(10);	  
   		}
}



void lcd_alarm (void)		// výpis při změně alarmu
{
	DDRC=0xff;				// port c výstupní
	PORTC=0b11000010;		// aktivace LCD

	// 1.řádek
	gotoxy(1,9);		
	lcd_byt(alarm[3]);		// zapiš na pozici zmáčknutou klávesu
	gotoxy(1,8);			// jdi na pozici
	lcd_byt(alarm[2]);		
	gotoxy(1,6);			
	lcd_byt(alarm[1]);		
	gotoxy(1,5);			
	lcd_byt(alarm[0]);		
	gotoxy(1,9);
	lcd_text(":");

	gotoxy(1,1);
	lcd_text("Alarm:");
	_delay_ms(150);

	gotoxy(1,14);			// stav alarmu
	if (alarm_temp==1) lcd_text("ON ");
		else lcd_text("OFF");

	gotoxy(2,1);			// výpis dnů alarmu
	lcd_text("Day:");
	gotoxy(2,8);
		switch (alarm_den)
		{
			case 0:	lcd_text("Ne ");
				break;
			case 1:	lcd_text("Po ");
				break;
			case 2: lcd_text("Ut ");
				break;
			case 3: lcd_text("St ");
				break;
			case 4: lcd_text("Ct ");
				break;
			case 5: lcd_text("Pa ");
				break;
			case 6: lcd_text("So ");
				break;
			case 7: lcd_text("All");
		}
		
}

void load_alarm (void)		// nahraje novou hodnotu alarmu
{
	unsigned char nastav, data;

	if (alarm_temp==1) nastav=0x00;	// pokud je alarm zapnut
		else nastav=0x80;

	// stanovení hodnoty alarmu z pole
	alarm_hodiny=alarm[0]*10+alarm[1];
	alarm_minuty=alarm[2]*10+alarm[3];

	// před zapnutím I2C vymazat port B
	DDRB = 0xff;
	PORTB = 0x00;

	// hodiny
	data = ( ((((alarm_hodiny/10)<<4)&0b00110000)|((alarm_hodiny%10)&0b00001111)));
	data = data + nastav;

	i2c_start_wait(WRITE_adresa);	// adresace zařízení RTC
	i2c_write(alarm_hodiny_A);		// adresace položky v zařízení RTC
	i2c_write(data);				// zapíši na sběrnici data
	i2c_stop();						// ukončím komunikaci na sběrnici
	
	// minuty
	data = ( ((((alarm_minuty/10)<<4)&0b01110000)|((alarm_minuty%10)&0b00001111)));
	data = data + nastav;

	i2c_start_wait(WRITE_adresa);	// adresace zařízení RTC
	i2c_write(alarm_minuty_A);		// adresace položky v zařízení RTC
	i2c_write(data);				// zapíši na sběrnici data
	i2c_stop();						// ukončím komunikaci na sběrnici


	// nastavení alarmu pro dny v týdnu
	if (alarm_den!=7) data = alarm_den+nastav;
		else 	data = alarm_den+0x80;

	i2c_start_wait(WRITE_adresa);
	i2c_write(alarm_den_A);
	i2c_write(data);
	i2c_stop();


	TWCR = 0x00;					// vypnutí I2C
}

void poplach (void)
{
	// nastavení časovače -------------------------------
	TCCR2 = 0b00011111;		// nastavení děličky 1024, CTC mód, změna OC2 při komparaci
	OCR2 = 3;				// nastavení generované frekvence
}

void error_time (void)		// varování špatně zadaného času
{
	cls();
	_delay_ms(2);

	gotoxy(1,1);
	lcd_text("Spatna volba!!");
	gotoxy(2,1);
	lcd_text("Zadej znovu");

	_delay_ms(200);		// časová prodleva 1s
	_delay_ms(200);
	_delay_ms(200);
	_delay_ms(200);
	_delay_ms(200);

	cls();
	_delay_ms(2);
}

unsigned char kontrola_formatu (void)	// kontrola času
{
	unsigned char check = 0	;

	alarm_hodiny=alarm[0]*10+alarm[1];
	alarm_minuty=alarm[2]*10+alarm[3];

	if (alarm_hodiny>23) check=1;		// kontrola maxima
	if (alarm_minuty>59) check=1;

	return check;		// vrací log 1. pokud je špatně zadán čas
}

void lcd_zmena (void)	// změna času
{
	DDRC=0xff;			//port c výstupní
	PORTC=0b11000010;	//aktivace LCD
	

	//1. řádek
	gotoxy(1,9);		
	lcd_byt(alarm[3]);	
	gotoxy(1,8);		
	lcd_byt(alarm[2]);	
	gotoxy(1,6);		
	lcd_byt(alarm[1]);	
	gotoxy(1,5);		
	lcd_byt(alarm[0]);	
	gotoxy(1,9);
	lcd_text(":");
	gotoxy(1,1);

	lcd_text("Time:");
	_delay_ms(150);

	gotoxy(2,1);
	lcd_text("Settings Time");



}

void zmena_casu (void)		// nahraje zadaný čas z klávesnice
{
	hodiny=alarm[0]*10+alarm[1];
	minuty=alarm[2]*10+alarm[3];

	// vynulování portu B
	DDRB = 0xff;
	PORTB = 0x00;

	// zápis času do RTC
	writeRTC(hodiny_A,hodiny);
	writeRTC(minuty_A,minuty);
	writeRTC(sekundy_A,0);		// sekundy =0

	TWCR = 0x00;				// vypnutí I2C
}
