/****************************************************************************
 ■ Power Unit
   2012(C) Mr.Honey
****************************************************************************/

#include <p24Hxxxx.h>

#define FOSC	(2001415UL)
#define FCY	(FOSC / 2)
#include <libpic30.h>

//=================================================================

// Configuration
_FBS(BWRP_WRPROTECT_OFF & BSS_NO_BOOT_CODE)
_FGS(GWRP_OFF & GSS_OFF & GCP_OFF)
_FOSCSEL(FNOSC_LPRCDIVN & IESO_OFF)
_FOSC(POSCMD_NONE & OSCIOFNC_ON & IOL1WAY_ON & FCKSM_CSECMD)
_FWDT(WDTPOST_PS4096 & WDTPRE_PR32 & WINDIS_OFF & FWDTEN_OFF)
_FICD(JTAGEN_OFF)

//=================================================================

// IOPorts
#define SW3		_RA3
#define VIS		_LATA2
#define DIG1_V	_LATB4
#define DIG2_V	_LATB6
#define DIG3_V	_LATB5
#define DIG4_V	_LATB7
#define DIG1_I	_LATB7
#define DIG2_I	_LATB5
#define DIG3_I	_LATB6
#define DIG4_I	_LATB4

//=================================================================

typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned long U32;
typedef unsigned long long U64;

//=================================================================

#define PR_ON		300		// 点灯期間(Clock) <TODO>
#define PR_OFF	500		// 消灯期間(Clock) <TODO>
#define CN_AD		18		// 計測間隔(Loop) <TODO>
#define PR_LED1	700		// 点滅期間(長)(Clock) <TODO>
#define PR_LED2	(PR_LED1 * 4)

static U8 s_CDataV[10] = {	// 電圧7セグ文字データ
	0x14, 0xD7, 0x4C, 0x45, 0x87, 0x25, 0x24, 0x17, 0x04, 0x05
};
static U8 s_CDataI[10] = {	// 電流7セグ文字データ
	0x28, 0xEB, 0x32, 0xA2, 0xE1, 0xA4, 0x24, 0xE8, 0x20, 0xA0
};
#define DOT_V	0xFB
#define DOT_I	0xDF
#define DSPOFF	0xFF

static U8 s_SDataV[4] = {	// 電圧7セグコモンデータ
	0x10, 0x40, 0x20, 0x80
};
static U8 s_SDataI[4] = {	// 電流7セグコモンデータ
	0x80, 0x20, 0x40, 0x10
};

static volatile int s_nMode = 0;		// 表示モード
static volatile U16 s_uV_LV1 = 0;		// 電圧(低)生データ
static volatile U16 s_uV_LV2 = 0;		// 電圧(高)生データ
static volatile U16 s_uI_LV1 = 0;		// 電流(低)生データ
static volatile U16 s_uI_LV2 = 0;		// 電流(高)生データ

static volatile U16 s_uVmV = 0;		// 電圧値(mV)
static volatile U16 s_uImA = 0;		// 電流値(mA)
static volatile U16 s_uMaxI = 0;		// 最大電流値(mA)
static volatile U16 s_uMinI = 9999;	// 最小電流値(mA)
static volatile U8 s_VVVV[4];			// 電圧表示文字列
static volatile U8 s_IIII[4];			// 電流表示文字列

static volatile int s_nPos = 0;		// 表示位置
static volatile int s_fT2 = 0;		// モード点滅用

//=================================================================

static void Initialize(void);
static void Run(void);
static void MakeData(void);
static void DelayMs(U16 ms);

//=================================================================

// ●メインエントリ
int main(void)
{
	// システム初期化
	Initialize();

	// 実行開始
	Run();

	return 0;
}

