/*
 * main.c
 *
 *  Created on: 2010-09-28
 *       Autor: Mirosław Kardaś
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "LCD/lcd44780.h"
#include "1Wire/ds18x20.h"
#include "d_led.h"
#include "I2C_TWI/i2c_twi.h"
#include "MKUART/mkuart.h"
#include "IR_DECODE/ir_decode.h"


//******************************* definicje preprocesora
#define PCF8583_ADDR 0xA2	// adres układu RTC na I2C


#define LED1 (1<<PC3)	// zwykła dioda LED
#define LED2 (1<<PA7)	// podświetlenie LCD

#define LED1_ON PORTC &= ~LED1
#define LED1_OFF PORTC |= LED1
#define LED1_TOG PORTC ^= LED1
#define LED2_ON PORTA |= LED2
#define LED2_OFF PORTA &= ~LED2
#define LED2_TOG PORTA ^= LED2
#define LED1_DDR DDRC
#define LED2_DDR DDRA

#define KL1 (1<<PD3)
#define KL2 (1<<PD4)
#define KL3 (1<<PD5)
#define KL4 (1<<PD7)
#define KL_DDR DDRD
#define KL_PORT PORTD



//******************************* deklaracje funkcji
void display_temp(uint8_t x);
// konwersja liczby dziesiętnej na BCD
uint8_t dec2bcd(uint8_t dec);
// konwersja liczby BCD na dziesiętną
uint8_t bcd2dec(uint8_t bcd);

/************** funkcja SuperDebounce do obsługi pojedynczych klawiszy ***************/
void SuperDebounce(uint8_t * key_state, volatile uint8_t *KPIN,
		uint8_t key_mask, uint16_t rep_time, uint16_t rep_wait,
		void (*push_proc)(void), void (*rep_proc)(void) );

void kl1_press(void);
void kl1_rep(void);
void kl2_press(void);
void kl2_rep(void);


//******************************* definicje zmiennych globalnych i typów
uint8_t subzero, cel, cel_fract_bits;
uint8_t czujniki_cnt;		/* ilość czujników na magistrali */

volatile uint8_t int0_flag=1;	// flaga zmieniana w przerwaniu i sprawdzana w pętli głównej

// definiujemy sobie dla polepszenia czytelności programu typ wyliczeniowy
// wskazujący nam później na odpowiednie indeksy w tablicy (buforze)
enum {ss=1, mm, hh};
uint8_t bufor[4];		// rezerwacja bufora 4 bajty
uint8_t sekundy, minuty, godziny;

// definicja znaku stopnia Celsiusza dla temperatury
uint8_t stopien[] = {12,18,18,12,32,32,32,32};

volatile uint16_t Timer1, Timer2, Timer3, Timer4;	/* timery programowe 100Hz */

uint8_t lcd_blink;	/* zmienna odpowiedzialna za załączanie migotania podświetlenia LCD */
uint8_t lcd_cnt;	/* zmienna licznik do wyświetlania na LCD */


