/*-------------------------------------------------------------------------

		Program:	Universal Measurement Device UMD.C
		
		Author:		Herbert Dingfelder, DL5NEG
		
		Function: A small handheld RF powermeter. Is able to read DC-voltages
							from three different sensors (diode, thermo and log-amp), 
							convert the voltage to a dBm value using internal lookup-
							tables. The result is displayed on a LCD.
							
							The idea at the beginning of the project was to also measure 
							other physical parameters like temperature, brightness, etc, 
							hence the name UMD for universal measurement device.
							
		Features:	- Displays dBm for three different sensors
							- Battery voltage displayed in 1/10th of Volts
							- Displayment of values relative and absolute
							- Can display raw values of the ADCs in Hex-Format
							- Can correct the frequency dependency of the log-amp sensor
							  if the user puts in the rough frequency of the signal
							- Uses two measurments ranges for the diode-sensor, the
							  range can be manually selected or autorange can be switched on
															
		Disclaimer: This software, just like the hardware description, are
							provided "as is", without any guarantee of correct functionality.
							
							I hope this provides many radio amateurs and other hobbyists with
							a means to measure RF power in homemade projects. Using my IP
							for commercial projects however, is not approved by me.
							
							For compilation I have used the free GNU C-compiler as it is
							distributed by AVRFREAKS, together with Atmels free development
							environment AVR-Studio 3.54.
							
		Hardware: An Atmel AT90LS4433, running on 3.2Volts with a 3.68 MHz crystal.
							For detailed hardware description see:
															www.dl5neg.de/powermeter/powermeter.html
							
		Version:	1.2 tested on hardware for diode sensor and logamp-sensor
							(not tested yet for the thermo-sensor)
																	
		Contact:	Go to WWW.DL5NEG.DE or send an email to herbert@dl5neg.de
									
-------------------------------------------------------------------------*/


/* Includefiles (the standart included-files are automatically added in 
   the makefile, only special includes must be listed here) */ 
#include <progmem.h> /* enables reading of data from the flash-memory */


/* Prototypes for functions */
void i2c_start(void);
void i2c_send(unsigned char sendbyte);
void init_lcd(void);
void update_lcd(void);
unsigned char asc2lcd(unsigned char asc_char);
void pause(unsigned char n);
unsigned int read_adc(unsigned char adc_number);
unsigned char hex2asc(unsigned char hexbyte);
void preset_videoram(unsigned char screen);
void smoothkey(void);
void int2lcd(int x);
void parameter2videoram(void);
void interpol(int adc);
unsigned char bit(unsigned char param);
unsigned int PRG_RDI(unsigned int *prgmem);


/* Define names for special registers and bits */

/* Number of ADC readings that are averaged before an value is returned, 
   interpredation is in 2^N, possible is 0-6 i.e. 1-64 cycles. The limitation
   to 2^6 comes from the fact that the ADC delivers 10bits, so 6 bits
   are left in an integer variable (2bytes=16bits).
   A good value is 4, 5 gives an even better averaging but the key-response 
   gets a bit slow since the averaging takes too much time. */
#define average 4

/* bit 4 is clock line for software i2c-bus */
#define i2c_clock 0x10 
/* bit 5 is clock line for software i2c-bus */
#define i2c_data  0x20 
/* i2c-bus port is Port C */
#define i2c_bus   PORTC	
/* data direction register for i2c is DDRC */
#define i2c_dir   DDRC	

// 36 bytes for the lcd videoram (+1 for \n) as global variable
unsigned char videoram[37];

unsigned char mode=1;        // mode determines the screen that is currently
				 						         // used (rawadc, diodesensor, logampsensor, etc.)
unsigned char adc_select=0;  // number of ADC which is currently used
unsigned char auto_select=1; // 1=ADC is autorange, 0=manual ADC sel.
		
unsigned char sens_freq=0;   // number of the rough sensor frequency for the log-amp sensor

// freq_ranges is the number of defined frequency ranges for the la-sensor
#define freq_ranges 3

unsigned char abs_rel=0; 		// 0=absolute power in dBm is shown
				 								 		// 1=relative power in dB is shown
