/* source code for a simple binary clock, targeted to the AVR attiny2313.  
 * Copyright (c) 2007, Patrick Tait
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of secondpagemedia.com nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Patrick Tait ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Patrick Tait BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 */
#define DEBOUNCE_K 4
/*^ Debounce coefficient, this * wait time == debounce time ^*/
#define WAIT_TIME 3
/*^ Time to wait between polling ^*/
#define SECONDPRESS_K 40
/*Second press coefficent.  This * Wait time == time until repeats begin*/
#define REPEAT_K 15
/*^Key repeat coefficient.  This * wait time == time between repetes*/
int main(void);
void Cascade(unsigned int *Seconds, unsigned int *Minutes, unsigned int *Hours);
#include <avr/interrupt.h>    /* Defines pins, ports, etc to make programs easier to read */
#define F_CPU 1000000UL/*for the timer*/
#include <util/delay.h>/*_delay_ms stuff*/

/*
 * Pins:
 * 	PB0-5: ground for LED matrix
 * 	PD[01]: debug blinkers.  0 is every second, 1 is every pulse
 * 	PD[234]: Vcc for matrix
 * 	PD5: 60hz clock 
 * 	PD6: Input for the buttons.  H++, M++ and ajudt brightness
 */	
int main(){
	unsigned int State= 0; 	/* Holds the previous state of the 
				 * trigger pulse, 0 for low, 1 for
				 * high */
	unsigned int MS = 0;	/* Milliseconds/trigger pulses */
	unsigned int Seconds = 0;	/* Next 3 vars hold the time */
	unsigned int Minutes = 0;
	unsigned int Hours = 0;
	unsigned int String = 0; /*0-2, for lighting individual strings*/
	unsigned int HoursPressed = 0, MinutesPressed = 0, BrightnessPressed = 0;
	unsigned int Brightness = 0;
		/*^Controls the brightness level.  From 0 (Brightest) to 
		 * WAIT_TIME - 1 (Darkest), works by turning the lights on for
		 * WAIT_TIME - Brightness, and then off for Brightness*/

	/*Set up the ports*/
	DDRB = _BV(PD0) + _BV(PD1) + _BV(PD2) + _BV(PD3) + _BV(PD4) + _BV(PD5);
		/*^ enables output on pins 0-5 on port D ^*/
	DDRD = ~(_BV(PD5) + _BV(PD6));/* Enable input on 5&6 of port D, * output elsewhere*/
	PORTD = _BV(PD6); /*Pullup on PD6, no pullup on PD5, outputs low*/
	PORTB |= _BV(PD0) + _BV(PD1) + _BV(PD2) + _BV(PD3) + _BV(PD4) +
		_BV(PD5);/*all high, therefore all off*/
	while(1){
		/*This segment checks to see if the clock signal (halfwave from 
		 * the power line) is high, if so it increments the clock*/
		if(PIND & _BV(PD5)) /*PD5 is on*/
		{
			PORTD |= _BV(PD1);
			if(State == 0)/*It wasn't on before*/
			{
				MS++;/*1/60 of a second*/
				if(MS == 60)
				{
					MS = 0;
					Seconds++;
					Cascade(&Seconds, &Minutes, &Hours);
				}
			}
			State = 1;
		}
		else/*PD0 is low*/
		{
			PORTD &= ~_BV(PD1);
			State = 0;
		}
		/*This segment flips on the sets of LEDs for the seconds, 
		 * minutes, and hours, it also controls the inputs.  
		 * PB0-5 are set to low for on, PD 2(s) 3(m) and 4(h) are set to
		 * high to activate the respective string.  
		 * Switches are debounced for DEBOUNCE_K * WAIT_TIME, after
		 * SECONDPRESS_K*WAIT_TIME and every REPEAT_K another keypress
		 * is recorded
		 */
		/* turns off both sides of the matrix */
		switch(String){
			case 0: /*Hours*/
				PORTD |= _BV(PD4);/*Pin four high*/
				PORTB &= ~Hours;
				String = 1;
				if(!(PIND & _BV(PD6))){ /*Button is pressed*/
					HoursPressed++;
					if(HoursPressed == DEBOUNCE_K){
						Hours++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
					else if(HoursPressed == SECONDPRESS_K){
						Hours++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
					else if(HoursPressed > SECONDPRESS_K && !(HoursPressed % REPEAT_K)){
					/*^every multiple of REPEAT_K*/
						Hours++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
				}
				else{
					HoursPressed = 0;
				}
				break;
			case 1: /*minutes*/
				PORTD |= _BV(PD3);/*Pin three high*/
				PORTB &= ~Minutes;/*display minutes*/
				String = 2;
				if(!(PIND & _BV(PD6))){ /*Button is pressed*/
					MinutesPressed++;
					if(MinutesPressed == DEBOUNCE_K){
						Minutes++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
					else if(MinutesPressed == SECONDPRESS_K){
						Minutes++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
					else if(MinutesPressed > SECONDPRESS_K && !(MinutesPressed % REPEAT_K)){
					/*^every multiple of REPEAT_K*/
						Minutes++;
						Cascade(&Seconds, &Minutes, &Hours);
					}
				}
				else{
					MinutesPressed = 0;
				}
				break;
			case 2: /*seconds*/
				PORTD |= _BV(PD2);/*Pin two high*/
				PORTB &= ~Seconds;/*display seconds*/
				String = 0;
				/*Code for brightness button*/
				/*nonfunctional, due to deadlines*/
				if(!(PIND & _BV(PD6))){ /*Button is pressed*/
					BrightnessPressed++;
					if(BrightnessPressed == DEBOUNCE_K){
						Brightness++;
						PIND = _BV(PD0);/*toggle pin 0*/
						if(Brightness == WAIT_TIME){
							Brightness = 0;
						}
					}
				}
				else{
					BrightnessPressed = 0;
				}
				break;
		}
		/*keep it on for this long*/
		_delay_ms(WAIT_TIME - Brightness);
		/*Turn it off for this long*/
		PORTD &= ~(_BV(PD2) + _BV(PD3) + _BV(PD4));
		PORTB |= _BV(PD0) + _BV(PD1) + _BV(PD2) + _BV(PD3) + _BV(PD4) 
				+ _BV(PD5);/*all high, therefore all off*/
		_delay_ms(Brightness);
	}
	return(0);
}

/*Rolls over the clock times*/
void Cascade(unsigned int *Seconds, unsigned int *Minutes, unsigned int *Hours){
	if(*Seconds >= 60) {
		*Seconds = 0;
		*Minutes += 1;
	}
	if(*Minutes >= 60) {
		*Seconds = 0;
		*Minutes = 0;
		*Hours += 1;
	}
	if(*Hours >= 24) {
		*Seconds = 0;
		*Minutes = 0;
		*Hours = 0;
	}
}
