
// ****************************************************************************
//
//                         Page REP - repeater
//
// ****************************************************************************

#include "../include.h"

#define REP_BUFSIZE	2048	// max. number of samples

volatile int REPSamples;	// number of samples
Bool REPStartLevel = False;	// start level True=HIGH, False=LOW

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

	// clear interrupt request
	TIM1->INTFR = 0;

	// flip edge
	TIM1->CCER ^= B1;

	// store edge
	int inx = REPSamples;
	if (inx < REP_BUFSIZE)
	{
		((u16*)Buf)[inx] = t;
		REPSamples = inx + 1;
	}
}
*/

// REP initialize
void REP_Init()
{
	// number of samples
	REPSamples = 0;

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

	// setup output pin to the same level asi input pin
	GPIO_Out(REP_OUT_GPIO, GPIO_In(REP_IN_GPIO));
	GPIO_Mode(REP_OUT_GPIO, GPIO_MODE_OUT);
}

// REP terminate
void REP_Term()
{
	// reset Timer 1
	TIM1_Reset();

	// Timer 1 clock disable
	RCC_TIM1ClkDisable();

	// reset output pin
	GPIO_PinReset(REP_OUT_GPIO);

	// reset input pin
	GPIO_PinReset(REP_IN_GPIO);
}

// get play index
int REP_RepInx()
{
	return REPSamples - DMA_GetCnt(DMA1_Chan(REP_DMA));
}

// REP display
// mode: 0 = buffer, 1=record, 2=play, 3=replay
void REP_Disp(int mode)
{
	// clear area
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);

	// select font 8x12
	SelFont12();

	// display help
	DrawText("10\x60s..100ms", (WIDTH-11*8)/2, ROW1_Y);

	// display mode
	int n = REPSamples;
	int m = REP_BUFSIZE;
	switch (mode)
	{
	//case 0:
	default:
		break;

	case 1:
		DrawText("Recording...", (WIDTH-12*8)/2, ROW4_Y);
		break;

	case 2:
		DrawText("Playing...", (WIDTH-10*8)/2, ROW4_Y);
		m = n;
		n = REP_RepInx();
		break;

	case 3:
		DrawText("Replaying...", (WIDTH-12*8)/2, ROW4_Y);
		m = n;
		n = REP_RepInx();
		break;
	}

	// display progress
	if (m == 0)
		n = 0;
	else
		n = (n*100 + m/2)/m;
	int len = DecUNum(DecNumBuf, n, 0);
	DecNumBuf[len] = '%';
	len++;
	DecNumBuf[len] = 0;
	DrawText2(DecNumBuf, (WIDTH-len*16)/2, ROW2_Y);

	// display update
	DispUpdate();
}

// REP record (IN_GPIO stays in IN_PD mode)
void REP_Record()
{
	// get start level
	REPStartLevel = GPIO_In(REP_IN_GPIO);

	// setup output pin to the same level asi input pin
	GPIO_Out(REP_OUT_GPIO, REPStartLevel);

	// reset samples
	REPSamples = 0;

	// start record (2=falling, 3=rising)
	DUT_Init(REPStartLevel ? 2 : 3);

	// time to repaint
	u32 t = Time() - 200000*HCLK_PER_US;
	u32 t2;

	while (True)
	{
		// break by key
		if (KeyBuf != NOKEY) break;

		// reload watchdog counter
		IWDG_Reload();

		// repaint progress
		t2 = Time();
		if (((u32)t2 - t) >= 100000*HCLK_PER_US)
		{
			t = t2;
			REP_Disp(1);
		}

		// buffer is full
		if (REPSamples >= REP_BUFSIZE) break;
	}

	// stop record
	// DUT terminate (shared with PH and REP pages; IN_GPIO stays in IN_PD mode)
	DUT_Term();

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

	// repaint display
	REP_Disp(0);

	// flush key
	KeyBuf = NOKEY;
}