int rel_value;					 		// memory for reference power for relative displaying
												 		// the value interpredation is in 1/100 dBm
											
//this data is kept in the flash, it is not copied to the data memory 
unsigned char __attribute__ ((progmem)) greeting[]=      "Universal RF Powermeter    DL5NEG   \n";
unsigned char __attribute__ ((progmem)) adc_screen[]=    "ADC-rawvalue ADC1  ADC2  $---  $--- \n";
unsigned char __attribute__ ((progmem)) diode_screen[]=  "Diode    --- :xx.xx dB- auto    ADC-\n";
unsigned char __attribute__ ((progmem)) logamp_screen[]= "LogAmp   --- :xx.xx dB- f = --00 MHz\n";
unsigned char __attribute__ ((progmem)) thermo_screen[]= "Thermo   --- :xx.xx dB- zero-adj: --\n";
unsigned char __attribute__ ((progmem)) battery_screen[]="  Battery    -.- Volts  Status: -oo-\n";
unsigned char __attribute__ ((progmem)) overld_screen[]= "  WARNING     OVERLOAD  reduce power\n";
  
/* Here start the lookup tables for the different sensors. Stated is the ADC-value for each dBm-value.
   The first hex-values represents the dBm value that is stated below the array in start_...
   Each following value is one dB more. Since the sensors have different dynamic ranges, the
   arrays have different lengths. Each string is terminated by the value FFFF hex.
   For the diodesensor the autorange-switching-limits for both tables (range1 and range2)
   are also stated. */ 
  
// look-up-table for the diode sensor adc1
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_ds1[]= {0xf, 0x11, 0x15, 0x1a, 0x1f, 0x25, 0x2c,
0x33, 0x3c, 0x46, 0x53, 0x60, 0x6e, 0x80, 0x93, 0xab, 0xc4, 0xe0, 0xfe, 
0x121, 0x14c, 0x179, 0x1ac, 0x1e9, 0x228, 0x276, 0x2c8, 0x322,
0x387, 0xFFFF};
#define start_ds1 -14
#define lut1_lowerlimit 0x33

// look-up-table for the diode sensor adc2
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_ds2[]= { 0xa, 0xc, 0xf, 0x11, 0x15, 0x1a, 0x20,
0x27, 0x30, 0x3c, 0x49, 0x5b, 0x6f, 0x88, 0xa5, 0xc8, 0xf5, 0x126, 0x160,
0x1a7, 0x1f5, 0x256, 0x2c0, 0x339, 0x3c8, 0xFFFF};
#define start_ds2 -30
#define lut2_upperlimit 0x3c8

// look-up-table for the thermo sensor 
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_th[]= { 0x27, 0x2A, 0x2D, 0x31, 0x34, 0x38, 0x41,
0x4B, 0x55, 0x5F, 0x69, 0x85, 0xA2, 0xBF, 0xEC, 0x119, 0x166, 0x1B3,
0x201, 0x294, 0x327, 0x3F2, 0xFFFF };
#define start_th 0
/* define the adc-value that is defined as zero for the thermo-head */
#define thermo_zero 0x20 

// look-up-table for the LogAmp sensor
// in the frequency range 500 MHz
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_la500[]= {161,163,166,170,173,177,182,187,192,198,204,
209,215,221,227,233,239,245,251,258,264,270,277,283,290,297,303,309,316,322,
329,334,341,347,354,360,366,372,378,385,391,397,403,409,416,422,428,434,440,
447,453,458,464,471,477,483,489,495,501,507,513,518,523,527,532,535,539,
0xFFFF};
#define start_las500 -60

// look-up-table for the LogAmp sensor
// in the frequency range 1300 MHz
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_la1300[]= {170,174,178,182,187,192,198,203,209,215,221,
226,232,238,244,249,256,261,267,272,278,284,290,295,300,306,312,317,323,328,
334,339,344,350,355,361,367,373,378,384,389,395,401,407,412,418,424,430,436,
441,447,453,459,464,470,476,482,488,494,500,505,512,517,522,527,531,535,
0xFFFF};
#define start_las1300 -60

