/*////////////////////////////////////////////////////////////////////////////////////

								Ultrazvukový měřič vzdálenosti - PT063

							Vedoucí projektu: Richard Vacula
								Autor kódu: Lukáš Herudek

								  (c) TIPA spol. s r. o.

///////////////////////////////////////////////////////////////////////////////////////*/


#include <avr/io.h>//základní ovládání mikroprocesoru
#include <util/delay.h>//knihovna pro čekání
#include <avr/interrupt.h>//knihovna pro přerušení

/*
	TIMER0 (8bit) - časování 7segmentovky
	TIMER1 (16bit) - časování ultrazvukového senzoru
*/

//*************************************** Nastavení ************************************

#define prodleva_zobrazeni 50	//(hodnota 1 = 64 us) (15.625 = 1 ms) (max. 255)
#define prodleva_blik_us 1000	//doba svitu jednoho segmentu v us

#define sleep()   __asm__ __volatile__ ("sleep")//použití instrukce sleep z assembleru

#include "7seg.c"//speciální knihovna pro ovládání displeje


//*************************************** Vstupy/výstupy ************************************
#define TLACITKO	(PIND&0b00000100)//hlavní
#define TLACITKO2	(PIND&0b00000010)//vypínací
#define SENZOR_RX 	(PIND&0b00001000)//vstup senzoru
#define SENZOR_TX 	(PORTD^=0b00010000)//spouštěcí pin senzoru
#define DISPLEJ_OFF	(TCCR0=0b00000000)//vypnutí čítače/časovače 0
#define DISPLEJ_ON	(TCCR0=0b00000011)//zapnutí čítače/časovače 0

//****************************************************************************************
//proměnné pro práci i v přerušení
static volatile unsigned char	go=0, state=2;//2=zobrazit pomlčky
static volatile unsigned long	vzdalenost=0;

//****************************************************************************************

void init(void)//inicializace čítačů/časovačů
{
	DDRD =	0b00010000;//Senzor out
	PORTD =	0b00000110;//tlačítka

	DDRC =	0b00111100;//Jumper pro tečku in
	PORTC =	0b00000001;//Jumper pro tečku log. 1

	MCUCR = 0b00100000;//power-down mode & sleep disable + low level interrupt INT0
	GICR = 0b00000000;//disable INT0

	LEDdisplay_init();//inicializace displeje
	
	//TCCR1B = 0b00000001;//CLK/1//ON
	TCCR1B = 0b00000000;//CLK/1//OFF
	TCNT1 = 0;
	//TIMSK1 = 0b00000001;//OVF INT

	MCUCR = 0b00000110;//any change interrupt INT0
	GICR = 0b11000000;//enable INT0 a INT1

	TCNT0  = 0xFF-prodleva_zobrazeni;
	TIMSK  = 0b00000101;//OVF INT
	DISPLEJ_ON;
}


//max pulse delay=25ms

ISR(TIMER1_OVF_vect, ISR_NOBLOCK)//přerušení od čítače/časovače 1
{
	TCCR1B = 0b00000000;//CLK/1//OFF
	state=2;//objekt příliš blízko/daleko
}


ISR(INT1_vect, ISR_BLOCK)//vnější přerušení z pinu INT1
{
	if(SENZOR_RX)
	{
		TCCR1B = 0b00000001;//CLK/1//ON
//		DISPLEJ_OFF;
	}
	else
	{
		TCCR1B = 0b00000000;//CLK/1//OFF
//		DISPLEJ_ON;
		if(state!=2)
			state=1;
	}
}

ISR(INT0_vect, ISR_NOBLOCK)//vnější přerušení z pinu INT0
{
	go=1;
}

ISR(TIMER0_OVF_vect, ISR_NOBLOCK)//přerušení od čítače/časovače 0 - multiplexní zobrazování na displeji
{
	if(state!=2)//maximální prodleva signálu dodržena
		zobraz_cislo(vzdalenost);
	else //ultrazvuk zpátky nedorazil - objekt příliš blízko/daleko
		zobraz_znaky('-','-','-','-');
	
	TCNT0  = 0xFF-prodleva_zobrazeni;
}

void zmer()
{
	//krátký pulz pro spuštění ultrazvukového modulu
	SENZOR_TX;
	_delay_us(20);
	SENZOR_TX;

	//pevné čekání zajistí stále stejné blikání displeje
	//doba měřena pomocí přerušení
	_delay_ms(66);//maximální zpoždění ultrazvuku může být 65535 us - pak přeteče časovač

	//hodnota 1 v TCNT1 ~ 1us => ((TCNT1/1000000 * 343) /2) = vzdálenost v metrech
	vzdalenost = (((unsigned long)TCNT1)*1715)/10000;//(čas*343m/s) / 2 ===> + přepočet na milimetry

	TCNT1=0;//vypnutí čítače časovače 1
}

int main(void)
{	
	unsigned long pocitadlo=0, vypnuti=0;

	init();//inicializace periferií
	sei();//povolení přerušení
	_delay_ms(250);
	zmer();//jedno prázdné měření pro zajištění správné funkce
	
	while(1)
	{
		if(!TLACITKO2 || vypnuti++>1000000)
		{
			DISPLEJ_OFF;
			MCUCR = 0b10100000;//power-down mode & sleep enable + low level interrupt INT0
			GICR = 0b01000000;//enable INT0
			sleep();//uspání, po probuzení se provede reset
		}

		if(go==1)
		{
			pocitadlo=0;

			while(!TLACITKO)//dokud je tlačítko drženo
			{
				if(pocitadlo++==30000)//cca 2 sekundy
					break;
			}
			go=2;
		}

		if(go==2)
		{
			vypnuti=0;
			DISPLEJ_OFF;
			state=0;//zajistí zobrazení čísla pokud ve vzdálenost v rozsahu
			zmer();
			DISPLEJ_ON;
			
			if(pocitadlo<30000)//tlačitko stisknuto po dobu cca 2 sekund
				go=0;

			_delay_ms(180);
		}
	}
}