// setup DMA
void REP_SetupDMA()
{
	// enable DMA1 clock
	RCC_DMA1ClkEnable();

	// get address of DMA1 channel (channel 4 with TIM1_CH4 request)
	DMAchan_t* chan = DMA1_Chan(REP_DMA);

	// set destination address - TIM1 compare register od channel 4 (as peripheral address)
	DMA_PerAddr(chan, &TIM1->CH4CVR);

	// set source addres (as memory address)
	DMA_MemAddr(chan, Buf);

	// set number of entries
	DMA_Cnt(chan, REPSamples);		// number of entries

	// configure DMA channel
	DMA_Cfg(chan,
	//	DMA_CFG_EN |			// channel enable
	//	DMA_CFG_COMPINT |		// completion interrupt enable
	//	DMA_CFG_HALFINT |		// over half interrupt enable
	//	DMA_CFG_TRANSERRINT |		// transmission error interrupt enable
		DMA_CFG_DIRFROMMEM |		// transfer direction from memory
	//	DMA_CFG_DIRFROMPER |		// ... transfer direction from peripheral
	//	DMA_CFG_CIRC |			// circular mode enabled
	//	DMA_CFG_PERINC |		// peripheral address increment
		DMA_CFG_MEMINC |		// memory address increment
	//	DMA_CFG_PSIZE(size) |		// peripheral data size DMA_SIZE_*
	//	DMA_CFG_PSIZE_8 |		// ... peripheral data size 8 bits
		DMA_CFG_PSIZE_16 |		// ... peripheral data size 16 bits
	//	DMA_CFG_PSIZE_32 |		// ... peripheral data size 32 bits
	//	DMA_CFG_MSIZE(size) |		// memory data size DMA_SIZE_*
	//	DMA_CFG_MSIZE_8 |		// ... memory data size 8 bits
		DMA_CFG_MSIZE_16 |		// ... memory data size 16 bits
	//	DMA_CFG_MSIZE_32 |		// ... memory data size 32 bits
	//	DMA_CFG_PRIOR(prior) |		// channel priority 0..3
	//	DMA_CFG_PRIOR_LOW |		// ... channel priority 0 low
	//	DMA_CFG_PRIOR_MED |		// ... channel priority 1 medium
	//	DMA_CFG_PRIOR_HIGH |		// ... channel priority 2 high
		DMA_CFG_PRIOR_VERYHIGH |	// ... channel priority 3 very high
	//	DMA_CFG_MERM2MEM |		// memory-to-memory mode enable
		0);

	// clear completion flag
	DMA1_CompClr(NG_DMA);
}

// REP play
// mode: 2=play, 3=replay
void REP_Play(int mode)
{
	u32 t, t2;

	// nothing to play
	if (REPSamples <= 1) return;

REPlayRestart:

	// setup output pin to init start level
	GPIO_Out(REP_OUT_GPIO, REPStartLevel);

	// setup DMA
	REP_SetupDMA();

	// setup timer
	NG_SetupTimer(True);

	// start DMA tranfer
	DMA_ChanEnable(DMA1_Chan(REP_DMA));

	// enable timer
	TIM1_Enable();

	// time to repaint
	t = Time() - 200000*HCLK_PER_US;

	while (True)
	{
		// break by key
		if (KeyBuf != NOKEY) break;

		// reload watchdog counter
		IWDG_Reload();

		// repaint progress
		t2 = Time();
		if (((u32)t2 - t) >= 100000*HCLK_PER_US)
		{
			t = t2;
			REP_Disp(mode);
		}

		// end
		if (DMA1_Comp(REP_DMA))
		{
			// short delay
			WaitMs(50);

			// disable timer
			TIM1_Disable();

			// disable DMA tranfer
			DMA_ChanDisable(DMA1_Chan(NG_DMA));

			// clear completion flag
			DMA1_CompClr(NG_DMA);

			// reset Timer 1
			TIM1_Reset();

			// reset output
			GPIO_Out(REP_OUT_GPIO, REPStartLevel);
			GPIO_Mode(REP_OUT_GPIO, GPIO_MODE_OUT);

			// replay
			if (mode == 3)
			{
				goto REPlayRestart;
			}
			else
				break;
		}
	}

	// disable timer
	TIM1_Disable();

	// disable DMA tranfer
	DMA_ChanDisable(DMA1_Chan(NG_DMA));

	// reset Timer 1
	TIM1_Reset();

	// reset output
	GPIO_Out(REP_OUT_GPIO, REPStartLevel);
	GPIO_Mode(REP_OUT_GPIO, GPIO_MODE_OUT);

	// repaint display
	REP_Disp(0);

	// flush key
	KeyBuf = NOKEY;
}

// Page REP (returns key PREV/NEXT)
u8 PageREP()
{
	u8 key;
	int i;

	// REP initialize
	REP_Init();

	// REP display
	REP_Disp(0);

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

		// get keys
		key = KeyGet();
		switch (key)
		{
		// Prev
		case KEY_PREV:
		// Next
		case KEY_NEXT:
			// REP terminate
			REP_Term();
			return key;

		// Fast - record
		case KEY_FAST:
			// REP record
			REP_Record();
			break;

		// Slow - play
		case KEY_SLOW:
			// REP play
			REP_Play(2);
			break;

		// Hold - replay
		case KEY_HOLD:
			// REP replay
			REP_Play(3);
			break;
		}
	}
}