// look-up-table for the LogAmp sensor
// in the frequency range 2300 MHz
// these adc values represent the dbm values 
// termination is marked by the value $FFFF
unsigned int __attribute__ ((progmem)) lut_la2300[]= {166,169,173,178,182,188,194,200,206,213,220,
226,233,240,247,254,262,269,276,283,290,298,305,312,319,325,333,339,346,353,
359,365,372,379,385,392,399,405,412,418,425,432,439,445,452,458,465,472,478,
485,491,497,503,509,515,521,528,535,541,548,554,560,564,568,570,572,575,
0xFFFF};
#define start_las2300 -60

//;------------------------ here we go --------------------------

void main(void)
{
  
	unsigned int adc_value;
	unsigned char keystatus;

	char z,e;
	int k;
	
	/* set clock and data as outputs on the port, rest as inputs */
	outp(0x30, i2c_dir); 
	
	/* set PB0 -  PB2 to high to activate the internal pull-ups */
	outp(0x07, PORTB); 
	
	/* load the greeting message into the video ram */
	preset_videoram(1);
		
	/* transver data from video ram to lcd display */
	update_lcd();
	
	/* wait 2 seconds to present the greeting message */	
	pause(0xFF);
	pause(0xFF);
	
	/* set the start mode, 2=rawadc, 3=diode sensor, 4=logamp sensor */
	mode=3; 
	
	/* set the screen-mask on the LCD, i.e. everything except measured data */
	preset_videoram(mode);
					
	while(1) // ----------------- main loop --------------------------
	{
							
		update_lcd();          	// transver data from video ram to lcd display

		keystatus = PINB;      	// read the status of the input keys
		
		if( !(keystatus & 1) ) 	// if key1 (mode select) is pressed
		{
		 		mode++;
				if( mode > 6 ) mode = 2;
				
				smoothkey(); 				//update the LCD with all current datas
										 				//and wait some time to avoid key repetition
		}

		if( !(keystatus & 2) ) 	//if key2 (ADC/Freq. select) is pressed
		{
		 		// in mode 3 (diodesensor) key2 is used to switch the used
				// ADC (man ACD1, man ADC2, Autoselect)
		 		if( mode==3 ) 
				{
				 		if(auto_select) adc_select=0; //preparation to have only adc_sel
																					//as criteria for the new parameters
							
						switch(adc_select)
						{
						 	case 0:  //act. is auto -> next methode is auto-off, man adc 1
							{	
								auto_select=0;
								adc_select=1;
							}break;
							
							case 1:  //act. is man adc 1 -> next methode is man adc 2
							{
							 	adc_select++;
							}break;
							
							case 2:  //act. is man adc 2 -> next methode is auto
							{
							  adc_select=0;
								auto_select=1;
							}break;
							
						}//end swithc
												
				}//end if mode=3
				
				// in mode 4 (logampsensor) key2 is used to select the operating
				// frequency to use the correct look-up table 
				// (logamp reading is somewhat frequency dependent)
				if(mode ==4 )
				{
				 
				  sens_freq++;  //select next frequency range
					
					if(sens_freq >= freq_ranges)  //if max. range is reached			
					  sens_freq=0;								//start at first one again
				 
				}//end if(mode==4)
												
				smoothkey(); //update the LCD with all current datas
										 //and wait some time to avoid key repetition
				
		}
		
		if( !(keystatus & 4) ) //if key3 (relativ/absolute) is pressed
		{
		 		if(abs_rel==0)  //toggle between abs-mode and rel-mode
					abs_rel=1;
				else
					abs_rel=0;
								
				smoothkey(); //update the LCD with all current datas
										 //and wait some time to avoid key repetition
		}
		
		// -----------------------------------------------------------------
		//  here comes the real action - reading the adc and diplaying the
		//                      the results in dBm
		
		switch(mode)
		{
		  case 2: // mode 2= raw adc values are displayed
			{
			 		 /* read ADC channel 1 */
					 adc_value = read_adc(1);
			
					 /* write the adc_value to the videoram */
					 videoram[28] = hex2asc(  adc_value     & 0x0F );
					 videoram[27] = hex2asc( (adc_value>>4) & 0x0F );
					 videoram[26] = hex2asc( (adc_value>>8) & 0x0F );

					 /* read ADC channel 2 */
					 adc_value = read_adc(2);

					 /* write the adc_value to the videoram */
					 videoram[34] = hex2asc(  adc_value     & 0x0F );
					 videoram[33] = hex2asc( (adc_value>>4) & 0x0F );
					 videoram[32] = hex2asc( (adc_value>>8) & 0x0F );
			}break;
						
			case 3: // mode 3= diode sensor values are displayed in dBm
			{
			 		 /* read ADC on selected channel */
					 adc_value = read_adc(adc_select);
					 
					 if(auto_select) // if autorange is switched on select the best sensor setting
					 {
					 	 
						 //if adc_value is to low switch to adc2 (if adc2 was already
						 //used it simply stays select and nothing can be done anyhow)
						 if(adc_value < lut1_lowerlimit)
						   adc_select=2;
						 
						 //if adc_value is to high switch to adc1 (if adc1 was already
						 //used it simply stays select and nothing can be done anyhow)
						 if(adc_value > lut2_upperlimit)
						   adc_select=1;
				 
				 		 /* now read ADC on automatically selected channel */
					   adc_value = read_adc(adc_select);
					 				 
					 }
					 						
					 /* read the corresponding values (one above, one below) for the
					    ADC-result, then interpolate to calculate the exact dBm value
					    (including fractions of dBs) and write that to the LCD */				
					 interpol(adc_value);
			
			}break;

			case 4: // mode 4= log amp sensor values are displayed in dBm
			{
				 	 /* read ADC channel 1 (log-amp-sensor used only adc1) */
					 adc_value = read_adc(1);
			
			     /* interpolate to calculate the dBm value and write that to LCD */				
					 interpol(adc_value);
			
			}break;


			case 5: // mode 5= thermo sensor values are displayed in dBm
			{
			 		 /* read ADC channel 1 (thermo-sensor uses only adc1) */
					 adc_value = read_adc(1);
			
			     /* interpolate to calculate the dBm value and write that to LCD */				
					 interpol(adc_value);
						
					 /* refresh the zero adjust indicatior */
					 /* a adc-value > $00 is definded as zero for the thermo-head */
					 /* to have some margin for lower values */
					 
					 if(adc_value == thermo_zero) //if the read adc-value is exactly
					 {							 							//the defined zero-value				 			 
					 videoram[34] = 'o';  
					 videoram[35] = 'k';					//write 'ok' to the videoram
					 }
					 else
					 {
					 	 
  			 		 if(adc_value > thermo_zero) //if the value is higher than the
						 {													 //defined zero-value
						 	 videoram[34] = '-';
							 videoram[35] = '>';			 //write '->' to indicate its bigger
						 }
						 else												 //if it is smaller than the
						 {													 //defined zero-value
						 	 videoram[34] = '<';
					     videoram[35] = '-';			 //write '<-' to indicate its less
						 }	 
					 }		
							 
					 			
			}break;
			
			case 6:  // mode 6= Battery voltage in Volts is displayed
			{
			
			/* read ADC channel 3 */
			adc_value = read_adc(3);
						
			if( adc_value > 560 ) // if Vbatt >= 3.5 Volts
			{
			 	videoram[35]='d';		      // write 'good'
		    videoram[32]='g';					// the double 'oo' is always in the LCD
			}
			else
			{
			 	videoram[35]='r';		      // write 'poor'
		    videoram[32]='p';					// the double 'oo' is always in the LCD
			}

			//the battery voltage is divided by 2 by a resistive divider before
			//it enters ADC3; Vref=3.2Volts -> ADC3=$3FF means Vbatt=6.4 Volts
			// -> ADC3/16= Vbatt in 1/10 Volts		 
			adc_value >>= 4; // 4 times shift right to perform the /16	
						
			k = 0;		 			 // find the tenth-number
		  
			for(z=0; k<=adc_value ; z++) //go in loop until the tenth-number
			  k += 10;									 //is found
			
		  z--;	 								
		  adc_value = adc_value-k+10;	// substract the already found digits
			
		  e=(char)adc_value;					// one's number remains
		 	 
		  videoram[13]=z+48;		      // put the result to the
		  videoram[15]=e+48;					// videomemory (converted to ASCII with +48)
			
			}break;
			
		} // end of switch block
		
	} //--------------------- end of main loop --------------------
		
}

