#include "hcs12dp256.h"
#include "basicLCD.h"
//#include "util.h"//for debugging, print

#define LED_RED_MASK 0x01
#define LED_YELLOW_MASK 0x04
#define LED_GREEN_MASK 0x08

#define SEVEN_SEGMENT_U7_EN 0x08 //U7_EN high (PM3)
#define SEVEN_SEGMENT_OUTPUT_EN 0x0F //Not use 0xCF because only need last 4 bit for this assignment. setup port T (PT0..3 for BCD) (1 = output)
#define SEVEN_SEGMENT_OFF 0xFF // Turn 7-segment display off

#define TEN_MS_COUNT 1375
#define NUM_TICKS_PER_SEC 64

#define DEFAULT_TIME 0
#define DEFAULT_TEMP -300

#define DUTY_MIN 20
#define DUTY_MAX 50 //value measured in lab
#define DUTY_STEP 5

#define FAN_MAX 80

//global vars
unsigned int RTI_counter = 0;
unsigned int curr_time = DEFAULT_TIME;
unsigned int curr_temp = DEFAULT_TEMP;
int step_motor_status = 0;
unsigned int delay_count = 0;//for 5sec delay timer
//unsigned char prev_speedRPS;
//unsigned char raw_RPS;
//unsigned char prev_raw_RPS;
unsigned char speedRPS;


//functions
void main(void);
char makeBit(char bit);
void initHW(void);
void initRTI(void);

void initLCD(void);
void LCD_displayStr(char str[]);
void LCD_display_time(int);
void LCD_display_temp(int);
void LCD_display_RPS(int);

void initKeypad(void);
int isKeyPressed(char key);
char getColumn(void);
void setRow(char row);

void initATD0(void);
void initHeaterFan(void);

void initOutputCampare(void);
void initTIMER_ISR_4(void);

void initDCmotor(void);
void initPWM(void);
void initPAI(void);

void initLED(void);
void toggleLED(char bit);

void initStepMotor(void);
void resetStepMotor(void);
void rotateStepMotor(int direction);
void togglePTx(char nthBit);

void DelayNX(unsigned int n);
void delay10MS(void);
void delay1sec(void);

void initFANPWM(void);
void controlFAN(int n);
//void turnOffFAN(void);
//void turnOnFAN(void);

//unused
void init7segment(void);
void set7Segment(char count);//count is between 0..9

void printCharNum(char number);
void printCRLF(void);


void main(void) {
	int i;
	
	int prev_time = DEFAULT_TIME;
	
	int prev_temp = DEFAULT_TEMP;
	int initial_temp = DEFAULT_TEMP;

	unsigned char prev_speedRPS = 0;
	
	int duty = DUTY_MIN;
	
	initHW();
	
	//PTM = makeBit(7);//turn on Heater
	
	while(!isKeyPressed('F')) { //do loop, when 'F' is not pressed
		//poll for quit key
		
		//time count
		if (prev_time != curr_time) {//if time changed
			prev_time = curr_time;
			LCD_display_time(prev_time);//display time //do not need buffer since int pass by value not reference
		}
		
		//temperature control
		if ((initial_temp == DEFAULT_TEMP) && (curr_temp != DEFAULT_TEMP)) {//if initial_temp was not initialized, and first reading of temperature has done
			initial_temp = curr_temp + 2;
		}
		if (prev_temp != curr_temp) {//if temp changed
			if (curr_temp > initial_temp){
				controlFAN(1);
				//PTP |= makeBit(4);//turn on the fan
				PTM &= ~makeBit(7);//turn off Heater
			} else if (curr_temp < initial_temp){
				controlFAN(0);
				//PTP &= ~makeBit(4);//turn off the fan
				PTM |= makeBit(7);//turn on Heater
			} else { // temp is fine, turn off both
				controlFAN(0);
				//PTP &= ~makeBit(4);//turn off the fan
				PTM &= ~makeBit(7);//turn off Heater
			}
			prev_temp = curr_temp;
			LCD_display_temp(prev_temp);//display temperature //do not need buffer since int pass by value not reference
		}
		

		
		
		//if have time, could add some code to remove re-active while key being hold.
		//ex: isKeyPressed('4') != prev_key4_stat;
		
		//blind control
		if ((isKeyPressed('4') == 1) && (step_motor_status == 0)) { //if timer is stop, and key is pressed
			step_motor_status = 1; // clock wise
			resetStepMotor();//this call include 20ms delay, helps debounce handling.
			initTIMER_ISR_4(); //start 5s delay timer
		} //raise the blind
			
		if ((isKeyPressed('5') == 1) && (step_motor_status == 0)) { //if timer is stop, and key is pressed
			step_motor_status = -1; //counter clock wise
			resetStepMotor();//this call include 20ms delay, helps debounce handling.
			initTIMER_ISR_4(); //start 5s delay timer
		} //lower the blind
		

		//water pump
		if ((isKeyPressed('1') == 1) &&((duty+DUTY_STEP)<=DUTY_MAX)) { //increase motor rate
			//controlFAN(1);//for debugging
			//toggleLED(1);
			for (i=0; i<DUTY_STEP; i++) {
				duty++;
				PWMDTY7 = duty;
				DelayNX(1); //wait motor to speed up, also helps debounce handling.
			}
		} 
			
		if ((isKeyPressed('2') == 1) &&((duty-DUTY_STEP)>=DUTY_MIN)) { //decrease motor rate
			//controlFAN(0);//for debugging
			//toggleLED(2);
			for (i=0; i<DUTY_STEP; i++) {
				duty--;
				PWMDTY7 = duty;
				DelayNX(1); //wait motor to speed up, also helps debounce handling.
			}
		} //lower the blind
		
		
		//RPS monitoring
		if (prev_speedRPS != speedRPS) {//if speedRPS changed
		   //toggleLED(3);
			prev_speedRPS = speedRPS;
			LCD_display_RPS(prev_speedRPS);//display RPS //do not need buffer since int pass by value not reference
		}
	}//while loop end
	
	//exit program
	asm("SEI");//globally stop ISR
	PWME = 0x00; //disable PWM
	PTP &= ~makeBit(4);//turn off the fan
	PTM &= ~makeBit(7);//turn off Heater
	LCD_instruction(0x01); //clear LCD display
}



