
// ****************************************************************************
//
//                        Page R - resistor meter
//
// ****************************************************************************

#include "../include.h"

// result resitance in 0.1 ohm (without tare)
s32 R_Res = 0;

// tare in 0.1 ohm
s32 R_Tare = 1;

// fast measure
s32 R_Fast[R_NUM];

// Resistor GPIO
const u8 R_Gpio[R_NUM] = { R1_GPIO, R2_GPIO, R3_GPIO, R4_GPIO, R5_GPIO };

// Resistor value in 0.1 ohm
const s32 R_Val[R_NUM] = { R1_RAW+R0_RAW, R2_RAW+R0_RAW, R3_RAW+R0_RAW, R4_RAW+R0_RAW, R5_RAW+R0_RAW };

// set all resistors OFF
void R_SetAllResOff()
{
	GPIO_ModeFast(R1_GPIO, GPIO_MODE_IN);
	GPIO_ModeFast(R2_GPIO, GPIO_MODE_IN);
	GPIO_ModeFast(R3_GPIO, GPIO_MODE_IN);
	GPIO_ModeFast(R4_GPIO, GPIO_MODE_IN);
	GPIO_ModeFast(R5_GPIO, GPIO_MODE_IN);
}

// setup one resistor OFF by index
void R_SetResOff(int inx)
{
	GPIO_ModeFast(R_Gpio[inx], GPIO_MODE_IN);
}

// setup one resistor HIGH by index
void R_SetResH(int inx)
{
	u8 gpio = R_Gpio[inx];
	GPIO_Out1(gpio);
	GPIO_ModeFast(gpio, GPIO_MODE_OUT);
}

// setup one resistor LOW by index
void R_SetResL(int inx)
{
	u8 gpio = R_Gpio[inx];
	GPIO_Out0(gpio);
	GPIO_ModeFast(gpio, GPIO_MODE_OUT);
}

// R initialize
void R_Init()
{
	// Remap SWD debug interface (default 0)
	// 	0-3 ... SWD (SDI) enabled
	//	4 ... SWD (SDI) disable, pin served as GPIO
	// Note: Before switching the SWD pin to GPIO, insert a short delay
	// from system startup to allow the chip to be reprogrammed later.
	GPIO_Remap_SWD(4);

	// initialize GPIO
	int i;
	for (i = 0; i < R_NUM; i++) GPIO_Mode(R_Gpio[i], GPIO_MODE_IN);
	GPIO_Mode(RC_GPIO, GPIO_MODE_AIN);

	// 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 input to 71.5 cycles
	//  ADC sample time: 12.5+71.5=84 clock cycles, speed = 48000/64/84 = 9ksps, conversion time = 112 us
	ADC1_SampTime(RC_ADC, 6);

	// select channel to be converted
	ADC1_RSeq(1, RC_ADC);
}

// R terminate
void R_Term()
{
	// reset GPIO
	int i;
	for (i = 0; i < R_NUM; i++) GPIO_PinReset(R_Gpio[i]);

	// Remap SWD debug interface (default 0)
	// 	0-3 ... SWD (SDI) enabled
	//	4 ... SWD (SDI) disable, pin served as GPIO
	// Note: Before switching the SWD pin to GPIO, insert a short delay
	// from system startup to allow the chip to be reprogrammed later.
	GPIO_Remap_SWD(0);
}

// check one type of resistor (returns resistance in 0.1 ohm) ... takes 20ms
s32 R_CheckRes1(u8 inx)
{
	// setup resistor HIGH, clear all other resistors
	R_SetAllResOff();
	R_SetResH(inx);

	// wait for stabilisation
	WaitClk(WAIT_STAB);

	// start measure
	int n = 0;	// measure counter
	s32 valsum = 0;	// sum of values
	u32 t = Time();	// start time

	// load samples
	// 20 ms ... 178 samples (112 us per sample), max. sum 728910
	while ((u32)(Time() - t) < PERIOD)
	{
		// add ADC
		valsum += R_GetADC();
		n++;
	}

	// set resistor off
	R_SetResOff(inx);

	// get resistance
	//   adc = valsum/n = Rx * 4095 / (Rx + Rref)
	//   adc*Rx + adc*Rref = Rx * 4095
	//   Rx = Rref * valsum / (4095*n - valsum)
	s64 num = (s64)R_Val[inx] * valsum;
	s64 den = (s64)(4095+5) * n - valsum;	// +5 for ADC nelinearity correction
	s32 r = 0;
	if (den > 0) r = (num + (den >> 1)) / den;

	// correct ADC input impedance (3.5M and more)
	if ((r >= 35000000) && (r < R_IN))
	{
		s64 r2 = R_IN_SCALE / r;	// reciprocal value of the resistance
		if (r <= 95000000)	// < 9.5M
			r2 -= R_IN_REC;		// subtract input impedance
		else
			r2 -= R_IN2_REC;	// subtract input impedance
		r = R_IN_SCALE / r2;
	}

	// subtract ADC input resistance
	return r;
}

