// (c) 2010. Ballage.

#include <pic.h>

// feny bekapcsolasi minta struktura
typedef struct pattern
{
	unsigned char value;	// aktualis ertek, az egyes bitek hatarozzak meg, hogy mi vilagitson es mi nem
	unsigned char time;		// az adott allapotban mennyi idot toltson
} pattern_t;

// hangmagassag struktura
typedef struct pitch_pattern
{
	signed char value;		// aktualis hangmagassag
	signed char time;		// mennyi ideig varjon ebben az allapotban
	signed char increment;	// mennyivel valtozzon a magassag a varakozas kozben
 } pitch_pattern_t;

// lampa bekapcsolasi mintak
#define OFF 0b00000000		// minden lampa ki
#define RED 0b10000000		// voros be
#define BLU 0b00010000		// kek
#define LWH 0b00100000		// bal oldali feher
#define RWH 0b00000010		// jobb oldali feher
#define RBK 0b00000100		// jobb oldali feklampa
#define LBK 0b00001000		// bal oldali feklampa
#define FRN (RED|BLU|LWH|RWH)	// minden elulso lampa bekapcsolasa
#define EOS 0b11111111		// szekvencia vege (a mintasor veget jelzi)

// feny es hangeffektek

const pattern_t flash_pattern1[] = {
	{ OFF, 40 },
	{ BLU|LBK|RBK, 5 },
	{ OFF, 5 },
	{ BLU|LBK|RBK, 5 },
	{ OFF, 5 },
	{ BLU|LBK|RBK, 5 },
	{ OFF, 5 },
	{ BLU|LBK|RBK, 5 },
	{ OFF, 5 },
	{ BLU|LBK|RBK, 5 },
	
	{ OFF, 40 },
	{ RED|LBK|RBK, 5 },
	{ OFF, 5 },
	{ RED|LBK|RBK, 5 },
	{ OFF, 5 },
	{ RED|LBK|RBK, 5 },
	{ OFF, 5 },
	{ RED|LBK|RBK, 5 },
	{ OFF, 5 },
	{ RED|LBK|RBK, 5 },
	{ EOS, 0 }
};

const pattern_t flash_pattern2[] = {
	{ LWH|RWH, 30 },
	{ LBK|RBK, 30 },
	{ LWH|RWH, 30 },
	{ LBK|RBK, 30 },
	{ OFF, 60 },
	{ 0b11111111, 0 }
};

const pattern_t flash_pattern3[] = {
	{ OFF, 40 },
	{ RED|LWH, 25 },
	{ RBK|LBK, 25 },
	{ BLU|RWH, 25 },
	{ RBK|LBK, 25 },
	{ EOS, 0 }
};

const pattern_t flash_pattern4[] = {
	{ OFF, 40 },
	{ LWH|RWH|LBK, 5 },
	{ OFF, 5 },
	{ BLU|RED|RBK, 5 },
	{ OFF, 5 },
	{ LWH|RWH|LBK, 5 },
	{ OFF, 5 },
	{ BLU|RED|RBK, 5 },
	{ OFF, 5 },
	{ LWH|RWH|LBK, 5 },
	{ OFF, 5 },
	{ BLU|RED|RBK, 5 },
	{ OFF, 5 },
	{ LWH|RWH|LBK, 5 },
	{ OFF, 5 },
	{ BLU|RED|RBK, 5 },
	{ OFF, 5 },
	{ 0b11111111, 0 }
};

const pattern_t flash_pattern5[] = {
	{ LWH, 4 },
	{ BLU, 4 },
	{ RWH, 4 },
	{ RED, 4 },
	{ RBK, 4 },
	{ LBK, 4 },
	{ OFF, 8 },
	{ LWH, 4 },
	{ BLU, 4 },
	{ RWH, 4 },
	{ RED, 4 },
	{ RBK, 4 },
	{ LBK, 4 },
	{ OFF, 32 },
	{ 0b11111111, 0 }
};
const pattern_t flash_pattern6[] = {
	{ LWH|RWH|RBK|LBK, 30 },
	{ OFF, 30 },
	{ EOS, 0 }
	
};	

const pitch_pattern_t siren1[] = 
{
	{ 120, 50, 0 },
	{ 170, 50, 0 },
	{ 120, 50, 0 },
	{ 0, 100, 0 },
	{ 255, 0, 0 }
};

const pitch_pattern_t siren4[] = 
{
	{ 80, 160, 1 },
	{ 240, 160, -1 },
	{ -1, 0, 0 }
};

const pitch_pattern_t siren6[] = 
{
	{ 240, 200, -1 },
	{ -1, 0, 0 }
};

