/****************************************************************************

	HoneyHouse
		Copyright(C) 2014 Mr.Honey

****************************************************************************/

#include <pic.h>

typedef unsigned char U8;
typedef unsigned short U16;
typedef U8 BOOL;

#define TRUE	1
#define FALSE	0

//=================================================================

// CONFIG1
#pragma config CPD = OFF        // Data memory code protection is disabled
#pragma config BOREN = OFF      // Brown-out Reset disable
#pragma config IESO = OFF       // Internal/External Switchover mode is disabled
#pragma config FOSC = INTOSC    // Oscillator Selection：INTOSC Oscillator
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor is disabled
#pragma config MCLRE = ON       // MCLR/VPP pin function
#pragma config WDTE = SWDTEN    // WDT controlled by the SWDTEN bit in the WDTCON register
#pragma config CP = OFF         // Program memory code protection is disabled
#pragma config PWRTE = OFF      // PWRT disabled
#pragma config CLKOUTEN = OFF   // CLKOUT function is disabled

// CONFIG2
#pragma config PLLEN = OFF		// 4x PLL disabled
#pragma config WRT = OFF        // Write protection off
#pragma config STVREN = ON      // Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO        // Brown-out Reset Voltage (Vbor)
#pragma config LVP = OFF        // High-voltage on MCLR/VPP must be used for programming

//=================================================================

#define NIGHT_THR		1000	// 昼モードと夜モードの境目
#define LIGHT_LV		525	// 暗くなった時にここの値以下なら点灯する
#define LIGHT_L_PS		0b01101 // 1:262144 (218) (Interval 8s typ)　 // 暗くなって点灯した時の点灯時間
#define LIGHT_T_PS		0b10000 // 1:2097152 (221) (Interval 64s typ) // タッチして点灯した時の点灯時間

#if 0 // Test
#undef NIGHT_THR
#undef LIGHT_LV
#undef LIGHT_L_PS
#undef LIGHT_T_PS
#define NIGHT_THR		1000
#define LIGHT_LV		1000
#define LIGHT_L_PS		0b01010 // 1:32768 (Interval 1s typ)
#define LIGHT_T_PS		0b01100 // 1:131072 (217) (Interval 4s typ)
#endif

#define DET_DELTA		25	// 明るさがこの値以上変化した場合にのみ点灯する
#define DAY_THR			(NIGHT_THR + 4)	// 昼夜間のヒステリシス値

//#define NOUSE_WDT // for test
#ifndef NOUSE_WDT
#define Sleep()			SLEEP()
#else
#define Sleep()			DelayMs(1000)
#endif

//=================================================================

#define LED_STEP		255
const U16 s_PwmTbl[LED_STEP] = { 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 57, 58, 59, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 75, 76, 78, 80, 81, 83, 85, 87, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 113, 115, 117, 120, 122, 125, 127, 130, 132, 135, 138, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 171, 174, 178, 181, 185, 188, 192, 196, 200, 204, 208, 212, 216, 220, 224, 229, 233, 238, 242, 247, 252, 257, 262, 267, 272, 277, 283, 288, 294, 299, 305, 311, 317, 323, 329, 336, 342, 349, 356, 362, 369, 376, 384, 391, 399, 406, 414, 422, 430, 438, 447, 455, 464, 473, 482, 491, 500, 510, 520, 530, 540, 550, 561, 571, 582, 593, 605, 616, 628, 640, 652, 664, 677, 690, 703, 716, 730, 744, 758, 773, 787, 802, 817, 833, 849, 865, 881, 898, 915, 932, 950, 968, 986, 1005, 1023 };

static BOOL s_bFast;
static U16 s_uDelay;
static U8 s_nTmr0;
static BOOL s_bDir;
static U8 s_uStep;

static U16 s_uPreCds = 0;
static U16 s_uCurCds = 0;
static U16 s_uCap = 0;

//=================================================================

static void InitSystem(void);
static void Run(void);

static void GetLight(void);
static void EnableCPS(BOOL bEnable);
static void InitTouch(void);
static BOOL GetTouch(void);
static void Lighting(U8 uWdtPs);
static void LedCtrl(BOOL bOn, U8 uSpeed);
static void PowerOn(BOOL bOn);
static void SleepDelay(U8 uWdtPs);
static void EnableWDT(BOOL bEnable, U8 nPreScale);
static void SetClock(BOOL bFast);
static void DelayMs(U16 uMs);

//=================================================================

