/*
 * pokus_hodiny_ds1307.c
 *
 * Created: 29.05.2021 10:39:46
 * Author : Já
 */ 
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <stdio.h>
////////////////////////// pridano
#include <util/twi.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include <util/delay.h>
////////////////////////// konec

//Definice pozic segmentů na pinech portu
#define _s_A  PD2
#define _s_B  PD0
#define _s_C  PD6
#define _s_D  PD4
#define _s_E  PD3
#define _s_F  PD1
#define _s_G  PD7
#define _s_dot PD5

/////////////////////////////////////// pridano
////////// i2c read and write command in readable format
#define READ 1
#define WRITE 0

unsigned char bcd2bin(unsigned char);
unsigned char bin2bcd(unsigned char);

void i2c_init(void);
unsigned char i2c_start(unsigned char address);
void i2c_start_wait(unsigned char address);
unsigned char i2c_rep_start(unsigned char address);
void i2c_stop(void);
unsigned char i2c_write( unsigned char data );
unsigned char i2c_readAck(void);
unsigned char i2c_readNak(void);

volatile unsigned char button_pressed=0,timer_0_i=0,timer_2_i=0,active_cathode=0,temp_digit=0;
volatile unsigned char time[4]={16,16,16,16},can_change=0,min_plus=0,hour_plus;
volatile unsigned char temp_time[4]={16,16,16,16},anim=0;
///////////////////////////////////////// konec*/


const unsigned char segs[] =  //Definice číslic na 7segmentové displeji
{
	_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F),                //0
	_BV(_s_B) | _BV(_s_C),                                                                //1
	_BV(_s_A) | _BV(_s_B) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G),                            //2
	_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_G),                            //3
	_BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G),                                        //4
	_BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_F) | _BV(_s_G),                            //5
	_BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),                //6
	_BV(_s_A) | _BV(_s_B) | _BV(_s_C),                                                    //7
	_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),    //8
	_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_F) | _BV(_s_G),                //9

	_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),                //A
	_BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),                            //B
	_BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F),                                        //C
	_BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G),                            //D
	_BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),                            //E
	_BV(_s_A) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G)                                         //F

};
/////////////////////////////////// pridano
ISR (TIMER0_OVF_vect)
{
	timer_0_i++;
	if(timer_0_i==10)
	{
		TIMSK &= ~(1<<TOIE0);
		button_pressed=0;
		timer_0_i=0;
	}
}
///////////////////////////////// konec */
#define _ms(n) (17*n)

void wait(unsigned int a)    //basic wait
{
	volatile unsigned int b,c;
	for(b=0;b!= a; b++)for(c=0;c!= 50;c++);
	return;
}



unsigned char prescale=0;
unsigned char sec=0;
unsigned char min_1=0;
unsigned char min_10=0;
unsigned char hour_1=0;
unsigned char hour_10=0;
unsigned char show_t=0;


ISR(TIMER1_OVF_vect)
{
	if(++prescale == 225){prescale = 0;sec++;};
	if(sec>59){min_1++;sec=0;};
	if(min_1>9){min_1=0;min_10++;};
	if(min_10>5){min_10=0;hour_1++;};
	if(hour_1>9){hour_1=0;hour_10++;};
	if(hour_10>1 && hour_1>3){hour_1=0;hour_10=0;};

	if(++show_t==4) show_t=0;

	switch(show_t)
	{
		
		case 0:	//show minutes
		PORTC = 0x04;
		PORTD = (~segs[min_1]) & ~_BV(_s_dot);
		break;
		case 1:	//show 10 minutes
		PORTC = 0x08;
		PORTD = (~segs[min_10]) & ~_BV(_s_dot);
		break;
		case 2:	//show hours
		PORTC = 0x11;
		PORTD = (~segs[hour_1]);// & ~_BV(_s_dot);
		break;
		case 3:	//show 10hours
		PORTC = 0x12;
		PORTD = (~segs[hour_10]);// & ~_BV(_s_dot);
		break;
		default:
		show_t = 0;
		break;
	}
	return;
}

#define B1() (bit_is_clear(PINB,0)) //3
#define B2() (bit_is_clear(PINB,1)) //4
#define B_WAIT 300

#define nop() asm volatile ("nop;")

