
// ****************************************************************************
//
//                         Page P - power and energy meter
//
// ****************************************************************************

#include "../include.h"

double PState = 0;	// P power in uW
double PStateRaw = 0;	// P power without tare
double PEnergy = 0;	// P energy in uWh
double PTare = 0;	// P tare
u32 PLast = 0;		// time of last measure
Bool PLastValid = False; // time of last measure is valid

// P update (takes 500ms)
// ADC clock divider is set to 750kHz. One ADC clock cycle is 1.33us.
// U sampling time is (7) 239.5 cycles. One sample is 239.5+12.5=252 ADC clock cycles = 336us.
// I sampling time is (5) 55.5 cycles. One sample is 55.5+12.5=68 ADC clock cycles = 91us.
// One U+I sample = 427 us. 1171 samples take approx. 500ms.
// The measurement period is 500 ms – this is so that the measurement period is an integer
// multiple of the electrical distribution period, i.e., Europe 20 ms (50 Hz), USA 16.7 ms
// (60 Hz). This ensures greater resistance to noise from the electrical distribution network.
void P_Update()
{
	int n;
	u32 i, u, t;
	s64 p;
	u8 key;

	// measure U voltage
	n = 0;		// sample counter
	p = 0;		// P sample accumulator
	cb();
	t = Time();	// start time
	cb();
	while ((u32)(Time() - t) < 500000*HCLK_PER_US)
	{
		// get U sample
		u = ADC1_GetSingle(U_ADC) & 0xfff;

		// get I sample
		i = ADC1_GetSingle(I_ADC) & 0xfff;

		// add to accumulator
		p += u * i;

		// sample counter
		n++;

		// break from keyboard
		key = KeyBuf;
		if ((key == KEY_PREV) || (key == KEY_NEXT)) break;
	}

	// voltage in mV U = usum * Supply * Ux_K / (n * 4095 * (1<<U_SHIFT))
	// current in mA I = isum * Supply * I_K / (n * 4095 * (1<<I_SHIFT))
	// power in uW P = U * I = psum * Supply*Supply * Ux_K*I_K / ((n * 4095*4095 * (1<<U_SHIFT)*(1<<I_SHIFT))
	double k1 = (double)p * Supply*Supply * ((UMode == U_MODE_HIGH) ? U2_K : U1_K) * I_K;
	double k2 = (double)n * 4095*4095 * (1<<I_SHIFT)*(1<<U_SHIFT);
	double val = k1/k2;
	PStateRaw = val;
	val -= PTare;
	PState = val;

	// energy accumulator
	t = Time();
	if (PLastValid)
	{
		if (val < ((UMode == U_MODE_HIGH) ? 10 : 1)) val = 0;
		u32 dt = t - PLast;
		val = val * dt / ((double)3600*1000000*HCLK_PER_US);
		PEnergy += val;
	}
	PLast = t;
	PLastValid = True;
}

// P initialize (initializes BAT measure, too)
void P_Init()
{
	// BAT initialize, initialize ADC single conversion mode, set ADC clock to 750kHz
	BAT_Init();

	// setup input pins
	GPIO_Mode(U_GPIO, GPIO_MODE_AIN);	// U GPIO
	GPIO_Mode(I_GPIO, GPIO_MODE_AIN);	// I GPIO

	// set sampling time of I input ADC to 55.5 cycles
	ADC1_SampTime(I_ADC, 5);		// I input ADC channel

	// set sampling time of U input ADC to 239.5 cycles
	// - U input has higher resistance, need more time to sample
	ADC1_SampTime(U_ADC, 7);		// U input ADC channel

	// set 'Hold' key to long mode
	KeyHoldLong();
}

// P terminate (terminates BAT measure, too)
void P_Term()
{
	// set 'Hold' key to default short mode
	KeyHoldShort();

	// BAT terminate
	BAT_Term();

	// reset input pins
	GPIO_PinReset(U_GPIO);	// U GPIO
	GPIO_PinReset(I_GPIO);	// I GPIO
}

// P display
void P_Disp()
{
	// clear area
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// select font 8x12
	SelFont12();

	// P power in uW
	u32 p = (u32)PState;

	// display power
	DispUVal(12, ROW1_Y, p, 4, -6, 'W', False, False);

	// prepare energy
	double val = PEnergy;
	if (val < 0) val = 0;
	int ex = -6;
	while ((val > 0) && (val < 10000) && (ex > -12))
	{
		val *= 10;
		ex--;
	}

	while ((val > 100000) && (ex < 9))
	{
		val /= 10;
		ex++;
	}

	// display energy
	p = (u32)val;
	int x = DispUVal(8, ROW3_Y, p, 4, ex, 'W', False, False);
	DrawChar2('h', x, ROW3_Y);

	// display update
	DispUpdate();
}

// P tare
void P_Tare()
{
	// Tare message
	TareMsg();
	PTare = PStateRaw;
	DispUpdate();
	WaitMs(500);
}

// Page P (returns key PREV/NEXT)
u8 PageP()
{
	int i;
	u8 key;

	// P initialize (initializes BAT measure, too)
	P_Init();

	// P display
	P_Disp();

	while (True)
	{
		// reload watchdog counter
		IWDG_Reload();

		// update battery supply voltage (takes 100ms)
		BAT_Update();

		// P update (takes 500ms)
		P_Update();

		// P display
		P_Disp();

		// keyboard input
		key = KeyGet();
		switch (key)
		{
		// change page
		case KEY_PREV:
		case KEY_NEXT:
			// P terminate (terminates BAT measure, too)
			P_Term();
			return key;

		// Hold - reset
		case KEY_HOLD:
			PEnergy = 0;	// P energy in uWh
			PLastValid = False; // time of last measure is not valid
			P_Disp();
			break;

		// Hold long - tare
		case KEY_HOLD_LONG:
			// P tare
			P_Tare();
			break;
		}
	}
}
