/* uC: Attiny 25
	Fuse Settings for 4MHz external quarz:
	LOW  = 0xDC
	HIGH = 0xDF
	EXT  = 0xFF
*/

#define CLOCK_ID	1		// unique ID of clock, 1 to 6 possible
#define CLOCKPIN1	PB0
#define CLOCKPIN2	PB1
#define PULSEWIDTH	130
#define COUNTMAX	50
#define TRUE		1
#define FALSE		0

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// function declarations
void gotoHHMM(void);

// global variables
volatile uint16_t 	secfraction = 0;
volatile uint16_t 	nfrac = 10000-197;
volatile uint8_t 	sekunde = 0;
volatile uint8_t 	minute = 0;
volatile uint8_t 	stunde = 0;
volatile uint8_t	flag = 0;
volatile uint8_t	timecnt = 0;	// counter for bus signal
volatile uint8_t	incomingMsg = FALSE;
volatile uint8_t	msgReceived = FALSE;
volatile uint8_t	runStatus = TRUE;
volatile uint8_t	pos = 0;
volatile uint8_t	command[4];
 
int main(void)
{
  // PORTB setzen
  DDRB |= (1<<CLOCKPIN1) | (1<<CLOCKPIN2);		// CLOCKPIN1, CLOCKPIN2 as output
  PORTB &= ~((1<<CLOCKPIN1) | (1<CLOCKPIN2));	// both to 0
  // Timer 0 konfigurieren
  TCCR0A = (1<<WGM01); // CTC Modus
  TCCR0B |= (1<<CS01); // Prescaler 8
  // Sysclock / Prescaler / secfraction = COUNTMAX to finally achieve 1 Hz
  OCR0A = COUNTMAX;
 
  // compare interrupt
  TIMSK |= (1<<OCIE0A);
  // pin change interrupt
  PCMSK  = (1<<PCINT2);	// enable pin change interrupt on PB2
  GIMSK  = (1<<PCIE);	// enable pin change interrupts

  sei();	// allow interrupts

// TEST CODE
/*
	command[2] = 12;
	command[3] = 5;
	gotoHHMM();
*/ 
// END TEST CODE

  while(1)
  {
	if (flag) {	// another second is over...
		flag = 0;
		if (sekunde & 1) {
			PORTB |= (1<<CLOCKPIN1);	// pulse CLOCKPIN1 at odd seconds
		}
		else {
			PORTB |= (1<<CLOCKPIN2);	// pulse CLOCKPIN2 at even seconds
		} 
		_delay_ms(PULSEWIDTH);		// pulse width to be optimized!
		PORTB &= ~((1<<CLOCKPIN1) | (1<<CLOCKPIN2));
	}
	// *** now process bus commands
	if (msgReceived == TRUE) {
		msgReceived = FALSE;	// reset message flag
		if (command[0] & (1<<CLOCK_ID)) {	// was message for me?
			switch (command[1]) {
			case 1:		// stop clock
				runStatus = FALSE;
				break;
			case 2:		// restart clock
				break;
				runStatus = TRUE;
			case 3:		// hourly ping
				if (runStatus) {	// react only if clock is running
					if (minute < 30) nfrac += minute; // slower
					else nfrac += minute - 60;	// faster
				} 
				break;
			case 4:		// set clock
				gotoHHMM();
				break;
			}
		}
	}
  }
}	// end of main

void gotoHHMM(void)
{
uint16_t t1,t2;
uint8_t pulsepin = CLOCKPIN1;
	cli();	// disable interrupts
	t1 = (stunde % 12) * 3600U + minute * 60 + sekunde;
	t2 = (command[2] % 12) * 3600U + command[3] * 60;
	if (t2 > t1) t2 = t2 - t1;
	else t2 = t1 - t2;
	for (t1=0;t1<t2;t1++) {
		PORTB |= 1<<pulsepin;
		_delay_ms(PULSEWIDTH);
		PORTB &= ~((1<<CLOCKPIN1) | (1<<CLOCKPIN2));
		_delay_ms(100);
		if (pulsepin == CLOCKPIN1) pulsepin = CLOCKPIN2;
		else pulsepin = CLOCKPIN1;
	}
	runStatus = FALSE;	// wait there for a restart command
	stunde = command[2];// set new time	
	minute = command[3];
	sekunde = 0;
	sei();	// enable interrupts again
}
 
/*
Der Compare Interrupt Handler wird aufgerufen, wenn 
TCNT0 = OCR0A = COUNTMAX, d.h. genau alle 1 ms
*/
ISR (TIMER0_COMPA_vect)
{
	timecnt++;		// counter for bus signals
	if (runStatus) {	// noting more if clock was stopped
		secfraction++;
		if(secfraction == nfrac) {
			secfraction = 0;
			sekunde++;
			flag = 1;	
			if(sekunde == 60) { minute++; sekunde = 0; }
			if(minute == 60)  { stunde++; minute = 0; }
			if(stunde == 24)  stunde = 0;
		}
	}
}	// end of TIMER0_COMPA_vect

ISR (PCINT0_vect)	// pin change interrupt
{
	static uint8_t	ring = 0;
	static uint8_t	byt;
	static uint8_t	bitcnt;
	if (incomingMsg == FALSE) {	// wait for message
		if ((timecnt > 4) && (timecnt < 8)) ring++;
		else ring = 0;	// ring tune will be broken by any differing freq.
		if (ring > 9) {
			incomingMsg = TRUE;
			pos = 0xFF;
		}
	}
	else {	// receive incoming message
		if ((timecnt > 7) && (timecnt < 12)) {	// word separator
			byt = 0;	// prepare variable for next incoming word
			bitcnt = 0;	// bit counter	
			pos++;		// increment word counter
		}
		else { 
			if (timecnt < 5) {	// a ZERO bit
				bitcnt++;
				byt = byt << 1;		// bits come in with MSB first
			}
			else {
				if ((timecnt > 4) && (timecnt < 8)) {	// a ONE bit
					bitcnt++;
					byt = (byt << 1) & 1;
				}
			}
		}
		if (bitcnt == 6) command[pos] = byt;	// store next command word
		if (pos == 3) {
			incomingMsg = FALSE;	// transmission is over after 4 words
			msgReceived = TRUE;		// this indicates a complete message was received
		}
	}
}	// end of PCINT_vect

