/************************************************************************/
/*									*/
/*		Frequency meter 5 digits: 0.5Hz ... 125MHz		*/
/*									*/
/*		Author: Peter Dannegger					*/
/*			danni@specs.de					*/
/*									*/
/************************************************************************/
/************************************************************************/
sfr 	SP    	= 0x81;

sfr16	DPTR	= 0x82;
sfr 	DPL   	= 0x82;
sfr 	DPH   	= 0x83;

sfr 	PCON  	= 0x87;
#define	IDL_	0x01
#define	PD_	0x02
#define	GF0_	0x04
#define	GF1_	0x08
#define SMOD_ 	0x80

sfr 	TCON  	= 0x88;
sbit 	IT0   	= 0x88;
#define	IT0_	0x01
sbit 	IE0   	= 0x89;
#define	IE0_	0x02
sbit 	IT1   	= 0x8A;
#define	IT1_	0x04
sbit 	IE1   	= 0x8B;
#define	IE1_	0x08
sbit 	TR0   	= 0x8C;
#define	TR0_	0x10
sbit 	TF0   	= 0x8D;
#define	TF0_	0x20
sbit 	TR1   	= 0x8E;
#define TR1_	0x40
sbit 	TF1   	= 0x8F;
#define	TF1_	0x80

sfr 	TMOD  	= 0x89;
#define	T0_M0_	1
#define	T0_M1_	2
#define	T0_CT_	4
#define	T0_GA_	8
#define	T1_M0_	0x10
#define T1_M1_	0x20
#define	T1_CT_	0x40
#define	T1_GA_	0x80

sfr 	TL0   	= 0x8A;

sfr 	TL1   	= 0x8B;

sfr 	TH0   	= 0x8C;

sfr 	TH1   	= 0x8D;

sfr 	P1    	= 0x90;
sbit	ACINPOS = P1^0;
sbit	ACINNEG = P1^1;

sfr 	SCON  	= 0x98;
sbit 	RI    	= 0x98;
sbit 	TI    	= 0x99;
#define TI_	0x02
sbit 	RB8   	= 0x9A;
sbit 	TB8   	= 0x9B;
sbit 	REN   	= 0x9C;
#define REN_ 	0x10
sbit 	SM2   	= 0x9D;
#define	SM2_	0x20
sbit 	SM1   	= 0x9E;
#define	SM1_	0x40
sbit 	SM0   	= 0x9F;
#define SM0_	0x80

sfr 	SBUF  	= 0x99;

sfr 	IE    	= 0xA8;
sbit 	EX0   	= 0xA8;
#define EX0_ 	1
sbit 	ET0   	= 0xA9;
#define ET0_ 	2
sbit	EX1   	= 0xAA;
#define EX1_ 	4
sbit 	ET1   	= 0xAB;
#define ET1_ 	8
sbit 	ES    	= 0xAC;
#define ES_ 	0x10
sbit 	EA    	= 0xAF;
#define EA_ 	0x80

sfr 	P3    	= 0xB0;
sbit 	RXD   	= 0xB0;
#define	RXD_	0x01
sbit 	TXD   	= 0xB1;
#define	TXD_	0x02
sbit 	INT0  	= 0xB2;
#define	INT0_	0x04
sbit 	INT1  	= 0xB3;
#define	INT1_	0x08
sbit 	T0    	= 0xB4;
#define	T0_	0x10
sbit 	T1    	= 0xB5;
#define	T1_	0x20
sbit	ACOUT	= P3^6;

sfr 	IP    	= 0xB8;
sbit 	PX0   	= 0xB8;
#define	PX0_	0x01
sbit 	PT0   	= 0xB9;
#define	PT0_	0x02
sbit 	PX1   	= 0xBA;
#define	PX1_	0x04
sbit 	PT1   	= 0xBB;
#define	PT1_	0x08
sbit 	PS    	= 0xBC;
#define	PS_	0x10

sfr 	PSW   	= 0xD0;
sbit	P     	= 0xD0;
sbit 	F1    	= 0xD1;
sbit 	OV    	= 0xD2;
sbit 	RS0   	= 0xD3;
sbit 	RS1   	= 0xD4;
sbit 	F0    	= 0xD5;
sbit 	AC    	= 0xD6;
sbit 	CY    	= 0xD7;

sfr ACC   	= 0xE0;

sfr B     	= 0xF0;

/****************************** Interrupt sources ***********************/

#define INT_EX0		0
#define INT_T0		1	// Interrupt timer 0
#define INT_EX1		2
#define INT_T1		3
#define INT_UART	4
/************************************************************************/
#define	bit		char
#define	xdata
#define	idata
#define	bdata
#define pdata
#define	data
#define code
#define	sfr		char
#define	sbit		char
#define	sfr16		int
#define interrupt	/##/		// comment
#define int_unused(x)	/##/
#define _at_		;/##/
#else

