
// ****************************************************************************
//
//                      Page FT - frequency and period meter
//
// ****************************************************************************

#include "../include.h"

#define WIN_LEN		(500000*HCLK_PER_US) // length of time window
#define COUNT_OUT	32768	// number of pulses per measure window, when Timer 1 interrupt
			// can overload (max. 500kHz, but better 65kHz - SysTick can distort measurements)

#define DEBUG	0	// debug: 0=normal, 1=display method 'c' (count) od 't' (time)

// Timer 1 (count pulses):
volatile u32 FT_CntHigh; // current pulse counter HIGH
u32 FT_Count1;		// start count of pulses
u32 FT_Count2;		// stop count of pulses
u32 FT_Time1;		// start time
u32 FT_Time2;		// stop time (= start time of this measure window)

// Timer 2 (time of edges):
u32 FT_Edge1;		// time of 1st edge
volatile u32 FT_Edge2;	// time of last edge
volatile u32 FT_EdgeNum; // number of edges, without 1st edge
u32 FT_EdgeDif;		// time delta of edges after capture
u32 FT_EdgeCnt;		// count of edges after capture
u8 FT_EdgeValid;	// number of valid edges 0, 1 or 2

/*
// FT Timer 1 interrupt handler - overflow of edge counter ... handler located at page_asm.S
//HANDLER void TIM1_UP_IRQHandler()
HANDLER void FT_Handler1()
{
	// clear interrupt flag
	TIM1_UpIntClr();

	// increase pulse counter HIGH
	FT_CntHigh++;
}
*/

/*
// FT Timer 2 interrupt handler - time of edges ... handler located at page_asm.S
//HANDLER void TIM2_IRQHandler()
HANDLER void FT_Handler2()
{
	// read capture result
	u16 capture = TIM2_GetComp3();

	// clear interrupt request
	TIM2_CC3IntClr();

	// get current time
	u32 t = Time();

	// get edge time
	s16 d = (s16)(capture - t);
	t += d;	

	// save time of last edge
	FT_Edge2 = t;

	// increase number of edges
	u32 n = FT_EdgeNum + 1;
	FT_EdgeNum = n;

	// check overload
	if (n >= COUNT_OUT) TIM2_CC3IntDisable();
}
*/

