View previous topic :: View next topic |
Author |
Message |
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
Auto calibration of line sensor |
Posted: Sat Oct 28, 2017 1:15 pm |
|
|
I tried to convert Arduino calibration example to ccs c. In this method i have taken 1 sensor readings for five seconds during the startup, and tracks the highest and lowest values it gets. These sensor readings during the first five seconds of the sketch execution define the minimum and maximum of expected values for the readings taken during the loop. Then i mapped the next incoming reading based on maximum and minimum value taken before.
Code: |
#include <18f2550.h>
#device adc=10
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#FUSES HS,NOWDT
#USE delay(clock=20M)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7,PARITY=N,BITS=8)
#USE TIMER(TIMER=1,TICK=1s,BITS=16,NOISR)
unsigned int16 tick_difference(unsigned int16 current,unsigned int16 previous)
{
return (current - previous);
}
double map(double value, float min, float max, float y_min, float y_max)//function definition
{
return (y_min + (((y_max - y_min)/(max - min)) * (value - min)));
}
void main(void)
{
double d;
int16 sensorMin =1023 ; // minimum sensor value
int16 sensorMax =0 ; // maximum sensor value
int16 sensorValue;
setup_adc_ports(AN0);
//setup_adc(ADC_CLOCK_INTERNAL);
setup_adc(ADC_CLOCK_DIV_32);
unsigned int16 current_tick, previous_tick;
current_tick = previous_tick = get_ticks();
while(1)
{
current_tick= get_ticks();
if (tick_difference(current_tick, previous_tick) <5) //take sensor reading for 5 second
{
set_adc_channel(0);
sensorValue = read_adc();
delay_us(15);
if (sensorValue> sensorMax)
{
sensorMax = sensorValue;
}
if (sensorValue < sensorMin)
{
sensorMin = sensorValue;
}
output_toggle(PIN_B0);
delay_ms(300);
}
set_adc_channel(0);
sensorValue = read_adc();/// again take the sensor value while running
delay_us(15);
d = map(sensorValue,sensorMin,sensorMax,0,1023);//function call(calling the map function)
printf("%f \r\n",d);
delay_ms(1000);
}
}
|
The problem is while tracking the highest and lowest value the line sensor the sensor also takes readings and mapped into some garbage reading before the actual minimum and maximum value is determined. Any suggestion?
Last edited by srikrishna on Wed Nov 08, 2017 1:06 am; edited 3 times in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Sat Oct 28, 2017 1:55 pm |
|
|
this...
setup_adc(ADC_CLOCK_INTERNAL);
... is probably not the correct option.
'internal' is usually only chosen when the PIC is asleep.
You should look at the 'speed chart' in the ADC section...
With any analog capturing ,it's best to take an 'olympic' average. |
|
|
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
|
Posted: Sat Oct 28, 2017 2:39 pm |
|
|
temtronic wrote: | this...
setup_adc(ADC_CLOCK_INTERNAL);
... is probably not the correct option.
'internal' is usually only chosen when the PIC is asleep.
You should look at the 'speed chart' in the ADC section...
With any analog capturing ,it's best to take an 'olympic' average. |
what do you mean by asleep??
I have found the following mode in .h file of pic 18f2550
Quote: | // Constants used for SETUP_ADC() are:
#define ADC_OFF 0 // ADC Off
#define ADC_CLOCK_DIV_2 0x100
#define ADC_CLOCK_DIV_4 0x04
#define ADC_CLOCK_DIV_8 0x01
#define ADC_CLOCK_DIV_16 0x05
#define ADC_CLOCK_DIV_32 0x02
#define ADC_CLOCK_DIV_64 0x06
#define ADC_CLOCK_INTERNAL 0x07 // Internal 2-6us |
In the list which one should i use?? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Sat Oct 28, 2017 3:14 pm |
|
|
You need to download the Microchip datasheet for that PIC. When you look at the ADC section, there will be a chapter about selecting the ADC clock rate.
From my memory, I believe div_8 or div_16 would be appropriate. This PC is NOT my PIC machine, so I can't easily confirm.
Just get the datasheet, read the chapter.
Asleep, means the PIC has gone into 'sleep' mode, again, read the datasheet. Yes, I KNOW 500-800 pages is a LOT to read, but just read a chapter at a time....most you can 'skim' over BUT when possible read the entire datasheet.
Jay |
|
|
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
|
Posted: Sat Oct 28, 2017 3:30 pm |
|
|
OK. But how to solve the above problem |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Sat Oct 28, 2017 8:02 pm |
|
|
I'd have the program print out ( send data to PC) for EVERY reading. That way you can determine if it's actually a bad ADC reading( sensor problem) or a 'math' ( computational) problem.
Without seeing the data I don't know where the 'bad' number is from
1) bad sensor wiring
2) EMI
3) incorrect calculations( 'math' )
4) bad logic
BTW the ADC setup is still incorrect
Jay |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Oct 28, 2017 9:20 pm |
|
|
Quote: |
I have taken 1 sensor readings for five seconds during the startup.
The problem is while tracking the highest and lowest value the sensor also
takes readings and mapped into some garbage reading before the actual
minimum and maximum value is determined |
Don't take readings until the calibration is done.
Do the calibration for 5 seconds. Then take readings and call the map()
function. |
|
|
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
|
Posted: Mon Oct 30, 2017 3:25 pm |
|
|
PCM programmer wrote: |
Don't take readings until the calibration is done.
Do the calibration for 5 seconds. Then take readings and call the map()
function. |
I have to get calibration data Before the while loop And save the data. But how can i did that ? The #use timer option needed a while loop to perform operation.
So while taking reading for calibration it also takes reading for mapping. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Tue Oct 31, 2017 2:29 am |
|
|
You do one _then_ then other.
Have a loop that exits after five seconds. Then take a set of perhaps eight readings, average and store these, then enter the main sampling loop. |
|
|
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
|
Posted: Tue Oct 31, 2017 9:12 pm |
|
|
Ttelmah wrote: | You do one _then_ then other.
Have a loop that exits after five seconds. Then take a set of perhaps eight readings, average and store these, then enter the main sampling loop. |
I don't want to use a for loop. Any other suggestion?? |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Wed Nov 01, 2017 4:39 am |
|
|
srikrishna wrote: |
I don't want to use a for loop. Any other suggestion?? |
What's the problem with using a for loop? It seems to me that you may not understand what these control structures, while loops, for loops etc, actually do. These are not special to particular tasks; #use timer option did NOT need a while loop to perform operation, but the example shows it using one. You can use while loops for all sorts of other things, and you can have as many as you need.
As Ttelmah says, you write code that calibrates for five seconds, and then when that's done, dooes the actual measuring. Two distinct phases of operation. Your original code is trying to do both at the same time. Using simple C control statements to do that is trivial. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Wed Nov 01, 2017 10:42 am |
|
|
srikrishna wrote: | Ttelmah wrote: | You do one _then_ then other.
Have a loop that exits after five seconds. Then take a set of perhaps eight readings, average and store these, then enter the main sampling loop. |
I don't want to use a for loop. Any other suggestion?? |
You don't have to use a for loop. Read carefully what Ttelmah (and RF_Developer) just said. They are suggesting splitting up the process into two steps: calibration and data collection. Whether you use a for loop, while loop, etc. is up to you. I'll give you an uncompiled/untested example so you can see what they mean.
I would recommend creating a calibration type:
Code: |
typedef struct{
unsigned int16 min;
unsigned int16 max;
} calibration_t;
|
and then a calibrate function
Code: |
void calibrate_adc(calibration_t * calibration_data){
unsigned int16 current_tick;
unsigned int16 starting_tick;
unsigned int16 sensor_value;
unsigned int16 max = 0;
unsigned int16 min = 1023;
set_adc_channel(0);
delay_us(15);
current_tick = get_ticks();
starting_tick = current_tick;
//get as many readings as you can. the more the
//merrier
while(tick_difference(current_tick,starting_tick) < 5){
sensor_value = read_adc();
if(sensor_value > max){
max = sensor_value;
}
if(sensor_value < min){
min = sensor_value;
}
output_toggle(PIN_B0);
current_tick = get_ticks();
}
calibration_data->min = min;
calibration_data->max = max;
}
|
Then you can simplify your main pretty easily:
Code: |
void main(void){
calibration_t cal;
unsigned int16 sensor_value;
double map_result;
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_32);
delay_ms(1000); //some delay to stabilize sensor
calibrate_adc(&cal); //takes about 5 secs
//next two lines not technically needed
//since you only have one ADC and it
//was set in calibrate_adc(), but here
//for later expansion
set_adc_channel(0);
delay_us(15);
while(TRUE){
sensor_value = read_adc();
map_result = map(sensor_value,cal.min,cal.max,0,1023);
printf("%f \r\n",map_result);
delay_ms(1000);
}
}
|
Again, all untested/uncompiled. Just wanted to you give you a feeler for how it might look.
EDIT: added delay before calibration per Ttelmah's suggestion
Last edited by jeremiah on Wed Nov 01, 2017 2:28 pm; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Wed Nov 01, 2017 1:36 pm |
|
|
Jeremiah has covered it very well.
However I would pause for a short while _before_ starting the actual calibration.
PIC's wake up very quickly. They wake when the supply is still well below it's full voltage. The sensor itself will take some time to stabilise when switched on. The supply will not be up to a stable working level when the PIC actually starts. You should not do anything for perhaps 500mSec, before actually starting the calibration. There also needs to be a slight delay in the loop to allow the ADC to re-acquire.
An Arduino takes a lot longer to start than a PIC. About 8 seconds!... |
|
|
srikrishna
Joined: 06 Sep 2017 Posts: 82
|
|
Posted: Wed Nov 01, 2017 4:01 pm |
|
|
Thanks to all for responding and helping me
Code: | #include <18f2550.h>
#device adc=10
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#FUSES HS,NOWDT
#USE delay(clock=20M)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7,PARITY=N,BITS=8)
#USE TIMER(TIMER=1,TICK=1s,BITS=16,NOISR)
unsigned int16 tick_difference(unsigned int16 current,unsigned int16 previous)
{
return (current - previous);
}
double map(double value, float min, float max, float y_min, float y_max)//function definition
{
return (y_min + (((y_max - y_min)/(max - min)) * (value - min)));
}
typedef struct{
unsigned int16 min;
unsigned int16 max;
} calibration_t;
void calibrate_adc(calibration_t *calibration_data)
{
unsigned int16 current_tick;
unsigned int16 starting_tick;
unsigned int16 sensor_value;
unsigned int16 max = 0;
unsigned int16 min = 1023;
set_adc_channel(0);
delay_us(15);
starting_tick = current_tick = get_ticks();
current_tick = get_ticks();
//get as many readings as you can. the more the
//merrier
while(tick_difference(current_tick,starting_tick) < 30)
{
sensor_value = read_adc();
delay_us(15);
if(sensor_value > max){
max = sensor_value;
}
if(sensor_value < min){
min = sensor_value;
}
output_toggle(PIN_B0);
current_tick = get_ticks();
}
calibration_data->min = min; //a->b is just short for (*a).b in every way (same for functions: a->b() is short for (*a).b()).
calibration_data->max = max; //(*calibration_data).max,
}
void main(void)
{
calibration_t cal;
unsigned int16 sensor_value;
double map_result;
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_32);
delay_ms(1000); //some delay to stabilize sensor
calibrate_adc(&cal); //takes about 5 secs
//next two lines not technically needed
//since you only have one ADC and it
//was set in calibrate_adc(), but here
//for later expansion
set_adc_channel(0);
delay_us(15);
while(TRUE){
sensor_value = read_adc();
delay_us(15);
map_result = map(sensor_value,cal.min,cal.max,0,1023);
printf("%f \r\n",map_result);
delay_ms(1000);
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Thu Nov 02, 2017 1:14 am |
|
|
and is it working?. ... |
|
|
|