/*
;-------------------- subroutines from here on ------------------
;================================================================
*/


/*;------------------------ i2c start command ---------------------*/
void i2c_start()
{
 	outp( i2c_data + i2c_clock, i2c_bus ); /* data and clock to high */
	outp( i2c_clock,            i2c_bus);  /* take data low first */				
	outp( 0,                    i2c_bus);	 /* then take clock low */				
}

/*;--------------------- send byte via i2c bus ---------------------*/
/* parameter: sendbyte contains the byte to be send */
void i2c_send(unsigned char sendbyte)
{
 	unsigned char dummy;

	for(dummy=0; dummy<8; dummy++)		 		// send all 8 bits 
	{
	
		if( sendbyte & 0x80 )        				// if msb of sendbyte is high
				outp(i2c_data, i2c_bus); 				// data to high, clock remains low 

		outp(inp(i2c_bus) + i2c_clock, i2c_bus); // send one pulse on the clock-line
		outp(inp(i2c_bus) - i2c_clock, i2c_bus);

		outp(0, i2c_bus); 									// clear data_line
	
		sendbyte = sendbyte << 1;				 		// logic shift left to shift the next
						 							 					 		// bit to the msb position
	}			
	
		// send one additional pulse on the clock-line for i2c-acknowledge 
		outp(inp(i2c_bus) + i2c_clock, i2c_bus); // send one pulse on the clock-line
		outp(inp(i2c_bus) - i2c_clock, i2c_bus);

}

