
#include "../include.h"

int Page = PAGE_DEF;	// current selected page
int PageX;		// X coordinate of page title

// common data buffer
ALIGNED u32	Buf[BUF_SIZE_MAX];

// vector table
__attribute__((section(".ram_vectors"))) irq_handler_t VectorTab[IRQ_NUM];

// page name
const char* const PageName[PAGE_NUM] = {
	"U",			// page U - voltage meter
	"I",			// page I - current meter
	"P",			// page P - power meter
	"CH",			// page CH - charging meter
	"R",			// page R - resistance meter
	"C",			// page C - capacitance meter
	"L",			// page L - inductance meter
	"OSC",			// page OSC - oscilloscope
	"LA",			// page LA - logic analyzer
	"GEN",			// page GEN - frequency generator
	"PWM",			// page PWM - function generator
	"DAC",			// page DAC - voltage regulator
	"FT",			// page FT - frequency and period meter
	"FFT",			// page FFT - spectrum analyzer
	"CNT",			// page CNT - counter
	"TIM",			// page TIM - timer
	"TG",			// page TG - time gate
	"DUT",			// page DUT - duty cycle
	"COM",			// page COM - UART communication
	"I2C",			// page I2C - I2C scanner
	"NG",			// page NG - noise generator
	"NA",			// page NA - noise analog
	"PH",			// page PH - pulse histogram
	"REP",			// page REP - repeater
	"BAT",			// page BAT - battery
};

// page title
const char* const PageTitle[PAGE_NUM] = {
	"Voltage",		// page U - voltage meter
	"Current",		// page I - current meter
	"Power",		// page P - power meter
	"Charging",		// page CH - charging meter
	"Resistance",		// page R - resistance meter
	"Capacitance",		// page C - capacitance meter
	"Inductance",		// page L - inductance meter
	"",			// page OSC - oscilloscope
	"",			// page LA - logic analyzer
	"Generator",		// page GEN - frequency generator
	"",			// page PWM - function generator
	"",			// page DAC - voltage regulator
	"Frequency",		// page FT - frequency and period meter
	"Spectrum",		// page FFT - spectrum analyzer
	"Counter",		// page CNT - counter
	"Timer",		// page TIM - timer
	"Time Gate",		// page TG - time gate
	"Duty Cycle",		// page DUT - duty cycle
	"",			// page COM - UART communication
	"Scanner",		// page I2C - I2C scanner
	"Noise Gen",		// page NG - noise generator
	"Noise Ana",		// page NA - noise analog
	"Pulse Hist",		// page PH - pulse histogram
	"Repeater",		// page REP - repeater
	"Battery",		// page BAT - battery
};

// page functions
pPageFnc const PageFnc[PAGE_NUM] = {
	PageU,			// page U - voltage meter
	PageI,			// page I - current meter
	PageP,			// page P - power meter
	PageCH,			// page CH - charging meter
	PageR,			// page R - resistance meter
	PageC,			// page C - capacitance meter
	PageL,			// page L - inductance meter
	PageOSC,		// page OSC - oscilloscope
	PageLA,			// page LA - logic analyzer
	PageGEN,		// page GEN - frequency generator
	PagePWM,		// page PWM - function generator
	PageDAC,		// page DAC - voltage regulator
	PageFT,			// page FT - frequency and period meter
	PageFFT,		// page FFT - spectrum analyzer
	PageCNT,		// page CNT - counter
	PageTIM,		// page TIM - timer
	PageTG,			// page TG - time gate
	PageDUT,		// page DUT - duty cycle
	PageCOM,		// page COM - UART communication
	PageI2C,		// page I2C - I2C scanner
	PageNG,			// page NG - noise generator
	PageNA,			// page NA - noise analog
	PagePH,			// page PH - pulse histogram
	PageREP,		// page REP - repeater
	PageBAT,		// page BAT - battery
};

// select font 8x8
void SelFont8()
{
	SetFont(FontBold8x8x128, 8);
}

// select font 8x12
void SelFont12()
{
	SetFont(FontBold8x12x128, 12);
}

