/*
No Copyright

The person who associated a work with this deed has dedicated the work to the
public domain by waiving all of his or her rights to the work worldwide under
copyright law, including all related and neighboring rights, to the extent
allowed by law.

You can copy, modify, distribute and perform the work, even for commercial
purposes, all without asking permission.

See https://creativecommons.org/share-your-work/public-domain/cc0/ for more
information on the license.


This file is part of the clock sculpture project, made by
Maarten Tromp <maarten@geekabit.nl>.

More info on the project: https://www.geekabit.nl/projects/clock-sculpture/
*/


#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>


// pins
#define mains	PB1 // mains frequency input
#define sr_pld	PB2	// load
#define sr_pda	PB3 // data
#define sr_pck	PB4 // clock


// global vars
uint8_t mains_cycles = 100; // force update seconds
uint8_t seconds = 100; // force update minutes
uint8_t minutes = 58; // soon to be 59 which displays 0
uint8_t hours = 11; // displays 0
uint8_t minsr = 11, minq = 0; // these values will soon be overwritten
uint8_t hoursr = 11, hourq = 7; // but these will not until a quarter past
uint8_t sr_data[12];

// Because of the way the shift registers are wired, the dial always shows one
// more than what the variables hours and minutes are set to. So hours = 0,
// minutes = 0 displays 1:01


void sr_write()
{
	uint8_t sr, q;

	// shift sr data out
	for(sr = 0; sr < 12; sr++)
	{
		// for each sr
		for(q = 0; q < 8; q++)
		{
			// for each sr output pin
			if(sr_data[sr] & (1<<q))
			{
				// output 1
				PORTB = (1<<sr_pda); // data high
				PORTB = (1<<sr_pda | 1<<sr_pck); // data + clock high
			}
			else
			{
				// output 0
				PORTB = 0; // data low
				PORTB = (1<<sr_pck); // clock high
			}
			// clock low happens at the next PORTB=
		} // for each q
	} // for each sr
	PORTB = (1<<sr_pld); // load high
	// load low happens at the next PORTB=
}


void display_markers()
{
	// simpler version of write_sr that only displays 5 minute markers
	uint8_t sr, q;

	// shift sr data out
	for(sr = 0; sr < 12; sr++)
	{
		// for each sr
		for(q = 0; q < 8; q++)
		{
			// for each sr output pin
			if(q == 6 || q == 7)
			{
				// output 1
				PORTB = (1<<sr_pda); // data high
				PORTB = (1<<sr_pda | 1<<sr_pck); // data + clock high
			}
			else
			{
				// output 0
				PORTB = 0; // data low
				PORTB = (1<<sr_pck); // clock high
			}
			// clock low happens at the next PORTB=
		} // for each q
	} // for each sr
	PORTB = (1<<sr_pld); // load high
	// load low happens at the next PORTB=
}


// https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
ISR(INT0_vect)
{
	// external interrupt
	// fires on mains zero crossing
	// which happens at 50 Hz

	uint8_t i;

	if(++mains_cycles >= 50)
	{
		// next second starts
		mains_cycles = 0;
		if(++seconds >= 60)
		{
			// next minute starts
			seconds = 0;
			if(++minutes >= 60)
			{
				minutes = 0;
			}
			else if(minutes == 59)
			{
				// next hour starts (compensate for off by 1)
				if(++hours >= 12)
				{
					// next 12h cycle starts
					hours = 0;
				}
			}
			// calculate hours sr and port
			else if(minutes == 15)
			{
				// half hour led
				hoursr = hours;
				hourq = 2;
			}
			else if(minutes == 44)
			{
				// whole hour led
				hoursr = hours;
				hourq = 7;
			}
			// calculate minute sr and port
			minsr = minutes / 5;
			minq = minutes % 5;
			if(minq >= 2) minq += 2;

			// build new sr data
			for(i = 0; i < 12; i++)
			{
				sr_data[i] = 0;
			}
			sr_data[hoursr] = 1<<hourq;
			sr_data[minsr] += 1<<minq;
		}
	}

	// show 5 minute markers briefly
	display_markers();
	// display 'hands' rest of the time
	sr_write();
}


int main(void)
{
	// configure gpio
	PORTB = 0;
	DDRB = (1<<sr_pda | 1<<sr_pck | 1<<sr_pld);

	// TODO: disable adc and timer

	// configure interrupts
	MCUCR = (1<<ISC01 | 1<<ISC00); // INT0 on rising edge
	GIMSK = (1<<INT0); // external interrupt request 0 enable

	sei();
	sleep_enable();
	// sleep examples: https://gist.github.com/JChristensen/5616922
	while(1)
	{
		sleep_cpu();
	}
}


//  end of file