/*;------------------- initialize lcd display ---------------------*/
void init_lcd()
{

// I2C address of the display
#define adr   0x74      // 01110100	
// Ctrl-Byte -> Command follows
#define cntr  0x80      // 10000000	
// Command: Display on
#define cmd1  0x0C      // 00001100	
// set cursor to 1st position in display
#define cmd2  0xA0      // 10100000 
// Ctrl-Byte -> normal characters for displaying will follow
#define cntr2 0x40      // 01000000 
	
	// send an start-command on i2c-bus
	i2c_start();		
	// address the display on i2c bus
	i2c_send(adr);
	// Ctrl-Byte -> Command follows
	i2c_send(cntr);
	// Command: Display on
	i2c_send(cmd1);
	// Ctrl-Byte -> Command follows
	i2c_send(cntr);
	// set cursor to 1st position in display
	i2c_send(cmd2);
	// normal data follows
	i2c_send(cntr2);
		
}

//---------------------------- update lcd ---------------------------------
// transfers the content of the videoram to the LCD
void update_lcd()
{
	unsigned char char_cnt;
	
	init_lcd();		 			// switch on lcd, clear screen and set cursor home

	// write to all 3 lines, all 12 coloums
	for(char_cnt=0; char_cnt < 3*12; char_cnt++)
	{
		i2c_send( asc2lcd(videoram[char_cnt]) ); // write the char. to LCD display,
																						 // before convert ASCII -> LCD-Code
  }
}