// display current page header
void PageHead()
{
	// clear header
	DrawRectClrFast(0, 1, WIDTH, TITLE_H-2);

	// prepare page number
	u8* s = DecNumBuf;
	int len0 = DecUNum(s, Page+1, 0) * 8;

	// prepare page name
	const char* t = PageName[Page];
	int len = StrLen(t) * 8;

	// draw frame
	DrawFrameSet(0, 0, WIDTH, TITLE_H);

	// draw name frame
	DrawRectSet(1, 1, len0+4+len+1, TITLE_H-2);

	// select font 8x12
	SelFont12();

	// print page number
	int x = 1;
	DrawTextInv(s, x, ROW0_Y);
	x += len0+4;

	// draw page name
	DrawTextInv(t, x, ROW0_Y);
	x += len+1;
	PageX = x;

	// draw page title
	const char* t2 = PageTitle[Page];
	int len2 = StrLen(t2) * 8;
	DrawText(t2, (WIDTH-1-x-len2)/2+x, ROW0_Y);
}

// setup IRQ priorities to default state
void IrqPriorDef()
{
// This default priority setting is used for most functions. For some functions,
// that scan edges from the input, interrupts from the timer must have a lower
// priority than other interrupts. If the signal from the input is too frequent,
// the interrupts may become overloaded, in which case interrupts with lower
// priority may be blocked. This applies to interrupts from SysTick, which in
// this case will remain set to Active and the interrupt controller will no longer
// trigger it. For such functions, the SysTick priority is therefore set higher
// than the timer interrupt priority, thereby preventing the aforementioned
// error from occurring.

	enable_nesting();					// enable interrupt nesting
	NVIC_IRQPriority(IRQ_SYSTICK, IRQ_PRIO_NORMAL);		// SysTick
	NVIC_IRQPriority(IRQ_TIM1_UP, IRQ_PRIO_HIGH);		// Timer 1 update
	NVIC_IRQPriority(IRQ_TIM1_TRG, IRQ_PRIO_HIGH);		// Timer 1 trigger
	NVIC_IRQPriority(IRQ_TIM1_CC, IRQ_PRIO_HIGH);		// Timer 1 capture
	NVIC_IRQPriority(IRQ_TIM2, IRQ_PRIO_HIGH);		// Timer 2
}

/*
// setup GPIO defaults
//  Unused GPIOs are set to output or pull-down to minimize processor
//  crashes due to accidental pulses on the wires.
void PinDef()
{
	// PD1: SWDIO, RC5 (2M2 ohm)
	GPIO_Mode(PD1, GPIO_MODE_IN_PD);

	// PC0: Inductance input from LC oscillator
	GPIO_Mode(PC0, GPIO_MODE_IN_PU);

	// PC5: PWM output
	GPIO_Out0(PC5);
	GPIO_Mode(PC5, GPIO_MODE_OUT);

	// PC4: GEN output
	GPIO_Out0(PC4);
	GPIO_Mode(PC4, GPIO_MODE_OUT);

	// PD2: IN1 input
	GPIO_Out0(PD2);
	GPIO_Mode(PD2, GPIO_MODE_OUT);

	// PD3: IN2 input
	GPIO_Out0(PD3);
	GPIO_Mode(PD3, GPIO_MODE_OUT);

	// PD4: U input
	GPIO_Out0(PD4);
	GPIO_Mode(PD4, GPIO_MODE_OUT);

	// PD5: I input
	GPIO_Out0(PD5);
	GPIO_Mode(PD5, GPIO_MODE_OUT);

	// PD6: RC input
	GPIO_Out0(PD6);
	GPIO_Mode(PD6, GPIO_MODE_OUT);

	// PD7: RC3 (22K ohm)
	GPIO_Out0(PD7);
	GPIO_Mode(PD7, GPIO_MODE_OUT);

	// PA4: RC1 (220 ohm)
	GPIO_Mode(PA4, GPIO_MODE_IN_PD);

	// PA5: RC2 (2K2 ohm)
	GPIO_Out0(PA5);
	GPIO_Mode(PA5, GPIO_MODE_OUT);

	// PB3: SWCLK, RC4 (220K ohm)
	GPIO_Mode(PB3, GPIO_MODE_IN_PD);
}
*/

