
// ****************************************************************************
//
//                        Page BAT - battery
//
// ****************************************************************************

#include "../include.h"

int SupplyBuf[SUPPLY_NUM]; // supply voltage buffer
int SupplyBufInx = 0;	// index to supply voltage buffer
int SupplyBufSum;	// sum of supply voltage buffer
int Supply = -1;	// supply voltage in [mV] (-1 = not yet measured)

// update battery supply voltage (takes 100ms)
// ADC clock divider is set to 750kHz. One ADC clock cycle is 1.33us.
// BAT sampling time is (6) 71.5 cycles. One sample is 71.5+12.5=84 ADC clock cycles = 112us.
// 893 samples take approx. 893*112=100ms.
// Reference internal voltage 1.2V; datasheet: Vrefint = 1.18..1.22, tolerance +-1.7%.
// The measurement period is 100 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 BAT_Update()
{
	int i;
	u32 v, 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(REF_ADC, 10);

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

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

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

		// sample counter
		i++;

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

	// get supply voltage in [mV]
	int sup = (int)((((s64)REF_VOLTAGE * 4095 * i) + (v>>1)) / v);

	// first usage
	if (Supply < 0)
	{
		// use this first measure
		for (i = 0; i < SUPPLY_NUM; i++) SupplyBuf[i] = sup;
		SupplyBufSum = sup*SUPPLY_NUM;
		Supply = sup;
	}
	else
	{
		// supply filter
		i = SupplyBufInx;
		SupplyBufSum -= SupplyBuf[i];
		SupplyBufSum += sup;
		SupplyBuf[i] = sup;
		Supply = (SupplyBufSum + SUPPLY_NUM/2) / SUPPLY_NUM;
		i++;
		if (i >= SUPPLY_NUM) i = 0;
		SupplyBufInx = i;
	}
}

// BAT initialize, initialize ADC single conversion mode, set ADC clock to 750kHz
void BAT_Init()
{
	int i;

	// initialize ADC single conversion mode
	ADC1_InitSingle();

	// set ADC divider to get 48MHz/64 = 750kHz
	RCC_ADCDivEnable();
	RCC_ADCDiv(RCC_ADCCLK_DIV64);

	// set sampling time of reference input to 71.5 cycles
	ADC1_SampTime(REF_ADC, 6);

	// do first measure of reference voltage
	if (Supply < 0) BAT_Update();
}

// BAT terminate
void BAT_Term()
{
}

// display battery ("3.284V")
void BAT_Disp()
{
	// 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
	DispUVal((WIDTH-5*16-8)/2, ROW2_Y, Supply, 4, -3, 'V', False, False);

	// display update
	DispUpdate();
}

// Page BAT (returns key PREV/NEXT)
u8 PageBAT()
{
	int i;
	u8 key;

	// BAT initialize, initialize ADC single conversion mode, set ADC clock to 750kHz
	BAT_Init();

	// BAT display
	BAT_Disp();

	i = 0;
	while (True)
	{
		// reload watchdog counter
		IWDG_Reload();

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

		// display battery (after 8 measures, 8*100 = 800ms)
		i++;
		if ((i & 0x07) == 0) BAT_Disp();

		// keyboard input
		key = KeyGet();
		if ((key == KEY_PREV) || (key == KEY_NEXT))
		{
			// BAT terminate
			BAT_Term();
			return key;
		}
	}
}
