/****************************************************************************
 ■ Function Generator
   2012(C) Mr.Honey
****************************************************************************/

#include <p24Hxxxx.h>

#define FOSC	8000000UL
#define FCY	(FOSC / 2)
#include <libpic30.h>

//=================================================================

// Configuration
_FBS(BWRP_WRPROTECT_OFF & BSS_NO_BOOT_CODE)
_FGS(GWRP_OFF & GSS_OFF & GCP_OFF)
_FOSCSEL(FNOSC_PRI & IESO_OFF)
_FOSC(POSCMD_XT & OSCIOFNC_ON & IOL1WAY_ON & FCKSM_CSECMD)
_FWDT(WDTPOST_PS4096 & WDTPRE_PR32 & WINDIS_OFF & FWDTEN_OFF)
_FICD(JTAGEN_OFF)

//=================================================================

// IOPorts
#define LCD_RS		_LATC8
#define LCD_RW		_LATB2
#define LCD_E			_LATB3
#define MAX038_A0		_LATA8
#define MAX038_A1		_LATB4
#define RL1			_LATA9
#define RL2			_LATB5
#define RL3			_LATB8
#define RL4			_LATB6
#define RL5			_LATB7
#define SW2			_RB13
#define SW3			_RB12

//=================================================================

typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned long U32;

//=================================================================

static volatile U32 s_nT1Cnt = 0;	// Timer1オーバーフローカウンタ
static volatile U16 s_nStep = 1;	// 集計回数(1〜COUNT_INTERVAL)
static volatile U32 s_nCount = 0;	// 現在のカウント値
static volatile U32 s_nFreq = 0;	// 現在の周波数(1秒間計測の値)
static volatile int s_nWave = 0;	// 現在の波形タイプ
static volatile int s_nRange = 0;	// 現在の周波数レンジ
static volatile U8 s_bSW2 = 0;	// SW押下フラグ
static volatile U8 s_bSW3 = 0;	// SW押下フラグ

#define COUNT_INTERVAL		2		// 1秒間あたりの集計間隔
#define T2_PERIOD			(FCY / COUNT_INTERVAL)

//=================================================================

static void Initialize(void);
static void Run(void);
static void InitLCD(void);
static void Display(int line, int col, char* str);
static void Write(U8 rs, U8 data);
static void DelayMs(U16 ms);

//=================================================================

// ●メインエントリ
int main(void)
{
	// システム初期化
	Initialize();

	// LCD初期化
	InitLCD();

	// 初期表示
	Display(0, 0, "MAX038 Function");
	Display(1, 1, "Generator (^^)/");
	DelayMs(3000);

	// 実行開始
	Run();

	return 0;
}

// ●システム初期化
static void Initialize(void)
{
	// 必要なモジュールのみ有効化
	PMD1 = 0xC7FF; // T1,T2,T3のみON
	PMD2 = 0xFFFF;

	// ポートクリア
	PORTA = LATA = 0;
	PORTB = LATB = 0;
	PORTC = LATC = 0;

	// デジアナ設定
	AD1PCFGL = 0b0001100111110000; // Analog: AN0,1,2,3,9,10

	// 方向設定
	TRISA = 0b0000000000011100;
	TRISB = 0b0011000000000000;
	TRISC = 0b0000000000000000;

	// 初期状態
	MAX038_A1 = 1;	// SIGN波
	RL5 = 1;		// レンジ0(最低周波数)

	// プルアップ設定
	_CN13PUE = 1;
	_CN14PUE = 1;

	// タイマー1設定 … 周波数カウンタ
	T1CON = 0b0000000000000010;
	TMR1 = 0x0000;
	PR1 = 0xFFFF;
	_T1IP = 7;
	_T1IE = 1;

	// タイマー2/3設定 … 集計タイマー
	T2CON = 0b0000000000001000;
	TMR2 = 0x0000;
	TMR3 = 0x0000;
	PR2 = (U16)T2_PERIOD;
	PR3 = (U16)(T2_PERIOD >> 16);
	_T3IP = 6;
	_T3IE = 1;

	DelayMs(100);
}