#define int_unused(x)	void int_##x(void) interrupt x {}
#endif

typedef unsigned char	uchar;
typedef	unsigned int	uint;
typedef unsigned long	ulong;

#define ucchar	uchar code
#define uichar	uchar idata
//typedef	uchar idata	uichar;

#define upchar	uchar pdata
#define uxchar	uchar xdata

#define BIN8(b7,b6,b5,b4,b3,b2,b1,b0) ((uchar)\
                          (b0 << 0)\
                        | (b1 << 1)\
                        | (b2 << 2)\
                        | (b3 << 3)\
                        | (b4 << 4)\
                        | (b5 << 5)\
                        | (b6 << 6)\
                        | (b7 << 7))

union bw {
  uint w;
  struct{
    uchar h;                    // MSB first
    uchar l;
  }b;
};

union blw {
  ulong lw;
  struct{
    uchar b3;                    // MSB first
    uchar b2;
    uchar b1;
    uchar b0;
  }b;
};


#define CALL(addr)	(((void(*)(void))(char code *)addr)())
#define elseif 		else if
#define b2(x,y) 	(((uint)(x)<<8)|(y))		// combine 2 bytes
//#define b2(x,y)       (((uint)(x|0x20)<<8)|(y|0x20))  // lower case

#define TOGGLE(x)	x = ~ x

#define DELAY(x)	{uint i=x;for(;--i;i);}		// x * 8 cycle
/************************************************************************/





#define	XTAL		24e6
//#define	MUX_TIME	5			// 49Hz
#define	MUX_TIME	3				// 195HZ
//#define	MUX_TIME	1			// 781Hz

#define PRESCALER	128
//#define PRESCALER	256

#define	TMIN		0.5				// min 0.5sec
#define TMAX		(2.0 - TMIN)	// max 2sec

//------------------------------------------------------------------------
//				Display definitions
//------------------------------------------------------------------------
#define	_A	0x10				//segment order
#define	_B	0x08
#define	_C	0x04
#define	_D	0x02
#define	_E	0x01
#define	_F	0x40
#define	_G	0x20
#define	_DP	0x80				//decimal point

#define PIN_D0	~0x04 | 0x70	//digit order
#define	PIN_D1	~0x80 | 0x70
#define	PIN_D2	~0x01 | 0x70
#define	PIN_D3	~0x02 | 0x70
#define	PIN_D4	~0x08 | 0x70

#define	_kHz	~_E
#define	_MHz	~_D

#define	_0	~( _A+_B+_C+_D+_E+_F    )	//number pattern, low active
#define	_1	~(    _B+_C             )
#define	_2	~( _A+_B+   _D+_E+   _G )
#define	_3	~( _A+_B+_C+_D+      _G )
#define	_4	~(    _B+_C+      _F+_G )
#define	_5	~( _A+   _C+_D+   _F+_G )
#define	_6	~( _A+   _C+_D+_E+_F+_G )
#define	_7	~( _A+_B+_C             )
#define	_8	~( _A+_B+_C+_D+_E+_F+_G )
#define	_9	~( _A+_B+_C+_D   +_F+_G )
#define	BLANK	~( 0                    )
#define	MINUS	~(                   _G )
//------------------------------------------------------------------------


void	valout( void );
bit		measure( void );
bit		check_range( void );

// ***********************************************************************************

bit		lf_mode;				// LF (0.5Hz ... 50kHz)
uchar	timeout;

uchar	count_m;				// T0 counter value (counts down)
uchar	count_h;				// high byte extension of count_m (counts up)

uchar	time_m;					// T1 counter (counts down)
uchar	time_h;					// high byte extension of count_m (counts up)

uchar	data display[];
uchar	digit;
uchar	data display[5];


