Well you are still using the averaging code?
Average over a much longer operiod of time, like average 32 samples over 1 whole second.
If the "slosh" frequency is very slow (ie large container) then average over a longer time still.
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h"
#include <stdio.h>
#include <stdlib.h> //
void userMenu(char pos); //
void FloatToStr(float , char[]);
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char);
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);
unsigned char user_input(void);
void home_screen(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);
unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;
#define LCD_RS RC0 //LCD RS pin
#define LCD_EN RC1 //LCD EN pin
#define LCD_STROBE() LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0
unsigned char cm10; //
unsigned char cm; //
unsigned int math,[B][COLOR="Red"]adc_temp[31][/COLOR][/B],adc_actual; // used for voltage calculations
unsigned char NumDec;
unsigned char NumSep[2];
unsigned char i,j,k,x;
unsigned char height=50;
unsigned char range;
unsigned char area;
unsigned char SensorPos=10;
// New Vars
char input_sw;
char mnuPOS;
unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres; // for bresenham 2-second timer system
unsigned char ;
#define HOME_SW RC2 //HOME switch
#define INCREASE_SW RC3 //INCREASE switch
#define DECREASE_SW RC4 //DECREASE switch
#define ENTERSETTINGS_SW RA4 //ENTERSETTINGS switch
/////////////////////////////////////////CONVERT FLOAT TO STRING///////////////////////////////
// This function was taken from the CAVR library. It was modified slightly to suit the design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;};
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
{
scale=floor(0.5+scale/10.0);
d=(unsigned char) (n/scale);
str[f]=d+'0';
n=n-scale*d;
f++;
};
str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
{
n=n*10.0;
d=(unsigned char) n;
str[f]=d+'0';
n=n-d;
f++;
};
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////
/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if XTAL_FREQ <= 2MHZ
do {
DelayUs(996);
} while(--cnt);
#endif
#if XTAL_FREQ > 2MHZ
unsigned char p;
do {
p = 4;
do {
DelayUs(250);
} while(--p);
} while(--cnt);
#endif
}
void DelayS(unsigned char cnt)
{
for (j=0; j<(cnt*10); j++)
DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////
/////////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
LCD_RS = 0; //write instruction
PORTB = (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
}
/* send data to the LCD */
void lcd_data(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
PORTB = 0x00;
LCD_RS = 1; //write data
PORTB |= (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB &= 0x00; //load upper nibble on LCD data lines
PORTB |= ( (c << 4) & 0xF0);
LCD_STROBE(); //send instruction to LCD
}
/*Clear the LCD*/
void lcd_clear(void)
{
lcd_cmd(0x01); //command to clear LCD
}
/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
j = -1;
while(s[++j]!=('\0')) // send characters until null character reached
lcd_data(s[j]);
}
/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
lcd_cmd(0b10000000); // command to go to line 1
}
/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
lcd_cmd(0b11000000); // command to go to line 2
}
/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
lcd_cmd(((x)&0x7F)|0x80);
}
/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
LCD_RS = 0;
LCD_EN = 0;
DelayMs(20); //wait for LCD startup
lcd_cmd(0x02);
lcd_cmd(0x28); // 4-bit mode
lcd_cmd(0x08); // display off
lcd_cmd(0x01); // clear display
lcd_cmd(0x0C); // disp. on, cursor off, cursor blink off
lcd_cmd(0x06); // entry mode
lcd_cmd(0x80); // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
///////////INITIALISING PORTS, REGISTERS, CLOCK/////////////////
void init(void)
{
// setup the PIC 16f690
OSCCON = 0x72; // internal osc, 8MHz
PORTA = 0;
TRISA = 0b10010010; // RA7 high imp, RA3 is serial out, RA4 button input
PORTB = 0; // PORTB not used
WPUB = 1; // PORTB pullups ON
RABPU = 0;
/* Init ADC */
ADCON0 = 0b10000101; // bit 7 right justify,analogue channel select bits bits5-2 0001=AN1,ADC ON, RA1 is ADC input
ADCON1 = 0b00100000; //bits6-4 fosc/32
ADON=1; // turn on the A2D conversion module
ANSEL=0x02; //set RA1 as analog input for GP2 sensor
ANSELH=0x00;
T1CON = 0b00010001; // TMR1 is ON, 1:2 prescale, =1MHz
T2CON = 0b00000101; // TMR2 is ON, 1:4 prescale, =1MHz
MyVal = 0; //initializn these variables here
MyMinVal = 0;
MyMaxVal = 99;
TRISB=0x00;
TRISC=0xFC;
lcd_init(); //call LCD initialisation
}
/////////////////////////End INITIALISING PORTS, REGISTERS, CLOCK///////////////////////
////////////////////////////////WaitForInput///////////////////////////////////////////
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and
//waiting for user to exit.
//This function determines which button the user pressed. It assigns the variable "temp" a number
//which corresponds to a certain button press. Example if "temp" equals:
//1---->ENTER/SETTINGS button was pressed
//2---->HOME button was pressed
//3---->INCREASE button was pressed
//4---->DECREASE button was pressed
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
if(!ENTERSETTINGS_SW){
while(ENTERSETTINGS_SW);
temp = 1;
done = 0xff;
}
if(!HOME_SW){
while(HOME_SW);
temp = 2;
done = 0xff;
}
if(!INCREASE_SW){
while(INCREASE_SW);
temp = 3;
done = 0xff;
}
if(!DECREASE_SW){
while(DECREASE_SW);
temp = 4;
done = 0xff;
}
if(expire == 1) break;
}//end of while
DelayMs(150); //debounce
return temp;
}//
//////////////////End WaitForInput///////////////////
//////////////////////////////////////////////////////userMenu///////////////////////////////////////////////////////
//This is the User Menu. Once the "ENTER/SETTINGS" button is pressed, the user enters this menu.
// This menu promps the user to enter the HEIGHT(ie. The distance between the IR sensor & the base of the container)
//
void userMenu(char pos){
unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY
lcd_clear();
lcd_goto_L1();
switch(pos){
case 0:
lcd_puts(" HEIGHT ");
break;
case 1:
lcd_puts(" RANGE ");
break;
case 2:
lcd_puts(" SURFACE AREA ");
break; //
case 3:
lcd_puts(" MEASURED ");
while(WaitForInput(1) != 2){ //Wait for user to press ENTER button to leave loop
// wait for 2 seconds, uses TMR1 free running at 1Mhz
while(!TMR1IF) // wait for TMR1 overflow
TMR1IF = 0; // clear overflow flag
bres += 65536; // add 65536uS to bres value
if(bres >= delaytime ) // if reached 2 seconds!
{
bres -= delaytime ; // subtract 2 seconds, keep error
// read the ADC voltage RA1 (Sharp GP2 sensor)
GODONE=1; // initiate conversion on the channel 0
while(GODONE) continue; // Wait convertion done
adc_temp[x] = (ADRESH << 8) | ADRESL;
x++;
x = (x & 0x03); //(or bcf x,2 or x.F2=0 on a better compiler)
//Now average the readings
[B][COLOR="Red"] adc_actual = (adc_temp[0] + adc_temp[1] + adc_temp[2]+ adc_temp[3]+ adc_temp[4]+ adc_temp[5]+ adc_temp[6]+ adc_temp[7]+ adc_temp[8]+ adc_temp[9]+adc_temp[10]+ adc_temp[11]+adc_temp[12]+ adc_temp[13]+ adc_temp[14]+adc_temp[15]+adc_temp[16]+adc_temp[17]+ adc_temp[18]+ adc_temp[19]+ adc_temp[20]+adc_temp[21]+ adc_temp[22]+ adc_temp[23]+ adc_temp[24]+ adc_temp[25]+ adc_temp[26]+ adc_temp[27]+ adc_temp[28]+ adc_temp[29]+ adc_temp[30]+ adc_temp[31] +2) / 32;[/COLOR][/B]
calc_distance(); // convert ADC value to distance
lcd_goto_L2(); //Only change line 2
//
if(posneg == 'p')
lcd_data('+');
else
lcd_data('-');
//
lcd_data(LLHigh);
lcd_data(LLLow);
lcd_puts(" [cm] "); //comment this out if you want
}
}
lcd_goto_L1();
lcd_puts(" Loading Home ");
lcd_goto_L2();
lcd_puts(" ");
DelayS(1);
break;
}
if(pos == 3) return;
lcd_goto_L2();
lcd_puts("Press Up/Down"); //home screen message (line 2)
}
//////////////////////End userMenu///////////////////////////
/////////////////////EnterScreen/////////////////////////////
void EnterScreen(void){
lcd_clear();
lcd_goto_L1();
lcd_puts(" [cm] ");
}
//////////////////End EnterScreen/////////////////////////////
////////////////////////ShowDigits////////////////////////////
void ShowDigits(unsigned char val){
MyValLCD[0] = val /10; //returns the quotient (if temp = 35 the result is 3)
MyValLCD[1] = val % 10; //Returns remainder (if temp = 35 the result is 5)
MyValLCD[0] += 0x30; //to ASCII
MyValLCD[1] += 0x30; //to ASCII
EnterScreen();
lcd_goto_L2();
lcd_data(MyValLCD[0]); //to LCD
lcd_data(MyValLCD[1]); //to LCD
}
////////////////////End ShowDigits////////////////////////////
///////////////////////calc_distance//////////////////////////
void calc_distance(void)
{
unsigned int tmp;
unsigned int mathKeep; // used for voltage calculations backup
// from the transeiver datasheet the analog voltage is
// the inverse of distance, so distance can be calculated
// d = (1 / volts) then just scaled to suit the transeiver
// load ADC value in 16bit math var
math = (adc_actual >> 8) & 0x00FF;
math = (math * 256);
math += adc_actual & 0x00FF;
// now invert it; (1 / volts) use (6050 / volts) for scaling
math = (6050 / math);
if(math >= 2) math -=2; // fix linear error (-2)
if(math > 99) math = 99; // max limit at 99cm
//Create a copy of math for more use
mathKeep = math;
// convert from 0-99 to 2 decimal digits, 0-99cm
cm10=0;
while(math >= 10)
{
cm10++;
math -= 10;
}
cm = math;
math = mathKeep; //Now our original data is back and can be used.
//This will check if negative
LiquidLevel=height-math;
//
posneg = 'p';
if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
LiquidLevel = -LiquidLevel ;
posneg = 'n';
}
//
//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
//LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
LLHigh = ( LiquidLevel / 10 ) + '0'; //
LLLow = ( LiquidLevel % 10 ) + '0'; //
}
////////////////////End calc_distance//////////////////////////
///////////////////////user_input//////////////////////////////
unsigned char user_input(void)
{
char done = 0;
MyVal = 0; //Start on 0
while(done == 0){
input_sw = WaitForInput(0);
switch(input_sw){
case 1:
done = 0xff; //This tells us the user finished ENTERING by displaying "OK"
lcd_goto_L1();
lcd_puts(" OK ");
break;
//The User will be prompted to enter the "HEIGHT" in userMenu(). The LCD will Display:
// HEIGHT
// Press Up/Down
//Up------->represented by INCREASE Button
//Down----->represented by DECREASE Button
// The user can enter "HEIGHT" values from [0-99]cm because
// MyMinVal = 0;
// MyMaxVal = 99;
///Now the variable MyVal is used in user_input() to represent this "HEIGHT" value.
//If User presses the INCREASE button:
case 3:
if(MyVal < MyMaxVal) //checks if MyVal<99
MyVal++; //Increases MyVal when INCREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
//If the User presses the DECREASE button:
case 4:
if(MyVal > MyMinVal) //checks if MyVal>0
MyVal--; //Decreases MyVal when DECREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
default:
break;
}
}
DelayMs(250); //switch debouncing
DelayMs(250); //switch debouncing
return MyVal;//This will return MyVal which represents the "HEIGHT". The height var will be assigned myVal in home_screen()
//height = user_input() in home_screen. This will be used in calc_distance() to calculate the Liquid Leval.
}
/////////////////////////End user_input//////////////////////////////
///////////////////////////home_screen///////////////////////////////
void home_screen(void){
mnuPOS = 0;
lcd_clear();
lcd_goto_L1();
lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
lcd_goto_L2();
lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
input_sw = 0; //Reset the value of the button the user presses
while(input_sw != 1) //Wait until ENTER button is pressed
input_sw = WaitForInput(0);
userMenu(0);
DelayMs(2); //shorter delay
height = user_input(); //The HEIGHT var will have the myVal
/* userMenu(1);
DelayMs(2); //shorter delay
range = user_input(); //The HEIGHT var will have the myVal
userMenu(2);
DelayMs(2); //shorter delay
area = user_input(); //The HEIGHT var will have the myVal
*/
userMenu(3);
DelayMs(2); //shorter delay
input_sw = 0; //Reset the value of the button the user presses
}
///////////////////////////End home_screen///////////////////////////////
//////////////////////main//////////////////////
void main(void)
{
init(); // initialise I/O ports, LCD
while(1){
home_screen();
}
}
//////////////////End main//////////////////////
Clean: Deleting intermediary and output files.
Clean: Deleted file "C:\cFinalDesign\cAV.p1".
Clean: Deleted file "C:\cFinalDesign\cAV.cof".
Clean: Deleted file "C:\cFinalDesign\cAV.hex".
Clean: Deleted file "C:\cFinalDesign\cAV.sym".
Clean: Deleted file "C:\cFinalDesign\cAV.map".
Clean: Deleted file "C:\cFinalDesign\cAV.hxl".
Clean: Deleted file "C:\cFinalDesign\startup.lst".
Clean: Deleted file "C:\cFinalDesign\startup.rlf".
Clean Warning: File "C:\cFinalDesign\doprnt.p1" doesn't exist.
Clean Warning: File "C:\cFinalDesign\doprnt.pre" doesn't exist.
Clean: Deleted file "C:\cFinalDesign\cAV.obj".
Clean: Deleted file "C:\cFinalDesign\cAV.lst".
Clean: Deleted file "C:\cFinalDesign\cAV.rlf".
Clean: Deleted file "C:\cFinalDesign\cAV.sdb".
Clean: Done.
Build C:\cFinalDesign\cAV for device 16F690
Using driver C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe
Executing: "C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe" --pass1 C:\cFinalDesign\cAV.c -q --chip=16F690 -P --runtime=default --opt=default -D__DEBUG=1 -g --asmlist "--errformat=Error [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s"
Warning [360] C:\cFinalDesign\cAV.c; 343.446 array index out of bounds
Executing: "C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe" -ocAV.cof -mcAV.map --summary=default --output=default cAV.p1 --chip=16F690 -P --runtime=default --opt=default -D__DEBUG=1 -g --asmlist "--errformat=Error [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s"
HI-TECH C PRO for the PIC10/12/16 MCU family (Lite) V9.65
Copyright (C) 1984-2009 HI-TECH SOFTWARE
(1273) Omniscient Code Generation not available in Lite mode (warning)
Warning [765] C:\cFinalDesign\cAV.c; 440. degenerate unsigned comparison
Error [1253] double.c; 55. could not find space (81 bytes) for auto/param block
Error [1253] double.c; 55. could not find space (81 bytes) for auto/param block
Error [1253] C:\Program Files\HI-TECH Software\PICC\PRO\9.65\sources\awmod.c; 30. could not find space (82 bytes) for auto/param block
Error [1253] C:\Program Files\HI-TECH Software\PICC\PRO\9.65\sources\awmod.c; 30. could not find space (82 bytes) for auto/param block
Error [1253] C:\Program Files\HI-TECH Software\PICC\PRO\9.65\sources\lwdiv.c; 26. could not find space (82 bytes) for auto/param block
Error [1253] C:\Program Files\HI-TECH Software\PICC\PRO\9.65\sources\lwdiv.c; 26. could not find space (82 bytes) for auto/param block
********** Build failed! **********
adc_actual = (adc_temp[0] + adc_temp[1] + adc_temp[2]+ adc_temp[3]+ adc_temp[4]+ adc_temp[5]+ adc_temp[6]+ adc_temp[7]+ adc_temp[8]+ adc_temp[9]+adc_temp[10]+ adc_temp[11]+adc_temp[12]+ adc_temp[13]+ adc_temp[14]+adc_temp[15]+adc_temp[16]+adc_temp[17]+ adc_temp[18]+ adc_temp[19]+ adc_temp[20]+adc_temp[21]+ adc_temp[22]+ adc_temp[23]+ adc_temp[24]+ adc_temp[25]+ adc_temp[26]+ adc_temp[27]+ adc_temp[28]+ adc_temp[29]+ adc_temp[30]+ adc_temp[31] +2) / 32;
x = (x & 0x1f); //wrap x so x = 0 to 31
adc_actual=0; //initialise total
for(i=0;i<32;i++){ //loop 32 times
adc_actual+=adc_temp[i];//add all the values
}
adc_actual/=32; //and divide by 32
(..............adc_temp[30]+ adc_temp[31] +2) / 32;Also note the +2 (ie +4/2) in the averaging calc to remove your averaging rounding down error.
x = (x & 0x1f); //wrap x so x = 0 to 31
adc_actual=[COLOR="Red"]16[/COLOR]; //initialise total
for(i=0;i<32;i++){ //loop 32 times
adc_actual+=adc_temp[i];//add all the values
}
adc_actual/=32; //and divide by 32
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h"
#include <stdio.h>
#include <stdlib.h> //
void userMenu(char pos); //
void FloatToStr(float , char[]);
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char);
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);
unsigned char user_input(void);
void home_screen(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);
unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;
#define LCD_RS RC0 //LCD RS pin
#define LCD_EN RC1 //LCD EN pin
#define LCD_STROBE() LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0
unsigned char cm10; //
unsigned char cm; //
unsigned int math,adc_temp[5],adc_actual; // used for voltage calculations
unsigned char NumDec;
unsigned char NumSep[2];
unsigned char i,j,k,x;
unsigned char height=50;
unsigned char range;
unsigned char area;
unsigned char SensorPos=10;
// New Vars
char input_sw;
char mnuPOS;
unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres; // for bresenham 2-second timer system
unsigned char ;
#define HOME_SW RC2 //HOME switch
#define INCREASE_SW RC3 //INCREASE switch
#define DECREASE_SW RC4 //DECREASE switch
#define ENTERSETTINGS_SW RA4 //ENTERSETTINGS switch
/////////////////////////////////////////CONVERT FLOAT TO STRING///////////////////////////////
// This function was taken from the CAVR library. It was modified slightly to suit the design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;};
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
{
scale=floor(0.5+scale/10.0);
d=(unsigned char) (n/scale);
str[f]=d+'0';
n=n-scale*d;
f++;
};
str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
{
n=n*10.0;
d=(unsigned char) n;
str[f]=d+'0';
n=n-d;
f++;
};
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////
/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if XTAL_FREQ <= 2MHZ
do {
DelayUs(996);
} while(--cnt);
#endif
#if XTAL_FREQ > 2MHZ
unsigned char p;
do {
p = 4;
do {
DelayUs(250);
} while(--p);
} while(--cnt);
#endif
}
void DelayS(unsigned char cnt)
{
for (j=0; j<(cnt*10); j++)
DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////
/////////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
LCD_RS = 0; //write instruction
PORTB = (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
}
/* send data to the LCD */
void lcd_data(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
PORTB = 0x00;
LCD_RS = 1; //write data
PORTB |= (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB &= 0x00; //load upper nibble on LCD data lines
PORTB |= ( (c << 4) & 0xF0);
LCD_STROBE(); //send instruction to LCD
}
/*Clear the LCD*/
void lcd_clear(void)
{
lcd_cmd(0x01); //command to clear LCD
}
/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
j = -1;
while(s[++j]!=('\0')) // send characters until null character reached
lcd_data(s[j]);
}
/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
lcd_cmd(0b10000000); // command to go to line 1
}
/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
lcd_cmd(0b11000000); // command to go to line 2
}
/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
lcd_cmd(((x)&0x7F)|0x80);
}
/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
LCD_RS = 0;
LCD_EN = 0;
DelayMs(20); //wait for LCD startup
lcd_cmd(0x02);
lcd_cmd(0x28); // 4-bit mode
lcd_cmd(0x08); // display off
lcd_cmd(0x01); // clear display
lcd_cmd(0x0C); // disp. on, cursor off, cursor blink off
lcd_cmd(0x06); // entry mode
lcd_cmd(0x80); // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
///////////INITIALISING PORTS, REGISTERS, CLOCK/////////////////
void init(void)
{
// setup the PIC 16f690
OSCCON = 0x72; // internal osc, 8MHz
PORTA = 0;
TRISA = 0b10010010; // RA7 high imp, RA3 is serial out, RA4 button input
PORTB = 0; // PORTB not used
WPUB = 1; // PORTB pullups ON
RABPU = 0;
/* Init ADC */
ADCON0 = 0b10000101; // bit 7 right justify,analogue channel select bits bits5-2 0001=AN1,ADC ON, RA1 is ADC input
ADCON1 = 0b00100000; //bits6-4 fosc/32
ADON=1; // turn on the A2D conversion module
ANSEL=0x02; //set RA1 as analog input for GP2 sensor
ANSELH=0x00;
T1CON = 0b00010001; // TMR1 is ON, 1:2 prescale, =1MHz
T2CON = 0b00000101; // TMR2 is ON, 1:4 prescale, =1MHz
MyVal = 0; //initializn these variables here
MyMinVal = 0;
MyMaxVal = 99;
TRISB=0x00;
TRISC=0xFC;
lcd_init(); //call LCD initialisation
}
/////////////////////////End INITIALISING PORTS, REGISTERS, CLOCK///////////////////////
////////////////////////////////WaitForInput///////////////////////////////////////////
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and
//waiting for user to exit.
//This function determines which button the user pressed. It assigns the variable "temp" a number
//which corresponds to a certain button press. Example if "temp" equals:
//1---->ENTER/SETTINGS button was pressed
//2---->HOME button was pressed
//3---->INCREASE button was pressed
//4---->DECREASE button was pressed
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
if(!ENTERSETTINGS_SW){
while(ENTERSETTINGS_SW);
temp = 1;
done = 0xff;
}
if(!HOME_SW){
while(HOME_SW);
temp = 2;
done = 0xff;
}
if(!INCREASE_SW){
while(INCREASE_SW);
temp = 3;
done = 0xff;
}
if(!DECREASE_SW){
while(DECREASE_SW);
temp = 4;
done = 0xff;
}
if(expire == 1) break;
}//end of while
DelayMs(150); //debounce
return temp;
}//
//////////////////End WaitForInput///////////////////
//////////////////////////////////////////////////////userMenu///////////////////////////////////////////////////////
//This is the User Menu. Once the "ENTER/SETTINGS" button is pressed, the user enters this menu.
// This menu promps the user to enter the HEIGHT(ie. The distance between the IR sensor & the base of the container)
//
void userMenu(char pos){
unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY
lcd_clear();
lcd_goto_L1();
switch(pos){
case 0:
lcd_puts(" HEIGHT ");
break;
case 1:
lcd_puts(" RANGE ");
break;
case 2:
lcd_puts(" SURFACE AREA ");
break; //
case 3:
lcd_puts(" MEASURED ");
while(WaitForInput(1) != 2){ //Wait for user to press ENTER button to leave loop
// wait for 2 seconds, uses TMR1 free running at 1Mhz
while(!TMR1IF) // wait for TMR1 overflow
TMR1IF = 0; // clear overflow flag
bres += 65536; // add 65536uS to bres value
if(bres >= delaytime ) // if reached 2 seconds!
{
bres -= delaytime ; // subtract 2 seconds, keep error
// read the ADC voltage RA1 (Sharp GP2 sensor)
GODONE=1; // initiate conversion on the channel 0
while(GODONE) continue; // Wait convertion done
adc_temp[x] = (ADRESH << 8) | ADRESL;
x++;
x = (x & 0x03); //(or bcf x,2 or x.F2=0 on a better compiler)
//Now average the readings
adc_actual = (adc_temp[0] + adc_temp[1] + adc_temp[2]
+ adc_temp[3]+adc_temp[4] +2.5) / 5;
calc_distance(); // convert ADC value to distance
lcd_goto_L2(); //Only change line 2
//
if(posneg == 'p')
lcd_data('+');
else
lcd_data('-');
//
lcd_data(LLHigh);
lcd_data(LLLow);
lcd_puts(" [cm] "); //comment this out if you want
}
}
lcd_goto_L1();
lcd_puts(" Loading Home ");
lcd_goto_L2();
lcd_puts(" ");
DelayS(1);
break;
}
if(pos == 3) return;
lcd_goto_L2();
lcd_puts("Press Up/Down"); //home screen message (line 2)
}
//////////////////////End userMenu///////////////////////////
/////////////////////EnterScreen/////////////////////////////
void EnterScreen(void){
lcd_clear();
lcd_goto_L1();
lcd_puts(" [cm] ");
}
//////////////////End EnterScreen/////////////////////////////
////////////////////////ShowDigits////////////////////////////
void ShowDigits(unsigned char val){
MyValLCD[0] = val /10; //returns the quotient (if temp = 35 the result is 3)
MyValLCD[1] = val % 10; //Returns remainder (if temp = 35 the result is 5)
MyValLCD[0] += 0x30; //to ASCII
MyValLCD[1] += 0x30; //to ASCII
EnterScreen();
lcd_goto_L2();
lcd_data(MyValLCD[0]); //to LCD
lcd_data(MyValLCD[1]); //to LCD
}
////////////////////End ShowDigits////////////////////////////
///////////////////////calc_distance//////////////////////////
/*RomanBlack.com 19th July 2009.
uses my "zero error 1 second timer"
system to generate a 2 second interval, then every
2 seconds it reads an analog voltage from a
Sharp GP2 distance sensor and converts it to decimal
distance*/
void calc_distance(void)
{
unsigned int tmp;
unsigned int mathKeep; // used for voltage calculations backup
// from the transeiver datasheet the analog voltage is
// the inverse of distance, so distance can be calculated
// d = (1 / volts) then just scaled to suit the transeiver
// load ADC value in 16bit math var
math = (adc_actual >> 8) & 0x00FF;
math = (math * 256);
math += adc_actual & 0x00FF;
// now invert it; (1 / volts) use (6050 / volts) for scaling
math = (6050 / math);
if(math >= 2) math -=2; // fix linear error (-2)
if(math > 99) math = 99; // max limit at 99cm
//Create a copy of math for more use
mathKeep = math;
// convert from 0-99 to 2 decimal digits, 0-99cm
cm10=0;
while(math >= 10)
{
cm10++;
math -= 10;
}
cm = math;
math = mathKeep; //Now our original data is back and can be used.
//This will check if negative
LiquidLevel=height-math;
//
posneg = 'p';
if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
LiquidLevel = -LiquidLevel ;
posneg = 'n';
}
//
//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
//LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
LLHigh = ( LiquidLevel / 10 ) + '0'; //
LLLow = ( LiquidLevel % 10 ) + '0'; //
}
////////////////////End calc_distance//////////////////////////
///////////////////////user_input//////////////////////////////
unsigned char user_input(void)
{
char done = 0;
MyVal = 0; //Start on 0
while(done == 0){
input_sw = WaitForInput(0);
switch(input_sw){
case 1:
done = 0xff; //This tells us the user finished ENTERING by displaying "OK"
lcd_goto_L1();
lcd_puts(" OK ");
break;
//The User will be prompted to enter the "HEIGHT" in userMenu(). The LCD will Display:
// HEIGHT
// Press Up/Down
//Up------->represented by INCREASE Button
//Down----->represented by DECREASE Button
// The user can enter "HEIGHT" values from [0-99]cm because
// MyMinVal = 0;
// MyMaxVal = 99;
///Now the variable MyVal is used in user_input() to represent this "HEIGHT" value.
//If User presses the INCREASE button:
case 3:
if(MyVal < MyMaxVal) //checks if MyVal<99
MyVal++; //Increases MyVal when INCREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
//If the User presses the DECREASE button:
case 4:
if(MyVal > MyMinVal) //checks if MyVal>0
MyVal--; //Decreases MyVal when DECREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
default:
break;
}
}
DelayMs(250); //switch debouncing
DelayMs(250); //switch debouncing
return MyVal;//This will return MyVal which represents the "HEIGHT". The height var will be assigned myVal in home_screen()
//height = user_input() in home_screen. This will be used in calc_distance() to calculate the Liquid Leval.
}
/////////////////////////End user_input//////////////////////////////
///////////////////////////home_screen///////////////////////////////
void home_screen(void){
mnuPOS = 0;
lcd_clear();
lcd_goto_L1();
lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
lcd_goto_L2();
lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
input_sw = 0; //Reset the value of the button the user presses
while(input_sw != 1) //Wait until ENTER button is pressed
input_sw = WaitForInput(0);
userMenu(0);
DelayMs(2); //shorter delay
height = user_input(); //The HEIGHT var will have the myVal
/* userMenu(1);
DelayMs(2); //shorter delay
range = user_input(); //The HEIGHT var will have the myVal
userMenu(2);
DelayMs(2); //shorter delay
area = user_input(); //The HEIGHT var will have the myVal
*/
userMenu(3);
DelayMs(2); //shorter delay
input_sw = 0; //Reset the value of the button the user presses
}
///////////////////////////End home_screen///////////////////////////////
//////////////////////main//////////////////////
void main(void)
{
init(); // initialise I/O ports, LCD
while(1){
home_screen();
}
}
//////////////////End main//////////////////////
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h"
#include <stdio.h>
#include <stdlib.h> //
void userMenu(char pos); //
void FloatToStr(float , char[]);
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char);
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);
unsigned char user_input(void);
void home_screen(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);
unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;
#define LCD_RS RC0 //LCD RS pin
#define LCD_EN RC1 //LCD EN pin
#define LCD_STROBE() LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0
unsigned char cm10; //
unsigned char cm; //
unsigned int math,[COLOR="Red"][B]adc_temp[32];[/B][/COLOR] // used for voltage calculations
long int [COLOR="Red"][B]adc_actual;[/B][/COLOR]
unsigned char NumDec;
unsigned char NumSep[2];
unsigned char i,j,k,x;
unsigned char height=50;
unsigned char range;
unsigned char area;
unsigned char SensorPos=10;
// New Vars
char input_sw;
char mnuPOS;
unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres; // for bresenham 2-second timer system
unsigned char ;
#define HOME_SW RC2 //HOME switch
#define INCREASE_SW RC3 //INCREASE switch
#define DECREASE_SW RC4 //DECREASE switch
#define ENTERSETTINGS_SW RA4 //ENTERSETTINGS switch
/////////////////////////////////////////CONVERT FLOAT TO STRING///////////////////////////////
// This function was taken from the CAVR library. It was modified slightly to suit the design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;};
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
{
scale=floor(0.5+scale/10.0);
d=(unsigned char) (n/scale);
str[f]=d+'0';
n=n-scale*d;
f++;
};
str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
{
n=n*10.0;
d=(unsigned char) n;
str[f]=d+'0';
n=n-d;
f++;
};
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////
/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if XTAL_FREQ <= 2MHZ
do {
DelayUs(996);
} while(--cnt);
#endif
#if XTAL_FREQ > 2MHZ
unsigned char p;
do {
p = 4;
do {
DelayUs(250);
} while(--p);
} while(--cnt);
#endif
}
void DelayS(unsigned char cnt)
{
for (j=0; j<(cnt*10); j++)
DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////
/////////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
LCD_RS = 0; //write instruction
PORTB = (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
}
/* send data to the LCD */
void lcd_data(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
PORTB = 0x00;
LCD_RS = 1; //write data
PORTB |= (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB &= 0x00; //load upper nibble on LCD data lines
PORTB |= ( (c << 4) & 0xF0);
LCD_STROBE(); //send instruction to LCD
}
/*Clear the LCD*/
void lcd_clear(void)
{
lcd_cmd(0x01); //command to clear LCD
}
/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
j = -1;
while(s[++j]!=('\0')) // send characters until null character reached
lcd_data(s[j]);
}
/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
lcd_cmd(0b10000000); // command to go to line 1
}
/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
lcd_cmd(0b11000000); // command to go to line 2
}
/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
lcd_cmd(((x)&0x7F)|0x80);
}
/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
LCD_RS = 0;
LCD_EN = 0;
DelayMs(20); //wait for LCD startup
lcd_cmd(0x02);
lcd_cmd(0x28); // 4-bit mode
lcd_cmd(0x08); // display off
lcd_cmd(0x01); // clear display
lcd_cmd(0x0C); // disp. on, cursor off, cursor blink off
lcd_cmd(0x06); // entry mode
lcd_cmd(0x80); // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
///////////INITIALISING PORTS, REGISTERS, CLOCK/////////////////
void init(void)
{
// setup the PIC 16f690
OSCCON = 0x72; // internal osc, 8MHz
PORTA = 0;
TRISA = 0b10010010; // RA7 high imp, RA3 is serial out, RA4 button input
PORTB = 0; // PORTB not used
WPUB = 1; // PORTB pullups ON
RABPU = 0;
/* Init ADC */
ADCON0 = 0b10000101; // bit 7 right justify,analogue channel select bits bits5-2 0001=AN1,ADC ON, RA1 is ADC input
ADCON1 = 0b00100000; //bits6-4 fosc/32
ADON=1; // turn on the A2D conversion module
ANSEL=0x02; //set RA1 as analog input for GP2 sensor
ANSELH=0x00;
T1CON = 0b00010001; // TMR1 is ON, 1:2 prescale, =1MHz
T2CON = 0b00000101; // TMR2 is ON, 1:4 prescale, =1MHz
MyVal = 0; //initializn these variables here
MyMinVal = 0;
MyMaxVal = 99;
TRISB=0x00;
TRISC=0xFC;
lcd_init(); //call LCD initialisation
}
/////////////////////////End INITIALISING PORTS, REGISTERS, CLOCK///////////////////////
////////////////////////////////WaitForInput///////////////////////////////////////////
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and
//waiting for user to exit.
//This function determines which button the user pressed. It assigns the variable "temp" a number
//which corresponds to a certain button press. Example if "temp" equals:
//1---->ENTER/SETTINGS button was pressed
//2---->HOME button was pressed
//3---->INCREASE button was pressed
//4---->DECREASE button was pressed
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
if(!ENTERSETTINGS_SW){
while(ENTERSETTINGS_SW);
temp = 1;
done = 0xff;
}
if(!HOME_SW){
while(HOME_SW);
temp = 2;
done = 0xff;
}
if(!INCREASE_SW){
while(INCREASE_SW);
temp = 3;
done = 0xff;
}
if(!DECREASE_SW){
while(DECREASE_SW);
temp = 4;
done = 0xff;
}
if(expire == 1) break;
}//end of while
DelayMs(150); //debounce
return temp;
}//
//////////////////End WaitForInput///////////////////
//////////////////////////////////////////////////////userMenu///////////////////////////////////////////////////////
//This is the User Menu. Once the "ENTER/SETTINGS" button is pressed, the user enters this menu.
// This menu promps the user to enter the HEIGHT(ie. The distance between the IR sensor & the base of the container)
//
void userMenu(char pos){
unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY
lcd_clear();
lcd_goto_L1();
switch(pos){
case 0:
lcd_puts(" HEIGHT ");
break;
case 1:
lcd_puts(" RANGE ");
break;
case 2:
lcd_puts(" SURFACE AREA ");
break; //
case 3:
lcd_puts(" MEASURED ");
while(WaitForInput(1) != 2){ //Wait for user to press ENTER button to leave loop
// wait for 2 seconds, uses TMR1 free running at 1Mhz
while(!TMR1IF) // wait for TMR1 overflow
TMR1IF = 0; // clear overflow flag
bres += 65536; // add 65536uS to bres value
if(bres >= delaytime ) // if reached 2 seconds!
{
bres -= delaytime ; // subtract 2 seconds, keep error
// read the ADC voltage RA1 (Sharp GP2 sensor)
GODONE=1; // initiate conversion on the channel 0
while(GODONE) continue; // Wait convertion done
adc_temp[x] = (ADRESH << 8) | ADRESL;
x++;
x = (x & 0x03); //(or bcf x,2 or x.F2=0 on a better compiler)
//Now average the readings
[B][COLOR="Red"] x = (x & 0x1f); //wrap x so x = 0 to 31
adc_actual=16; //initialise total
for(i=0;i<32;i++){ //loop 32 times
adc_actual+=adc_temp[i];//add all the values
DelayMs(30);
}
adc_actual/=32; //and divide by 32
[/COLOR][/B]
calc_distance(); // convert ADC value to distance
lcd_goto_L2(); //Only change line 2
//
if(posneg == 'p')
lcd_data('+');
else
lcd_data('-');
//
lcd_data(LLHigh);
lcd_data(LLLow);
lcd_puts(" [cm] "); //comment this out if you want
}
}
lcd_goto_L1();
lcd_puts(" Loading Home ");
lcd_goto_L2();
lcd_puts(" ");
DelayS(1);
break;
}
if(pos == 3) return;
lcd_goto_L2();
lcd_puts("Press Up/Down"); //home screen message (line 2)
}
//////////////////////End userMenu///////////////////////////
/////////////////////EnterScreen/////////////////////////////
void EnterScreen(void){
lcd_clear();
lcd_goto_L1();
lcd_puts(" [cm] ");
}
//////////////////End EnterScreen/////////////////////////////
////////////////////////ShowDigits////////////////////////////
void ShowDigits(unsigned char val){
MyValLCD[0] = val /10; //returns the quotient (if temp = 35 the result is 3)
MyValLCD[1] = val % 10; //Returns remainder (if temp = 35 the result is 5)
MyValLCD[0] += 0x30; //to ASCII
MyValLCD[1] += 0x30; //to ASCII
EnterScreen();
lcd_goto_L2();
lcd_data(MyValLCD[0]); //to LCD
lcd_data(MyValLCD[1]); //to LCD
}
////////////////////End ShowDigits////////////////////////////
///////////////////////calc_distance//////////////////////////
/*RomanBlack.com 19th July 2009.
uses my "zero error 1 second timer"
system to generate a 2 second interval, then every
2 seconds it reads an analog voltage from a
Sharp GP2 distance sensor and converts it to decimal
distance*/
void calc_distance(void)
{
unsigned int tmp;
unsigned int mathKeep; // used for voltage calculations backup
// from the transeiver datasheet the analog voltage is
// the inverse of distance, so distance can be calculated
// d = (1 / volts) then just scaled to suit the transeiver
// load ADC value in 16bit math var
math = (adc_actual >> 8) & 0x00FF;
math = (math * 256);
math += adc_actual & 0x00FF;
// now invert it; (1 / volts) use (6050 / volts) for scaling
math = (6050 / math);
if(math >= 2) math -=2; // fix linear error (-2)
if(math > 99) math = 99; // max limit at 99cm
//Create a copy of math for more use
mathKeep = math;
// convert from 0-99 to 2 decimal digits, 0-99cm
cm10=0;
while(math >= 10)
{
cm10++;
math -= 10;
}
cm = math;
math = mathKeep; //Now our original data is back and can be used.
//This will check if negative
LiquidLevel=height-math;
//
posneg = 'p';
if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
LiquidLevel = -LiquidLevel ;
posneg = 'n';
}
//
//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
//LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
LLHigh = ( LiquidLevel / 10 ) + '0'; //
LLLow = ( LiquidLevel % 10 ) + '0'; //
}
////////////////////End calc_distance//////////////////////////
///////////////////////user_input//////////////////////////////
unsigned char user_input(void)
{
char done = 0;
MyVal = 0; //Start on 0
while(done == 0){
input_sw = WaitForInput(0);
switch(input_sw){
case 1:
done = 0xff; //This tells us the user finished ENTERING by displaying "OK"
lcd_goto_L1();
lcd_puts(" OK ");
break;
//The User will be prompted to enter the "HEIGHT" in userMenu(). The LCD will Display:
// HEIGHT
// Press Up/Down
//Up------->represented by INCREASE Button
//Down----->represented by DECREASE Button
// The user can enter "HEIGHT" values from [0-99]cm because
// MyMinVal = 0;
// MyMaxVal = 99;
///Now the variable MyVal is used in user_input() to represent this "HEIGHT" value.
//If User presses the INCREASE button:
case 3:
if(MyVal < MyMaxVal) //checks if MyVal<99
MyVal++; //Increases MyVal when INCREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
//If the User presses the DECREASE button:
case 4:
if(MyVal > MyMinVal) //checks if MyVal>0
MyVal--; //Decreases MyVal when DECREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
default:
break;
}
}
DelayMs(250); //switch debouncing
DelayMs(250); //switch debouncing
return MyVal;//This will return MyVal which represents the "HEIGHT". The height var will be assigned myVal in home_screen()
//height = user_input() in home_screen. This will be used in calc_distance() to calculate the Liquid Leval.
}
/////////////////////////End user_input//////////////////////////////
///////////////////////////home_screen///////////////////////////////
void home_screen(void){
mnuPOS = 0;
lcd_clear();
lcd_goto_L1();
lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
lcd_goto_L2();
lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
input_sw = 0; //Reset the value of the button the user presses
while(input_sw != 1) //Wait until ENTER button is pressed
input_sw = WaitForInput(0);
userMenu(0);
DelayMs(2); //shorter delay
height = user_input(); //The HEIGHT var will have the myVal
/* userMenu(1);
DelayMs(2); //shorter delay
range = user_input(); //The HEIGHT var will have the myVal
userMenu(2);
DelayMs(2); //shorter delay
area = user_input(); //The HEIGHT var will have the myVal
*/
userMenu(3);
DelayMs(2); //shorter delay
input_sw = 0; //Reset the value of the button the user presses
}
///////////////////////////End home_screen///////////////////////////////
//////////////////////main//////////////////////
void main(void)
{
init(); // initialise I/O ports, LCD
while(1){
home_screen();
}
}
//////////////////End main//////////////////////
You need to actually TEST the adc voltage in the loop, so you test it 32 times.
You are testing it BEFORE the loop, then just adding the same value 32 times.
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h"
#include <stdio.h>
#include <stdlib.h> //
void userMenu(char pos); //
void FloatToStr(float , char[]);
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char);
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);
unsigned char user_input(void);
void home_screen(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);
unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;
#define LCD_RS RC0 //LCD RS pin
#define LCD_EN RC1 //LCD EN pin
#define LCD_STROBE() LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0
unsigned char cm10; //
unsigned char cm; //
unsigned int math,adc_temp[32]; // used for voltage calculations
long int adc_actual;
unsigned char NumDec;
unsigned char NumSep[2];
unsigned char i,j,k,x;
unsigned char height=50;
unsigned char range;
unsigned char area;
unsigned char SensorPos=10;
// New Vars
char input_sw;
char mnuPOS;
unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres; // for bresenham 2-second timer system
unsigned char ;
#define HOME_SW RC2 //HOME switch
#define INCREASE_SW RC3 //INCREASE switch
#define DECREASE_SW RC4 //DECREASE switch
#define ENTERSETTINGS_SW RA4 //ENTERSETTINGS switch
/////////////////////////////////////////CONVERT FLOAT TO STRING///////////////////////////////
// This function was taken from the CAVR library. It was modified slightly to suit the design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;};
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
{
scale=floor(0.5+scale/10.0);
d=(unsigned char) (n/scale);
str[f]=d+'0';
n=n-scale*d;
f++;
};
str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
{
n=n*10.0;
d=(unsigned char) n;
str[f]=d+'0';
n=n-d;
f++;
};
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////
/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if XTAL_FREQ <= 2MHZ
do {
DelayUs(996);
} while(--cnt);
#endif
#if XTAL_FREQ > 2MHZ
unsigned char p;
do {
p = 4;
do {
DelayUs(250);
} while(--p);
} while(--cnt);
#endif
}
void DelayS(unsigned char cnt)
{
for (j=0; j<(cnt*10); j++)
DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////
/////////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
LCD_RS = 0; //write instruction
PORTB = (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
}
/* send data to the LCD */
void lcd_data(unsigned char c)
{
DelayMs(2); //wait for LCD to be ready shorter delay
PORTB = 0x00;
LCD_RS = 1; //write data
PORTB |= (c & 0xF0); //load upper nibble on LCD data lines
LCD_STROBE(); //send instruction to LCD
PORTB &= 0x00; //load upper nibble on LCD data lines
PORTB |= ( (c << 4) & 0xF0);
LCD_STROBE(); //send instruction to LCD
}
/*Clear the LCD*/
void lcd_clear(void)
{
lcd_cmd(0x01); //command to clear LCD
}
/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
j = -1;
while(s[++j]!=('\0')) // send characters until null character reached
lcd_data(s[j]);
}
/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
lcd_cmd(0b10000000); // command to go to line 1
}
/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
lcd_cmd(0b11000000); // command to go to line 2
}
/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
lcd_cmd(((x)&0x7F)|0x80);
}
/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
LCD_RS = 0;
LCD_EN = 0;
DelayMs(20); //wait for LCD startup
lcd_cmd(0x02);
lcd_cmd(0x28); // 4-bit mode
lcd_cmd(0x08); // display off
lcd_cmd(0x01); // clear display
lcd_cmd(0x0C); // disp. on, cursor off, cursor blink off
lcd_cmd(0x06); // entry mode
lcd_cmd(0x80); // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
///////////INITIALISING PORTS, REGISTERS, CLOCK/////////////////
void init(void)
{
// setup the PIC 16f690
OSCCON = 0x72; // internal osc, 8MHz
PORTA = 0;
TRISA = 0b10010010; // RA7 high imp, RA3 is serial out, RA4 button input
PORTB = 0; // PORTB not used
WPUB = 1; // PORTB pullups ON
RABPU = 0;
/* Init ADC */
ADCON0 = 0b10000101; // bit 7 right justify,analogue channel select bits bits5-2 0001=AN1,ADC ON, RA1 is ADC input
ADCON1 = 0b00100000; //bits6-4 fosc/32
ADON=1; // turn on the A2D conversion module
ANSEL=0x02; //set RA1 as analog input for GP2 sensor
ANSELH=0x00;
T1CON = 0b00010001; // TMR1 is ON, 1:2 prescale, =1MHz
T2CON = 0b00000101; // TMR2 is ON, 1:4 prescale, =1MHz
MyVal = 0; //initializn these variables here
MyMinVal = 0;
MyMaxVal = 99;
TRISB=0x00;
TRISC=0xFC;
lcd_init(); //call LCD initialisation
}
/////////////////////////End INITIALISING PORTS, REGISTERS, CLOCK///////////////////////
////////////////////////////////WaitForInput///////////////////////////////////////////
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and
//waiting for user to exit.
//This function determines which button the user pressed. It assigns the variable "temp" a number
//which corresponds to a certain button press. Example if "temp" equals:
//1---->ENTER/SETTINGS button was pressed
//2---->HOME button was pressed
//3---->INCREASE button was pressed
//4---->DECREASE button was pressed
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
if(!ENTERSETTINGS_SW){
while(ENTERSETTINGS_SW);
temp = 1;
done = 0xff;
}
if(!HOME_SW){
while(HOME_SW);
temp = 2;
done = 0xff;
}
if(!INCREASE_SW){
while(INCREASE_SW);
temp = 3;
done = 0xff;
}
if(!DECREASE_SW){
while(DECREASE_SW);
temp = 4;
done = 0xff;
}
if(expire == 1) break;
}//end of while
DelayMs(150); //debounce
return temp;
}//
//////////////////End WaitForInput///////////////////
//////////////////////////////////////////////////////userMenu///////////////////////////////////////////////////////
//This is the User Menu. Once the "ENTER/SETTINGS" button is pressed, the user enters this menu.
// This menu promps the user to enter the HEIGHT(ie. The distance between the IR sensor & the base of the container)
//
void userMenu(char pos){
unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY
lcd_clear();
lcd_goto_L1();
switch(pos){
case 0:
lcd_puts(" HEIGHT ");
break;
case 1:
lcd_puts(" RANGE ");
break;
case 2:
lcd_puts(" SURFACE AREA ");
break; //
case 3:
lcd_puts(" MEASURED ");
while(WaitForInput(1) != 2){ //Wait for user to press ENTER button to leave loop
// wait for 2 seconds, uses TMR1 free running at 1Mhz
while(!TMR1IF) // wait for TMR1 overflow
TMR1IF = 0; // clear overflow flag
bres += 65536; // add 65536uS to bres value
if(bres >= delaytime ) // if reached 2 seconds!
{
bres -= delaytime ; // subtract 2 seconds, keep error
[COLOR="Red"][B]
//Now average the readings
x = (x & 0x1f); //wrap x so x = 0 to 31
adc_actual=16; //initialise total
for(i=0;i<32;i++){ //loop 32 times
// read the ADC voltage RA1 (Sharp GP2 sensor)
GODONE=1; // initiate conversion on the channel 0
while(GODONE) continue; // Wait convertion done
adc_temp[x] = (ADRESH << 8) | ADRESL;
x++;
x = (x & 0x03); //(or bcf x,2 or x.F2=0 on a better compiler)
adc_actual+=adc_temp[i];//add all the values
DelayMs(30);
}
adc_actual/=32; //and divide by 32[/B][/COLOR]
calc_distance(); // convert ADC value to distance
lcd_goto_L2(); //Only change line 2
//
if(posneg == 'p')
lcd_data('+');
else
lcd_data('-');
//
lcd_data(LLHigh);
lcd_data(LLLow);
lcd_puts(" [cm] "); //comment this out if you want
}
}
lcd_goto_L1();
lcd_puts(" Loading Home ");
lcd_goto_L2();
lcd_puts(" ");
DelayS(1);
break;
}
if(pos == 3) return;
lcd_goto_L2();
lcd_puts("Press Up/Down"); //home screen message (line 2)
}
//////////////////////End userMenu///////////////////////////
/////////////////////EnterScreen/////////////////////////////
void EnterScreen(void){
lcd_clear();
lcd_goto_L1();
lcd_puts(" [cm] ");
}
//////////////////End EnterScreen/////////////////////////////
////////////////////////ShowDigits////////////////////////////
void ShowDigits(unsigned char val){
MyValLCD[0] = val /10; //returns the quotient (if temp = 35 the result is 3)
MyValLCD[1] = val % 10; //Returns remainder (if temp = 35 the result is 5)
MyValLCD[0] += 0x30; //to ASCII
MyValLCD[1] += 0x30; //to ASCII
EnterScreen();
lcd_goto_L2();
lcd_data(MyValLCD[0]); //to LCD
lcd_data(MyValLCD[1]); //to LCD
}
////////////////////End ShowDigits////////////////////////////
///////////////////////calc_distance//////////////////////////
/*RomanBlack.com 19th July 2009.
uses my "zero error 1 second timer"
system to generate a 2 second interval, then every
2 seconds it reads an analog voltage from a
Sharp GP2 distance sensor and converts it to decimal
distance*/
void calc_distance(void)
{
unsigned int tmp;
unsigned int mathKeep; // used for voltage calculations backup
// from the transeiver datasheet the analog voltage is
// the inverse of distance, so distance can be calculated
// d = (1 / volts) then just scaled to suit the transeiver
// load ADC value in 16bit math var
math = (adc_actual >> 8) & 0x00FF;
math = (math * 256);
math += adc_actual & 0x00FF;
// now invert it; (1 / volts) use (6050 / volts) for scaling
math = (6050 / math);
if(math >= 2) math -=2; // fix linear error (-2)
if(math > 99) math = 99; // max limit at 99cm
//Create a copy of math for more use
mathKeep = math;
// convert from 0-99 to 2 decimal digits, 0-99cm
cm10=0;
while(math >= 10)
{
cm10++;
math -= 10;
}
cm = math;
math = mathKeep; //Now our original data is back and can be used.
//This will check if negative
LiquidLevel=height-math;
//
posneg = 'p';
if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
LiquidLevel = -LiquidLevel ;
posneg = 'n';
}
//
//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
//LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
LLHigh = ( LiquidLevel / 10 ) + '0'; //
LLLow = ( LiquidLevel % 10 ) + '0'; //
}
////////////////////End calc_distance//////////////////////////
///////////////////////user_input//////////////////////////////
unsigned char user_input(void)
{
char done = 0;
MyVal = 0; //Start on 0
while(done == 0){
input_sw = WaitForInput(0);
switch(input_sw){
case 1:
done = 0xff; //This tells us the user finished ENTERING by displaying "OK"
lcd_goto_L1();
lcd_puts(" OK ");
break;
//The User will be prompted to enter the "HEIGHT" in userMenu(). The LCD will Display:
// HEIGHT
// Press Up/Down
//Up------->represented by INCREASE Button
//Down----->represented by DECREASE Button
// The user can enter "HEIGHT" values from [0-99]cm because
// MyMinVal = 0;
// MyMaxVal = 99;
///Now the variable MyVal is used in user_input() to represent this "HEIGHT" value.
//If User presses the INCREASE button:
case 3:
if(MyVal < MyMaxVal) //checks if MyVal<99
MyVal++; //Increases MyVal when INCREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
//If the User presses the DECREASE button:
case 4:
if(MyVal > MyMinVal) //checks if MyVal>0
MyVal--; //Decreases MyVal when DECREASE button is pressed
EnterScreen(); //Displays "[cm]" in Line 1
ShowDigits(MyVal); //Splits MyVal into 2 separate digits, then convert digits to ASCII
//so it can be displayed on the LCD screen
break;
default:
break;
}
}
DelayMs(250); //switch debouncing
DelayMs(250); //switch debouncing
return MyVal;//This will return MyVal which represents the "HEIGHT". The height var will be assigned myVal in home_screen()
//height = user_input() in home_screen. This will be used in calc_distance() to calculate the Liquid Leval.
}
/////////////////////////End user_input//////////////////////////////
///////////////////////////home_screen///////////////////////////////
void home_screen(void){
mnuPOS = 0;
lcd_clear();
lcd_goto_L1();
lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
lcd_goto_L2();
lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
input_sw = 0; //Reset the value of the button the user presses
while(input_sw != 1) //Wait until ENTER button is pressed
input_sw = WaitForInput(0);
userMenu(0);
DelayMs(2); //shorter delay
height = user_input(); //The HEIGHT var will have the myVal
/* userMenu(1);
DelayMs(2); //shorter delay
range = user_input(); //The HEIGHT var will have the myVal
userMenu(2);
DelayMs(2); //shorter delay
area = user_input(); //The HEIGHT var will have the myVal
*/
userMenu(3);
DelayMs(2); //shorter delay
input_sw = 0; //Reset the value of the button the user presses
}
///////////////////////////End home_screen///////////////////////////////
//////////////////////main//////////////////////
void main(void)
{
init(); // initialise I/O ports, LCD
while(1){
home_screen();
}
}
//////////////////End main//////////////////////
void calc_distance(void)
{
unsigned int tmp;
unsigned int mathKeep; // used for voltage calculations backup
// from the transeiver datasheet the analog voltage is
// the inverse of distance, so distance can be calculated
// d = (1 / volts) then just scaled to suit the transeiver
// load ADC value in 16bit math var
math = (adc_actual >> 8) & 0x00FF;
math = (math * 256);
math += adc_actual & 0x00FF;
// now invert it; (1 / volts) use (6050 / volts) for scaling
math = (6050 / math);
if(math >= 2) math -=2; // fix linear error (-2)
if(math > 99) math = 99; // max limit at 99cm
//Create a copy of math for more use
mathKeep = math;
[COLOR="Red"][B]// convert from 0-99 to 2 decimal digits, 0-99cm
cm10=0;
while(math >= 10)
{
cm10++;
math -= 10;
}
cm = math;[/B][/COLOR]
math = mathKeep; //Now our original data is back and can be used.
//This will check if negative
LiquidLevel=height-math;
//
posneg = 'p';
if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
LiquidLevel = -LiquidLevel ;
posneg = 'n';
}
//
//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
//LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
LLHigh = ( LiquidLevel / 10 ) + '0'; //
LLLow = ( LiquidLevel % 10 ) + '0'; //
}
////////////////////End calc_distance//////////////////////////
lcd_data(LLHigh);
lcd_data(LLLow);
lcd_puts(" [cm] ");
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?