// FT initialize
void FT_Init()
{
	// setup input pin to floating input with pull-up
	GPIO_Mode(IN_GPIO, GPIO_MODE_IN_PU);

	// setup priorities
// In this measurement mode, it is necessary for the SysTick interrupt to have
// a higher priority than the Timer interrupt. If the user feeds a high-frequency
// signal (1 MHz or more) to the input, the meter will become overloaded and jam
// - which is logical, of course, because the interrupts cannot be processed fast
// enough. However, with this processor, the overload of higher-priority interrupts
// causes the lower-priority interrupts to get stuck - the ACTIVE flag for SysTick
// interrupts remains set in the PFIC controller. Subsequently, SysTick interrupts
// are never triggered again, even if the user disconnects the signal. It will
// start working again after about 1 minute, when the internal watchdog restores
// the PFIC functions. The keys on the meter will then stop working. This can be
// remedied by a higher interrupt from SysTick, which will cause a slight decrease
// in measurement accuracy, but this is not a significant error.
	NVIC_IRQPriority(IRQ_SYSTICK, IRQ_PRIO_HIGH);		// SysTick
	NVIC_IRQPriority(IRQ_TIM1_UP, IRQ_PRIO_HIGH);		// Timer 1
	NVIC_IRQPriority(IRQ_TIM2, IRQ_PRIO_NORMAL);		// Timer 2 capture

// ---- initialize Timer 1 to count pulses

	// Enable timer clock source
	TIM1_ClkEnable();

	// Reset timer to default setup
	TIM1_Reset();

	// remap Timer 1
	// Input: PD2, Timer 1 channel 1 mapping 0
	// 0 ... PC5:ETR, >>> PD2:CH1 <<<<, PA1:CH2, PC3:CH3, PC4:CH4, PC2:BKIN, PD0:CH1N, PA2:CH2N, PD1:CH3N
	GPIO_Remap_TIM1(0);

	// setup CH1 as input TI1
	TIM1_CC1Sel(TIM_CCSEL_TI1);

	// no filter
	TIM1_IC1Filter(TIM_FILTER_0);

	// no prescaler
	TIM1_IC1Div(0);

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

	// capture on rising edge
	TIM1_IC1Rise();

	// reset counter
	TIM1_Cnt(0);

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

	// set FT Timer 1 interrupt handler
	SetHandler(IRQ_TIM1_UP, FT_Handler1);

	// enable update interrupt
	TIM1_UpIntEnable();
	NVIC_IRQEnable(IRQ_TIM1_UP);
	TIM1_UpIntClr();

	// reset counter
	FT_CntHigh = 0;
	FT_Count1 = 0;
	FT_Count2 = 0;
	u32 t = Time();
	FT_Time1 = t;
	FT_Time2 = t;

	// enable timer
	TIM1_Enable();

// ---- Initialize Timer 2 to capture time of edges

	// Enable timer clock source
	TIM2_ClkEnable();

	// Reset timer to default setup
	TIM2_Reset();

	// remap Timer 2
	// Input: PD2, Timer 2, channel 3, mapping 2
	// 2 ... PC5:CH1/ETR, PC2:CH2, >>> PD2:CH3/CH1N <<<, PC1:CH4/CH2N
	GPIO_Remap_TIM2(2);

	// setup prescaler to full precision
	TIM2_Presc(0);

	// direction up
	TIM2_DirUp();

	// maximum range
	TIM2_Load(0xffff);

	// select input from internal clock CK_INT
	TIM2_InMode(TIM_INMODE_INT);

	// no filter
	TIM2_IC3Filter(TIM_FILTER_0);

	// no prescaler
	TIM2_IC3Div(0);

	// CH3 as input TI1
	TIM2_CC3Sel(TIM_CCSEL_TI1);

	// select rising edge
	TIM2_IC3Rise();

	// enable channel
	TIM2_CC3Enable();

	// set FT Timer 2 interrupt handler
	SetHandler(IRQ_TIM2, FT_Handler2);

	// enable capture interrupt
	TIM2_CC3IntEnable();

	// interrupt enable
	NVIC_IRQEnable(IRQ_TIM2);

	// clear flags
	TIM2_CC3IntClr();

	// interrupt disable - save start time synchronized
	di();

	// reset counters
	FT_EdgeValid = 0; // no edge is valid
	FT_EdgeNum = 0; // number of edges
	FT_EdgeDif = 0; // time delta
	FT_EdgeCnt = 0; // count of edges
	FT_Edge1 = Time(); // time of 1st edge

	// synchronize timer with SysTick
	TIM2_Cnt((u16)Time());

	// enable timer
	TIM2_Enable();

	// interrupt enable
	ei();
}

// FT terminate
void FT_Term()
{
	// interrupt disable Timer 1
	NVIC_IRQDisable(IRQ_TIM1_UP);
	TIM1_UpIntDisable();
	TIM1_UpIntClr();
	NVIC_IRQClear(IRQ_TIM1_UP);

	// interrupt disable Timer 2
	NVIC_IRQDisable(IRQ_TIM2);
	TIM2_CC3IntDisable();
	TIM2_CC3IntClr();
	NVIC_IRQClear(IRQ_TIM2);

	// reset input pin
	GPIO_PinReset(IN_GPIO);

	// reset Timer 1
	TIM1_Reset();

	// Timer 1 clock disable
	RCC_TIM1ClkDisable();

	// reset Timer 2
	TIM2_Reset();

	// Timer clock disable 2
	RCC_TIM2ClkDisable();

	// setup IRQ priorities to default state
	IrqPriorDef();
}

// capture state after time window
void FT_Capture()
{
	// disable interrupts
	di();

// ---- capture Timer 2 state (time of edges)

	// If an overload interruption occurs, the measurement must be restarted.
	if (!TIM2_CC3IntIsEnabled())
	{
		FT_EdgeValid = 0; // no edge is valid
		FT_EdgeNum = 0; // number of edges
		TIM2_CC3IntEnable(); // enable interrupt

		// we are using count of pulses from Timer 1 - try to sync to signal edge for a short time 100us, to get more stable value
		u32 t1 = Time();
		u16 c1 = TIM1_GetCnt();
		while (((u32)(Time() - t1) < 100000*HCLK_PER_US) && (TIM1_GetCnt() == c1)) {}	
	}
	else
	{
		// need at least 1 edge
		if (FT_EdgeNum != 0)
		{
			// get state
			FT_EdgeDif = FT_Edge2 - FT_Edge1; // time delta
			FT_EdgeCnt = FT_EdgeNum; // count of edges

			// restart edge counter
			FT_EdgeNum = 0; // number of edges
			FT_Edge1 = FT_Edge2; // time of 1st edge

			// increase state
			if (FT_EdgeValid < 2) FT_EdgeValid++;
		}
	}

// ---- capture Timer 1 state (count of edges)

	// get Timer 1 HIGH counter
	u32 high = FT_CntHigh;	

	// get Timer 1 LOW counter
	u16 low = TIM1_GetCnt();

	// check overflow flag
	while (TIM1_UpInt()) // check if update flag is set
	{
		// increase high counter
		high++;
		FT_CntHigh = high;

		// clear update flag
		TIM1_UpIntClr();

		// get Timer 1 new LOW counter
		low = TIM1_GetCnt();
	}

	// compose new counter
	u32 cnt = (high << 16) | low;

	// get current time
	u32 t = Time();

	// update Timer 2 state
	FT_Count1 = FT_Count2;
	FT_Count2 = cnt;
	FT_Time1 = FT_Time2;
	FT_Time2 = t;

	// enable interrupts
	ei();
}