int main(void)
{
////////////////////////////// pridano
	 unsigned char numtemp=0;
     TCCR0  =0b00000101;
     TCCR2  =0b00000101;
	 TIMSK |= 1<<TOIE2;   //timer 2 overflow on
/////////////////////////////// konec /	
	TIMSK  = 0x04; //04
	TCCR1B = 0x01; //01
	
	DDRD   = 0xFF;
	DDRC   = 0x0F; //3F
	DDRB   = 0x00;
	PORTB  = 0xFF;


	sei();
//////////////////////////////////////////////////////// pridano	
	i2c_init();

	i2c_start_wait(0xd0 | WRITE); //addressing the RTC with 0xd0 device code and the WRITE command
	i2c_write(0);
	i2c_stop();

	i2c_start_wait(0xd0 | READ);
	numtemp=i2c_readNak(); // read one byte and no more
	i2c_stop();
	
	numtemp &= ~(1<<7);   //reset the sedond's register's first bit to 0 (set 24h mode)

	_delay_ms(100);
	i2c_start_wait(0xd0 | WRITE);
	i2c_write(0);
	i2c_write(numtemp);
	i2c_stop();
//////////////////////////////////////////////// konec /	
	while (1)
	{
		if(B1())
		{
			wait(_ms(B_WAIT));
			min_1++;
			sec=0;
		}

		if(B2())
		{
			wait(_ms(B_WAIT));
			hour_1++;
			sec=0;
		}
/////////////////////////////////////////////// pridano
if(min_plus) //when adjusting the minute
{
	if(!i2c_start(0xd0 | WRITE)) // call the RTC
	{
		i2c_write(1); //minutes address is the '1'

		i2c_start(0xd0 | READ); //read it out
		numtemp=i2c_readNak(); // read one byte and no nore
		i2c_stop();
		
		numtemp=bcd2bin(numtemp); //converting to bin
		numtemp++;                //increasing the minute
		if(numtemp==60) numtemp=0;//if it has reached the 60, go back to 0
		numtemp=bin2bcd(numtemp); //convert back

		i2c_start_wait(0xd0 | WRITE);
		i2c_write(1);
		i2c_write(numtemp);  //write it back to the RTC
		i2c_stop();
		min_plus=0;
	}
}

if(hour_plus)//adjusting the hours, same as the minutes
{
	if(!i2c_start(0xd0 | WRITE))
	{
		i2c_write(2);

		i2c_start(0xd0 | READ);
		numtemp=i2c_readNak();
		i2c_stop();
		
		numtemp=bcd2bin(numtemp);
		numtemp++;
		if(numtemp==24) numtemp=0;
		numtemp=bin2bcd(numtemp);

		i2c_start_wait(0xd0 | WRITE);
		i2c_write(2);
		i2c_write(numtemp);
		i2c_stop();
		hour_plus=0;
	}
}

if(!i2c_start(0xd0 | WRITE)) //normal operation, reading out the time...
{
	i2c_write(0);
	i2c_stop();
	i2c_start(0xd0| READ);
	numtemp=i2c_readAck(); // read the second, and keep going with Ack function
	//if(numtemp%2)COLON_OFF;//it is just used to blink the colon
	//else COLON_ON;
	numtemp=i2c_readAck(); // minutes coming
	time[2]=bcd2bin(numtemp)/10;
	time[3]=bcd2bin(numtemp)%10;
	numtemp=i2c_readNak(); // hours... and no more
	if(numtemp>>4 == 0) time[0]=16;
	else time[0]=bcd2bin(numtemp)/10;
	time[1]=bcd2bin(numtemp)%10;

	_delay_ms(100); //some break...

	i2c_stop();
}

	}
}

////////// converts a bcd number to binary
unsigned char bcd2bin(unsigned char bcd)
{
	unsigned char bin=0;
	bcd&=~(1<<7);
	bin+=bcd&0x0f;
	bcd>>=4;
	bin+=bcd*10;
	return bin;
}

////////// converts a binary number to bcd
unsigned char bin2bcd(unsigned char bin)
{
	unsigned char bcd=0;
	bcd+=bin/10;
	bcd<<=4;
	bcd+=bin%10;
	return bcd;
}


void i2c_init(void)
{
	TWSR = 0;
	TWBR = 32;
}// i2c_init

////////// start i2c operation, parameter is the slave's address or'ed with the READ or WRITE command in the LSB bit
////////// returns with 0-val if the device was unreachable, 1 if OK
unsigned char i2c_start(unsigned char address)
{
	unsigned char   twst;

	// start signal
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

	// wait for the end of the transmission
	while(!(TWCR & (1<<TWINT)));

	// analysing TWI status register (exept the prescaler bits), in case of error, return back with 1
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

	// sending slave's address
	TWDR = address;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// waiting for the end of transmission, recieving ACK or NACK
	while(!(TWCR & (1<<TWINT)));

	// TWI status register vizsgálata, előosztó bitek kimaszkolva
	twst = TW_STATUS & 0xF8;
	// analysing TWI status register (exept the prescaler bits), in case of unreachable slave device, return back with 1
	if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
	
	// return with 0
	return 0;

}// i2c_start


////////// start i2c operation with blocking, parameter is the slave's address or'ed with the READ or WRITE command in the LSB bit
void i2c_start_wait(unsigned char address)
{
	unsigned char   twst;

	//infinite loop
	while ( 1 )
	{
		// start signal
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
		
		// wait for the end of the transmission
		while(!(TWCR & (1<<TWINT)));
		
		// analysing TWI status register (exept the prescaler bits), in case of error, return back with 1
		twst = TW_STATUS & 0xF8;
		if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
		
		// sending slave's address
		TWDR = address;
		TWCR = (1<<TWINT) | (1<<TWEN);
		
		// waiting for the end of transmission, recieving ACK or NACK
		while(!(TWCR & (1<<TWINT)));
		
		// analysing TWI status register (exept the prescaler bits)
		twst = TW_STATUS & 0xF8;
		if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
		{
			// if device is busy, send stop signal
			TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
			
			// waiting for the end of transmission
			while(TWCR & (1<<TWSTO));
			
			continue;
		}
		// else: break and return
		break;
	}

}// i2c_start_wait


////////// send i2c stop signal
void i2c_stop(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	
	// waiting for the end of transmission
	while(TWCR & (1<<TWSTO));

}// i2c_stop


////////// send one byte to i2c device
////////// parameter: the one bute data
////////// returns with 0 is success, 1 if not
unsigned char i2c_write( unsigned char data )
{
	unsigned char   twst;
	
	// sending data
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// waiting for the end of transmission
	while(!(TWCR & (1<<TWINT)));

	// analysing TWI status register (exept the prescaler bits)
	twst = TW_STATUS & 0xF8;
	if( twst != TW_MT_DATA_ACK) return 1;
	return 0;

}// i2c_write


////////// recieving one byte from i2c device, and indicate to recieve more
////////// returns with the data
unsigned char i2c_readAck(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));

	return TWDR;

}// i2c_readAck


////////// recieving one byte from i2c device, and indicate that this is the last we want to recieve
////////// returns with the data
unsigned char i2c_readNak(void)
{
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
	
	return TWDR;

}// i2c_readNak ///// konec
