
// ****************************************************************************
//
//                         Page DUT - duty cycle
//
// ****************************************************************************

#include "../include.h"

u32 DutH, DutL; 	// accumulators of pulses
int DutN;		// sample counter
u32 DutH_Tmp;		// temporary HIGH pulse
u32 DutLast;		// time of last edge

/*
// interrupt on signal edge ... handler located at page_asm.S
//HANDLER void NOFLASH(TIM1_CC_IRQHandler)()
HANDLER void NOFLASH(DUT_Handler)()
{
	// read capture result
	u16 capture = TIM1_GetComp1();

	// clear interrupt request
	TIM1_CC1IntClr();

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

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

	// falling edge - end of HIGH pulse (1st interrupt after init)
	if ((TIM1->CCER & B1) != 0)
	{
		// select rising edge
		TIM1_IC1Rise();

		// save interval
		DutH_Tmp = t - DutLast;
	}

	// rising edge - end of LOW pulse (2nd interrupt after init)
	else
	{
		// select falling edge
		TIM1_IC1Fall();

		// get pulse counter
		int dutn = DutN;

		// pulse is valid
		if (dutn >= 0)
		{
			DutH += DutH_Tmp;	// add last HIGH pulse to the accumulatpr
			DutL += t - DutLast;	// add LOW pulse to the accumulator
		}

		// increase pulse counter
		DutN = dutn + 1;
	}

	// save last time
	DutLast = t;
}
*/

// DUT initialize (shared with PH and REP pages)
//  page ... 0=DUT page, 1=PH page, 2=REP page falling, 3=REP page rising
void DUT_Init(int page)
{
	// reset accumulators
	DutH = 0;	// accumulator HIGH
	DutL = 0;	// accumulator LOW
	DutN = -1;	// sample counter, -1 to skip 1st sample

	// 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 (400 kHz 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_CC, IRQ_PRIO_NORMAL);		// Timer 1 capture

	// setup input pin to floating input with pull-down
	GPIO_Mode(IN_GPIO, GPIO_MODE_IN_PD);

	// Enable timer clock source
	TIM1_ClkEnable();

	// Reset timer to default setup
	TIM1_Reset();

	// remap Timer 1
	// Input: PD4, 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 prescaler to full precision, or to 2 us
	TIM1_Presc((page == 0) ? 0 : (HCLK_PER_US*2-1));

	// direction up
	TIM1_DirUp();

	// maximum range
	TIM1_Load(0xffff);

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

	// no filter
	TIM1_IC1Filter(TIM_FILTER_0);

	// no prescaler
	TIM1_IC1Div(0);

	// CH1 as input TI1
	TIM1_CC1Sel(TIM_CCSEL_TI1);

	// select falling edge
	if (page == 3)
		TIM1_IC1Rise();
	else
		TIM1_IC1Fall();

	// enable channel
	TIM1_CC1Enable();

	// set DUT Timer 1 interrupt handler
	SetHandler(IRQ_TIM1_CC, (page == 0) ? DUT_Handler : ((page == 1) ? PH_Handler : REP_Handler));

	// enable capture interrupt
	TIM1_CC1IntEnable();

	// interrupt enable
	NVIC_IRQEnable(IRQ_TIM1_CC);

	// clear flags
	TIM1_CC1IntClr();

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

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

	// enable timer
	TIM1_Enable();

	// interrupt enable
	ei();
}

// DUT terminate (shared with PH and REP pages; IN_GPIO stays in IN_PD mode)
void DUT_Term()
{
	// disable interrupt
	NVIC_IRQDisable(IRQ_TIM1_CC);
	TIM1_CC1IntDisable();
	TIM1_CC1IntClr();
	NVIC_IRQClear(IRQ_TIM1_CC);

	// reset Timer
	TIM1_Reset();

	// Timer clock disable
	RCC_TIM1ClkDisable();

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

	// reset input pin
//	GPIO_PinReset(IN_GPIO);
}

// DUT measure (takes 500 ms)
void DUT_Check()
{
	u8 key;

	// DUT initialize
	DUT_Init(False);

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

	// DUT terminate (shared with PH and REP pages; IN_GPIO stays in IN_PD mode)
	DUT_Term();
	if (DutN < 0) DutN = 0;		// no pulse, so correct number of pulses
}

// DUT display
void DUT_Disp()
{
	// select font 8x12
	SelFont12();

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

	// prepare ratio * 10000 (= % * 100, 4 digits)
	u32 r = 0;
	u32 duth = DutH;
	u32 dutl = DutL;
	u32 dutn = DutN;
	u32 f = 0;
	if (duth + dutl != 0)
	{
		u32 d = duth + dutl;
		r = (u32)(((u64)duth*10000 + (d>>1))/d);
		if (r > 10000) r = 10000;

		if (dutn != 0)
		{
			f = (u32)(((u64)dutn*SystemCoreClock + (d>>1))/d);
		}
	}

	// get 100% ratio - no pulses, level is HIGH
	else
	{
		if (GPIO_In(IN_GPIO)) r = 10000;
	}

	// split ratio
	int x = 0;
	int ri = r/100; // ri=0..100
	r -= ri*100;
	u8* s = DecNumBuf;
	int len = DecUNum(s, ri, 0);
	if (len < 3) x += 16;
	if (len < 2) x += 16;
	s += len;
	*s++ = '.';
	ri = r/10;
	r -= ri*10;
	*s++ = ri + '0';
	*s++ = r + '0';
	*s++ = '%';
	*s = 0;

	// draw ratio '100.00%'
	DrawText2(DecNumBuf, x, ROW2_Y);

	// draw frequency
	s = DecNumBuf;
	len = DecUNum(s, f, '\'');
	s[len] = 'H';
	s[len+1] = 'z';
	s[len+2] = 0;
	DrawText(s, (WIDTH-(len+2)*8)/2, ROW4_Y);

	// display update
	DispUpdate();
}

// Page DUT (returns key PREV/NEXT)
u8 PageDUT()
{
	u8 key;
	int i;

	// DUT display
	DutH = 0;
	DutL = 0;
	DutN = 0;
	GPIO_Mode(IN_GPIO, GPIO_MODE_IN_PD);
	DUT_Disp();

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

		// DUT measure (takes 500 ms, minimal measured frequency is 3Hz)
		DUT_Check();

		// DUT display
		DUT_Disp();

		// get keys
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// reset input pin
			GPIO_PinReset(IN_GPIO);
			return key;
		}
	}
}