// ●メインエントリ
void main(void)
{
	InitSystem();

	PowerOn(TRUE);
	for (int i = 0; i < 3; i++)
	{
		LATA5 = 1; DelayMs(300);
		LATA5 = 0; DelayMs(300);
	}
	PowerOn(FALSE);
	
	Run();
}

// ●システム初期化
static void InitSystem(void)
{
	// ポート初期化
	PORTA = LATA = 0x00;
	TRISA =  0b00011101;
	ANSELA = 0b00010101; // RA0(Power), RA2(CPS2), RA4(AN3)

	WPUA = 0b00001000; // RA3
	nWPUEN = 0;

	// ADC(CDS)
	ADCON1 = 0b10110000; // Right justified, FRC, VREF+ is connected to AVDD
	ADCON0 = 0b00001101; // AN3, ADC is enabled

	// CPS
	//CPSCON0 = 0b00000100; // Oscillator is in Low Range.
	//CPSCON0 = 0b00001000; // Oscillator is in Medium Range.
	//CPSCON0 = 0b01000100; // Oscillator is in Low Range.
	CPSCON0 = 0b00001100; // Oscillator is in High Range.
	//CPSCON0 = 0b01001000; // Oscillator is in Medium Range.
	CPSCON1 = 0b00000010; // channel 2, (CPS2)

	// PWM
	CCP1SEL = 1; // CCP1/P1A function is on RA5 (2pin)

	// Timer0(CPS, Sweep, Delay)
	TMR0CS = 0;
	OPTION_REGbits.PS = 0b101; // 1 : 64

	// Timer1(CPS)
	T1CON = 0b11000000; // Timer1 clock source is Capacitive Sensing Oscillator, 1:1 Prescale value
	T1GCON = 0b10110001; // Timer1 gate function, active-high, Toggle mode, Single-Pulse mode, Timer0 overflow output
	T1GPOL = 1;

	// Timer2(PWM)
	T2CON = 0b00000000; // 1:1 Postscaler, Prescaler is 1
	PR2 = 255;

	// 割込み許可
	PEIE = 1;
	GIE = 1;

	// クロック(500KHz⇒31KHz)
	SetClock(FALSE);
}

// ●実行
static void Run(void)
{
	//for(;;)GetLight();

	for(;;)
	{
		// デイモード
		EnableCPS(FALSE);
		for(;;)
		{
			GetLight();
			if (s_uCurCds > NIGHT_THR)
			{
				SleepDelay(0b01010); // 1:32768 (Interval 1s typ)
				continue;
			}
			break;
		}

		// ナイトモード
		EnableCPS(TRUE);
		BOOL bInit = FALSE;
		for(;;)
		{
			if ( (s_uCurCds <= LIGHT_LV) && (s_uPreCds > s_uCurCds) && ((s_uPreCds - s_uCurCds) >= DET_DELTA) )
			{
				Lighting(LIGHT_L_PS);
				DelayMs(500); // 電源安定化待ち

				s_uCurCds = 0;
				InitTouch();
			}
			else if (GetTouch())
			{
				Lighting(LIGHT_T_PS);
				DelayMs(500); // 電源安定化待ち

				s_uCurCds = 0;
				InitTouch();
			}
			else
			{
				SleepDelay(0b01000); // 1:8192 (Interval 256 ms typ)
				if (!bInit)
				{
					InitTouch();
					bInit = TRUE;
				}
			}

			GetLight();
			if (s_uCurCds >= DAY_THR)
				break;
		}
	}
}

// ●光量検出
static void GetLight(void)
{
	GO_nDONE = 1;
	while(GO_nDONE);

	s_uPreCds = s_uCurCds;
	s_uCurCds = (ADRESH << 8) | ADRESL;
}

// ●タッチ検出ON/OFF
static void EnableCPS(BOOL bEnable)
{
	CPSON = bEnable;
	DelayMs(10);

	TMR1ON = bEnable;
}

// ●タッチ検出初期化
static void InitTouch(void)
{
	s_uCap = 0;
	GetTouch();
	
	// 非接触を100ms間に5回検出するまで平均化するループ
	U8 uCount = 0;
	do {
		DelayMs(10);
		if (GetTouch())
		{
			uCount = 0;
			continue;
		}
	}
	while(++uCount < 5);
}