/*----------------------------- główna funkcja programu  ---------------*/
int main(void) {


/*----------------------------- inicjalizacja kierunków pinów  ---------------*/
	// ustalamy kierunki portów klawiszy na wyjściowe
	KL_DDR 	&= ~(KL1|KL2|KL3|KL4);
	// włączamy podciąganie do VCC
	KL_PORT |= KL1|KL2|KL3|KL4;

	// ustalamy kierunki pinów diod LED
	LED1_DDR |= LED1; LED2_DDR |= LED2;
	// wyłączay diodę
	LED1_OFF;
	// załączamy podświetlenie
	LED2_ON;



/*----------------------------- inicjalizacja przerwań  ---------------*/
	// Przerwanie INT0
	MCUCR |= (1<<ISC01);	// wyzwalanie zboczem opadającym
	GICR |= (1<<INT0);		// odblokowanie przerwania
	PORTD |= (1<<PD2);		// podciągnięcie pinu INT0 do VCC

	/* Timer2 – inicjalizacja przerwania co 10ms */
	TCCR2 	|= (1<<WGM21);			// tryb pracy CTC
	TCCR2 	|= (1<<CS22)|(1<<CS21)|(1<<CS20);	// preskaler = 1024
	OCR2 	= 108;					// przerwanie porównania co 10ms (100Hz)
	TIMSK 	= (1<<OCIE2);			// Odblokowanie przerwania CompareMatch


/*----------------------------- inicjalizacja modułów programowych  ---------------*/
	/* inicjalizacja multipleksowania LED */
	d_led_init();
	/*ustawienie samych zer do wyświetlania */
	cy1=0; cy2=0; cy3=0; cy4=0;

	/* inicjalizacja LCD */
	lcd_init();
	lcd_str("Start...");
	lcd_defchar(0x80,stopien); // definicja znaku stopnia w LCD

	/* inicjalizacja odbiornika podczerwieni RC5 */
	ir_init();

	/* inicjalizacja USART */
	USART_Init( __UBRR );


	/* globalne  odblokowanie przerwań */
	sei();

	uart_puts("Projekt wielozadaniowy\r\n");

	/* sprawdzamy ile czujników DS18xxx widocznych jest na magistrali */
	czujniki_cnt = search_sensors();
	DS18X20_start_meas( DS18X20_POWER_EXTERN, NULL );

	/* czekamy 750ms na dokonanie konwersji przez podłączone czujniki */
	_delay_ms(1500);

	lcd_cls();

	/* dokonujemy odczytu temperatury z pierwszego czujnika o ile został wykryty */
	/* wyświetlamy temperaturę gdy czujnik wykryty */
	if( DS18X20_OK == DS18X20_read_meas(gSensorIDs[0], &subzero, &cel, &cel_fract_bits) ) display_temp(0);

	uint8_t tcl=0;

	char input;
	uint8_t k1, k2;

	while(1) {

		SuperDebounce(&k1, &PIND, KL1, 20, 500, kl1_press, kl1_rep );
		SuperDebounce(&k2, &PIND, KL2, 80, 400, kl2_press, kl2_rep );

		if(!Timer2) {
				Timer2=25;
				if( lcd_blink ) LED2_TOG;
		}

		input = uart_getc();

		if(input=='1') LED2_ON;
		else
		if(input=='2') LED2_OFF;
		else
		if(input=='3') lcd_blink ^= 1;

		/* ****** ZDARZENIE Z UKŁADU RTC ********** */
		if ( int0_flag ) {
			//odczyt 4 bajtów do bufora od adresu 0x01 z pamięci RAM naszego RTC
			TWI_read_buf( PCF8583_ADDR, 0x01, 4, bufor );

			sekundy = bcd2dec( bufor[ss] );
			minuty = bcd2dec( bufor[mm] );
			godziny = bcd2dec( bufor[hh] );

			cy1 = bufor[mm]>>4;
			cy2 = bufor[mm]&0x0f;
			cy3 = bufor[ss]>>4;
			cy4 = bufor[ss]&0x0f;

			// wyświetlenie czasu na LCD
			lcd_locate(0,8);
			if( godziny < 10 ) lcd_str("0");
			lcd_int(godziny);
			lcd_str(":");
			if( minuty < 10 ) lcd_str("0");
			lcd_int(minuty);
			lcd_str(":");
			if( sekundy < 10 ) lcd_str("0");
			lcd_int(sekundy);

			if(!tcl) DS18X20_start_meas( DS18X20_POWER_EXTERN, NULL );

			if(tcl) {
				if( DS18X20_OK == DS18X20_read_meas(gSensorIDs[0], &subzero, &cel, &cel_fract_bits) ) display_temp(0);
			}

			if(++tcl>1) tcl=0;

			int0_flag=0;
		}

		/* ****** ZDARZENIE Z ODBIORNIKA PODCZERWIENI ********** */
		if(Ir_key_press_flag) {

			if( !address && 1==command ) lcd_blink = 1;	// załączamy miganie podświetlenia LCD
			if( !address && 2==command ) lcd_blink = 0;	// wyłączamy miganie podświetlenia LCD
			if( !address && 4==command ) LED2_ON; // włączamy podświetlenie LCD
			if( !address && 5==command ) LED2_OFF; // wyłączamy podświetlenie LCD

			lcd_locate(1,8);
			lcd_str_P(PSTR("rc5: "));
			lcd_int(command);
			lcd_str_P(PSTR("  "));

			uart_puts("rc5: adr=");
			uart_putint(address, 10);
			uart_puts("  cmd=");
			uart_putint(command, 10);
			uart_puts("\r\n");

			Ir_key_press_flag = 0;
			command=0xff;
			address=0xff;
		}

	}
}


// procedura obsługi przerwania INT 0
ISR( INT0_vect ) {
	int0_flag = 1;
}


