#include <At892051.h>


/************************ DEFINITIONS **************************************************************/
#define TIMEMODE 0
#define PROGMODE 1
#define DECIMAL 0
#define HEXADECIMAL 1
#define HALFSECLENGTH 1954     
// 1954 = 0.5 sec. @ 12MHz        1800 @ 11.059...Mhz
#define STING_REPEATTIME 36
#define SOFTVER 	1
#define SOFTSUBVER      0
#define SOFTSUBSUBVER   0
/***************************************************************************************************/


/*****************  DECLARATIONS OF VARIABLES ******************************************************/
int intcount, timecounthalfsec=0;
/*  
the following sbits are defined in At892051.h
sbit P1_0 = 0x90;
sbit P1_1 = 0x91;
sbit P1_2 = 0x92;
sbit P1_3 = 0x93;
sbit P1_4 = 0x94;
sbit P1_5 = 0x95;
sbit P1_6 = 0x96;
sbit P1_7 = 0x97;
sbit P3_0 = 0xB0;
sbit P3_1 = 0xB1;
sbit P3_2 = 0xB2;    
sbit P3_3 = 0xB3;    
sbit P3_4 = 0xB4;    
sbit P3_5 = 0xB5;    
sbit P3_7 = 0xB7; */

sbit mode = 0xB5;          /* mode key */
sbit up   = 0xB3;          /* up key   */
sbit down = 0xB4;          /* down key */
sbit startstop = 0xB2;     /* start/stop key */
sbit posout = 0xB0;        /* output driving transistor T1 */
sbit negout = 0xB1;	   /* output drivings transistor T2 */	
sbit LD1_a = 0x91;	   /* 7-seg display LD1 seg a */
sbit LD1_b = 0x90;         /* 7-seg display LD1 seg b */
sbit LD1_c = 0x92;	   /* 7-seg display LD1 seg c */
sbit LD1_d = 0x93;         /* 7-seg display LD1 seg d */
sbit LD1_e = 0x94;         /* 7-seg display LD1 seg e */
sbit LD1_f = 0x95;         /* 7-seg display LD1 seg f */
sbit LD1_g = 0x96;         /* 7-seg display LD1 seg g */
sbit LD2_bc = 0x97;        /* 7-seg display LD2 seg b & seg c */
sfr LDs=0x90;              /* segmemts declared as bits above are declared here as one byte */ 
sbit LEDs_mode = 0xB7;     /* toggles the mode LEDs (LED D1 & D2) */


bit 	mode_old=1, up_old=1, down_old=1, startstop_old=1; /* bits for saving the previous state of the input-keys */
bit 	modepressedflag=0, uppressedflag=0, downpressedflag=0, startstoppressedflag=0,
	/* flags indicating whether a key has been pressed or not */
	outflag=0, startnotstopflag=0, displayrunning=0;
	/* flags indicating whether there outputting is required or not (outflag),
	   whether processing has to start or stop (startnotstopflag),
	   whether the display has to show running segments (displayrunning) */	 

unsigned int antidender, outpcount;
/* counters used in the interrupt routine for stabilizing key-inputs (debounce)
   and calculation of output-pulse pattern */

unsigned char treatmenttime=0; // contains the time left during treatment. Unit: minute. Initial value=0.
unsigned int totaltime=0; //contains the time set before the user started treatment. Unit: minute. Initial value=0
char 	prog=0, dutycycle=0;  //prog will contain the selected program (1,2...8,9   e.g.  A,B...E,F)
char testarray[]={205,163,72,194,133,205,145,207,163,200};    	//"ROM VERSION"
char versionarray[]={SOFTVER,SOFTSUBVER,SOFTSUBSUBVER};    	 //version array contains software version number
char limsarray[]={199,207,72,145}; 				 //"LIMS"
char segmentarray[]={253,254,251,247,239,223,191,127,255}; 	 //single segments, used in test routine
/******************************************************************************************************/





/************ FUNCTION PROTOTYPES ********************************************/
void timer0int (void); 		//prototype of interrupt function
void display (char,bit); 	//prototype of function for general display handling
void GetDutycycle(void); 	//prototype of function for calculating the duty cycle
void testroutine(void);         //prototype of test routine
void delay(unsigned integer);   //prototype of delay function
/****************************************************************************/







