
// ****************************************************************************
//
//                         Page LA - logic analyzer
//
// ****************************************************************************

#include "../include.h"

#define LA_FCHECK	0	// 1=DEBUG display sample frequency, 0=normal mode

#if LA_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
u32 LA_Freq;		// debug: sample frequency in [Hz]
#endif

// Required number of samples:
// - Every row is 128 pixels width, requires 128 samples
// - Grid is every 8 samples - there are 8 grids on every row.
// - 128 samples per row * 8 rows = total 1024 samples = 1024 bytes
#define LA_SAMPNUM	1024

int LA_Mode = LA_MODE_FREE;	// current LA run mode
int LA_Base = LA_BASE1_100US;	// current LA time base

// LA run mode text (4 characters, position 5..8)
const char* const LA_ModeTxt[LA_MODE_NUM] = {
	"FREE",		// LA_MODE_FREE free fast running
	"HOLD",		// LA_MODE_HOLD hold last sample
	"AUTO",		// LA_MODE_AUTO auto start
};

// LA time base text (7 characters, position 9..15)
//  Title: "x LA AUTO1 100us"
const char* const LA_BaseTxt[LA_BASE_NUM] = {
	"1   1\x60s",	// LA_BASE1_1US 1 channel, time base 1us, sample rate 8 MHz, total time 128us
	"1  10\x60s",	// LA_BASE1_10US 1 channel, time base 10us, sample rate 800 kHz, total time 1.28ms
	"1 100\x60s",	// LA_BASE1_100US 1 channel, time base 100us, sample rate 80 kHz, total time 12.8ms
	"1   1ms",	// LA_BASE1_1MS 1 channel, time base 1ms, sample rate 8 kHz, total time 128ms
	"1  10ms",	// LA_BASE1_10MS 1 channel, time base 1ms, sample rate 8 kHz, total time 1.28sec
	"2   1\x60s",	// LA_BASE2_1US 2 channels, time base 1us, sample rate 8 MHz, total time 64us
	"2  10\x60s",	// LA_BASE2_10US 2 channels, time base 10us, sample rate 800 kHz, total time 640us
	"2 100\x60s",	// LA_BASE2_100US 2 channels, time base 100us, sample rate 80 kHz, total time 6.4ms
	"2   1ms",	// LA_BASE2_1MS 2 channels, time base 1ms, sample rate 8 kHz, total time 64ms
	"2  10ms",	// LA_BASE2_10MS 2 channels, time base 1ms, sample rate 8 kHz, total time 640ms
};

// additional wait time in 10 ms to get 500ms
const u8 LA_AddWait[LA_BASE_NUM] = {
	50,		// LA_BASE1_1US 1 channel, time base 1us, sample rate 8 MHz, total time 128us
	50,		// LA_BASE1_10US 1 channel, time base 10us, sample rate 800 kHz, total time 1.28ms
	49,		// LA_BASE1_100US 1 channel, time base 100us, sample rate 80 kHz, total time 12.8ms
	38,		// LA_BASE1_1MS 1 channel, time base 1ms, sample rate 8 kHz, total time 128ms
	0,		// LA_BASE1_10MS 1 channel, time base 1ms, sample rate 8 kHz, total time 1.28sec
	50,		// LA_BASE2_1US 2 channels, time base 1us, sample rate 8 MHz, total time 64us
	50,		// LA_BASE2_10US 2 channels, time base 10us, sample rate 800 kHz, total time 640us
	50,		// LA_BASE2_100US 2 channels, time base 100us, sample rate 80 kHz, total time 6.4ms
	44,		// LA_BASE2_1MS 2 channels, time base 1ms, sample rate 8 kHz, total time 64ms
	0,		// LA_BASE2_10MS 2 channels, time base 1ms, sample rate 8 kHz, total time 640ms
};

// Timer period setup, system frequence is 48MHz
const int LA_TimLoad[LA_BASE_NUM] = {
	6-1,		// LA_BASE1_1US 1 channel, time base 1us, sample rate 8 MHz, total time 128us
	60-1,		// LA_BASE1_10US 1 channel, time base 10us, sample rate 800 kHz, total time 1.28ms
	600-1,		// LA_BASE1_100US 1 channel, time base 100us, sample rate 80 kHz, total time 12.8ms
	6000-1,		// LA_BASE1_1MS 1 channel, time base 1ms, sample rate 8 kHz, total time 128ms
	60000-1,	// LA_BASE1_10MS 1 channel, time base 1ms, sample rate 8 kHz, total time 1.28sec
	6-1,		// LA_BASE2_1US 2 channels, time base 1us, sample rate 8 MHz, total time 64us
	60-1,		// LA_BASE2_10US 2 channels, time base 10us, sample rate 800 kHz, total time 640us
	600-1,		// LA_BASE2_100US 2 channels, time base 100us, sample rate 80 kHz, total time 6.4ms
	6000-1,		// LA_BASE2_1MS 2 channels, time base 1ms, sample rate 8 kHz, total time 64ms
	60000-1,	// LA_BASE2_10MS 2 channels, time base 1ms, sample rate 8 kHz, total time 640ms
};