//------------------------------- read adc -------------------------------
// reads the selected ADC (does some averaging to stabilize the value) 
unsigned int read_adc(unsigned char adc_number)
{
  unsigned char adlow, adhigh, counter;
	unsigned int adsum; 
	
	// status for ADC: enable adc, start 1st conversion, free run,
	// clock prescaled with factor 64 ->57kHz clock with fq=3.686MHz
	outp( 0xE6, ADCSR); 
		
	// select ADC, i.e. switch the MUX to the selected ADC
	outp( adc_number, ADMUX); 

	// reset the averaging sum
	adsum = 0;
	
	for(counter=0; counter<bit(average); counter++ )
	{

	  // wait N times 4.4ms in subroutine pause (here N=1)
		// for AD-conversion to be performed
		pause(1);
	
		// read adc-value (lower byte must be read first !!!)
		adlow = ADCL;
		adhigh = ADCH;

		// add the read value to the averaging sum	
		adsum += (adhigh<<8) + adlow;
		
	}
	
	// divide the averaging sum by the number of values
	adsum >>= average;
	
	return adsum;
	
}
		
		
//----------------------------- pause --------------------------------------
// waits for n times 4.4 ms
void pause(unsigned char n)
{

 	// set counter (low and higbyte) back to zero
 	// highbyte must be written first, then the lowbyte must be written
 	// CPU latches the values and takes over both byte simulataniously
	outp( 0, TCNT1H ); 
	outp( 0, TCNT1L ); 
	
  // reset the compare-match flag => write a one to ocf1 in tifr
	outp( inp(TIFR) | bit(OCF1) , TIFR ); 

	// 16bit counter on, prescaler factor 64
	// => 1 bit in highbyte = 4.44ms at fq = 3.686 MHz
	outp( 0x03, TCCR1B ); 
	
	// set the compare-highbyte according to the parameter n
	// compare-lowbyte is fixed set to zero here
	// if higher resolution is necessary the LB can also be parametered
	outp( n, OCR1H ); 
	outp( 0, OCR1L ); 
		
  // wait in loop until compare match in timer1 
	// (i.e. wait for OCF1 Bit is set in TIFR) 
	while( !( inp(TIFR) & bit(OCF1) ) );
		
}

//--------------------------- hex to asc ---------------------------------
// converts a hex value (00..0F) into the ASCII-character '0'..'F'
unsigned char hex2asc(unsigned char hexbyte)
{
	
	hexbyte += 48;			// ASCII code for 0-9 is 48-57
	
	if(hexbyte>=58)			// ASCII code for A-F is 65-70 -> in that case add 7
		hexbyte += 7;
	
	return hexbyte;
}

//--------------------------- ascii to lcd ------------------------------
// converts a ascii character into the lcd-character
// works for A..Z, a..z, 0..9, space and spec. char /()!%=
// other characters must be handeled with if-conditions
unsigned char asc2lcd(unsigned char asc_char)
{
	
	if(asc_char == '$') /* LCD code for $ is 130 (=2+128) */
		asc_char = 2;
	
	asc_char += 128;		/* all normal characters have ASC+128 as LCD cod */
			
	return asc_char;
}		 
	
	
//--------------------------- preset videoram ---------------------------

// presets the videoram with the different mask
void preset_videoram(unsigned char screen)
{

 unsigned char counter;

 //copy all 36 characters from the selected string to the videoram
 for(counter=0; counter<36; counter++ )
 {
 				
		switch(screen)
		{
		
		  case 1:
		  	videoram[counter]=greeting[counter];
				break;
				
			case 2:
				videoram[counter]=adc_screen[counter];
				break;
				
			case 3:
				videoram[counter]=diode_screen[counter];
				break;
				
			case 4:
				videoram[counter]=logamp_screen[counter];
				break;
			
			case 5:
				videoram[counter]=thermo_screen[counter];
				break;
			
			case 6:
				videoram[counter]=battery_screen[counter];
				break;
				
			case 7:
				videoram[counter]=overld_screen[counter];
				break;
		
		} //end switch(screen)
		
	}

}		 

// ---------------------------------------------------------------	

// typical action after key-pushings have been worked out
// fill the screen, refresh it and avoid contact jitter from the keys
void smoothkey(void)
{
	
	preset_videoram(mode);		//copy the permanent characters of the current
														//screen to the videoram

	update_lcd();							//copy the videoram to the physical LCD display

	pause(100);								//short delay
	
}


// ---------------------------------------------------------------	
	
