
// ****************************************************************************
//
//                           Page L - inductance meter
//
// ****************************************************************************

#include "../include.h"

// last value
u32 LTime;		// last time
u32 LValue;		// last counter value (0 = not valid)
u32 LTare = L0_REF;	// L tare in 10nH
s32 LRaw = 0;		// last result inductance, without tare

volatile u32 LHigh;		// higher value of the counter

/*
// L Timer 2 interrupt handler ... handler located at page_asm.S
//HANDLER void TIM2_IRQHandler()
HANDLER void L_Handler()
{
	// clear interrupt flag
	TIM2_UpIntClr();

	// increment counter
	LHigh++;
}
*/

// L initialize
void L_Init()
{
	// setup input pin to floating input
	GPIO_Mode(L_GPIO, GPIO_MODE_IN);

	// Enable timer clock source
	TIM2_ClkEnable();

	// Reset timer to default setup
	TIM2_Reset();

	// remap Timer 2
	// Input: PC0, Timer 2, channel 1, mapping 4
	// 4 ... >>> PC0:CH1 <<< /ETR, PC1:CH2, PC3:CH3, PB6:CH4/CH2N
	GPIO_Remap_TIM2(4);

	// setup CH3 as input TI1
	TIM2_CC1Sel(TIM_CCSEL_TI1);

	// no filter
	TIM2_IC1Filter(TIM_FILTER_0);

	// no prescaler
	TIM2_IC1Div(0);

	// max. load period
	TIM2_Load(0xffff);

	// capture on rising edge
	TIM2_IC1Rise();

	// external clock mode 1: TIM1 clockes by TI1 edges
	TIM2_InMode(TIM_INMODE_EDGE);
	TIM2_Trig(TIM_TRIG_TI1FP1);

	// set L Timer 2 interrupt handler
	SetHandler(IRQ_TIM2, L_Handler);

	// enable update interrupt
	TIM2_UpIntEnable();
	NVIC_IRQEnable(IRQ_TIM2);
	TIM2_UpIntClr();

	// reset counter
	LHigh = 0;

	// enable timer
	TIM2_Enable();
}

// L terminate
void L_Term()
{
	// interrupt disable
	NVIC_IRQDisable(IRQ_TIM2);
	TIM2_UpIntDisable();
	TIM2_UpIntClr();

	// reset input pin
	GPIO_PinReset(L_GPIO);

	// reset Timer 2
	TIM2_Reset();

	// Timer 2 clock disable
	RCC_TIM2ClkDisable();
}

// load counter value
u32 L_Get()
{
	u32 high, high2;
	u16 low;
	while (True)
	{
		cb();
		high = LHigh;
		cb();
		low = TIM2_GetCnt();
		nop();
		nop(); // - here may come interrupt
		nop();
		cb();
		high2 = LHigh;
		if (high == high2)
		{
			return (high << 16) | low;
		}
	}
}

// measure L value (takes 500 ms)
void L_Update()
{
	u8 key;

	// save start counter value
	u32 start = L_Get(); // start counter
	u32 tstart = Time(); // start time

	// wait 500ms for measurement
	int i;
	for (i = 50; i > 0; i--)
	{
		WaitMs(10);
		key = KeyBuf;
		if ((key == KEY_PREV) || (key == KEY_NEXT)) break;
	}

	// get stop counter value
	LValue = (u32)(L_Get() - start); // delta counter
	LTime = Time() - tstart; // delta time
}

// display tare correction
void L_DispTare()
{
	// get tare correction in nH
	int tare = LTare*10;
	char* s = DecNumBuf;
	int len = DecNum(s, tare, '\'');

	// select font 8x12
	SelFont12();

	// display tare correction "tare -123nH"
	int x = (WIDTH - (len+7)*8)/2;
	DrawText("tare", x, ROW4_Y);
	x += 5*8;
	DrawText(s, x, ROW4_Y);
	x += len*8;
	DrawText("nH", x, ROW4_Y);
}