// LA initialize
void LA_Init()
{
	// setup input pins to floating input with pull-down
	GPIO_Mode(LA_GPIO1, GPIO_MODE_IN_PD);
	GPIO_Mode(LA_GPIO2, GPIO_MODE_IN_PD);

	// enable DMA1 clock
	RCC_DMA1ClkEnable();

	// Enable Timer clock source
	TIM2_ClkEnable();

	// set 'Hold' key to long mode
	KeyHoldLong();
}

// LA terminate
void LA_Term()
{
	// set 'Hold' key to default short mode
	KeyHoldShort();

	// reset Timer
	TIM2_Reset();

	// stop DMA
	DMA_ChanDisable(DMA1_Chan(LA_DMA));

	// Timer clock disable
	RCC_TIM2ClkDisable();

	// reset input pins
	GPIO_PinReset(LA_GPIO1);
	GPIO_PinReset(LA_GPIO2);
}

// setup DMA channel
NOINLINE void LA_SetupDMA()
{
	// get address of DMA1 channel
	DMAchan_t* chan = DMA1_Chan(LA_DMA);

	// set source address - PD port (as peripheral address)
	DMA_PerAddr(chan, &GPIO_Port(IN_GPIO)->INDR);

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

	// set number of entries
	Bool chan2 = LA_Base >= LA_BASE_FIRST2;	// 2-channel mode
	DMA_Cnt(chan, chan2 ? (LA_SAMPNUM/2) : LA_SAMPNUM);

	// prepare memory or peripheral mode
	u32 mem = (((LA_Base == LA_BASE1_1US) || (LA_Base == LA_BASE2_1US)) ? DMA_CFG_MERM2MEM : 0);

	// 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
		mem |				// memory-to-memory mode enable
		0);

	// clear trasmission completion flag
	DMA1_CompClr(LA_DMA);
}

// setup Timer
NOINLINE void LA_SetupTimer()
{
	// Reset timer to default setup
	TIM2_Reset();

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

	// set prescaler to full system clock 48MHz
	TIM2_Presc(0);

	// time base mode
	int base = LA_Base;

	// set reload value
	TIM2_Load(LA_TimLoad[base]);

	// reload immediately
	TIM2_Update();

	// reset counter
	TIM2_Cnt(0);

	// enable update DMA request
	TIM2_UDMAReqEnable();
};

// LA update
FASTCODE void NOFLASH(LA_Update)()
{
	int i;

	// setup DMA channel
	LA_SetupDMA();

	// setup Timer
	LA_SetupTimer();

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

	// enable timer
	TIM2_Enable();

#if LA_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	cb();
	di();
	cb();
	u32 t1 = Time();
	cb();
#endif

	// wait for transfer to complete
	while (!DMA1_Comp(LA_DMA))
	{
		// break with key
		if (KeyBuf != NOKEY) break;
	}

#if LA_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	cb();
	u32 t2 = Time();
	cb();
	ei();
	cb();
#endif

	// disable update DMA request
	TIM2_UDMAReqDisable();

	// stop Timer
	TIM2_Disable();

	// stop DMA
	DMA_ChanDisable(DMA1_Chan(LA_DMA));

#if LA_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	u32 dt = t2 - t1;
	Bool chan2 = LA_Base >= LA_BASE_FIRST2;	// 2-channel mode
	int num = chan2 ? (LA_SAMPNUM/2) : LA_SAMPNUM;
	LA_Freq = (u32)(((s64)SystemCoreClock * num  + dt/2) / dt);
#endif

	// wait to 500ms - only free mode
	if (LA_Mode == LA_MODE_FREE)
	{
		for (i = LA_AddWait[LA_Base]; i > 0; i--)
		{
			WaitMs(10);
			if (KeyBuf != NOKEY) break;
		}
	}
}

// LA display title
void LA_DispTitle()
{
	// display title
	SelFont12();	// select font 8x12
	int x = (WIDTH - PageX - 11*8)/2 + PageX;
	DrawText(LA_ModeTxt[LA_Mode], x, ROW0_Y); // display run mode
	x += 4*8;
	DrawText(LA_BaseTxt[LA_Base], x, ROW0_Y); // display time base

#if LA_FCHECK	// 1=DEBUG display sample frequency, 0=normal mode
	x = PageX + 8;
	DrawRectClr(x, ROW0_Y, WIDTH-1-x, FONTH);
	DecUNum(DecNumBuf, LA_Freq, '\'');
	DrawText(DecNumBuf, x, ROW0_Y);
#endif
}