// FT display
void FT_Disp()
{
	int ex;

	// clear screen
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// get approximate frequency
	u32 f = 0;
	u32 tdif = FT_Time2 - FT_Time1;		// time difference in system ticks
	u32 cntdif = FT_Count2 - FT_Count1;	// count difference of pulses
	if (tdif > 0) // is number of ticks valid?
	{
		f = ((s64)1000000*HCLK_PER_US * cntdif + tdif/2) / tdif;
	}

	// we can use this frequency directly, if >= 1 MHz
	ex = 0;

	// check if capture method is valid
	tdif = FT_EdgeDif;
	cntdif = FT_EdgeCnt;
	char method = 'c';
	if ((FT_EdgeValid == 2) && (tdif > 0) && (cntdif > 0))
	{
		// >90000 (in fact, we do not measure these frequencies using this method)
		if (f >= 90000)
		{
			f = ((s64)1000000*HCLK_PER_US*10 * cntdif + tdif/2) / tdif;
			ex = -1;
		}

		// 9000..90000
		else if (f >= 9000)
		{
			f = ((s64)1000000*HCLK_PER_US*100 * cntdif + tdif/2) / tdif;
			ex = -2;
		}

		// 900..9000
		else if (f >= 900)
		{
			f = ((s64)1000000*HCLK_PER_US*1000 * cntdif + tdif/2) / tdif;
			ex = -3;
		}

		// 90..900
		else if (f >= 90)
		{
			f = ((s64)1000000*HCLK_PER_US*10000 * cntdif + tdif/2) / tdif;
			ex = -4;
		}

		// 9..90
		else if (f >= 9)
		{
			f = ((s64)1000000*HCLK_PER_US*100000 * cntdif + tdif/2) / tdif;
			ex = -5;
		}

		// <9
		else
		{
			f = ((s64)1000000*HCLK_PER_US*1000000 * cntdif + tdif/2) / tdif;
			ex = -6;
		}
		method = 't';
	}

	// display frequency
	// 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
	int x = DispUVal(0, ROW1_Y, f, 6, ex, 'H', True, False);
	DrawChar('z', x, ROW1_Y+SMALL_Y);

#if DEBUG == 1	// debug: 0=normal, 1=display method 'c' (count) od 't' (time)
	DrawChar(method, WIDTH-8, TITLE_H);
#endif

	// calculate period
	u32 t = 0;
	if (f != 0)
	{
		s64 tt = (1000000000000ll + (f>>1)) / f;
		ex = -12 - ex;
		while (tt < 1000000ll)
		{
			tt *= 10;
			ex--;
		}
		while (tt >= 100000000ll)
		{
			tt /= 10;
			ex++;
		}
		t = (u32)tt;
	}

	// display period
	DispUVal(0, ROW3_Y, t, 6, ex, 's', True, False);

	// display update
	DispUpdate();
}

// Page FT (returns key PREV/NEXT)
u8 PageFT()
{
	u8 key;

	// FT initialize
	FT_Init();

	// load first small state
	FT_Capture();	// capture state
	WaitMs(10);	// small delay
	FT_Capture();	// capture state

	// FT display
	FT_Disp();

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

		// wait new measure window
		if ((u32)(Time() - FT_Time2) >= WIN_LEN)
		{
			// capture state
			FT_Capture();

			// FT display
			FT_Disp();
		}

		// get keys
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// FT terminate
			FT_Term();
			return key;
		}
	}
}
