/*
*******************************************************************************************************************
*											RING TONE DEMO 1 BY HARDWARE TIMER
*
* Description:  This program plays a short ring tone music composed of different notes with individual note duration
*				defined in the arrays noteDuration[] and noteFreq[]. Instead of using software delayMs() loop,
*				hardware Timer 0 is used here to make a "real-time" system. The noteDuration[] is defined
*				in the unit of millisec. Therefore, noteDuration[0]=15 => the first note sounds for 150ms;
*				noteFreq[0]=2093 => the note frequency is 2093 Hz. Timer 0 is used to count expiry
*				of the noteDuration[noteIdx], with noteIdx being the array index.
*				Since hardware timer Timer 0 has been loaded with (TH0,TL0) of 0xDC00, it gives 10ms interrupt
*				when external crystal is 11.0592MHz. A variable notePlayCtr is used to count down on
*				noteDuration[noteIdx] until the end of the ring tone music, indicated by a noteDuration of 0.
*
* File:			cd:\src\chp3\src3_2\main.c
* Software:		Keil C (C51) compiler, Eval version 7.09 under uVision2 IDE v2.40
* Programmer: 	John Leung, TechToys Co. (www.TechToys.com.hk)
* Hardware:		AT89S52-gLCD-STK1, with Atmel 89S52, 11.0592 MHz crystal
* Date:			3rd June 2006
* Version:		1.0
* Programmer:	John Leung
*******************************************************************************************************************
*/

#include <REGX52.h>
#include "delay.h"

#define ON 		0
#define OFF		1
#define TRUE 	1
#define FALSE	0
#define CLOSE	0
#define OPEN	1

/*
*******************************************************************************************************************
*												HARDWARE DEFINITION
*******************************************************************************************************************
*/
sbit LED 	= P2^0;
sbit KEYUP	= P1^6;
sbit KEYDN	= P1^4;
sbit BUZZER = P1^0;

bit notePlayEn = FALSE;
unsigned int notePlayCtr=0;
unsigned char noteIdx=0;
code unsigned int noteDuration[] 	= {15,15,30,15,15,30,15,15,15,15,15,15,30,15,15,15,15,15,15,15,30,15,15,30,15,\
30,30,60,0};
code unsigned int noteFreq[] 		= {2093,880,0,2093,1760,0,2093,880,2093,880,2093,1760,0,0,2093,2349,2637,0,\
2637,2794,3136,0,2349,2093,2349,2794,1865,1760,4186,0};

/*
*******************************************************************************************************************
*												FUNCTION PROTOTYPE
*******************************************************************************************************************
*/
void Tmr0Init();
void ToneGen(unsigned int tone);
void notePlayStart(void);


/*
*******************************************************************************************************************
*											Timer 0 interrupt service routine
*
* Function:	This function reloads Timer 0 for a 10ms interrupt. A gate 'notePlayEn' is used to start and stop
*			the 'notePlayCtr' decrement process, thus enable or disable ring tone play and stop. 
*			Music playback is started by making 'notePlay' to TRUE, and setting notePlayCtr to '1' to control an
*			immediate playback. Upon the first notePlayCtr--, notePlayCtr decrement to 0, thus ToneGen() is loaded
*			with the first note frequency noteFreq[0]. Then, notePlayCtr is loaded with noteDuration[0], being the
*			corresponding playback duration of the first note. After a hardware delay of 10ms * noteDuration[0],
*			the process is repeated by loading the second noteFrequency 'noteFreq[1]' 
*			and noteDuration 'noteDuration[1] until the end of the music indicated by noteDuration[noteIdx]=0.
*			
* Argument:	none
* Return:	none
* Note:	
*******************************************************************************************************************
*/
Tmr0() interrupt 1
{
	if(notePlayEn==TRUE){
		if(notePlayCtr>0){
			notePlayCtr--;
			if(notePlayCtr==0){
				notePlayEn = FALSE;
				if(noteDuration[noteIdx]!=0)
				{
					ToneGen(noteFreq[noteIdx]);
					notePlayCtr = noteDuration[noteIdx];
					noteIdx++;
					notePlayEn = TRUE;
				} else {
					TR2 	= 0;
					BUZZER 	= OFF;
				}
			}
		}
	}
	TH0 =	0xDC;			//10ms hardware delay, 11.0592MHz => 1 machine cycle at 1.085usec
	TL0 =	0x00;			//Number of cycles to delay 10ms = 10000/1.085 = 9216
							//T0 in 16-bit mode => T0 preload with a value (65536-9216)=56,320, i.e. 0xDC00
}

