
// ****************************************************************************
//
//                         Page U - voltage meter
//
// ****************************************************************************

#include "../include.h"

int UMode = U_MODE_LOW;	// U mode
int UState;		// U voltage in mV
int UTare[U_MODE_NUM] = { 0, 0 }; // U voltage tare in mV

// mode text
const char* const U_ModeText[U_MODE_NUM] = {
	"U1 max.",		// low input mode 0..3.3V
	"U2 max.",		// high input mode 0..36V
};

// U update (takes 500ms)
// ADC clock divider is set to 750kHz. One ADC clock cycle is 1.33us.
// U sampling time is (6) 71.5 cycles. One sample is 71.5+12.5=84 ADC clock cycles = 112us.
// 4464 samples take approx. 4464*112=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 U_Update()
{
	int n;
	u32 u, t;
	u8 key;

	// Select channel to be converted and discard first few samples.
	// With a short sample time, the voltage in the sampling capacitors
	// needs to stabilize.
	ADC1_GetSingleMul(U_ADC, 10);

	// measure U voltage
	n = 0;		// sample counter
	u = 0;		// sample accumulator (max. value 4464*4095 = 18M, OK for u32)
	cb();
	t = Time();	// start time
	cb();
	while ((u32)(Time() - t) < 500000*HCLK_PER_US)
	{
		// software start conversion
		ADC1_SwStart();

		// wait end of conversion
		while (!ADC1_EndGet()) {}

		// get conversion result (and clear "end of conversion" flag)
		u += ADC1_Data() & 0xfff;

		// sample counter
		n++;

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

	// get voltage in mV (val = u * Supply * Ux_K / (n * 4095 * (1<<12))
	s64 k1 = (s64)(u64)u * Supply * ((UMode == U_MODE_HIGH) ? U2_K : U1_K);
	s64 k2 = ((s64)(u64)n * 4095 * (1<<U_SHIFT));
	int val = (int)((k1 + (k2>>1))/k2);

	// tare
	UState = val - UTare[UMode];
}

// U initialize (initializes BAT measure, too)
void U_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

	// set sampling time of U input ADC to 71.5 cycles
	ADC1_SampTime(U_ADC, 6);		// U input ADC channel

	// clear measure
	UState = 0;
}

// U terminate (terminates BAT measure, too)
void U_Term()
{
	// BAT terminate
	BAT_Term();

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

// display UI value
void UI_DispVal(s32 val, int y, char unit)
{
	// clear row
	DrawRectClrFast(0, y, WIDTH, FONTH2);

	// select font 8x12
	SelFont12();

	// sign
	char ch = ' ';
	if (val < 0)
	{
		ch = '-';
		val = -val;
	}

	// value: -234mA (dot is small)
	int x = (WIDTH-7*16-8)/2;

	// value < 1.000
	if ((val > 0) && (val < 1000))
	{
		int len = DecUNum(DecNumBuf, val, 0);
		u8* s = DecNumBuf;
		s[len] = 'm';
		s[len+1] = unit;
		s[len+2] = 0;
		x += 16;
		if (len < 3) x += 16;
		if (len < 2) x += 16;
		DrawChar2(ch, x-16, y);
		DrawText2(s, x, y);
		return;
	}

	// display unsigned value (returns next X coordinate; need to call DispUpdate())
	//  x ... X coordinate
	//  y ... Y coordinate
	//  val ... value
	//  dig ... number of valid digits
	//  ex ... decimal exponent related to base unit, in range -18..+8
	//  unit ... unit character, 0=none
	//  small ... use small unit characters
	DrawChar2(ch, x, y);	// draw sign
	x += 16;
	DispUVal(x, y, val, 4, -3, unit, False, False);
}

// U display mode ("U1 max. 3.3V")
void U_DispMode()
{
	// select font
	SelFont12();

	// clear row
	DrawRectClrFast(0, ROW1_Y, WIDTH, FONTH);

	// print text (length 7 chars)
	int x = (WIDTH-12*8)/2;
	DrawText(U_ModeText[UMode], x, ROW1_Y);
	x += 8*8;

	// print max. voltage
	u32 v = Supply;
	if (UMode == U_MODE_HIGH) v = (Supply*U2_K + 2048) >> 12;
	DispUVal(x, ROW1_Y, v, 2, -3, 'V', True, True);
}

// U display tare ("tare 12mV")
void U_DispTare()
{
	// select font
	SelFont12();

	// clear row
	DrawRectClrFast(0, ROW4_Y, WIDTH, FONTH);

	// draw title
	int x = (WIDTH-9*8)/2;
	DrawText("tare", x, ROW4_Y);
	x += 5*8;

	// voltage tare
	char* s = DecNumBuf;
	int len = DecNum(s, UTare[UMode], 0);
	DrawText(s, x, ROW4_Y);
	x += len*8;
	DrawText("mV", x, ROW4_Y);
}

// U display
void U_Disp()
{
	// display values
	UI_DispVal(UState, ROW2_Y, 'V');

	// U display mode
	U_DispMode();

	// display tare correction
	U_DispTare();

	// display update
	DispUpdate();
}

// U tare
void U_Tare()
{
	TareMsg();
	UTare[UMode] = UState + UTare[UMode];
	U_DispTare();
	DispUpdate();
	WaitMs(500);
}

// Page U (returns key PREV/NEXT)
u8 PageU()
{
	int i;
	u8 key;

	// U initialize (initializes BAT measure, too)
	U_Init();

	// U display
	U_Disp();

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

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

		// U update (takes 500ms)
		U_Update();

		// U display
		U_Disp();

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

		// FAST - high voltage
		case KEY_FAST:
			UMode = U_MODE_HIGH;
			U_Disp();
			break;
			
		// SLOW - low voltage
		case KEY_SLOW:
			UMode = U_MODE_LOW;
			U_Disp();
			break;

		// Hold - tare
		case KEY_HOLD:
			U_Tare();
			break;
		}
	}
}