main()
{
/*********************************** INIT **************************************************/
unsigned int n; char counter;
posout = 1;	// make sure that transistors T1 & T2 conduct
negout = 1;     // as soon as possible, so that C5 cannot build up any charge


	/******** SET UP INTERRUPT TIMING **************/
	/*    Using an 8 bit timer that counts at a    */ 
	/*      frequency of 1/12*CLOCK FREQUENCY      */
	/* generates an interrupt on every overflow    */
	/* so every 255 microseconds @ 12Mhz christal  */
	
/*

select mode 3 for timer 0: T0_M0_=1
                           T0_M1_=1                            
configure timer 0 as a timer and not as a counter:     			T0_CT_=0  	
timer 0 always running; no external control by means of /INT0 io pin:   T0_GATE_=0
above bits are only available as a byte register declared in the At892051.h headerfile as TMOD,
thus TMOD byte register = 00000011 = 3 (dec.)
*/
TMOD=3;


TR0=1;	      // timer 0 run control bit on
EA=1;	      // enable all interrupts	
ET0=1;	      // enable timer0 interrupt	

	/******** END INTERRUPT TIMER SETUP ************/


/**********************************************************************-> END INIT <-**********/




                                                                              




/********************ON POWER-UP **********************************/
while(1==1)
{
	delay(16000);
	if(uppressedflag==1 && downpressedflag==1) testroutine();
	else break; //break unless test mode is entered by means of pressing up & down key during delay(16000)
}


	for(counter=0;counter<4;counter++)
	{
	delay(17500); LDs=limsarray[counter];
	delay(17500); LDs=255;
	} // display "LIMS"

LEDs_mode=PROGMODE; 		//on power-up, program-mode is selected
display(prog,HEXADECIMAL);      //display program. Initial value of prog was set to '0'
				//in the display() subroutine, we determine that
				//only seg d lights when display() is called HEXADECIMAL
/********************************************************************/



while(1==1)
{
	if(startnotstopflag==0) //ready to run treatment program, currently not running
	{
		if(uppressedflag==1) // up-key pressed
		{
			if(LEDs_mode==PROGMODE)
			{
			if(prog>14 || prog <1) prog=1;
			else prog++;
			display(prog,HEXADECIMAL);
			}

			else     // LEDsmode==TIMEMODE
			{
			if(treatmenttime>18) treatmenttime=1;
			else treatmenttime++;
			display(treatmenttime,DECIMAL);
			timecounthalfsec=0;
			}
                uppressedflag=0;
		}

		if(downpressedflag==1) // down-key pressed
		{
			if(LEDs_mode==PROGMODE)
			{
			if(prog<2) prog=15;
			else prog--;
			display(prog,HEXADECIMAL);
			}

			else // LEDs_mode=TIMEMODE
			{
			if(treatmenttime<2) treatmenttime=19; 
			else treatmenttime--;
			timecounthalfsec=0;
			display(treatmenttime,DECIMAL);
			}
		downpressedflag=0;
		}

		if(modepressedflag==1) // select key pressed
		{
 		LEDs_mode=~LEDs_mode;  //toggle mode
                if(LEDs_mode==PROGMODE) display(prog,HEXADECIMAL);
		if(LEDs_mode==TIMEMODE) display(treatmenttime,DECIMAL);
		modepressedflag=0;
		}

		if(startstoppressedflag==1) // start/stop key pressed
		{
			if(prog==0)              // when prog==0 the user did not select a program
			{                        // in that case make shure we are in prog mode 
			LEDs_mode=PROGMODE;      // display a flashing seg d
			display(21,0);
			}

			else
			{	
			 	if(treatmenttime==0)  //when treatment time==0 user did not select
   				{                    //the duration. In that case 
				LEDs_mode=TIMEMODE;  //make shure we are in time mode and display a flasing '0'
				display(22,0);
                		}

				else                 //when prog and time are set reset interrupt counter 
				{                    //reset time counters and set startnotstop flag.
		       		intcount=0;          //make n zero so that the display starts running immediately
				totaltime=treatmenttime;
					/* processing time will be decreased when treatment program is running.
					   A saved value of the initial selected time is needed for the 
					   automated programs (progA..F) to calculate when the duty cycle should change
					   therefore treatment time is copied to total time. 			*/
                       		startnotstopflag=1;
				n=0;
				}
			}
		startstoppressedflag=0;
		}
	} /* end if(startnotstopflag==0) */






/**************** The following code is executed when treatment program is started  **************************/

	if(startnotstopflag==1) //processing program running
        {
	    	GetDutycycle();
		if (timecounthalfsec%dutycycle<2) 	   //calculates when there should be output
		{
		outflag=1;                     //output
		}
		else outflag=0;		       //no output

		if(uppressedflag==1 || downpressedflag==1) // if the user presses the up or down key, make the readout visible
		{                                          // for a few seconds. (see further down to 
		n=40000;                                   // understand the construction with n)
		uppressedflag=0; downpressedflag=0;
                }

		if(modepressedflag==1)                     // if the user presses the mode key, toggle mode
		{                                          // and make the readout visible for a few seconds
 		LEDs_mode=~LEDs_mode; //toggle mode        // (see further down to
		n=40000;                                   // understand the construction with n)
		modepressedflag=0;
		}		



		if(startstoppressedflag==1 || treatmenttime==0) //treatment has to be stopped
		{                    	//because the user pressed the stop key OR because 'time's up'
		outflag=0;
	 	modepressedflag=0; uppressedflag=0; downpressedflag=0; startstoppressedflag=0;
		startnotstopflag=0;
                	if(treatmenttime==0)             // time is up; in that case the user needs to re-enter
			{                                // prog and treatment time before he/she can start again 
			prog=0;                          // Therefore prog is reset to its initial value of '0'.
			}
			if(LEDs_mode==TIMEMODE) display(treatmenttime,DECIMAL);
			else display(prog,HEXADECIMAL); 
		}


        	else                //treatment has to continue
		{
			if (n>20000)    // if n is set to a value greater than 20000, 
					// the readout is made visible and n is decreased with 1 until n<20000
			{
			displayrunning=0;
 			if(LEDs_mode==TIMEMODE) display(treatmenttime,DECIMAL);	
			else display(prog,HEXADECIMAL);
			n--;
			}
			else displayrunning=1; // keep displaying a running '8'
		} 
	}
} /* end while (1==1) */
} /* end main */