// int2lcd, displays a integer number on the LCD 
// x is -9999..+9999, interpredation is -99.99dBm to +99.99dBm
// the separation to BCD reprensentation can be also be calcualated 
// mathematically but the methode used below is very memory efficient
// and still very fast (max. 40 for()-cycles)
// if the relative display mode is selected the difference between
// the reference value and the measured value is calculated first
void int2lcd(int x)
{
		char t,h,z,e;
		int k;
		
		if(abs_rel==1)    //if the relative mode is selected
		{
		  x=x-rel_value;  //calc. difference to reference value
		}	
		else							//if absolute values are currently shown
		{
		  rel_value=x;		//remember the recent value, if the user
											//switches to relative mode in the next cycle
											//this value will be used as reference
		}
								
		// write + as sign into videoram 
		videoram[13]='+';
		
		if( x<0 ) // if x is negativ
		{
		videoram[13]='-';	 // overwrite the + sign with a - sign
		x = -x;		 				 // absolute value of x for further displaying
		}
		
		 
		 k = 0;						 		// find the thausand-number
		for(t=0; k<=x ; t++)
			k += 1000;

		t--;	 							
		x=x-k+1000;						// substract the already found digits

		k = 0;		 						// find the hundred-number
		for(h=0; k<=x ; h++)
			k += 100;

		h--;	 		 						
		x=x-k+100;						// substract the already found digits

		k = 0;		 						// find the tenth-number
		for(z=0; k<=x ; z++)
			k += 10;
			
		z--;	 								
		x=x-k+10;							// substract the already found digits
				k = 0;

		e=(char)x;						// one's number remains
		 	 
		videoram[14]=t+48;		// conversion of all digits from a BCD-number
		videoram[15]=h+48;		// to a ASCII character and writing to 
		videoram[17]=z+48;		// videomemory directly
		videoram[18]=e+48;
		
}


// ---------------------------------------------------------------	


void interpol(int adc)
{

		int d,k,x;
		unsigned char counter;
		int lut_c0, lut_cm1;
		int startvalue;

		preset_videoram(mode); // write screen new to overwrite old overload
													 // or low signal messages

		parameter2videoram();	 // screen if totally overwritten, parameters must
													 // must be written again to the bottom line of LCD
		
		// depending on the currently selected look-up-table the dBm values
		// directly above and below the measured value are searched
		
		if(mode == 3) //if diode-sensor is selected -> use the diode sensor
		{					 		//look-up-tables values according to the selected adc
									
  		if(adc_select==1)
	  	{								 
		  	for( counter=0 ; PRG_RDI(&lut_ds1[counter]) <= adc ; counter++ );
			  startvalue=start_ds1;
  			lut_c0  = PRG_RDI(&lut_ds1[counter]);
	  		lut_cm1 =	PRG_RDI(&lut_ds1[counter-1]);	
		  }

		  if(adc_select==2)
		  {								 
			  for( counter=0 ; PRG_RDI(&lut_ds2[counter]) <= adc ; counter++ );
			  startvalue=start_ds2;
			  lut_c0  = PRG_RDI(&lut_ds2[counter]);
			  lut_cm1 =	PRG_RDI(&lut_ds2[counter-1]);	
		  }

		}
		
				
		if(mode == 4) //if log-amp-sensor is selected -> use the log-amp-senor
    {					 		//look-up-table values

		  if( sens_freq == 0 ) //frequency range is around 500 MHz
			{
		  	for( counter=0 ; PRG_RDI(&lut_la500[counter]) <= adc ; counter++ );
			  startvalue = start_las500;
  			lut_c0  = PRG_RDI(&lut_la500[counter]);
	  		lut_cm1 =	PRG_RDI(&lut_la500[counter-1]);	
			}
					
		  if( sens_freq == 1 ) //frequency range is around 1300 MHz
			{
		  	for( counter=0 ; PRG_RDI(&lut_la1300[counter]) <= adc ; counter++ );
			  startvalue = start_las1300;
  			lut_c0  = PRG_RDI(&lut_la1300[counter]);
	  		lut_cm1 =	PRG_RDI(&lut_la1300[counter-1]);	
			}
		
		  if( sens_freq == 2 ) //frequency range is around 2300 MHz
			{
		  	for( counter=0 ; PRG_RDI(&lut_la2300[counter]) <= adc ; counter++ );
			  startvalue = start_las2300;
  			lut_c0  = PRG_RDI(&lut_la2300[counter]);
	  		lut_cm1 =	PRG_RDI(&lut_la2300[counter-1]);	
			}
		
		}
				
		if(mode == 5) //if thermo-sensor is selected -> use the thermo-senor
    {					 		//look-up-table values
		  	for( counter=0 ; PRG_RDI(&lut_th[counter]) <= adc ; counter++ );
			  startvalue=start_th;
  			lut_c0  = PRG_RDI(&lut_th[counter]);
	  		lut_cm1 =	PRG_RDI(&lut_th[counter-1]);	
		}
		
		// values are fetched out of the valid look-up-table, now the
		// processing of the data can begin independent of the mode adc etc.		
							
		if( counter == 0 )			// if adc_value is lower as lowest table value
		{
				videoram[12]='<';		// put '<' before number to inidicate low signal
		 		int2lcd(100*startvalue); // write the lowest table value meaning to scr.
				return;
		}
			
		if( lut_c0 == 0xFFFF )// if adc_value is higher as highest table v.
		{
		 		preset_videoram(7); // write overload message to screen
				return;
		}
			
			// calculate the difference between prev. and next known value
			d = lut_c0 - lut_cm1;
			
			// calculate the difference between adc-value and prev. known value
			k = adc - lut_cm1;
			
			// multiply with 100 since the result shall be in 1/100 of a dBm
			k = k *100;
						
			// do the interpolation and add the meaning of the prev. value
			x = k / d + (startvalue + counter-1)*100; 

			int2lcd(x);		 // output the value dezimal on the lcd display
										 // paramters unit is 1/100 of dBm
						
}