// L display
void L_Disp()
{
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// get frequency in [Hz]
	u32 f = 0;
	if ((LValue > 0) && (LTime > 0))
	{
		u32 t = LTime;
		f = (u32)(((u64)LValue * SystemCoreClock + (t>>1)) / t);
	}

	// oscillator does not work (input pin is open)
	if (f < 100)
	{
		SelFont12();
		DrawText2("  ----  ", 0, ROW2_Y);
	}
	else
	{
		// calculate inductance in 10nH (scale = -8)
		// Resonator: LM311D
		// Frequency: f=1/(2*pi*sqrt(L*C))
		// L range: 0.1uH ... 100mH
		// frequency: 1 kHz ... 640 kHz
		// L = (1/((f*2*pi))^2 * C0) - L0
		// L0_REF = 6800 ... reference L in 10nH (= 68uH, scale = -8)
		// C0_REF = 10000 ... reference C in 0.1pF (= 1nF, scale = -13)
		//  C0 = C0_REF*10^-13, L0 = L0_REF*10^-8, output Lout = L*10^8
		// scale calculations:
		// Lout = 10^8/((2*pi*f)^2*C0_REF*10^-13) - L0_REF
		//      = 10^21 / ((2*pi*f)^2*C0_REF) - L0_REF
		//	= ((10^21/(8*(2*pi)^2)) / f^2) * 8 / C0_REF - L0_REF .... constant correction '8', to fit into s64 range
		// constant: 10^21/(8*(2*pi)^2) = 3166286988823055358
		// Lout = (3166286988823055358 / f^2) * 8 / C0_REF - L0_REF
		s64 tmp = (s64)f*f;
		tmp = 3166286988823055358ll / tmp;
		tmp = tmp * 8;
		tmp = tmp / C0_REF;
		LRaw = (s32)tmp;	// raw result, without tare
		tmp = tmp - LTare;
		int val = (int)tmp;

		// check limit frequency 100Hz:
		//	tmp = 100*100 = 10000
		//	tmp = 3166286988823055358 / 10000 = 316628698882305
		//	tmp = 316628698882305 * 8 = 2533029591058440
		//	tmp = 2533029591058440 / 10000 = 253302959105
		//	LRaw = 253302959105 in 10nH = 2533029591uH = 2533029mH = 2533H

		// check low frequency 1000Hz (input of 230V transformator):
		//	tmp = 1000*1000 = 1000000
		//	tmp = 3166286988823055358 / 1000000 = 3166286988823
		//	tmp = 3166286988823 * 8 = 25330295910584
		//	tmp = 25330295910584 / 10000 = 2533029591
		//	LRaw = 2533029591 in 10nH = 25330295uH = 25330mH = 25H

		// check high frequency 640000 (input shortcut):
		//	tmp = 640000*640000 = 409600000000
		//	tmp = 3166286988823055358 / 409600000000 = 7730192
		//	tmp = 7730192 * 8 = 61841536
		//	tmp = 61841536 / 10000 = 6184
		//	LRaw = 6184 in 10nH = 61uH

		// negative value (untared)
		int x = 0;
		if (val < 0)
		{
			val = -val;
			SelFont12();
			DrawChar2('-', x, ROW2_Y);
			x += 16;
		}
		else
			x += 8;

		// display result
		//  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(x, ROW2_Y, val, 4, -8, 'H', False, False);
	}

#if 0
	// DEBUG: display frequency
	int len = DecUNum(DecNumBuf, f, '\'');
	SelFont8();
	DrawRectClrFast(0, HEIGHT-8, WIDTH, 8);
	DrawText(DecNumBuf, (WIDTH-len*8)/2, HEIGHT-8);
#else
	// display tare correction
	L_DispTare();
#endif

	// display update
	DispUpdate();
}

// Page L (returns key PREV/NEXT)
u8 PageL()
{
	u8 key;

	// L initialize
	L_Init();
	LValue = 0;

	// L display
	L_Disp();

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

		// measure L value
		L_Update();

		// L display
		L_Disp();

		// keyboard input
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// L terminate
			L_Term();
			return key;

		// Hold - tare
		case KEY_HOLD:
			// Tare message
			TareMsg();
			LTare = LRaw;
			L_DispTare();
			DispUpdate();
			WaitMs(300);
			break;
		}
	}
}
