//********************************************************************************
// Project		:	Final Project(Receiver for remote controller)
// Start Date	:	11/4/2004
// Finish Date	:	11/13/2004
// Author		:	Xing Wang & Sunghwa Jung
// Company		:	Rose-Hulman Institute of Technology
// Purpose		:	1. Decode demodulated IR singal by measuring pulse width with TMR0 every external interrupt.
//					2. When receive IR data, flash trigger temprature 5 times on 7-segments and turn on red LED during flash.
//					3. When periperal temp exceeds trigger temp, turn on green LED.
// Input		:	TFM5360 & temperature from DS18B20
// Outputs		:	red LED, green LED & two digit temperature on two 7-segment displays
// Chip type	:	PIC16F877
// Clock frequency		:	13.5 MHz
//********************************************************************************
#include <pic.h>
//********************************************************************************
// I/O Definitions
//********************************************************************************
#define DQ_DIRECTION	TRISB1	// DQ wire direction bit
#define DQ_DS18B20		RB1
#define SegA			RD0		// pin 17, Segment A
#define SegB			RD1		// pin 18, Segment B
#define SegC			RD2		// pin 1, Segment C
#define SegD			RD3		// pin 2, Segment D
#define SegE			RD4		// pin 7, Segment E
#define SegF			RD5		// pin 8, Segment F
#define SegG			RD6		// pin 9, Segment G
#define TensDigit		RA0		// pin 10, tens digit control pin (on==1; off==0)
#define OnesDigit		RA1		// pin 11, ones digit control pin (on==1; off==0)
#define LED				RB7
#define IR_LED			RB6
//********************************************************************************
// Constant Definitions
//********************************************************************************
#define PORTA_DIRECTION		0B11100000
#define PORTB_DIRECTION		0B11000000
#define Input				1
#define Output				0
#define SkipCommand			0XCC
#define ConvertTCommand		0X44
#define ReadScratchpad			0XBE
#define WriteScratchpad			0X4E
#define TH						0X00
#define TL						0X00
#define DS18B20_Config			0X1F	//9bit resolution
#define enable				1
#define disable				0
#define timer0				0.07585		//  1/13.5e6*4*256*1000=0.07585
#define T					0.6667		//  1/36kHz*24periods*1000=0.6667
//********************************************************************************
// Subroutine Definitions
//********************************************************************************
void	Initialize_Ports_INT();
int		Initialize_DS18B20();
void	SendCommand(char command);
void	Write_DS1820(char Bit);
void	ReadByte(char *digit);
char	Read_DS18B20();
void	Wait_for_15useconds();
void	Display_Byte(char, char);
char	Decimal_To_SevenSegment(char);
char	Get_Decimal(char *);
void	interrupt External_TMR0();
void 	reset_IR_module();
void 	save_data(char Bit);
void 	Display_TriggerTemp();
//********************************************************************************
// Global Variable Definitions
//********************************************************************************
char	TriggerTemp = 24;	// trigger temperature
char	ActualTemp;			// peripheral temperature
char	tens,ones;			// tens and ones digit of temperature
int		i = 0;				// loop number for display
char	Head=0, Data_pulse=0, Stop_pulse=0;
char Data=0,not_Data=0,Bit=0,bit_no=0,DisplayTrigger=0;
char Trigger_tens,Trigger_ones,j=0,k=0;
//********************************************************************************
// Main Program
//********************************************************************************
void main()
{
	unsigned int y;
	char STATUS, digit, temperature[2];
	Initialize_Ports_INT();

	Head = enable;
	
	do
	{
		STATUS = Initialize_DS18B20();	//Send reset pulse to and get presence pulse from DS18B20
	}while(STATUS==1);	//If DS18B20 is ready, STATUS will be "0".
	SendCommand(SkipCommand);	//Skip the ROM command
	SendCommand(WriteScratchpad);	//Send function command
	SendCommand(TH);	//In this case, TH and TL register of scratchpad are not important.
	SendCommand(TL);	//We just follow the write order of scratchpad to set configuration of DS18B20.
	SendCommand(DS18B20_Config);	//R1R0 = 00 => 9bit resolution
	while(1)
	{
		INTE = disable;

		do
		{
			STATUS = Initialize_DS18B20();	//Send reset pulse to and get presence pulse from DS18B20
		}while(STATUS==1);	//If DS18B20 is ready, STATUS will be "0".

		SendCommand(SkipCommand);	//Skip the ROM command
		SendCommand(ConvertTCommand);	//Send function command
		do
		{
			ReadByte(&digit);	//If conversion is done, DS18B20 transmits "1".	
		}while(digit!=0xff);	//If 8bits of digit are all "1", conversion is done.
		do
		{
			STATUS = Initialize_DS18B20();	//Send reset pulse to and get presence pulse from DS18B20
		}while(STATUS==1);	//If DS18B20 is ready, STATUS will be "0".
		SendCommand(SkipCommand);	//Skip the ROM command
		SendCommand(ReadScratchpad);	//Send function command
		ReadByte(&digit);	// Read temperature LSByte, Byte 0 of scartchpad
		temperature[0] = digit;
		ReadByte(&digit);	// Read temperature MSByte, Byte 1 of scartchpad
		temperature[1] = digit;
		ActualTemp = Get_Decimal(temperature);
		tens = ActualTemp/10;
		ones = ActualTemp - tens*10;

		INTE = enable;

		while(DisplayTrigger==enable)
		{
			Display_TriggerTemp();
			DisplayTrigger = disable;			
		}
		if (ActualTemp>=TriggerTemp) LED = 1;
		else LED = 0;
		while(i<250*2)
		{
			Display_Byte(1, tens);
			for(y=0;y<450;y++); 
			Display_Byte(0, ones);
			for(y=0;y<450;y++);	
			OnesDigit = 0;
			i++;
		}
		i = 0;
	}
}
//********************************************************************************
// Initialize_Ports()
//********************************************************************************
void Initialize_Ports_INT()
{
// DS18B20 signal line, DQ
	TRISA = 0X00;
	TRISB = 0X0f;	//PORTB6,7 are input, the others are output.
	TRISD = 0x00;
	DQ_DIRECTION=Output;	// DQ on DS18B20 is output
	RBPU = 1;	// Enable internal resister of PORTB

	PORTD=0x00;

	TensDigit=1;	//Turn TensDigit 7-segment.
	OnesDigit=1;	//Turn OnesDigit 7-segment.

	TMR2ON=1;
	T0CS = 0; //Timer mode
	PSA  = 0;	//Prescaler is assigned to Timer0
	PS2  = 1;	//Prescaler=1:128
	PS1  = 0;
	PS0  = 1; 
	T0IF = 0;
	INTF = 0;
	INTEDG = 0; //External interrupt is triggered at falling edge.
	GIE  = enable;
	INTE = enable;

}
//********************************************************************************
// Reset_DS18B20()
//********************************************************************************
// Send a reset pulse of 0 volt to DS1820 and wait for presence pulse
// (1) Drop DQ line to send a reset low pulse of between 480 microseconds and 960 microseconds.
// (2) Raise DQ line and wait for 15 to 60 microseconds
// (3) Check presence of lower pulse from DS1820 from 60 to 240 microseconds in lenghth
// (4) Return 0 for success and 1 for error
int Initialize_DS18B20()
// DQ_DS1820 is the signal pin of DS1820
// DQ_DIRECTION is the pin direction configuration bit
{
char i, presence;
	DQ_DIRECTION = Output;	// DQ is output from PIC to DS1820
	DQ_DS18B20 = 0;
	for(i=0;i<32;i++) Wait_for_15useconds(); // wait for 691 useconds at 13MHz
	DQ_DS18B20 = 1;
	for(i=0;i<2;i++) Wait_for_15useconds();	// wait for 45 useconds at 13MHz
	DQ_DIRECTION = Input;	// DQ is input from DS18B20 to PIC
	presence = 1;
	for(i=0;i<13;i++) // read DQ for 311 useconds and check for zero
	{
		if(presence==1) presence = DQ_DS18B20;	// check if DS18B20 is ready
		Wait_for_15useconds();		
	};
	DQ_DIRECTION = Output;
	for(i=0;i<11;i++) Wait_for_15useconds();	// wait for 263 microseconds at 13MHz

	return(presence);	// return presence pulse status
}// end of Reset_DS18B20()
//********************************************************************************
// SendCommand() -- send one-byte command in command to DS1820
//              with least significant bit first
//********************************************************************************
void SendCommand(char command)
{
	char i;	// loop count
	DQ_DIRECTION = Output;	// DQ is output from PIC to DS18B20
	for(i=0;i<8;i++)
	{
		if((command&0B00000001)==0)	Write_DS1820(0);// bit-wise AND to pick out Bit 0
		else Write_DS1820(1);
		command = command>>1;	// right shift by one bit
	}
	return;
}
//********************************************************************************
// Write_DS18B20()
//********************************************************************************
void Write_DS1820(char Bit)
// write one bit in Bit to DS18B20
{
	char i;
	DQ_DIRECTION = Output;	// DQ is output from PIC to DS18B20
	Wait_for_15useconds();
	DQ_DS18B20 = 0;		// pull DQ down to start writing
	DQ_DS18B20 = 0;		// at least 1 us long
	if(Bit==0) 	// write "0"
	{
		for(i=0;i<4;i++) Wait_for_15useconds(); // wait for 100.3us at 13MHz
		DQ_DIRECTION = Input;	// release data line to write "0"
		Wait_for_15useconds();
	}
	else 		// write "1"
	{	
		DQ_DIRECTION = Input;	// release data line to write "1"
		for(i=0;i<5;i++) Wait_for_15useconds(); // wait for 124.3us at 13MHz
	}
	return;
}// end Write_DS1820()
//********************************************************************************
// 	ReadByte() -- read one byte from DS1820
//              with least significant bit first
//********************************************************************************
void 	ReadByte(char *digit)
{
	char i, byte;	// loop count
	byte = 0;					// clear digit
	for(i=0;i<7;i++)
	{
		if(Read_DS18B20()==1) byte = byte|0B10000000; // Receiving a "1", set the bit.
		byte = byte>>1;	// left shift by one bit with "0" in LSB
	}
	if(Read_DS18B20()==1)			// receive a "1" for Bit 7
		byte = byte|0B10000000;	// set the bit
	*digit = byte;
	return;
} //end of ReadByte()
//********************************************************************************
// Read_DS1820() -- read one bit from DS1820
//********************************************************************************
// mimimum duration is 60 microseconds.
char Read_DS18B20()
{
char i, sample;	// bit sampled from DS1820
	DQ_DIRECTION = Output;	// pull low for >1us to start reading cycle
	DQ_DS18B20=0;
	DQ_DS18B20=0;
	DQ_DIRECTION = Input;	// release DQ line
	for(i=0;i<3;i++);// wait for us at 13MHz
	sample = DQ_DS18B20;
	for(i=0;i<2;i++) Wait_for_15useconds(); // wait for 52.3us at 13MHz
	DQ_DIRECTION = Output;
	return (sample);	// sample and return either "0" or "1" from DS1820
}// end Read_DS1820()
//********************************************************************************
// Display_Byte()	- send a byte to 7-segment display
//********************************************************************************
void Display_Byte(char choice, char word)
// display word on 7 segment display, as XGFEDCBA
// choice=0 for ones digit, choice=1 for tens digit
{
	char code;
	code = Decimal_To_SevenSegment(word);
	SegA = code;
	code = code>>1;	// shift one bit to right
	SegB = code;
	code = code>>1;	// shift one bit to right
	SegC = code;
	code = code>>1;	// shift one bit to right
	SegD = code;
	code = code>>1;	// shift one bit to right
	SegE = code;
	code = code>>1;	// shift one bit to right
	SegF = code;
	code = code>>1;	// shift one bit to right
	SegG = code;
	if(choice==0)
	{
		TensDigit = 0;	// turn off tens digit segment
		OnesDigit = 1;	// turn on ones digit segment
	}
	else
	{
		OnesDigit = 0;	// turn off ones digit segment
		TensDigit = 1;	// turn on tens digit segment
	}
}
//********************************************************************************
// Decimal_To_SevenSegment()	- send a byte to 7-segment display
//********************************************************************************
char Decimal_To_SevenSegment(char digit)
{
	char BinaryString;
	switch (digit)
	{
		case 0: BinaryString = 0B01000000;
			break;
		case 1: BinaryString = 0B01111001;
			break;
		case 2: BinaryString = 0B00100100;
			break;
		case 3: BinaryString = 0B00110000;
			break;
		case 4: BinaryString = 0B00011001;
			break;
		case 5: BinaryString = 0B00010010;
			break;
		case 6: BinaryString = 0B00000010;
			break;
		case 7: BinaryString = 0B01111000;
			break;
		case 8: BinaryString = 0B00000000;
			break;
		case 9: BinaryString = 0B00010000;
			break;
	}
	return BinaryString;
}
//********************************************************************************
// Get_Decimal()
//********************************************************************************
char Get_Decimal(char *string)
{
	char digit;
	char sum = 0;
	digit = string[1];
	digit = digit>>4;
	if((digit&0x0F) == 0)	// positive
	{
		digit = string[0];
		digit = digit>>3;
		if((digit&0x01) ==1)	sum = sum + 1;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 2;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 4;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 8;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 16;
		digit = string[1];
		if((digit&0x01) ==1)	sum = sum + 32;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 64;
		digit = digit>>1;
		if((digit&0x01) ==1)	sum = sum + 128;
	}

	sum = sum/2;		// "1" stands for 0.5 centigrade
	return sum;
}
//********************************************************************************
// interrupt RB_Change()
//********************************************************************************
void interrupt External_TMR0()
{
//	static char Trigger_tens,Trigger_ones,i,j;
	if(T0IF)
	{
		reset_IR_module();
		T0IF = 0;
	}
	else if(INTF)
	{
		if(Head==enable)	//Head=start bit of IR signal
		{
			if(INTEDG==0)	//at falling edge
			{
				TMR0=0;
				T0IE=enable;
				INTEDG=1;	//triggered at rising edge
			}//end if(INTEDG==0)
			else if(INTEDG==1)	//at rising edge
			{
				if(TMR0>127)	//1/13.5e6*4*64*127=2.41 ms > 3.6*T = 2.4 ms
				{
					TMR0 = 0;
					Data_pulse=enable;
					Head=disable;
				} //end if(Time_intaval*timer0>3.6*T)
				else reset_IR_module();
			}  //end if(INTEDG==1)
		}  //end if(Head==enable)

		else if(Data_pulse==enable)
		{
			if(TMR0>57 && TMR0<84)//1.6*T < TMR0 < 2.4*T
			{
				TMR0 = 0;
				Bit = 0x00;
				save_data(Bit);
			}
			else if(TMR0>94 && TMR0<119 ) //2.6*T < TMR0 < 3.4*T
			{
				TMR0 = 0;
				Bit = 0x01;
				save_data(Bit);
			}
			else reset_IR_module();
		}  //end if(Data=enable)
		
		else if(Stop_pulse==enable)
		{
			if(TMR0>57 && TMR0<84)//1.6*T < TMR0 < 2.4*T
			{
				TMR0 = 0;
				if(Data^not_Data==0x0F)
				{
					switch(Data)
					{
						case 0B00001100: TriggerTemp++;
										 DisplayTrigger=enable;
								     	 break;
						case 0B00000011: TriggerTemp--;
									 	 DisplayTrigger=enable;
								 		 break;
						case 0B00001010: TensDigit=0; OnesDigit=0;
									     SLEEP();
										 break;
					} //end switch(Data)
				} //end if(Data^not_Data==0)	
			} //end if(Time_intaval*timer0>1.8*T && Time_intaval*timer0<2.2*T)
			reset_IR_module();
		} //end if(Stop_pulse==enable)
		INTF=0;
	} //end if(INTF)
} //end void interrupt External_TMR0()
//********************************************************************************
// Wait_for_15useconds()
//********************************************************************************
void Wait_for_15useconds()
// use Timer0 to generate 14.80 microsecond delay each time this routine is called
// at 13 MHz
{
	TMR2IF = 0;
	TMR2 = 256-42;
	while(TMR2IF==0);
	return;
} // end Wait_for_15useconds()
//********************************************************************************
// reset_IR_module()
//********************************************************************************
void reset_IR_module()
{
	T0IE = disable;
	TMR0 = 0;
	INTEDG = 0;  //triggered at falling edge
	Data = 0;
	not_Data = 0;
	Head = enable;
	Data_pulse = disable;
	Stop_pulse = disable;
}
//********************************************************************************
// save_data(char bit)
//********************************************************************************
void save_data(char Bit)
{
	if(bit_no<=3)
	{
		Data = Data<<1;
		Data = Data | Bit;
	}  //end if(bit_no<=3)
	else if(bit_no>3)
	{
		not_Data = not_Data<<1;
		not_Data = not_Data | Bit;
	}  //end if(bit_no>3)
	bit_no++;
	if(bit_no>=8)
	{
		Stop_pulse=enable;
		Data_pulse=disable;
		bit_no=0;
	}  //end if(bit_no>=8)
}
//********************************************************************************
// void Display_TriggerTemp()
//********************************************************************************
void Display_TriggerTemp()
{
	IR_LED = 1;
	Trigger_tens = TriggerTemp / 10;
	Trigger_ones = TriggerTemp - Trigger_tens*10;
	for(k=0;k<5;k++)
	{
		for(j=0;j<100;j++)
		{																	
			Display_Byte(1, Trigger_tens);
			for(i=0;i<450;i++); 
			Display_Byte(0, Trigger_ones);
			for(i=0;i<450;i++);
		}
		TensDigit = 0;
		OnesDigit = 0;
		for(i=0;i<450*15;i++);
	}
	IR_LED = 0;
}