// ***********************************************************************************
void init( void )
{
  TMOD		= T0_M1_ | T0_M0_ | T0_CT_		// TL0: 8 bit Counter, 
											//		count negative transition on T0 pin(256 prescaler counter)
											// TH0: 8 bit Timebase
				| T1_M0_ | T1_CT_;			// T1: 16 bit Counter, direct counter
											//		count negative transition on T1 pin
  TCON		= TR0_ | TR1_;					// enable counters	
  time_m	= 1;
  digit		= 11;
  IE		= EA_ | ET0_ | ET1_;
}
// ***********************************************************************************
void main( void )
{
	bit consecutive;

	init();

	for(;;)
	{					// main loop
		display[0] = BLANK;				// init display memory
		display[1] = BLANK;
		display[2] = BLANK;
		display[3] = BLANK;
		display[4] = _0;

		consecutive = 0;
		for(;;)
		{								// measuring loop
		#ifdef DEBUG
			if( lf_mode )
				display[0] &= MINUS;	// debug
		#endif
			if( check_range() )			// also wait 0.5 sec
				consecutive = 0;
			if( measure() )				// consecutive measurements
				break;
			if( consecutive )
				valout();				// display result
			consecutive = 1;
		}
	}
}
// ***********************************************************************************
bit check_range( void )
{
	bit		hf		= 0;
	uchar	i		= TL0;
	timeout = (uchar)(1 + XTAL / 12 / 65536 * TMIN);	// 0.5 sec
	uchar j;
	while( timeout )
	{
		j = TL0 - i;
		if( j > 50 )
			hf = 1;
	}
	if( hf && lf_mode || !hf && !lf_mode && j <= 5 )
	{
		lf_mode = ~lf_mode;
		return 1;
	}
	return 0;							// now in range
}
// ***********************************************************************************
void tl0_int( void ) interrupt INT_T0	// 8bit 256 prescaler counter
{
  if( --count_m == 0 )					// extend timer to 3 byte
    count_h++;
}
// ***********************************************************************************
void th0_int( void ) interrupt INT_T1	// 16bit direct counter
{
	{
		if( --digit == 0 )
		{				// multiplex display output
			P3 = PIN_D0;
			P1 = display[4];
			digit = 41;
		}
		if( --digit == 0 )
		{
			P3 = PIN_D1;
			P1 = display[3];
			digit = 41;
		}
		if( --digit == 0 )
		{
			P3 = PIN_D2;
			P1 = display[2];
			digit = 41;
		}
		if( --digit == 0 )
		{
			P3 = PIN_D3;
			P1 = display[1];
			digit = 41;
		}
		if( --digit == 0 )
		{
			P3 = PIN_D4;
			P1 = display[0];
			digit = 36;
		}
	}
	time_m--;
	if( time_m == 0 )
	{							// extend timer to 3 byte
		time_h++;
		timeout--;				// 30Hz count down timer
	}
}								// max cycles: 24
// ***********************************************************************************
float val;						// store result
// ***********************************************************************************
bit measure( void )
{
	static ulong old_count, old_time;		// save previous readings
	uchar i;
	union blw count, time;
	
	if( lf_mode )
	{
		timeout = (uchar)(1 + XTAL / 12 / 65536 * TMAX);	// 1.5 sec
		i = TL1;
		while( i == TL1 )
			if( timeout == 0 )
				return 1;
			
		EA			= 0;
		count.b.b1	= TH1;					// read LF counter
		count.b.b0	= TL1;
		if( (count.b.b0 & 0x80) == 0 )
			count.b.b1 = TH1;
	}
	else
	{
		timeout = (uchar)(1 + XTAL / 12 / 65536 * 0.2);		// 0.2 sec
		i = TL0;
		while( i == TL0 )
			if( timeout == 0 )			// after TMAX:
				return 1;				// time out
			
		EA			= 0;
		count.b.b0	= TL0;				// read HF counter
		count.b.b1	= -count_m;
		count.b.b2	= count_h;
		if( TF0 && (count.b.b0 & 0x80) == 0 )
		{	// check low byte overflow
			count.b.b1++;
			if( count.b.b1 == 0 )
				count.b.b2++;
		}
	}
	
	time.b.b0 = TH0;					// read timer
	time.b.b1 = -time_m;
	time.b.b2 = time_h;
	
	if( TF1 && (time.b.b0 & 0x80) == 0 )
	{		// check low byte overflow
		time.b.b1++;
		if( time.b.b1 == 0 )
			time.b.b2++;
	}
	EA = 1;
	
	val = (float)((count.lw - old_count) & (lf_mode ? 0xFFFFL : 0xFFFFFFL))
		/ (float)((time.lw  - old_time)  & 0xFFFFFFL)
		* (XTAL / 12);
	if( lf_mode == 0 )
		val *= PRESCALER;
	old_count	= count.lw;
	old_time	= time.lw;
	
	return 0;					// successful
}
// ***********************************************************************************
void valout( void )
{
	uchar kM, num, i, dp;
	uchar data * ptr = display;
	uchar code SEGMENTS[] = { _0, _1, _2, _3, _4, _5, _6, _7, _8, _9 };
	
	dp = 0;
	do
	{
		dp++;
		val *= 0.1;
	}while( 2 <= val );			// normalize: <= 1.9999
	
	kM = BLANK;
	if( dp > 3 )
	{							// kHz ?
		dp -= 3;
		kM = _kHz;
		if( dp > 3 )
		{						// MHz ?
			dp -= 3;
			kM = _MHz;
		}
	}
	num = val;
	if( num )
		kM &= _1;
	*ptr = kM;					// upper digit '1' and range LEDs
	
	for( i = 4; i; i-- )
	{			// 4 remaining digits
		ptr++;
		val = (val - num) * 10;	// next digit
		*ptr = SEGMENTS[(num = val)];	// get digit code
		if( --dp == 0 )			// set decimal point
			*ptr &= ~_DP;
	}
}
// ***********************************************************************************