void timer0int (void) interrupt 1 using 1
{
intcount++;
	if (startnotstopflag==1 && intcount>HALFSECLENGTH)    
	{	
	intcount=0;
	timecounthalfsec++; 
	if(timecounthalfsec%120==0) treatmenttime--;
        }
	
	if(displayrunning==1)
	{
                LD1_g=1;			
		LD2_bc=1;
		if (intcount<(HALFSECLENGTH/8)*1)	    	LDs=253; /*seg a*/ 
		else if (intcount<(HALFSECLENGTH/8)*2)  	LDs=254; /*seg b*/
		else if (intcount<(HALFSECLENGTH/8)*3)  	LDs=191; /*seg g*/
		else if (intcount<(HALFSECLENGTH/8)*4)  	LDs=239; /*seg e*/
		else if (intcount<(HALFSECLENGTH/8)*5)  	LDs=247; /*seg d*/
		else if (intcount<(HALFSECLENGTH/8)*6)  	LDs=251; /*seg c*/
		else if (intcount<(HALFSECLENGTH/8)*7)  	LDs=191; /*seg g*/
		else 				       		LDs=223; /*seg f*/
		displayrunning=0;
        }

	if(mode!=mode_old || up!=up_old || down!=down_old || startstop!=startstop_old)
	{	
	mode_old=mode;
	up_old=up;
	down_old=down;
	startstop_old=startstop;
	antidender=0;
        }

	if(mode==mode_old && up==up_old && down==down_old && startstop==startstop_old)
	{
	antidender++;
		if(antidender==40)
		{
		modepressedflag=~mode_old;  
		uppressedflag=~up_old;      
		downpressedflag=~down_old;  // ~ = 1's complement operator 
		startstoppressedflag=~startstop_old;
		}		
		else if(antidender==4500)
		{
		uppressedflag=~up_old;
		downpressedflag=~down_old;
                }

		else if(antidender==5100)
		{
		uppressedflag=~up_old;
		downpressedflag=~down_old;
		antidender=4500;
		}
	}

	if(outpcount==0)
	{
		if(outflag==1)
		{
		outpcount++;
		}
		else { posout=1; negout=1; }
	}

	else
	{
	outpcount++;
		if(outpcount==2)
		{
		posout=1;
		}

                else if(outpcount==3)
		{
		posout=0;
		}		

		else if(outpcount==(STING_REPEATTIME/2)+2)
		{
		negout=1;
		}

		else if(outpcount==(STING_REPEATTIME/2)+3)
		{
		negout=0;
                }

		else if(outpcount==STING_REPEATTIME+1)
		{
 		outpcount=0;
		}
        }
}