// ●タッチ検出
static BOOL GetTouch(void)
{
	PSA = 1; // Prescaler is not assigned to the Timer0 module
	TMR0 = 0xF8;
	TMR1H = TMR1L = 0;

	T1GGO_nDONE = 1;
	while(T1GGO_nDONE);

	U16 uVal = (TMR1H << 8) | TMR1L;
	if (s_uCap == 0)
	{
		// 値初期化
		s_uCap = uVal;
		return FALSE;
	}

	BOOL bDetect = FALSE;
	if (s_uCap > uVal)
	{
		if ((s_uCap - uVal) >= (s_uCap >> 2)) // 1/4より大きい場合
		{
			// 検出！
			bDetect = TRUE;
		}
	}

	s_uCap = (s_uCap + uVal) >> 1; // 平均化
	return bDetect;
}

// ●照明
static void Lighting(U8 uWdtPs)
{
	SetClock(TRUE);

	LedCtrl(TRUE, 15);
	SleepDelay(uWdtPs);
	LedCtrl(FALSE, 180);

	SetClock(FALSE);
}

// ●LED制御
static void LedCtrl(BOOL bOn, U8 uSpeed)
{
	if (bOn)
	{
		TMR2 = 0;
		TMR2ON = 1;

		CCPR1L = 0;
		CCP1CON = 0b00001100; // Single output, PWM mode: P1A active-high
		PowerOn(TRUE);
	}

	s_bDir = bOn;
	s_uStep = (s_bDir)? 0 : (LED_STEP - 1);
	s_nTmr0 = 0xFF - uSpeed + 1;

	PSA = 0; // Prescaler is assigned to the Timer0 module
	TMR0 = s_nTmr0;
	TMR0IF = 0;
	TMR0IE = 1;

	while (TMR0IE);

	if (!bOn)
	{
		PowerOn(FALSE);
		CCP1CON = 0;

		TMR2ON = 0;
	}
}

// ●DC-DC(LED電源)制御
static void PowerOn(BOOL bOn)
{
	if (bOn)
	{
		TRISAbits.TRISA0 = 0;
		ANSELAbits.ANSA0 = 0; // RA0 Digital output L
	}
	else
	{
		TRISAbits.TRISA0 = 1;
		ANSELAbits.ANSA0 = 1; // RA0 Hight Z
	}
}

// ●スリープ
static void SleepDelay(U8 uWdtPs)
{
	EnableWDT(TRUE, uWdtPs);
	CLRWDT();
	Sleep();
	EnableWDT(FALSE, 0);
}

// ●WDT制御
static void EnableWDT(BOOL bEnable, U8 nPreScale)
{
	if (bEnable)
	{
		WDTCONbits.WDTPS = nPreScale;
#ifndef NOUSE_WDT
		SWDTEN = 1;
#endif
	}
	else {
		SWDTEN = 0;
	}
}

// ●クロック設定
static void SetClock(BOOL bFast)
{
	if (bFast)
	{
		// 4MHz HF
		OSCCONbits.IRCF = 0b1101;
	}
	else
	{
		// 31 kHz LF
		OSCCONbits.IRCF = 0b0000;
	}
	s_bFast = bFast;
}

// ●ウェイト
static void DelayMs(U16 uMs)
{
	if (s_bFast)
	{
		s_nTmr0 = 0xFF - 250 + 1; // 0.25ms at 4MHz Fosc
		s_uDelay = uMs * 4;
	}
	else
	{
		s_nTmr0 = 0xFF - /*80*/68 + 1; // 10ms at 31KHz Fosc
		s_uDelay = uMs / 10;
	}
	
	PSA = 1; // Prescaler is not assigned to the Timer0 module
	TMR0 = s_nTmr0;
	TMR0IF = 0;
	TMR0IE = 1;

	while (TMR0IE);
}

// ●割込み
void interrupt Interrupt(void)
{
	if (TMR0IF)
	{
		// Timer0
		TMR0 += s_nTmr0;
		if (s_uDelay == 0)
		{
			// Sweep
			if (s_bDir)
			{
				if (++s_uStep == (LED_STEP - 1))
					TMR0IE = 0;
			}
			else
			{
				if (--s_uStep == 0)
					TMR0IE = 0;
			}

			U16 nVal = s_PwmTbl[s_uStep];
			CCPR1L = (U8)(nVal >> 2);
			CCP1CONbits.DC1B = nVal & 3;
		}
		else
		{
			// Delay
			if (--s_uDelay == 0)
				TMR0IE = 0;
		}
		TMR0IF = 0;
	}
}