// LA display
void LA_Disp()
{
	int x, y, m;
	u8 d;

	// LA display title
	LA_DispTitle();

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

	// source data
	const u8* s = (const u8*)Buf;

// 1-channel mode:
//   8 rows with 6 lines:
//	2 lines space
//	2 lines data
//	1 line base
//	1 line marks
	if (LA_Base < LA_BASE_FIRST2)
	{
		// draw base lines
		for (y = TITLE_H+4; y < HEIGHT; y += 6) DrawHLineSetFast(0, y, WIDTH);

		// draw marks, grid = 8 samples = 8 pixels
		for (y = TITLE_H+5; y < HEIGHT; y += 6)
		{
			for (x = 0; x < WIDTH; x += 8) DrawPointSetFast(x, y);
		}

		// draw samples of channel 1, input: PD2
		for (y = TITLE_H+2; y < HEIGHT; y += 6)
		{
			for (x = WIDTH; x > 0; x--)
			{
				if ((*s++ & B2) != 0) DrawVLineSet(x, y, 2);
			}
		}
	}

// 2-channel mode:
//   4 rows with 12 lines:
//	3 lines space
//	1 line marks
//	1 line base
//	2 lines data of channel 2
//	1 line space
//	2 lines data of channel 1
//	1 line base
//	1 line marks
	else
	{
		// draw base lines
		for (y = TITLE_H+4; y < HEIGHT; y += 12) DrawHLineSetFast(0, y, WIDTH);
		for (y = TITLE_H+10; y < HEIGHT; y += 12) DrawHLineSetFast(0, y, WIDTH);

		// draw marks - 2nd channel, grid = 8 samples = 8 pixels
		for (y = TITLE_H+3; y < HEIGHT; y += 12)
		{
			for (x = 0; x < WIDTH; x += 8) DrawPointSetFast(x, y);
		}

		// draw marks - 1st channel, grid = 8 samples = 8 pixels
		for (y = TITLE_H+11; y < HEIGHT; y += 12)
		{
			for (x = 0; x < WIDTH; x += 8) DrawPointSetFast(x, y);
		}

		// draw samples, input channel 1: PD2, input channel 2: PD3
		for (y = TITLE_H+5; y < HEIGHT; y += 12)
		{
			for (x = WIDTH; x > 0; x--)
			{
				d = *s++;
				if ((d & B3) != 0) DrawVLineSet(x, y, 2);
				if ((d & B2) != 0) DrawVLineSet(x, y+3, 2);
			}
		}
	}

	// display update
	DispUpdate();
}

// Page LA (returns key PREV/NEXT)
u8 PageLA()
{
	int i;
	u8 key;

	// LA initialize
	LA_Init();

	// LA display
	memset(Buf, 0, LA_SAMPNUM);
	LA_Disp();

	// last input state
	Bool lastin = GPIO_In(LA_GPIO1);

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

		// update in free mode
		if (LA_Mode == LA_MODE_FREE)
		{
			// LA display
			LA_Disp();

			// LA update
			LA_Update();
		}

		// auto start
		if (LA_Mode == LA_MODE_AUTO)
		{
			Bool in = GPIO_In(LA_GPIO1);
			if (in != lastin)
			{
				// change to HOLD state
				LA_Mode = LA_MODE_HOLD;

				// LA update
				LA_Update();

				// LA display
				LA_Disp();
			}
		}

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

		// Fast - faster mode
		case KEY_FAST:
			i = LA_Base-1;
			if (i < 0) i = LA_BASE_NUM-1;
			LA_Base = i;
			LA_Disp();
			break;

		// Slow - slower mode
		case KEY_SLOW:
			i = LA_Base+1;
			if (i >= LA_BASE_NUM) i = 0;
			LA_Base = i;
			LA_Disp();
			break;

		// Hold - switch mode
		case KEY_HOLD:
			i = LA_Mode;
			if (i == LA_MODE_FREE)
				i = LA_MODE_HOLD;
			else
				i = LA_MODE_FREE;
			LA_Mode = i;

			// do not redraw display, hold current display content - update only title
			LA_DispTitle();
			DispUpdate();
			break;

		// Hold long - Auto mode
		case KEY_HOLD_LONG:
			i = LA_Mode;
			if (i == LA_MODE_AUTO)
				i = LA_MODE_FREE;
			else
				i = LA_MODE_AUTO;
			LA_Mode = i;
			LA_Disp();
			lastin = GPIO_In(LA_GPIO1);
			break;
		}
	}
}
