/*
 * main.c
 *
 * Created: 27.04.2019 05:24:13
 * Author : 
 */ 

#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/interrupt.h>

const int		activity_strobe_delay		= 100;	// Blinkfrequenz ( n ms on => n ms off)
const int		activity_strobe_cycles		= 6;	// Bei erkannter Aktivität,  n mal blinken
unsigned char	led_output_buffer			= 0x00;	// wird verwendet in update_activity_indicator
unsigned char	activity_sense_status		= 0x00;	// wird verwendet in update_activity_indicator


#define ACT_INDICATOR_PORT PORTD
#define ACT_INDICATOR_DDR DDRD
#define ACT_INDICATOR_SER PORTD5 // 74HC595.14  (SER)
#define ACT_INDICATOR_SCK PORTD4 // 74HC595.11  (SCK)
#define ACT_INDICATOR_RCK PORTD3 // 74HC595.12  (RCK)
#define ACT_INDICATOR_OE  PORTD2 // 74HC595.13  (^OE)


#define RX_SENSE_PORT PORTC
#define RX_SENSE_DDR DDRC
#define RX_SENSE_PIN PINC
#define RX_SENSE_INPUT PORTC4 // Sense Leitung an INPUT-RX
#define RX_SENSE_OUT4 PORTC3 // Sense Leitung an OUTPUT4-RX
#define RX_SENSE_OUT3 PORTC2 // Sense Leitung an OUTPUT3-RX
#define RX_SENSE_OUT2 PORTC1 // Sense Leitung an OUTPUT2-RX
#define RX_SENSE_OUT1 PORTC0 // Sense Leitung an OUTPUT1-RX


// enthält die Anzahl der Blink-Zyklen, die je LED noch auszugeben sind.
// Die Pin-Change ISR setzt bei erkannter Aktivität den Zähler wieder auf 'activity_strobe_cycles'
// Die Funktion update_activity_indicator() dekrementiert die Zählerstände mit jedem durchlaufenen Zyklus
volatile unsigned char activity_strobe_counters[8] = {
	 0x00, //Counter für LED0 (IN-RX)
	 0x00, //Counter für LED1 (OUT1-RX)
	 0x00, //Counter für LED2 (OUT2-RX)
	 0x00, //Counter für LED3 (OUT3-RX)
	 0x00, //Counter für LED4 (OUT4-RX)
	 0x00, //frei
	 0x00, //frei
	 0x00  //frei
};



ISR(PCINT1_vect) {
	
	// INPUT-RX Sense?
	 if( RX_SENSE_PIN & (1 << RX_SENSE_INPUT) ) {
		activity_strobe_counters[0] = activity_strobe_cycles;	 
	 }
	
	// OUTPUT1-RX Sense?
	if(RX_SENSE_PIN & (1 << RX_SENSE_OUT1) ) {
		activity_strobe_counters[1] = activity_strobe_cycles;
	}
	
	// OUTPUT2-RX Sense?
	if(RX_SENSE_PIN & (1 << RX_SENSE_OUT2) ) {
		activity_strobe_counters[2] = activity_strobe_cycles;
	}
	
	// OUTPUT3-RX Sense?
	if(RX_SENSE_PIN & (1 << RX_SENSE_OUT3) ) {
		activity_strobe_counters[3] = activity_strobe_cycles;
	}
	
	// OUTPUT4-RX Sense?
	if(RX_SENSE_PIN & (1 << RX_SENSE_OUT4) ) {
		activity_strobe_counters[4] = activity_strobe_cycles;
	}
}


void activity_indicator_init();
void rx_sense_init();
void update_activity_indicator();


int main(void)
{	
	activity_indicator_init();
	rx_sense_init();	
	sei();
	
	while(1) {
		update_activity_indicator();
	}
}


void rx_sense_init() {
	// Pin Change Interrupts konfigurieren:
	PCMSK1 |= (1<<PCINT8);
	PCMSK1 |= (1<<PCINT9);
	PCMSK1 |= (1<<PCINT10);
	PCMSK1 |= (1<<PCINT11);
	PCMSK1 |= (1<<PCINT12);
	PCICR |= (1<<PCIE1);
		
	// Portpins als Eingang konfigurieren
	RX_SENSE_DDR &= ~( 1 << RX_SENSE_INPUT); 
	RX_SENSE_DDR &= ~( 1 << RX_SENSE_OUT4); 
	RX_SENSE_DDR &= ~( 1 << RX_SENSE_OUT3);
	RX_SENSE_DDR &= ~( 1 << RX_SENSE_OUT2);
	RX_SENSE_DDR &= ~( 1 << RX_SENSE_OUT1);
}

void activity_indicator_init() {	
	// Portpins als Ausgang konfigurieren
	ACT_INDICATOR_DDR |= ( 1 << ACT_INDICATOR_SER); 
	ACT_INDICATOR_DDR |= ( 1 << ACT_INDICATOR_SCK); 
	ACT_INDICATOR_DDR |= ( 1 << ACT_INDICATOR_RCK); 
	ACT_INDICATOR_DDR |= ( 1 << ACT_INDICATOR_OE);  
	

	// Portpin für serielle Daten auf LOW setzen
	ACT_INDICATOR_PORT &= ~(1<<ACT_INDICATOR_SER);

	// SCK, RCK und !OE auf HIGH
	ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_SCK);
	ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_RCK);
	ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_OE);

	// Nach Initialisierung !OE auf 0 setzen, um die Ausgabe freizugeben
	ACT_INDICATOR_PORT &= ~(1<<ACT_INDICATOR_OE);
}



void update_activity_indicator()
{
	activity_sense_status = 0x00;
	
	for(int i=0; i<activity_strobe_cycles; i++) {
		
		// Bitmuster anhand der Zählerstände für die Ausgabe ermitteln:
		for(int j=0; j<8; j++) {
			// Wenn laut Zähler noch geblinkt werden soll, dann wird das Bit getoggelt
			if(activity_strobe_counters[j] > 0) {
				activity_sense_status ^= (1<<j); 
			}
			// Wenn nicht mehr gebklinkt werden soll, dann wird das Bit auf 0 gesetzt
			else {
				activity_sense_status &= ~(1<<j);
			}
		}
		
		led_output_buffer = activity_sense_status;
		
		for (int j = 0; j < 8; j++) {
			ACT_INDICATOR_PORT &= ~(1<<ACT_INDICATOR_SER);
			if( led_output_buffer & 0x80) {
				ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_SER);
			}
			led_output_buffer <<= 1;
	
			// Schiebetakt ausgeben
			ACT_INDICATOR_PORT &= ~(1<<ACT_INDICATOR_SCK);  // SCK low
			ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_SCK);	// SCK high
		}
		
		// Latch - Neue Daten auf die LEDs übernehmen
		ACT_INDICATOR_PORT &= ~(1<<ACT_INDICATOR_RCK);  // RCK low
		ACT_INDICATOR_PORT |= (1<<ACT_INDICATOR_RCK);	// RCK high
		
		
		
		_delay_ms(activity_strobe_delay);
		
		for(int j=0; j<8; j++) {
			if(activity_strobe_counters[j] > 0) {
				activity_strobe_counters[j]--;
			}
		}
	}
}