// ●実行ループ
static void Run(void)
{
	int nRange = -1;
	int nWave = -1;

	// 基本表示(固定)
	Display(0, 0, "W:    R:        ");
	Display(1, 0, "F:            Hz");

	// 入力変化割込み許可
	_CN13IE = 1;
	_CN14IE = 1;
	_CNIE = 1;

	// タイマー開始
	T1CONbits.TON = 1;
	T2CONbits.TON = 1;

	for(;;)
	{
		// @波形切換え
		if (nWave != s_nWave)
		{
			char* pWave = "";
			switch (s_nWave)
			{
			case 0:
				pWave = "SIN";
				MAX038_A0 = 0;
				MAX038_A1 = 1;
				break;
			case 1:
				pWave = "SQU";
				MAX038_A0 = 0;
				MAX038_A1 = 0;
				break;
			case 2:
				pWave = "TRI";
				MAX038_A0 = 1;
				MAX038_A1 = 0;
				break;
			}
			Display(0, 2, pWave);
			nWave = s_nWave;
		}

		// Aレンジ切換え
		if (nRange != s_nRange)
		{
			char* pRange = "";
			switch (s_nRange)
			{
			case 0: // 実測値: 1Hz〜100Hz
				pRange = "1~100   ";
				RL5 = 1; RL1 = 0; RL2 = 0; RL3 = 0; RL4 = 0;
				break;
			case 1: // 実測値: 8Hz〜900Hz
				pRange = "8~900   ";
				RL4 = 1; RL1 = 0; RL2 = 0; RL3 = 0; RL5 = 0;
				break;
			case 2: // 実測値: 206Hz〜22.2kHz
				pRange = "200~22k ";
				RL3 = 1; RL1 = 0; RL2 = 0; RL4 = 0; RL5 = 0;
				break;
			case 3: // 実測値: 2kHz〜220KHz
				pRange = "2k~220k ";
				RL2 = 1; RL1 = 0; RL3 = 0; RL4 = 0; RL5 = 0;
				break;
			case 4: // 実測値: 26.1kHz〜2.6MHz
				pRange = "26k~2.6M";
				RL1 = 1; RL2 = 0; RL3 = 0; RL4 = 0; RL5 = 0;
				break;
			case 5: // 実測値: 343kHz〜28MHz
				pRange = "340k~28M";
				RL1 = 0; RL2 = 0; RL3 = 0; RL4 = 0; RL5 = 0;
				break;
			}
			Display(0, 8, pRange);
			nRange = s_nRange;
		}

		// B表示周波数の計算
		U32 nDisp = (U32)-1;
		if (s_nCount != s_nFreq)
		{
			// s_nCountは1/COUNT_INTERVAL秒間の計測値なのでスケーリングする
			U32 nTemp = (s_nCount * COUNT_INTERVAL) / (s_nStep - 1);
			long nDiff = s_nFreq - nTemp;

			// // スケーリング誤差を考慮
			if (nDiff < -COUNT_INTERVAL || nDiff > COUNT_INTERVAL) {
				nDisp = nTemp;
			}
		}
		else {
			// s_nCountは1秒間の計測値なのでそのまま表示
			nDisp = s_nFreq;
		}

		// C周波数表示
		if (nDisp != (U32)-1)
		{
			char buf[11], *p;
			int i = 4;

			buf[10] = '\0';
			p = buf + 9;
			do {
				if (--i == 0)
				{
					*p-- = ',';
					i = 3;
				}
				*p-- = (nDisp % 10) + '0';
				nDisp /= 10;
			}
			while (nDisp != 0);
			
			for (; p >= buf; p--)
				 *p = ' ';
		
			Display(1, 3, buf);
		}
		
		// D休憩
		DelayMs(250);
	}
}

// ●LCD初期化
static void InitLCD(void)
{
	Write(0, 0b00111000); // Function Set

	Write(0, 0b00000001); // Clear display
	DelayMs(2);

	Write(0, 0b00001100); // Display ON/OFF
}

// ●文字列表示
static void Display(int line, int col, char* str)
{
	U8 data = 0x80; // SetDDRAM address
	data |= line << 6; // Line1/2 start address
	data += col;

	Write(0, data);
	for (; *str != '\0'; str++) {
		Write(1, *str);
	}
}

// ●LCDへ書き込み
static void Write(U8 rs, U8 data)
{
	LCD_RS = rs;
	LCD_RW = 0;
	LCD_E = 1;

	LATC = (LATC & 0xFF00) | data;
	__delay_us(50);

	LCD_E = 0;
	__delay_us(1);
}

// ●簡易遅延
static void DelayMs(U16 ms)
{
	while (ms >= 100)
	{
		__delay_ms(100);
		ms -= 100;
	}
	if (ms != 0) {
		__delay_ms(ms);
	}
}

//=================================================================

// ●TMR1 Timer 1 expired
void _ISRFAST _T1Interrupt(void)
{
	s_nT1Cnt++;
	_T1IF = 0;
}

// ●TMR1 Timer 3 expired
void _ISR _T3Interrupt(void)
{
	s_nCount = (s_nT1Cnt << 16) + TMR1;

	if (s_nStep == COUNT_INTERVAL)
	{
		TMR1 = 0x0000;
		s_nT1Cnt = 0;
		
		s_nStep = 1;
		s_nFreq = s_nCount;
	}
	else {
		s_nStep++;
	}
	_T3IF = 0;
}

// ●CN Input change interrupt
void _ISR _CNInterrupt(void)
{
	int i, j, k;

	// 波形切換え
	i = 0; j = 0; k = 0;
	do {
		if (!SW2) j++; // 接触している
		else k++;     // 接触していない
	}
	while (++i < 101);
	if (j < k) {
		s_bSW2 = 0;
	}
	else if (!s_bSW2)
	{
		s_nWave = (++s_nWave % 3);
		s_bSW2 = 1;
	}

	// レンジ切換え
	i = 0; j = 0; k = 0;
	do {
		if (!SW3) j++; // 接触している
		else k++;     // 接触していない
	}
	while (++i < 101);
	if (j < k) {
		s_bSW3 = 0;
	}
	else if (!s_bSW3)
	{
		s_nRange = (++s_nRange % 6);
		s_bSW3 = 1;
	}

	_CNIF = 0;
}