// ---------------------------------------------------------------	


// put the current parameters to the videoram
void parameter2videoram()
{
 		 	if( abs_rel == 0)
			{
				videoram[9] =  'a';  //write 'abs' to the videoram to
				videoram[10] = 'b';	 //indicate the absolute-mode
				videoram[11] = 's';
				videoram[22] = 'm';  //m for dBm as absolute unit
			}
			else
			{
				videoram[9] =  'r';	 //write 'rel' to the videoram to
				videoram[10] = 'e';	 //indicate the relative-mode
				videoram[11] = 'l';
				videoram[22] = ' ';  //clear m to have dB as relative unit
			}

	 		if( mode==3 ) //if the diode sensor is selected 
				{
						
					if( auto_select ) // if auto-select is switched on write "auto"
					{
					 videoram[24] = 'a';
					 videoram[25] = 'u';
					 videoram[26] = 't';
					 videoram[27] = 'o';
					 }
					else             // otherwise write "man "
					{
					 videoram[24] = 'm';
					 videoram[25] = 'a';
					 videoram[26] = 'n';
					 videoram[27] = ' ';
					}	 		

					// write the number of the current ADC to the videoram (in ASCII)					
				 videoram[35] = adc_select +48;
										
				}
				
				// in mode 4 (logampsensor) key2 is used to select the operating
				// frequency to use the correct look-up table 
				// (logamp reading is somewhat frequency dependent)
				if(mode == 4)
				{
				  		
					switch(sens_freq) //which sensor frequency is currently chosen?
					{
		  
					  case 0: // sens_freq=0, frequencies around 500 MHz
						{
					    videoram[28] = ' ';
					 		videoram[29] = '5';
						}break;
						
					  case 1: // sens_freq=1, frequencies around 1300 MHz
						{
					    videoram[28] = '1';
					 		videoram[29] = '3';
						}break;
						
					  case 2: // sens_freq=2, frequencies around 2300 MHz
						{
					    videoram[28] = '2';
					 		videoram[29] = '3';
						}break;
									
					
					} //end switch(sens_freq)

				} //end if(mode == 4)

}


// ---------------------------------------------------------------	


// calculates the value of a bit (e.g. bit(5) -> 16, i.e. 2^x)
unsigned char bit( unsigned char param )
{

	return ( 1 << param );

}


// ---------------------------------------------------------------	

// reads an integer (2 bytes) from the flash (program memory)
// parameter is the address of the data, i.e. a call of
// this function is like: a=PRG_RDI(&table[i]); with table[]
// being an array of integers

unsigned int PRG_RDI( unsigned int *prgmem )
{
		
  return (PRG_RDB(((unsigned char*)prgmem)+1)<<8) + PRG_RDB(prgmem);
      
}