ISR(TIMER2_COMP_vect)
{
	uint16_t n;

	n = Timer1;		/* 100Hz Timer1 */
	if (n) Timer1 = --n;
	n = Timer2;		/* 100Hz Timer2 */
	if (n) Timer2 = --n;
	n = Timer3;		/* 100Hz Timer3 */
	if (n) Timer3 = --n;
	n = Timer4;		/* 100Hz Timer4 */
	if (n) Timer4 = --n;
}



/************** funkcja SuperDebounce do obsługi pojedynczych klawiszy ***************
 * 							AUTOR: Mirosław Kardaś
 * ZALETY:
 * 		- nie wprowadza najmniejszego spowalnienia
 * 		- posiada funkcję REPEAT (powtarzanie akcji dla dłużej wciśniętego przycisku)
 * 		- można przydzielić różne akcje dla trybu REPEAT i pojedynczego kliknięcia
 * 		- można przydzielić tylko jedną akcję wtedy w miejsce drugiej przekazujemy 0 (NULL)
 *
 * Wymagania:
 * 	Timer programowy utworzony w oparciu o Timer sprzętowy (przerwanie 100Hz)
 *
 * 	Parametry wejściowe:
 * 	*key_state - wskaźnik na zmienną w pamięci RAM (1 bajt)
 *  *KPIN - nazwa PINx portu na którym umieszczony jest klawisz, np: PINB
 *  key_mask - maska klawisza np: (1<<PB3)
 *  rep_time - czas powtarzania funkcji rep_proc w trybie REPEAT
 *  rep_wait - czas oczekiwania do przejścia do trybu REPEAT
 *  push_proc - wskaźnik do własnej funkcji wywoływanej raz po zwolenieniu przycisku
 *  rep_proc - wskaźnik do własnej funkcji wykonywanej w trybie REPEAT
 **************************************************************************************/
void SuperDebounce(uint8_t * key_state, volatile uint8_t *KPIN,
		uint8_t key_mask, uint16_t rep_time, uint16_t rep_wait,
		void (*push_proc)(void), void (*rep_proc)(void) ) {

	enum {idle, debounce, go_rep, wait_rep, rep};

	if(!rep_time) rep_time=20;
	if(!rep_wait) rep_wait=150;

	uint8_t key_press = !(*KPIN & key_mask);

	if( key_press && !*key_state ) {
		*key_state = debounce;
		Timer1 = 15;
	} else
	if( *key_state  ) {

		if( key_press && debounce==*key_state && !Timer1 ) {
			*key_state = 2;
			Timer1=5;
		} else
		if( !key_press && *key_state>1 && *key_state<4 ) {
			if(push_proc) push_proc();						/* KEY_UP */
			*key_state=idle;
		} else
		if( key_press && go_rep==*key_state && !Timer1 ) {
			*key_state = wait_rep;
			Timer1=rep_wait;
		} else
		if( key_press && wait_rep==*key_state && !Timer1 ) {
			*key_state = rep;
		} else
		if( key_press && rep==*key_state && !Timer1 ) {
			Timer1 = rep_time;
			if(rep_proc) rep_proc();						/* KEY_REP */
		}
	}
	if( *key_state>=3 && !key_press ) *key_state = idle;
}



void kl1_press(void) {
	LED1_ON;
}

void kl1_rep(void) {
	LED1_TOG;
}

void kl2_press(void) {
	LED1_OFF;
}

void kl2_rep(void) {
	lcd_locate(0,0);
	lcd_str_P(PSTR("cnt "));
	lcd_int(lcd_cnt++);
	lcd_str_P(PSTR("  "));
}




/* wyświetlanie temperatury na pozycji X w drugiej linii LCD */
void display_temp(uint8_t x) {
	lcd_locate(1,x);
	if(subzero) lcd_str("-");	/* jeśli subzero==1 wyświetla znak minus (temp. ujemna) */
	else lcd_str(" ");	/* jeśli subzero==0 wyświetl spację zamiast znaku minus (temp. dodatnia) */
	lcd_int(cel);	/* wyświetl dziesiętne części temperatury  */
	lcd_str(".");	/* wyświetl kropkę */
	lcd_int(cel_fract_bits); /* wyświetl dziesiętne części stopnia */
	lcd_str("\x80""C "); /* wyświetl znak jednostek (C - stopnie Celsiusza) */
}


// konwersja liczby dziesiętnej na BCD
uint8_t dec2bcd(uint8_t dec) {
return ((dec / 10)<<4) | (dec % 10);
}

// konwersja liczby BCD na dziesiętną
uint8_t bcd2dec(uint8_t bcd) {
    return ((((bcd) >> 4) & 0x0F) * 10) + ((bcd) & 0x0F);
}