void display (char display,bit mode)
{
//unsigned int n;


switch(display)
	{
	case 0:	if(mode==DECIMAL) LDs=192; 
		else LDs=247;
		break;

	case 1:	LDs=250; break;
	case 2: LDs=164; break;
	case 3: LDs=176; break;
	case 4:	LDs=154; break;
	case 5:	LDs=145; break;
	case 6:	LDs=129; break;
	case 7:	LDs=248; break;
	case 8:	LDs=128; break;
	case 9: LDs=144; break;
	case 10:
		if(mode==DECIMAL) LDs=64;
		else LDs=136;
		break;
	case 11:
		if(mode==DECIMAL) LDs=122;
		else LDs=131;
		break;
	case 12:
		if(mode==DECIMAL) LDs=36;
		else LDs=197;
		break;
	case 13:
		if(mode==DECIMAL) LDs=48;
		else LDs=162;
		break;
	case 14:
		if(mode==DECIMAL) LDs=26;
		else LDs=133;
		break;
	case 15:
		if(mode==DECIMAL) LDs=17;
		else LDs=141;
		break;
	case 16: LDs=1; break;
	case 17: LDs=120; break;
	case 18: LDs=0; break;
        case 19: LDs=16; break;

        case 21: 	   //'Prompt' like an MS-DOS prompt 
	                   // user has to enter a value by pressing up or down key before prompt disappears
		while(uppressedflag==0 && downpressedflag==0) //otherwhise user may not/cannot do anything
		{
		LDs=255; delay(6000);
		LDs=247; delay(5000);
		}
		break;

	case 22: 	//'flashing zero'  
                        // user has to enter a value by pressing up or down key before 'flashing zero' disappears
		while(uppressedflag==0 && downpressedflag==0)  //otherwhise user may not/cannot do anything 
		{
		LDs=255; delay(6000);
		LDs=192; delay(5000);
		}
                break;
	} /* end switch */
} /* end display() */


void GetDutycycle(void)
{
char para1, para2, para3;
/* The following 2 lines could be called 'dirty code'; on ALL even programs para1=10, para2=7 and para3=5
   on ALL odd programs para1=9, para2=6 and para3=3.
   The issue is that these para1,2,3 are only used in the preset programs, A,B...E,F.
   Code would be faster and, even more important, clearer if the assignment of para1,2,3 would take place	
   in the IF..ELSE constructions that determine the duty cycle (a few lines below).
   We have done it this 'the quick & dirty way' because the size of the code was reduced 'dramatically' this way (by several bytes).
*/	   
if(prog%2==0){para1=10; para2=7; para3=5;}
else {para1=9; para2=6; para3=3;}

  	if(prog<10) dutycycle=12-prog;

	else if(prog<12) /* prog = A eg. B */	
			{
			if(timecounthalfsec<totaltime*2*12) dutycycle=para1;
			else if(timecounthalfsec<totaltime*3*12) dutycycle=para2;
			else if(timecounthalfsec<totaltime*8*12) dutycycle=para3;
			else if(timecounthalfsec<totaltime*9*12) dutycycle=para2;
			else dutycycle=para1;
   			}

        else if(prog<14) /* prog = C eg D */
			{
			if(timecounthalfsec<totaltime*3*12) dutycycle=para1;
			else if(timecounthalfsec<totaltime*6*12) dutycycle=para2;
			else dutycycle=para3;
			}
	else		 /* prog = E eg F */
		 	{
			if(timecounthalfsec<totaltime*2*12) dutycycle=para2;
			else if(timecounthalfsec<totaltime*4*12) dutycycle=para3;
			else if(timecounthalfsec<totaltime*6*12) dutycycle=para2;
			else if(timecounthalfsec<totaltime*8*12) dutycycle=para3;
			else dutycycle=para2;
			}
}

void testroutine(void)
{
char counter=0;
LDs=255;
modepressedflag=0; startstoppressedflag=0;
   while(1==1)
   {
	if(startstoppressedflag==1)  //start / stop key was pressed
	{
  		for(counter=0;counter<9;counter++) //show all single segments on display
		{
      		delay(20000); LDs=segmentarray[counter];
		LEDs_mode=~LEDs_mode;
      		} 
	LEDs_mode=~LEDs_mode;
	startstoppressedflag=0;                     
	}


   	if(modepressedflag==1) // mode-key pressed
    	{
  		for(counter=0;counter<10;counter++) //show "ROM VERSION" on display
    		{
      		delay(20000); LDs=testarray[counter];
	    	delay(20000); LDs=255;
        	}
  		for(counter=0;counter<3;counter++) //show rom version number
    		{
      		delay(20000); display(versionarray[counter],DECIMAL);
	   	delay(20000); LDs=255;
        	}
    	modepressedflag=0; uppressedflag=0; downpressedflag=0; startstoppressedflag=0;
    	break;   
    	}        
   }
}

void delay(unsigned int dl)
{
unsigned int n;
for(n=0;n<dl;n++){}
}