const pitch_pattern_t siren5[] = 
{
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{  0, 20, 0 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{ 80, 4, 10 },
	{ 160, 4, -10 },
	{  0, 40, 0 },
	{ -1, 0, 0 }
};

const pitch_pattern_t siren2[] = 
{
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{ 160, 1, 0 },
	{ 240, 1, 0 },
	{  0, 40, 0 },
	{ -1, 0, 0 }
};
const pitch_pattern_t siren3[] = 
{
	{ 160, 80, 1 },
	{ 160, 80, 1 },
	{ 160, 80, 1 },
	{ 160, 80, 1 },
	{ -1, 0, 0 }
};

const pitch_pattern_t siren7[] = 
{
	{ 160, 20, -2 },
	{   0, 30,  0 },
	{ 120, 20, -2 },
	{   0, 30,  0 },
	{ -1, 0, 0 }
};

// fenyjatek allapotleiroi
struct pattern * flash_start = 0;		// elso fenyeffekt allapot
struct pattern * flash_current = 0;		// aktualis fenyeffekt allapot cime
int flash_wait = 0;						// mennyi ido van hatra az adott allapotbol

// szirena allapotleiroi
struct pitch_pattern * siren_start = 0;
struct pitch_pattern * siren_current = 0;
signed char siren_wait = 0;
signed char siren_period = 0;			// aktualis periodusido hangmagassag alapjan kiszamolt erteke
signed char siren_repeat = 0;			// ismetlesek szama

// a feny es hangeffektek fo tombjei
const struct pitch_pattern * siren_patterns[] = { siren1, siren2, siren3, siren4, siren5, siren6, siren7 };
const struct patter * flash_patterns[] = { flash_pattern1, flash_pattern2, flash_pattern3, flash_pattern4, flash_pattern5, flash_pattern6 };
unsigned char siren_repeats[] = { 2, 4, 1, 2, 2, 3, 1 }; // hanyszor ismetelje az egyes szirenahangokat

// aktualisan kivalasztott feny es hang
unsigned char current_siren_pattern = 0;
unsigned char current_flash_pattern = 0;

// varakozas
void delay(unsigned char value)
{
	while (value-- > 0)
		;
}	

void delay2(unsigned char value)
{
	while (value-- > 0)
		delay(100);
}	

// pwm periodusido beallitasa
void set_period(unsigned char value)
{
	CCPR1L = value >> 1;
	PR2 = value;	
}	

// inicializalas
void init()
{
	TRISC = 0;	// a C port minden laba kimenet
	TRISB = 0b00100000;	// B port kimenet, kiveve az RB5-ot

	T2CON = 0b01111111; // prescale = 16, postscaler = 0, TMR2 = on
	CCP1CON = 0b00001111; // PWM mode, duty cycle 2 LSB = 0
}

// hangminta kivalasztasa
void select_siren_pattern(unsigned char index)
{
	siren_current = siren_start = siren_patterns[index];
	siren_wait = siren_start->time;
	siren_period = siren_start->value;
	siren_repeat = siren_repeats[index];
	set_period(siren_period);
}	


// fenyek beallitasa
void set_lights(unsigned char value)
{
	PORTB = (flash_current->value & 0x0f) | ((flash_current->value << 1) & 0b00010000);
	PORTC = flash_current->value & 0xf0;
}	

// villogasi effekt kivalasztasa
void select_flash_pattern(unsigned char index)
{
	flash_current = flash_start = flash_patterns[index];
	flash_wait = flash_start->time;
	set_lights(flash_current->value);
}

// 
void main(void)
{
	init();
	
	select_siren_pattern(6);
	select_flash_pattern(5);
	
	unsigned char lastb = PORTB;

	while (1)
	{
		// ha megnyomtak a gombot, akkor leptetjuk a mintakat
		unsigned char nowb = PORTB;
		if ((nowb & 0b00100000) == 0 && (lastb & 0b00100000) != 0)
		{
			// mivel egyel kevesebb fenyeffekt van mint hang, ezert a leptetesek soran minden 
			// feny minden hanggal parositasra kerul egy bizonyos leptetesi szam utan
			current_siren_pattern++;
			if (current_siren_pattern >= sizeof(siren_patterns) / sizeof(siren_patterns[0]))
				current_siren_pattern = 0;
			select_siren_pattern(current_siren_pattern);

			current_flash_pattern++;
			if (current_flash_pattern >= sizeof(flash_patterns) / sizeof(flash_patterns[0]))
				current_flash_pattern = 0;
			select_flash_pattern(current_flash_pattern);
		}

		// a fociklus fo idozitese, ez hatarozza meg a mintak idozitesenek legkisebb egysegnek merteket
		delay2(10);
		lastb = nowb;

		// fenyminta allapotkezeles
		if (flash_current != 0)
		{
			// ha lejart az ido, akkor lepunk a kovetkezo allapotra
			if (flash_wait == 0)
			{
				flash_current++;
				if (flash_current->value == 0xff)
					flash_current = flash_start;
				flash_wait = flash_current->time;
				set_lights(flash_current->value);
			} else
				flash_wait--;
		}

		// szirena allapotkezeles
		if (siren_current != 0 && siren_repeat > 0)
		{
			if (siren_wait == 0)
			{
				siren_current++;
				if (siren_current->value == -1)
				{
					--siren_repeat;
					siren_current = siren_start;
				}
				if (siren_repeat > 0)
				{
					siren_wait = siren_current->time;
					siren_period = siren_current->value;
					set_period(siren_period);
				}
				else
					set_period(0);
			} 
			else
			{
				// az adott allapotban varakozva modositjuk a hangmagassagot
				siren_wait--;
				siren_period += siren_current->increment;
				set_period(siren_period);
			}	
		}
	}	
}