// ●システム初期化
static void Initialize(void)
{
	// クロック設定(Fosc = 2.0014155MHz)
	_TUN = 23; // +8.625% -> 8.0056625MHz
	_FRCDIV = 0b010; // FRC divide by 4
	
	// 必要なモジュールのみ有効化
	PMD1 = 0xE7FE; // T1,T2,AD1のみON
	PMD2 = 0xFFFF;

	// ポートクリア
	PORTA = LATA = 0;
	PORTB = LATB = 0;

	// デジアナ設定
	AD1PCFGL = 0b1111111111000000; // Analog: AN0,1,2,3,4,5

	// 方向設定
	TRISA = 0b0000000000001011;
	TRISB = 0b0000000000001111;

	// 入力通知設定
	_CN29PUE = 1;
	_CN29IE = 1;

	// タイマー設定
	T1CON = 0b0000000000000000;
	T2CON = 0b0000000000110000;

	/*** ADC設定 ***/

	// AD1CON1
	_ADSIDL = 0;
	_AD12B = 1; // 12Bit mode
	_FORM = 0b00; // 符号なし整数
	_SSRC = 0b111; // 自動変換
	_ASAM = 1; // 自動サンプリング
	_SAMP = 0;

	// AD1CON2
	_VCFG = 0b001; // VREF+ AVSS
	_CSCNA = 1; // CH0チャンネルスキャン
	_SMPI = 0b0111; // 8回の変換動作完了で割込み
	_BUFM = 0; // 全変換結果をバッファ
	_ALTS = 0; // 常にサンプルA

	// AD1CON3(変換時間=(4+14)xTAD=18x250ns= 4.5us)
	_ADRC = 1; // TAD=TADRC=250ns(4MHz)
	_SAMC = 4; // サンプルタイム=4TAD(1uS)

	// AD1CHS0
	_CH0NA = 1; // チャンネル0 負極性入力にAN1を選択

	// AD1CSSL
	AD1CSSL = 0b0000000000111100; // Analog AN2,3,4,5

	_ADON = 1;

	// 割込み優先度
	_T2IP = 4;
	_CNIP = 5;
	_T1IP = 6;
	_AD1IP = 7;

	DelayMs(100);
}

// ●実行ループ
static void Run(void)
{
	int n = 0;

	// リフレッシュタイマー
	T1CONbits.TON = 1;

	// 点滅タイマー
	PR2 = PR_LED1;
	T2CONbits.TON = 1;

	for(;;)
	{
		if (n-- <= 0)
		{
			// 電圧電流計測
			_CNIE = 0;
			_T2IE = 0;
			_T1IE = 0;

			_DONE = 0;
			while (!_DONE);
			_AD1IF = 0;
			_AD1IE = 1;
			Sleep();

			_T1IE = 1;
			_T2IE = 1;
			_CNIE = 1;
			n = CN_AD;
		}

		// 表示データ作成
		MakeData();
	}
}

// ●表示データ作成
static void MakeData(void)
{
	U16 uVmV, uImA;
	U8 bDot;

	// 電圧値(mV)
	#define CALCV(R1, R2, Val)	(U32)((3300ULL * (R1) * (U64)(Val)) / (4096ULL * (R2)))
	if (s_uV_LV1 <= 4000) {
		// IC7-A
		s_uVmV = CALCV(12400ULL, 10000ULL, s_uV_LV1);
	}
	else {
		// IC7-B
		s_uVmV = CALCV(20470ULL, 3300ULL, s_uV_LV2);
	}

	// 電流値(mA)
	#define CALCI(R1, R2, Val)	(U32)((33000ULL * (R1) * (U64)(Val)) / (4096ULL * (R2)))
	if (s_uI_LV1 <= 4000) {
		// IC7-D
		s_uImA = CALCI(4100ULL, 33000ULL, s_uI_LV1);
	}
	else {
		// IC7-C
		s_uImA = CALCI(8200ULL, 33000ULL, s_uI_LV2);
	}

	// 表示電流値/モード点滅表示決定
	_CNIE = 0;
	switch (s_nMode)
	{
	case 1: // 最大値
		if (s_uImA > s_uMaxI)
			s_uMaxI = s_uImA;
		uImA = s_uMaxI;
		bDot = (s_fT2)? DOT_I : DSPOFF;
		break;

	case 2:  // 最小値
		if (s_uImA < s_uMinI)
			s_uMinI = s_uImA;
		uImA = s_uMinI;
		bDot = (!s_fT2)? DOT_I : DSPOFF;
		break;

	default: // 標準
		uImA = s_uImA;
		bDot = DSPOFF;
		break;
	}
	_CNIE = 1;

	_T1IE = 0;

	// 文字列変換(電圧: ##.##V)
	uVmV = s_uVmV / 10;
	s_VVVV[3] = s_CDataV[uVmV % 10]; uVmV /= 10;
	s_VVVV[2] = s_CDataV[uVmV % 10]; uVmV /= 10;
	s_VVVV[1] = s_CDataV[uVmV % 10] & DOT_V; uVmV /= 10;
	s_VVVV[0] = (uVmV > 0)? s_CDataV[uVmV] : DSPOFF;

	// 文字列変換(電流: ####mA)
	s_IIII[3] = s_CDataI[uImA % 10] & bDot; uImA /= 10;
	s_IIII[2] = (uImA > 0)? s_CDataI[uImA % 10] : DSPOFF; uImA /= 10;
	s_IIII[1] = (uImA > 0)? s_CDataI[uImA % 10] : DSPOFF; uImA /= 10;
	s_IIII[0] = (uImA > 0)? s_CDataI[uImA] : DSPOFF;

	_T1IE = 1;
}