// measure resistance (without tare)
s32 R_CheckRes()
{
	// fast measure
	R_Fast[R1_INX] = R_CheckRes1(R1_INX);
	R_Fast[R2_INX] = R_CheckRes1(R2_INX);
	R_Fast[R3_INX] = R_CheckRes1(R3_INX);
	R_Fast[R4_INX] = R_CheckRes1(R4_INX);
	R_Fast[R5_INX] = R_CheckRes1(R5_INX);

	// last resistor will not be interpolated
	if (R_Fast[R5_INX] >= R_Val[R5_INX])
	{
		return R_Fast[R5_INX];
	}

	// first resistor will not be interpolated
	if (R_Fast[R1_INX] <= R_Val[R1_INX])
	{
		return R_Fast[R1_INX];
	}

	// find boundary (R4+R5, R3+R4, R2+R3, R1+R2)
	int rinx;
	for (rinx = R4_INX; rinx > R1_INX; rinx--)
	{
		if (R_Fast[rinx] >= R_Val[rinx]) break;
	}

	// interpolation coefficient (0..65535)
	s32 k1 = R_Fast[rinx] - R_Val[rinx];
	s32 k2 = R_Val[rinx+1] - R_Val[rinx];
	s64 k = 0;
	if (k1 != 0) k = (s64)k1*65536 / k2;
	if (k < 0) k = 0;
	if (k > 65535) k = 65535;

	// interpolate result
	s32 res = R_Fast[rinx] + (((s32)(R_Fast[rinx+1] - R_Fast[rinx])*k + 32768) >> 16);
	return res;
}

#define R_LOOP	8

// R update
void R_Update()
{
	int i;
	u8 key;

	// measure
	s64 sum = 0;
	for (i = R_LOOP; i > 0; i--)
	{
		key = KeyBuf;
		if ((key == KEY_PREV) || (key == KEY_NEXT)) break;
		sum += R_CheckRes();
	}
	R_Res = (sum + R_LOOP/2)/R_LOOP;
}

// display tare correction
void R_DispTare()
{
	// get tare correction in 0.1ohm
	char* s = DecNumBuf;
	int len = DecNum(s, R_Tare, 0);

	// correction to fixed point
	if ((len == 1) || ((len == 2) && (*s == '-')))
	{
		// "5" -> "0.5", "-5" -> "-0.5"
		s[len+2] = 0;
		s[len+1] = s[len-1];
		s[len] = '.';
		s[len-1] = '0';
		len += 2;
	}
	else
	{
		s[len+1] = 0;
		s[len] = s[len-1];
		s[len-1] = '.';
		len += 1;
	}

	// select font 8x12
	SelFont12();

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

// R display
void R_Disp()
{
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// 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
	s32 val = R_Res - R_Tare;
	int x = 0;
	if (val < 0)
	{
		val = -val;
		SelFont12();
		DrawChar2('-', x, ROW2_Y);
		x += 16;
	}
	else
		x += 8;

	x = DispUVal(x, ROW2_Y, val, 4, -1, 0, False, False);
	DrawText("ohm", x, ROW2_Y+SMALL_Y);

	// display tare correction
	R_DispTare();

	// display update
	DispUpdate();
}

// tare
void R_DoTare()
{
	// setup tare
	R_Tare = R_Res;

	// Tare message
	TareMsg();
}

// Page R (returns key PREV/NEXT)
u8 PageR()
{
	int i;
	u8 key;

	// R initialize
	R_Init();

	// R display
	R_Res = 0;
	R_Disp();

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

		// R update
		R_Update();

		// R display
		R_Disp();

		// keyboard input
		key = KeyGet();
		if (key == KEY_HOLD)
		{
			// do tare
			R_DoTare();
		}
		else if ((key == KEY_PREV) || (key == KEY_NEXT))
		{
			// R terminate
			R_Term();
			return key;
		}
	}
}