/*
*******************************************************************************************************************
*										Timer 0 initialization routine
* Function:	This function initializes Timer 0 to 16-bit manual reload mode while leaving Timer 1 parameters
*			unchanged. Timer 0 interrupt enabled and started here.
* Argument:	none
* Return:	none
* Note:	
*******************************************************************************************************************
*/
void Tmr0Init(void)
{

	TMOD=	TMOD&0xF0;		//reset TIMER0 configuration
	TMOD=	TMOD|0x01;		//leave TIMER1 configuration unchanged and set T0 to 16-bit timer mode

	TH0 =	0xDC;			//10ms hardware delay, 11.0592MHz => 1 machine cycle at 1.085usec
	TL0 =	0x00;			//Number of cycles to delay 10ms = 10000/1.085 = 9216
							//T0 in 16-bit mode => T0 preload with a value (65536-9216)=56,320, i.e. 0xDC00
	TF0 =	0;				//clear overflow flag
	ET0	=	1;				//enable T0 interrupt
	TR0 =	1;				//start timer 0
}

/*
*******************************************************************************************************************
*											TONE GENERATION ROUTINE
* Function:	This function generates a single tone of a certain frequency 'tone' in Hz by Timer 2 
*			in programmable clock out mode
* Argument:	'tone'		frequency of the tone to generate, in Hz
*
* Return:	none
*
* Note:		*** Please note that there is a limit in the frequency range. We are using a crystal frequency of
*				11.0592MHz. From equal Clock-out Freq= Oscillator Freq / (4*(65536-RCAP2H,RCAP2L)).
*				Thus, max Clock-out Freq = 2.7648MHz; min Clock-out Freq = 42 Hz.
*				Human audible range is around 20Hz - 20kHz, and the frequency response of piezo sounder has 
*				a limited range, too. We just cannot make infinite pitch from our hardware! ***
*******************************************************************************************************************
*/
void ToneGen(unsigned int tone)
{
	unsigned long tmp;
	if(tone!=0)
	{
	T2CON = 0x00;			// reset T2CON, the Timer 2 control register. Important for stopping Timer 2 by TR2=0
 
	T2MOD = T2MOD&0xFE;
	T2MOD = T2MOD|0x02;		// sets T2OE=1; DCEN=0; for a clock out mode, no interrupt generated in this mode

	tmp = (11059200UL>>2)/tone;
	tmp = 65536UL - tmp;	
	RCAP2H = (unsigned char) (tmp>>8);
	RCAP2L = (unsigned char) (tmp&0xFF);
	TH2 = RCAP2H;
	TL2 = RCAP2L;

	TR2 = 1;				//Start Timer 2 for clock out
	} 
	else
	{
	TR2= 0;					//tone frequency=0, TR2 = 0 to shut down clock out
	BUZZER = OFF;
	}
}

/*
*******************************************************************************************************************
*											RING TONE MUSIC PLAY STARTED
* Function:	This function starts the ring tone music defined by the arrays noteDuration[] and noteFreq[].
*			noteIdx set to 0 to play start the first note. 
*			notePlayEn set to TRUE to start the hardware countdown process, thus starting the music.
*			notePlayCtr set to an initial value of 1 to force play the music with almost no delay.
* Argument:	none
*
* Return:	none
*
* Note:
*******************************************************************************************************************
*/
void notePlayStart(void)
{
	noteIdx = 0; 			//Play from the first note
	notePlayEn = TRUE; 		//Play note enable
	notePlayCtr = 1;		//Play force immediately
}


/*
*******************************************************************************************************************
*											RING TONE MUSIC PLAY STOP
* Function:	This function stops the ring tone music defined by the arrays noteDuration[] and noteFreq[].
*			notePlayEn set to FALSE to stop the hardware countdown process, thus stoping the music.
* Argument:	none
*
* Return:	none
*
* Note:
*******************************************************************************************************************
*/
void notePlayStop(void)
{
	notePlayEn = FALSE;
	TR2 	= 0;
	BUZZER 	= OFF;
}

/*
*******************************************************************************************************************
*														MAIN
*******************************************************************************************************************
*/
void main(void)
{
	Tmr0Init();
	EA = 1;									//enable global interrupt flag
	
	for(;;)
	{	
		if(KEYUP==CLOSE)
		{
			DelayMs(20);					//key debounce for 20ms
			if(KEYUP==CLOSE) 				//a true user input, a true key
				{	while(KEYUP==CLOSE);	//the user should release the key to begin playback
					notePlayStart();		//start play
					LED = ON;				//ON LED for visual indication
				}
		}

		if(KEYDN==CLOSE)
		{
			DelayMs(20);
			if(KEYDN==CLOSE) notePlayStop();
			LED = OFF;
		}

	}
}