// Tare message
void TareMsg()
{
	DrawRectClrFast(0, TITLE_H, WIDTH, HEIGHT-TITLE_H);
	SelFont12();
	DrawText2("TARE", (WIDTH-4*16)/2, ROW2_Y);
	DispUpdate();
}

// watchdog setup
void WDInit()
{
	// enable write access
	IWDG_EnableWrite();

	// setup prescaler, LSI=128kHz, result clock 128kHz/256 = 500Hz
	IWDG_SetPresc(IWDG_PRESC_256);

	// set reload value to 3 seconds, 3*500 = 1500
	IWDG_SetReload(1500-1);

	// reload watchdog counter
	IWDG_Reload();

	// start watchdog
	IWDG_Enable();
}

// main function
int main(void)
{
	int i;
	u8 ch;

	// wait to stabilize power supply
	WaitMs(20);

	// move vector table to RAM
	memcpy(VectorTab, &_vector_base, sizeof(VectorTab));
	SetVTOR(VectorTab);	// set address of vector table

	// switch PC5 to GPIO mode, enable software IWDG, if not already set (disable RESET function)
	if ((OB->USER & (B0|B3|B4)) != (B3|B4))
	{
		OB_t ob;
		Flash_OBRead(&ob);	// read option bytes
		ob.USER |= B3|B4;	// disable RESET function
		ob.USER &= ~B0;		// select software IWDG
		Flash_OBWrite(&ob);	// write new option bytes
		WaitMs(50);
	}

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

	// Initialize keyboard service
	KeyInit();

	// Display initialize
	DispInit();

	// BAT initialize, initialize ADC single conversion mode, set ADC clock to 750kHz
	BAT_Init();

	// clear screen
	DrawClear();
	SelFont12();

	// watchdog reset
	if ((ResetReason & RCC_RESETON_IWDG) != 0)
	{
		DrawText("WatchDog", (WIDTH-8*8)/2, 16);
		DrawText2("RESET", (WIDTH-5*16)/2, 32);
		DispUpdate();
		WaitMs(1000);
	}
	else
	{
		// Display splash screen
		int x = (WIDTH-7*16)/2;
		int y = TITLE_H+3;
		DrawText2("PidiMet", x, y);
		SelFont8();
		DrawText(VERTXT, WIDTH-4*8-5, y+25);
		DrawFrame(x-3, y-3, 7*16+6, 2*12+4, COL_WHITE);
		DrawText("www.pajenicko.cz", (WIDTH-16*8)/2, HEIGHT-8);
	}

	// display battery
	SelFont12();
	DrawText("Battery:", 8, 0);
	DispUVal(9*8, 0, Supply, 4, -3, 'V', True, True);
	DispUpdate();

	// measure battery (16*29ms = 464ms)
	for (i = SUPPLY_NUM; i > 0; i--) BAT_Update();

	// display battery
	DispUVal(9*8, 0, Supply, 4, -3, 'V', True, True);
	DispUpdate();

	// delay
	WaitMs(1000);

	// check HSE external crystal
	if (RCC_GetPLLSrc() != RCC_PLLSRC_HSE)
	{
		DrawClear();
		SelFont12();
		DrawText("WARNING!", (WIDTH-8*8)/2, 4);
		SelFont8();
		DrawText("Crystal is not", (WIDTH-14*8)/2, 20);
		DrawText("working, using", (WIDTH-14*8)/2, 30);
		DrawText("inaccurate HSE", (WIDTH-14*8)/2, 40);
		DispUpdate();
		WaitMs(2000);
	}

	// TIM initialize
	TIM_Init();

	// watchdog setup
	WDInit();

	while(True)
	{
		// display current page
		DrawClear();
		PageHead();

		// set 'Hold' key to short mode
		KeyHoldShort();

		// setup GPIO defaults
		//PinDef();

		// call page service
		ch = PageFnc[Page]();

		// select page
		switch (ch)
		{
		// previous page
		case KEY_PREV:
			Page = Page-1;
			if (Page < 0) Page = PAGE_NUM-1;
			break;

		// next page
		case KEY_NEXT:
			Page = Page+1;
			if (Page >= PAGE_NUM) Page = 0;
			break;
		}
	}
}