// ●簡易遅延
static void DelayMs(U16 ms)
{
	while (ms >= 100)
	{
		__delay_ms(100);
		ms -= 100;
	}
	if (ms != 0) {
		__delay_ms(ms);
	}
}

//=================================================================

// ●ADC 1 convert complete
void _ISRFAST _ADC1Interrupt(void)
{
	static int nCount = 0;
	static U16 uV_LV1[4] = { 0 };
	static U16 uV_LV2[4] = { 0 };
	static U16 uI_LV1[4] = { 0 };
	static U16 uI_LV2[4] = { 0 };

	int i = (nCount++ % 4);
	uV_LV1[i] = ADC1BUF4;
	uV_LV2[i] = ADC1BUF5;
	uI_LV2[i] = ADC1BUF6;
	uI_LV1[i] = ADC1BUF7;

	#define AVG(v) (U16)((U32)((U32)v[0] + (U32)v[1] + (U32)v[2] + (U32)v[3]) / 4UL)
	s_uV_LV1 = AVG(uV_LV1);
	s_uV_LV2 = AVG(uV_LV2);
	s_uI_LV1 = AVG(uI_LV1);
	s_uI_LV2 = AVG(uI_LV2);

	_AD1IE = 0;
	_AD1IF = 0;
}

// ●TMR1 expired
void _ISRFAST _T1Interrupt(void)
{
	if (s_nPos & 1)
	{
		// 点灯
		int i;

		if (s_nPos < 8)
		{
			// 電圧
			VIS = 1;
			i = (s_nPos - 1) / 2;
			LATB = (s_VVVV[i] << 8) | s_SDataV[i];
		}
		else
		{
			// 電流
			VIS = 0;
			i = (s_nPos - 9) / 2;
			LATB = (s_IIII[i] << 8) | s_SDataI[i];
		}

		PR1 = PR_ON;
		TMR1 = 0x0000;
	}
	else
	{
		// 消灯
		LATB &= 0b1111111100000000;

		PR1 = PR_OFF;
		TMR1 = 0x0000;

		if (s_nPos >= 16)
			s_nPos = 0;
	}
	s_nPos++;

	_T1IF = 0;
}

// ●TMR2 expired
void _ISR _T2Interrupt(void)
{
	s_fT2 = !s_fT2;
	PR2 = (s_fT2)? PR_LED2 : PR_LED1;
	TMR2 = 0x0000;

	_T2IF = 0;
}

// ●CN Input change interrupt
void _ISR _CNInterrupt(void)
{
	static int s_bSW3 = 0;
	int i, j, k;

	i = 0; j = 0; k = 0;
	do {
		if (!SW3) j++; // 接触している
		else k++;     // 接触していない
	}
	while (++i < 101);
	if (j < k) {
		s_bSW3 = 0;
	}
	else if (!s_bSW3)
	{
		// モード切換えとホールド値リセット
		s_nMode = (++s_nMode % 3);
		s_uMaxI = 0;
		s_uMinI = 9999;
		s_bSW3 = 1;
	}

	_CNIF = 0;
}