void initHW(void)
{
	initRTI();
	initLCD();
	initKeypad();
	initLED();//for debugging
	
	initFANPWM();//init PWM for fan speed control
	initATD0();//temperature
	initHeaterFan();//heater
	
	initOutputCampare();//initialize output compare, but not activated yet
	//initTIMER_ISR_4();//call this to active 5 sec delay
	initStepMotor();//init step motor
	
	initPWM();//init PWM for DC motor speed control
	initDCmotor();
	initPAI();//no longer used

	asm("CLI");//Globally enable CPU interrupts
}




char makeBit(char bit)
{
	return (0x01 << bit); 
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//LCD display///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//LCD_instruction(0x01); //clear display
//LCD_instruction(0xC0); //2nd line
//LCD_instruction(0x80); //1st line
void initLCD(void)
{
	//char temp;
	//int i;
	Lcd2PP_Init();
	LCD_displayStr("GMS Time: xxxx s");
	LCD_instruction(0xC0);// 2nd line
	LCD_displayStr("T: xxC P: xxxRPS");
	LCD_display_time(0);
	LCD_display_temp(0);
	LCD_display_RPS(0);
}

void LCD_displayStr(char str[])
{
	int i;
	for (i = 0; str[i] != 0; i++) { //(str[i] != 0), char is not null
		LCD_display(str[i]);
	}
}

void LCD_display_time(int t) {
	LCD_instruction(0x80 + 10); //1st line, 10th char
	
	t %= 10000; //prevent it overflow 4 digit place
	LCD_display('0'+t/1000);
	t %= 1000;
	LCD_display('0'+t/100);
	t %= 100;
	LCD_display('0'+t/10);
	t %= 10;
	LCD_display('0'+t);
}

void LCD_display_temp(int t) {
	LCD_instruction(0xC0 + 3); //2nd line, 10th char
	
	t %= 100; //prevent it overflow 2 digit place
	LCD_display('0'+t/10);
	t %= 10;
	LCD_display('0'+t);
}

void LCD_display_RPS(int t) {
	LCD_instruction(0xC0 + 10); //2nd line, 10th char
	
	t %= 1000; //prevent it overflow 3 digit place
	LCD_display('0'+t/100);
	t %= 100;
	LCD_display('0'+t/10);
	t %= 10;
	LCD_display('0'+t);
}




////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Key Pad/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void initKeypad(void)
{
	//keypad
	SPI1CR1 = 0; // Turn off SPI (what's this???)
	DDRP |= 0x0F;//Keypad PP 0..3
	DDRH &= 0x0F;//Read the column scan (Key 5…8) from Port H (bits 4..7)
}

/**
* return boolean (0 or 1) depends on if a key in certain position of row and column is pressed.
*/
//int isKeyPressed(char row, char column)
int isKeyPressed(char key)
{
	char row;
	char column;
	unsigned int i, j;
	char keyArray[4][4] = {
		{'1', '2', '3', 'A'},
		{'4', '5', '6', 'B'},
		{'7', '8', '9', 'C'},
		{'E', '0', 'F', 'D'},
	};
	for (i=0; i<4; i++) {
		for (j=0; j<4; j++) {
			if (key == keyArray[i][j]) {
				row = i;
				column = j;//get column and row variable initialize to coresponding value
				break;
			}
		}
	}
	
	row = 0x01 << row;
	column = 0x01 << column;
   
    setRow(row);
	return (column == getColumn()); //return boolean
}

/**
* return column bit read from PTH
*/
char getColumn(void)
{
	return (PTH & 0xF0) >> 4;
}

/**
* set row bit write to PTP
*/
void setRow(char row)
{
	PTM |= SEVEN_SEGMENT_U7_EN;//Load data into U7 set U7_EN high (PM3) 
	row = (0x0F & row); //clear low nibble
	PTP &= 0xF0;//clear low nibble
	PTP |= row;//wont effect high nibble
	PTM &= ~SEVEN_SEGMENT_U7_EN;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//for RTI////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void initRTI(void)
{
	//RTI
	CRGFLG |= 0x80;//Clear any previous RTIs (CRGFLG)
	RTICTL |= 0x7F;//8MHz->8HZ
	CRGINT |= 0x80; //Locally enable the RTI (CRGINT)
}

#define LOWEST_FREQ 4
//** in my board, the lowest freq is 4Hz not 8Hz
#pragma interrupt_handler RTIISR
void RTIISR(void)
{
	RTI_counter++;
	if ((RTI_counter % LOWEST_FREQ) == 0) {//
		curr_time = RTI_counter / LOWEST_FREQ;//when calling the LCD function, will do furthur % 10000 to keep it in 4 digits

		speedRPS = PACN2;
		PACN2 = 0;//reset this register, had to do this or it wont work.
	}
	ATD0CTL5 = 0x86;//A7 right justified, scan
	CRGFLG = CRGFLG;//Clear the RTI so that the next one can be recognized (CRGFLG)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Fan and Heater //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//for fan and heater
void initHeaterFan(void) {
	DDRM |= makeBit(7);
	DDRP |= makeBit(4);
}

//for fan
void initFANPWM(void) {
	PWMPOL |= makeBit(4); // Initial Polarity = high.
	PWMCLK &= ~makeBit(4); //Select Clock A for channel 4
	PWMPRCLK = 0x77; //0x70; Prescale ClockA : busclock/128
	PWMCAE &= 0xEF; //Channel 4 left aligned
	PWMCTL &= 0xF3; //Allow PWM in Wait/Freeze
	// No concatenations
	PWMPER4 = 100; //Set period for PWM4
	PWMDTY4 = 0;
	PWME |= makeBit(4); //Enable PWM Channel 4
}


void controlFAN(int n) {
	int duty;
	if (n == 1) {
		for(duty = PWMDTY4; duty <= 80; duty++) {
			PWMDTY4 = duty;
			DelayNX(1);
		}
	}
	if (n == 0) {
		for(duty = PWMDTY4; duty >= 0; duty--) {
			PWMDTY4 = duty;
			DelayNX(1);
		}
	}
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///AD converter /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//for AD converter

void initATD0(void)
{
	ATD0CTL2 = 0xC2;//0xFA;//power on, fast clear, interrupt enable
	ATD0CTL3 = 0x00;//8 conversion
	ATD0CTL4 = 0x61;//max sample time, 10bit, 1/4 clock
	//ATD0CTL5 = 0x86;//A7 right justified, scan
}


#pragma interrupt_handler ATD0_ISR
void ATD0_ISR(void)
{
//temp = ADR06/8 - 5;
	curr_temp = (((ATD0DR6 & 0x03FF)/8 - 5)-32)/1.8;//-32)/1.8 to change the unit the C
	//ATD0CTL5 = 0x86;//start new conversion
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///step motor///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//for step motor

void initStepMotor(void)
{
	DDRT |= 0x60;
	DDRP |= 0x20;
	PTP |= makeBit(5);	//setPort(PTP, 5, 1);//PP5 is the enable for the stepper
	resetStepMotor();
}

void resetStepMotor(void)
{
	PTT &= ~makeBit(5);//Clear PT5
	delay10MS();
	PTT &= ~makeBit(6);//Clear PT6
	delay10MS();
}

//direction should be 1 (clock wise) or -1 (counter clock wise)
void rotateStepMotor(int direction)
{
	int i;
	if (direction > 0) { //counter clock wise
		i = direction - 1;
		togglePTx(5+i%2);//toggle 5th bit when i = even, toggle 6th bit when i = odd
	}
	if (direction < 0) {  //counter clock wise
		i = -direction - 1;
		togglePTx(6-i%2);//toggle 6th bit when i = even, toggle 5th bit when i = odd
	}
}

void togglePTx(char nthBit) {
	 PTT ^= makeBit(nthBit);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///step motor PWM and 5 sec delay///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//for step motor PWM and 5 sec delay

void initOutputCampare(void)
{
	////TIOS |= makeBit(5); //** set 5th channel //for step motor PWM //1 = output compare, 0 = input capture 
	TIOS |= makeBit(4); // set 4th channel //for 5sec delay //1 = output compare, 0 = input capture 
	TSCR2 = 0x05; //freq = 8MHz * 1/32 = 0.25MHz //TOI, x, x, x, TCRE, PR2, PR1, PR0
	TCTL1 = 0x01; //no effect at all???????? //0 0 no action (timer disconnected from output pin); 0 1 toggle OCn pin; 1 0 clear OCn pin to 0; 1 1 set OCn pin to high
	TSCR1 |= 0x90;//enable TCNT and fast timer flag clear //TEN TSWAI TSFRZ TFFCA 0 0 0 0
}



//should only be called when need to active motor
void initTIMER_ISR_4(void)
{
	TIE |= makeBit(4);
	TC4 = TCNT + 50000;//for 5sec delay
}


//5sec delay ISR
#pragma interrupt_handler TIMER_CH4_ISR
void TIMER_CH4_ISR(void)
{
	TC4 = TC4 + 50000;
	delay_count++;
	
	rotateStepMotor(delay_count * step_motor_status);	//no delay loop inside this function call

	if (delay_count % (25) == 0)//to get 1/5 Hz
	{
		delay_count = 0;
		TIE &= ~makeBit(4);//stop counting
		step_motor_status = 0;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///DC motor///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//for DC motor
void initPWM(void) {
	PWMPOL |= makeBit(7);//PWMPOL = 0xFF; // Initial Polarity is high //use |= 0x80?

	PWMCLK &= ~makeBit(7); //Select Clock B for channel 7 //not scaled clock
	PWMPRCLK = 0x77;//0x70; //Prescale ClockB : busclock/128
	
	PWMCAE &= 0x7F; //Channel 7 : left aligned 
	PWMCTL &= 0xF3; //Concatenate 67, 45, 23, 01; PWM in Wait and Freeze Modes //optional?
	
	PWMPER7 = 100; //Set period for PWM7 //Left: PWMxPeriod = Channel Clock Period * PWMPERx
	PWMDTY7 = DUTY_MIN;//duty = 15 
	
	PWME |= makeBit(7); //Enable PWM Channel 7
}

void initDCmotor(void) {
	DDRP |= makeBit(6);//DDRP |= 0x40; //For Motor Direction Control
	PTP |= makeBit(6); //PTP |= 0x40; // Counter-Clockwise
}

void initPAI(void) {
	PAFLG |= 1; //Clear out the interrupt flag // 0000 00 PAOVF PAIF
	PACTL = 0x50; //Enable PACA for Optical Sensor 0101 0001 //enable, event mode, rising edge, interrupt
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///Delay functions///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DelayNX(unsigned int n) {
	unsigned int i;
	for (i=0; i < n; i++) {
		delay10MS();
	}
}

void delay10MS(void)
{
	int a = 0;
	unsigned int i;
	for (i=0; i< TEN_MS_COUNT; i++)
	{
		a++;
		a--;//do something meaningless to get a tiny bit delay
	}
}

/**
* 1 second delay
*/
void delay1sec(void)
{
	unsigned int i;
	for (i=0; i< 100; i++) delay10MS( ); //simulate 1 second delay, actual result depends on processor preformance
}


//LED just for debugging

void initLED(void)
{
	DDRK |= 0x0F; //set LED port
	PORTK &= ~0x0F; //turn of all LED
}

void toggleLED(char bit)
{
	PORTK = PORTK ^ makeBit(bit);
}
